Commit Graph
75 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 8472fc9f40 refactor(B08,B09): 폴더째 old_code 로 옮기고 빈 화면 둘만 남김 (PLAN 7-3)
B08_Quantity 86 · B09_Estimation 111 파일을 old_code/ 로 옮김(지우지 않음).
화면은 메뉴·주소·단계 막대만 남은 빈 틀 둘 — main.py 라우터 13 개는 끊음.
B07 이 빌려 쓰던 비탈 길이·면적은 필요한 함수만 B07_DesignDetail_Engine_SlopeGeometry
로 옮겨 적음(면적 적분·노면 면적·측점 묶음은 안 옮김) · 시험 하나를 새로 둠.
B07 구조물도 조립(Cad_StandardSheet)은 2026-09-13 에 이미 도면 목록에서 빠져
부르는 곳이 없어 old_code 로 같이 보냄 — 구조물 그림은 되살리지 않음.
B06 구조물 몫 조회는 빈 값으로 두어 화면이 그대로 서게 함.
B08·B09 를 부르던 시험 25 개도 old_code/resources/tester 로 옮김.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
2026-09-22 12:27:45 +09:00
eomsangdonandClaude Sonnet 5 9c5c1d846b refactor(M01): Z01_MasterData 폴더째 old_code 로 옮기고 등록·문구 끊음 (PLAN 7-2)
라우터·라우트·문구에서 Z01 을 끊음 — M01 만 남김.
시험 여섯도 old_code/resources/tester 로 같이 옮김.
Z01 이 읽던 옛 자료 셋은 디스크에 이미 없음 · master_data/old 는 B08·B09 도 읽어 남김.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
2026-09-22 11:59:59 +09:00
eomsangdonandClaude Opus 5 e11827f8f4 feat(M01): 마스터 데이터 관리자 API 서버 쪽 — 읽기 · 시험 계산 · 저장
- M01_MasterData 라우터 + 파일 저장소 (/api/m01 · 시스템관리자 전용)
- 저장 = 판본 대조(409) → 적용 → 틀·로직 변수 검사(새 걸림 422) → 씀
- check_master.py 에 load(folder) · check_saved · calc 드러냄
- 계약 문서 장 이름 보정(공통3장)
- 시험 test_m01_api.py 8개 (임시 폴더 사본)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
2026-09-19 18:15:20 +09:00
eomsangdonandClaude Opus 5 f2fe4f4214 revert(Z01): 롤백된 마스터 DB·화면 작업이 dev 로 되들어온 것 걷어냄
- 사용자 「완전히 되돌려」 뒤 dev 를 거쳐 다시 들어온 1-2·1-3·1-4 코드 26 경로를 9629430a 모습으로(고친 17 되돌림 · 새로 생긴 9 지움)
- 대상: Z01_MasterData 17 · main.py · ui_template_locale_b3.ts · resources/tester 의 Z01 시험 7
- 품셈 md·도구는 안 건드림 · 이력 재작성 없음(되돌림 커밋)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145cr1DMC5Ft8fouQSXtp69
2026-09-19 01:36:21 +09:00
eomsangdonandClaude Opus 5 e1c71c3e8b feat(Z01): 공종 편집 저장소·API — 목록·상세·전체 줄 저장·초기값으로 (PLAN 1-3)
- GET /work-items(상태별 개수) · GET/PUT /work-items/{key} · POST …/reset 신설, 시스템관리자 전용 묶음에 등록
- 저장은 한 트랜잭션 — 낡은 revision 409 · 없는 자원 열쇠·남의 줄 id 422 · 실패 시 전부 되돌림
- 빠진 줄은 초기값 줄이면 숨김(초기값으로 때 살림) · 관리자 추가 줄이면 지움
- 검사·줄 비교·되돌리기 규칙은 WorkItems.py 순수 함수 · SQL 은 Repository_WorkItems.py
- 옛 기초단가 표의 공종 축 보기(JSON 읽기)는 그대로 둠 — BasePrices.py(sub_laptop_2 몫)가 동기로 부름
- 시험 15개(test_z01_work_item_edit.py) — DB 없이 가짜 커넥션으로 409·422·롤백 확인

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
2026-09-18 23:47:33 +09:00
eomsangdonandClaude Opus 5 05c9d1e1e6 feat(z01): main.py 에 마스터 데이터 라우터 등록(로그인 + 시스템 관리자만 · 회사 보호 없음) · 큰 표 쪽 넘기기 — 처음·끝 단추와 쪽 번호 칸(1~끝으로 조임) · 최신 응답만(늦게 온 옛 응답·옛 오류 버림)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-15 19:14:02 +09:00
eomsangdonandClaude Opus 5 399b219371 feat(z01): 줄에 없는 열은 「—」 · columns 밖 열은 안 그림 · main.py 에 시스템 관리자 전용 보호 미리 둠(라우터 import·등록 두 줄은 모듈이 올라오면 주석 풂 — 없는 모듈 import 는 서버를 못 띄움)(브레인)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-15 19:09:35 +09:00
eomsangdonandClaude Opus 5 38a5e785f2 feat(b08): STmate 엑셀 뽑기 창구 두 걸음 — [읽기]는 호표 수·구성 줄 수·못 읽은 사유만(안 씀) · [넣기]는 서버가 파일을 다시 읽어 고른 호표를 개인 단에 고정형으로(회사 없음 403 · 못 읽은 호표 404 · 모르는 종류 400) · main.py 에 창구 한 줄
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-14 15:59:11 +09:00
eomsangdon 273132f8f6 auto: 2026-09-14 10:03 (ESD_LAPTOP) 2026-09-14 10:03:21 +09:00
eomsangdonandClaude Opus 5 1edde503fe feat(b09): 기성 단계 1벌 — 금회직접공사비 × 계약잡비율 · 계약/전회/금회/누계와 각 율 · 부가세 넷
- 계약내역·계약 원가계산서(같은 엔진에 계약 직접비)를 읽기만 해 파생 — 계약 불변
- 제잡비 줄마다 계약잡비율 = 도급액 ÷ 계약 직접공사비 (구조로 읽음, 확인 대기)
- 회차 목록 저장 · 기성(%) · 잔량 · 계약 수량 넘으면 알림
- 부가세: 직접입력 / 공급가액 / 재료비 / 재료비+산출경비 (설계 vat_base 같이 씀)
- 원가계산서 조립·계약본 받기 조각을 떼어 기성이 같은 길로 받음
- 기성 탭 파일 · 사전 키(등록은 서브) · 시험 6건 · 전체 1717 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 06:03:22 +09:00
eomsangdonandClaude Opus 5 f1f8482e7d feat(b09): 실행예산 단계 1벌 — 중기 실행단가(1단위당 사용료 ÷ 시간 · 성분 비율 · 절사 · 차액 보정) · 실행수량
- 단가표 복사본의 중기(X) 호표만 실행단가로 갈아 끼워 일위대가 다시 조립 — 설계·계약 불변
- 조립값과 다른 줄은 설계 단가 그대로 + 비고에 까닭
- 「기본보정」은 뜻 미확인 — 차액을 안 몰고 남김(확인 대기)
- 계약 모듈의 다시 조립·묶음 합 조각을 두 단계가 같이 씀
- 실행예산 탭 파일 · 사전 키(등록은 서브) · 시험 7건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 05:29:05 +09:00
eomsangdonandClaude Opus 5 26097d6922 feat(b09): 계약 단계 1벌 — 노·재·경 단가 적용률 · 행별 적용 제외 · 0% 공내역 · W 코드
- STmate wM_Mk_Cont(27번 §2) 본뜸: 단가 적용율 【노】【재】【경】 · 적용 옵션 여섯(화면 표기 그대로) · 적용제외 공정
- 설계 내역(/estimation/bill) 줄을 복사해 성분마다 적용률 — 절사는 설계 내역 규칙(bill_line) 그대로 · 설계 불변
- W 코드: 동일코드 한 벌(기본) / 개별생성 켜면 줄마다 · 적용 제외 줄은 설계 단가·설계 코드
- 0%: 「공내역 생성」 켜면 0 원 공내역 줄 · 끄면 적용 안 함(판정 대기)
- 뜻 미확인 옵션(노무비율)·아직 안 선 옵션(기초단가 적용·일위대가 생성·비과세자재)은 칸만 받고 까닭을 화면에
- GET/PUT /estimation/contract · 탭 파일 B09_Estimation_UI_Tab_Contract.ts(등록은 서브) · 사전 b4
- ⚠ 계약 표본 0건 — 구조가 서는지까지만 시험

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 04:41:10 +09:00
eomsangdonandClaude Opus 5 b02c51db71 refactor(b09): 라우터 쪼개기 — 산출 조건 문(GET/PUT /estimation/factors)을 Router_Factors 로 · Router.py 842 → 537줄
- get_bill · _project_root_of · _build_for 는 Router.py 에 그대로(랩탑_메인 CostSheet import 그대로)
- 새 파일은 본 라우터의 _project_root_of·_build_for 를 부를 때마다 빌려 씀 — 시험이 본 라우터 것을 바꿔 끼움
- 검증: 시험 1666 통과 · 골든셋 초록 · 재시작 뒤 B09 문 열하나(factors·base-data·price-sources·basis-sheet·machine-expense·
  design-doc-index·bill·unit-prices·edits·cost-sheet·PUT factors) 전부 200

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-14 03:24:53 +09:00
eomsangdonandClaude Opus 5 9cf01ab15c feat(b09): 2차 편집 ① 자재단가대비표 채택 바꾸기 — 프로젝트 단위 고친 값(estimation.edits) · 서버가 얹어 다시 계산 · ↺
- B09_Estimation_Edits: 고친 값 한 벌 — 기본 조립(cached_build) 복사본에 얹음(캐시 키에 고친 값) · 기본 벌은 그대로(골든셋 무관)
  · 얹지 못한 값은 edit_skipped 로 남김 · 값 없는 슬롯은 받지 않음(422 + 까닭)
- 새 라우터 B09_Estimation_Router_Edits(GET/PUT /estimation/edits) — Router.py 는 _build_for 가 고친 값을 얹게만 바꿈
- 화면: 줄마다 채택 슬롯 고르개 · 일괄(변동없음/1~5 단가/최소단가) · 고친 줄 「사용자」 + ↺(지우면 계산값으로)
- 곁: 사용자 식 셈(B09_Estimation_Expression — ROUND·ROUNDDOWN·INT·SQRT, eval 없음)과 본표 편집 칸(UI_DetailEdit)을 먼저 둠 — 서버 편집 문이 서기 전까지 잠자 있음
- 검증: 시험 1656 통과 · ORCA 채택 저장 → 「사용자」·↺ → ↺ 뒤 고친 값 {} 로 복구 · 값 없는 슬롯 422 「경유: 1번 원천에 값이 없어」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-14 03:16:10 +09:00
eomsangdonandClaude Opus 5 cb33e81d98 feat(b09): 원가계산서(일반형식)·제비율 요율표 탭 파일 — STmate 배치 · 값은 우리 엔진
- 서버: /estimation/cost-sheet(GET·PUT) · /estimation/rate-table — 새 라우터 파일
- 직접비 = 설계내역서 합계 · 기준 입력 = estimation.cost_sheet · 폐기물처리비 = 임목폐기물 톤 × 수동 단가
- 서식 줄: 나.순공사원가 … 바.총공사비 (DFM TWM_KANJUB 차례·표기) · 경비 세목 번호 차례
- 기준 입력판: 형식(일반만) · 2.공사의종류 · 3.공사기간 · 6.관/사급 · 7.산업안전 · 8.환경보전 · 9.건설기계 — 선택지는 요율 데이터에서
- 요율표: 첫 탭 묶음 10개(보기 전용 · 수정은 잠김)
- 탭 계약 파일은 서브 314c0040 과 같은 내용(blob 동일) · 사전은 새 벌 b4
- 시험: 서식 차례·엔진 값 일치·폐기물 자리·저장값 거름·구간 표기

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 01:32:30 +09:00
eomsangdonandClaude Opus 5 f20213592c feat(b08): 구조물도 엔진·창구·화면 조각을 B07 에서 B08 로 이관
- 장 나눔·제원 입력 엔진과 기울기 판정 대상을 B08 로 옮김
- 창구를 /quantity/structure-sheets 로 옮기고 기초잡석 두께·지반 갈래를 함께 넘김
  (옛 B07 창구는 두께를 안 넘겨 원단위 탭과 값이 갈렸음)
- 구조물도 탭 조각 renderStructureSheets 신설 — 탭 등록은 브레인 몫이라 안 붙임
- B07 표준도 목록은 탭 배선 날까지 B08 엔진·창구를 불러 그대로 둠

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-13 14:48:13 +09:00
eomsangdonandClaude Opus 5 25c523b72c fix(dev): 윈도우 리로드가 영원히 멈추던 uvicorn 경로를 terminate() 로 갈아 끼움
uvicorn 0.24 BaseReload.restart() 는 윈도우에서만 옛 워커를
os.kill(worker_pid, CTRL_C_EVENT) 로 내리려 함. CTRL_C_EVENT 는 프로세스 그룹을
지정해 보낼 수 없어(MSDN: 그룹 id 가 0 이 아니면 호출은 성공하지만 신호는 전달되지
않음) 워커에 아무것도 안 감. 바로 다음 join() 이 영원히 안 풀려 옛 워커가 살아서 옛
코드로 계속 응답하고, 새 워커는 태어나지 않으며, 그 뒤 파일 변경은 감지조차 안 됨.

실측(그 자리를 흉내 낸 부모·자식 한 쌍):
  CTRL_C_EVENT   → 8초 안에 안 풀림 (DETACHED · CREATE_NO_WINDOW · +NEWGROUP 모두)
  terminate()    → 0.00초
  CTRL_BREAK     → 0.00초 (자식을 그룹 우두머리로 띄웠을 때)

고침은 uvicorn 자신의 리눅스 경로를 그대로 씀. DEBUG + win32 개발 기동에서만 갈아
끼우고, uvicorn 이 위쪽에서 고치면 _patch_windows_reload 를 지우면 됨.

⚠ 앞선 두 커밋(빌드 고리·종료 시한)은 이번 멈춤의 원인이 아니었음. 빌드 고리는 실제
문제였고, 종료 시한은 다른 경우를 막는 값이 있어 남김. [boot] 표식이 이 진단을
가능하게 했으므로 함께 남김.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GrXDD23Dvt2sR7q3X6oekp
2026-09-12 18:47:49 +09:00
eomsangdonandClaude Opus 5 57972659dd fix(dev): 종료가 멈추지 않게 시한을 두고, 리로드 멈춤 자리를 로그로 가름
- 백그라운드 태스크 셋을 cancel() 만 하고 지나가던 것을 gather 로 실제로 거둠
- 「백그라운드 작업 정리」·「DB 풀 종료」 두 걸음에 각 5초 시한. 못 끝내면 경고만
  남기고 넘어감 — 종료는 기다리는 곳이 아니라 끝내는 곳이고, 남은 정리는 OS 몫
- 끝에 「앱 종료 끝」을 찍어 종료가 끝까지 갔는지 로그로 갈림
- 모듈 맨 위에 [boot] 워커 프로세스 시작 pid 를 찍음. 리로드 뒤 이 줄이 찍히면
  새 워커는 뜬 것(포트를 못 잡은 자리), 안 찍히면 아예 시작도 못 한 것(리로더 자리).
  고칠 자리가 전혀 다른데 종전 로그로는 못 가렸음

⚠ 이번 멈춤의 원인이라는 증거는 아직 없음 — 로그에 「앱 종료 중」이 안 찍혀 종료
코드까지 가지도 못했을 수 있음. 위 [boot] 표식이 다음 멈춤 한 번으로 그것을 가름.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GrXDD23Dvt2sR7q3X6oekp
2026-09-12 18:27:07 +09:00
eomsangdon 57c1ce77c1 auto: 2026-09-12 16:57 (ESD_LAPTOP) 2026-09-12 16:57:48 +09:00
eomsangdon 3af2bc2b6b auto: 2026-09-09 11:47 (EOMSANGDON-HOME) 2026-09-09 11:47:04 +09:00
eomsangdonandClaude Opus 5 ca00ac278d feat(B07): 표준도를 「보여주기」에서 「고치기」로 — 제원 네 칸 입력
`phase: "detail"` 칸(돌 종류·조달·뒷길이·전면 기울기)을 **그리는 화면이 없었음**
(2026-09-09 실측: B06 구조물 폼은 b05 phase 열 칸만 그림. 화면 글자 전수에서 「조달」·
「뒷길이」·「돌 종류」 0건). 그 자리를 표준도가 맡음 — PLAN 4-5b 「표준도는 입력 화면이기도 함」.
B06 은 「어디에·몇 m」(배치), 표준도는 「어떤 제원」.

**장 하나 = 제원 조합 하나**라 한 번 고치면 그 조합의 개소 전부에 걸림.

지킨 것
  · **자동값을 저장에 박지 않음** — 빈 칸이 「정한 적 없음」의 뜻(확정 ⑨). 비우면 그 키를
    지움. 판정된 기울기는 칸이 아니라 **회색 도움말**로만 비춤.
  · **막지 않음** — 실무 도면에 1:0.7·1:0.8 이 실재(구조물도 53장). 품셈 범위(0.20~0.50)
    밖이어도 값은 받고 안내만.
  · **값을 여기서 셈하지 않음** — 정본에 적기만 하고 표·그림은 다음 조회에서 그 정본으로
    다시 섬. 표준도가 두 번째 정본이 되면 안 됨.

⚠ 만들다 잡은 것 셋
  ① `face_slope_ratio` 가 **구조물 등록부에 없어** 저장이 통째로 거절됐음
     (`ValueError: 돌쌓기(찰)에 정의되지 않은 옵션입니다`). 한 칸 때문에 전부 못 저장되는
     것은 나쁨 — 등록부에 없는 칸은 **빼고 이름으로 알림**(조용히 버리면 저장된 줄 앎).
     그 칸 신설은 랩탑 메인 몫.
  ② 저장 뒤 표·그림을 다시 받으면서 **폼이 새로 그려져 안내가 지워졌음.** 밖에서 들고
     있다가 다시 넣음.
  ③ **장 제목에 제원이 들어 있는데 좌측 단추 글자가 안 따라왔음.** 목록을 통째로 다시
     받지 않고 표준도 줄만 갈아 끼움.

실화면 확인 (프로젝트 936be972, 개발 우회로로 열어서)
  「표준도 2장」에서 돌 종류 견치돌·뒷길이 55 로 저장 →
    단추 글자가 「…뒷길이 35㎝ 야면석·호박돌」 → 「…뒷길이 55㎝ 견치돌」로 **즉시 바뀜**
  기울기 0.7 저장 → 안내 셋: 「1개소에 반영했습니다」 · 「1:0.7 는 품셈 표준경사 표
    범위(1:0.2~1:0.5) 밖입니다 — 값은 그대로 씁니다」 · 「「전면 기울기」 칸이 masonry_dry
    등록부에 아직 없어 저장하지 못했습니다」
  조달 「구입」 저장 → 그 장의 **2개소에만** 들어가고 다른 장은 그대로(정본 실측)
  확인 뒤 **지정받은 제원으로 되돌리고 우회로도 닫음**(목록 409 복귀).

700줄 제한으로 표준도 라우터를 `B07_DesignDetail_Router_Standard.py` 로 뗌(579 + 153).

자체검증 — 새 시험 7건 + 회귀 580 통과 · 0 실패, `tsc --noEmit` 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:43 +09:00
eomsangdonandClaude Opus 5 6e114ac990 feat(dev): 확정 없이 다음으로 — 등록 + 좌측 단추 (2/2)
앞 커밋 618b6cf4 의 신규 파일을 등록하고 화면 단추를 붙임.
두 걸음 규칙대로 파일이 먼저 push 된 뒤 등록 줄이 감.

문은 앞뒤 둘
- 서버(정본): ENVIRONMENT 가 개발이 아니면 세 입구 모두 403.
  라우터는 다른 것과 같은 보호(로그인·회사·프로젝트 접근)를 받고
  그 위에 환경을 한 번 더 봄
- 화면(보조): import.meta.env.DEV 일 때만 단추가 그려짐

눌렀을 때 바뀌는 것 — project_workflow_stages.state 한 칸뿐.
계산은 안 돌고 정본 값도 안 만듦. 실측(b269ea34):
  우회 전 [] → 우회 [3 STALE · 4 NOT_STARTED · 5 NOT_STARTED] → 되돌림 → []
  이미 COMPLETE 인 0·1·2 는 안 건드림 (진짜 확정은 보존)

되돌리기 — 푼 단계의 옛 상태를 message 에 DEV_UNLOCK:<옛상태> 로 적어 두고
[원래대로 되돌리기]가 그대로 복원. 그 표시가 없으면 진짜 확정이라 안 만짐.

드러남 — 우회 중이면 좌측에 「지금 확정을 건너뛴 상태입니다 — 값이 비어
보이는 것은 정상입니다. 건너뛴 단계 (3, 4, 5)」가 뜸.

⚠ 화면에서 잡은 것 — 단추를 패널 아래에 두니 overflow:hidden 에 잘려
  화면 밖(y=772, 패널 740)이라 눌리지 않았음. 맨 위로 옮김(상태 경고이기도 함).

시험: 677 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관) · tsc 오류 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 06:48:19 +09:00
eomsangdonandClaude Opus 5 6a487fc6e3 feat(B08): 자재총괄 — 할증이 붙는 유일한 자리
구조물 원단위의 `destination == "material"` 성분만 모아 자재별 합산 후
할증률을 한 번만 적용. 열은 순수량·할증률·합계 + 관급구분·설치주체·비고이며
금액은 없음(B09 경계).

- 할증률은 코드가 아니라 데이터 — `resources/data_material_surcharge/`
  (품셈 1-3-1 재료 할증률 19종 + sha256 매니페스트). 실무 관측값은
  `observed_practice` 로 분리(법대로 원칙).
- 표에 없는 자재는 0 % 로 넘기지 않고 「할증률 미확보」로 표시.
  이름 조회는 정확 일치 — 부분일치면 `막자갈` 이 `자갈` 할증을 뭄.
- 이중계상 방어 ㉠ — 앞 단계 `surcharge_applied` 깃발을 실제로 읽어 경고.
  자재총괄 응답은 `True`, 원단위표는 `False` 로 어느 쪽 값인지 명시.
- 관급/사급 이름은 B09 와 동일(`owner_supplied`/`contractor_supplied`).
  관급 줄에만 설치 주체(`install_by`)를 붙이고, 미지정은 기본값으로
  때우지 않고 드러냄 — 안전관리비 대상액이 「도급자설치 관급금액」이라서임.
- 라우터 `GET /quantity/material-summary` 신설, 화면에 「구조물 원단위」·
  「자재총괄」 탭 추가. `design_owner` 가 붙은 타입(측구)은 중복 계상 방지로 제외.

검증 — 전용 테스트 23건 통과, 전체 회귀 473 passed(기존 B05 깨짐 1건 제외).
공용 브라우저 실조작으로 탭 5장·머리글·값 4줄·미확보 안내 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 23:43:46 +09:00
eomsangdonandClaude Opus 5 9575ce230f merge: sub_desktop_1 되받기 — main.py 라우터 등록 충돌 해소
B08 토적표 라우터와 B09 원가계산 라우터가 main.py 의 같은 자리(import 블록·
include_router 목록)를 각자 한 줄씩 늘려 충돌함. 둘 다 필요한 줄이라 양쪽을
그대로 살림 — 어느 쪽도 버리지 않음.

확인 — 충돌 표시 0건, 앱 import 성공, 두 라우터 경로가 모두 등록됨
(quantity 3건 · estimation 3건).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:19:09 +09:00
eomsangdonandClaude Opus 5 b48ab7047c feat(B08): 토적표 엔진·API — 평균단면적법 체적화
PLAN 8-4b 열 명세대로. B06 이 이미 낸 측점별 단면적을 다시 재지 않고
체적화만 함 — 새 수량을 낳지 않으므로 캐시·조작 경로 불필요(CLAUDE.md 5장).

열 구성 (실무 토적표 = 오솔길 1.BOM 36열과 1:1)
  측점·거리·절토[토사·암 각 단면적/입적/보정량]·측구터파기[토사·암 각 3칸]
  ·보정량계·성토[단면적/입적]·유용토·차인토량·누가토량.
  사면 계열(층따기·면고르기·법면보호공·지장목제거)은 사면길이가 아직 없어 일감 3.

계산 규칙
  체적 = (앞 단면적 + 현 단면적)/2 x 거리. 첫 측점은 앞이 없어 체적 없음.
  보정량 = 체적 x 다짐 환산계수 — 정의처는 EARTHWORK_CONVERSION_FACTORS 한 곳,
  여기서 값을 다시 적지 않음(토사 0.90 / 리핑암 1.15 / 발파암 1.30).
  값을 자르지 않음 — 품셈 1-2-2 는 표기 규칙이고 절사는 화면 몫(PLAN 8-16).
  원가 쪽(줄마다 원 단위 절사)과 규칙이 반대라 섞지 말 것.

TODO(미결 · PLAN 8-4b) — 설계가 측구를 토사·암으로 안 나눠 줌(ditch_area_m2 한 값).
  잠정으로 그 측점 절토 토사:암 면적비로 안분함. 측구는 절토부에 파므로 같은
  지반을 만난다는 것이 근거. 설계가 측구 지반을 따로 내면 _split_ditch 만 교체.

API — GET /api/projects/{id}/quantity/{route_id}/earthwork-table,
  경로 생략형은 워크플로 최신 노선으로. main.py 는 자기 두 줄만 추가.

검증 — tmp/tests/test_b08_earthwork_table.py 14건 통과.
  거창 실무 BOM 실측값 재현(단면적 1.89 → 체적 9.45 → 보정 8.505,
  다음 측점 18.10 → 16.29, 측구 0.18㎡ → 10m당 1.80 → 1.62).
  공용 브라우저에서 실 API 호출로 route 150·측점 65곳 확인 — 측점 20 에서
  (0.2723+1.9615)/2x20 = 22.338, x0.9 = 20.1042, 암 25.427 x1.15 = 29.24105,
  측구 안분 0.0784+0.1016 = 0.18 로 전건 일치.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:11:53 +09:00
eomsangdonandClaude Opus 5 2a21f99ba4 feat(B09): 원가계산 라우터·화면 신설, 페이지명 「원가계산」으로 변경
- `B09_Estimation_Router.py` 신설 — 무상태 계산 엔드포인트 3종.
  · `POST …/estimation/cost` 원가계산서 한 장(줄마다 비목·금액·요율·산출근거)
  · `GET …/estimation/items` 비목 정의 목록
  · `POST …/estimation/confirm` stage 6 완료 전이
  요율 구간을 못 고르면 500 이 아니라 **422 + 사유**로 알림 (기본값으로 안 때움).
  `target_contract_amount_krw` 를 주면 필요한 이윤 조정액을 **보여만 줌**(★법대로 8-10).
- `B09_Estimation_UI_Page.ts` — 셸에서 실제 화면으로. 3단 레이아웃, 좌측 입력 4군
  (공사조건·요율 판 읽기전용·관급자재·이윤 조정) + 우측 탭 8장(원가계산서 활성).
  원가계산서 표는 **네 칸 + 비고**이고 **안전관리비 A·B 두 줄이 나란히**, 채택 줄은
  강조·미채택 줄은 취소선.
- `main.py` — B09 라우터 import·등록 **자기 줄만** 추가.
- `ui_template_locale_b2.ts` — B09 키 추가, `B09_Estimation_Title` 을
  「설계도서」→**「원가계산」**으로 변경 (PLAN 9-1 사용자 승인).
- 자체검증: `tsc --noEmit` 통과 · 백엔드 재시작 후 `openapi.json` 에 3개 경로 등록 확인 ·
  `/api/health` 200. 화면 조작 검증은 다음 단계(브라우저 창이 닫혀 재기동 필요).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 20:11:15 +09:00
eomsangdonandClaude Opus 5 df02793caf feat(dev): 지금 도는 서버가 옛 코드인지 헬스체크가 알림
자동 리로드가 조용히 죽어 **옛 코드로 도는 서버**에 오늘 두 창이 세 번 속았음
(속도 488초 오측 · 응답에 새 필드가 없는데 화면은 정상으로 보임 · 표준단면 응답이 옛 스키마).
매번 틀린 결론을 한 번씩 냈다가 되물렀음. 규칙("재기 전에 재시작")으로는 세 번 다 안 지켜져
**한 번 보면 아는 표시**로 바꿈.

- `/api/health` 가 개발 모드에서 `started_at` · `code_mtime`(기동 때 본 소스 최신 수정) ·
  `source_mtime`(지금 소스 최신 수정) · `stale` 을 함께 냄. `stale: true` 면 재시작 전
  어떤 측정도 믿으면 안 됨.
- 기동 로그에 감시 폴더 수와 소스 최신 수정 시각을 한 줄 남김.
- 실측 — 감시 폴더의 `.py` 를 고치니 리로드는 **안 뜨고** `stale` 이 즉시 true 로 바뀜.

원인 추적은 계속함(PLAN 7-2) — 한글 경로·정션은 **아님**(`watchfiles` 를 그 폴더에
직접 걸어 보니 변경을 정상 감지). uvicorn 이 감시 폴더 12개를 선언하고도 리로드를
안 내는 자리라 더 파야 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:57:33 +09:00
eomsangdonandClaude Opus 5 70b25a5c22 fix(main): 리로드 감시가 정션을 따라 들어가 되감기던 것
보조 창이 원인을 확정함 —
  B07_DesignDetail\openwebcad\node_modules\forest-road-webapp
     → Junction → 저장소 루트 자신 (npm 이 만든 로컬 패키지 링크)
리로더가 B07 을 훑다가 그 정션으로 루트로 되돌아가고, 거기서 venv·storage 로
다시 들어가며 무한히 되감겼음.

실측 — 감시 폴더를 rglob 하면 파일 486,809개 · 한 바퀴 1,045초.
PowerShell 로 세면 config 4개·B07 23개뿐임. 파이썬 rglob 은 정션을 따라
들어가고 PowerShell 은 안 따라가서 눈으로는 안 보였음.

링크(정션) 폴더를 통째로 건너뛰게 함 — 정션이 또 생겨도 재발 안 함.
앞 커밋(e7d30327)이 node_modules 품은 폴더를 빼며 이번 건은 이미 덮었으나
(CPU 85% → 18%), 그건 우연히 막힌 것이라 원인 자체를 막음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:43:48 +09:00
eomsangdonandClaude Opus 5 e7d3032727 perf(main): 한 겹 아래 숨은 node_modules 도 리로드 감시에서 뺌
앞 커밋이 바로 밑의 node_modules 만 봐서 새는 곳이 있었음 —
`B07_DesignDetail/openwebcad/node_modules`(15,944개)와
`A00_Common/config/node_modules`. 그 폴더들이 0.25초마다 통째로 훑히고 있었음.

한 겹 아래(`*/node_modules`)까지 보고 제외.

실측 (놀고 있는 서버, 같은 기기·같은 절차) — **CPU 85.2% → 18.3%**.
감시 대상은 파이썬 143개 · 하위폴더 39개로 줄었음.

⚠ 원인 규명은 보조 창 몫이 컸음 — 프로세스 역할이 뒤바뀌어 보였음(내가
띄운 것은 껍데기, 그 자식이 리로더, 손자가 실제 서버). 부모만 보고 재면
0% 로 보여 「앱이 먹는다」로 두 번 오진했음. 갈래 실험(reload 끔 0% /
켬 64%)이 그것을 갈랐음.

남은 18% 는 감시 탓으로 보기 어려움(파일 143개) — 별건으로 남김.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:35:53 +09:00
eomsangdonandClaude Opus 5 7a1d7db68f perf(main): 리로드 감시에서 node_modules 를 품은 폴더도 뺌
앞 커밋(351db312)이 storage·venv 는 뺐으나 `config` 가 남아 있었음.
그 안에 `node_modules` 가 있어 0.25초마다 의존성 트리를 훑고 있었음.

`node_modules` 를 품은 폴더는 감시에서 제외. 그 결과 `config/` 를 고칠 때는
`main.py` 와 마찬가지로 서버를 손으로 다시 띄워야 함 — 둘 다 자주 고치는
자리가 아님.

⚠ 측정 메모 — 루트 전체 훑기는 이 기기에서 **300초에도 한 줄을 못 뱉었음**
(옛 방식이 0.25초 주기를 지킬 수 없었다는 뜻). 다만 지금 기기가 계속
눌려 있어(서버 둘이 놀면서도 각 70%) 정확한 전후 수치는 못 냈음.
그 CPU 자체는 별건으로 계획서에 남겨 둠.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:12:15 +09:00
eomsangdonandClaude Opus 5 351db312ee perf(main): 자동 리로드가 훑는 폴더를 소스만으로 좁힘
uvicorn StatReload 는 0.25초마다 지정 폴더를 rglob("*.py") 로 재귀로 훑는데,
폴더를 안 주면 현재 폴더가 대상이라 storage(27GB) · venv(파이썬 파일만
7,530개) · node_modules 까지 통째로 돌고 있었음.

파이썬이 실제로 사는 폴더 14개만 감시하게 함(산출물·의존성·백업 제외).
루트는 넣지 않음 — 넣으면 재귀라 도로 다 훑음. 그 대신 main.py 를 고칠 때는
서버를 손으로 다시 띄워야 함.

⚠ 이 변경이 「요청 없는 서버가 CPU 70%」를 고치지는 못했음. 그 CPU 는
감시 프로세스(0%)가 아니라 **앱 프로세스** 쪽이며 원인 미상 — 계획서에
남겼음. 이 커밋은 감시 낭비만 걷어낸 것임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 06:30:37 +09:00
eomsangdonandClaude Opus 5 c0bfcba34b feat(B05,B06): 유토 배분을 서버로 옮기고 브라우저는 미리 받아 쓰게 함
배분·운반거리 산식(_balance 523 + _settle 235 = 758줄)을 브라우저 번들에서 빼기 위한
배선. 계산은 여전히 한 벌 — 서버가 같은 TS 를 Node 로 돈다(CLAUDE.md 5장).

- POST /projects/{id}/sections/{route_id}/haul-plan 신설
  (B06_Section_Router_HaulPlan.py). 브라우저가 낸 누가토량 결과를 받아 배분만 돌려줌.
  화면이 쓰는 꼴 그대로 내보내 그리기 코드를 안 건드림. 표시 전용 — 정본은 저장 때 따로.
- B05_Profile_Api_HaulPlan.ts 신설 — 편집이 멈추면(400ms) 조용히 받아 두는 선반입기.
  늦게 온 응답은 버림(최신 요청만 채택). 못 받아도 곡선 자체는 그대로 보임.
- common_util_mass_haul.massHaulPayload 가 배분을 값으로 물던 것을 끊음(extra 인자).
  이 커플링 때문에 화면에서 안 불러도 번들에 남았음.
- 저장 경로는 곡선도 배분도 안 만듦 — 서버가 정본을 내므로 balloon 위치만 보냄.
- 죽은 파일 B06_Section_UI_Section_View_MassHaul.ts(236줄) 삭제.

자체검증(공용 브라우저, 용화 route 149) — 엔드포인트 200 · 196ms.
서버 배분과 브라우저 computeHaulPlan 결과가 JSON 문자열까지 동일(blocks 1 · steps 2 ·
spoil/borrow/hauled/transferred/fill_total 전부 일치). 시험 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:47:22 +09:00
eomsangdonandClaude Opus 5 9a71f7bbaa feat(security): 호출량 감시 — 상한 넘은 세션만 시스템 로그에 표시
계산 결과는 화면에 나가도 된다는 방침이라, 남는 위험은 입력을 바꿔가며 출력을
긁어 모으는 것임. 사람이 화면을 쓰는 속도에는 한계가 있음 — 화면 한 번 여는 데
API 가 약 180회 나가므로 한 시간에 2만 회를 넘으면 사람이 아니라고 봄.

- 막지 않음. 고객 화면을 끊을 위험이 있고 어디서 끊을지는 실제 사용 기록을 본 뒤
  정할 일임. 지금은 시스템 로그(보관 1년)에 RATE_ANOMALY 한 줄만 남김.
- 세는 값은 메모리(세션·시간대별)라 요청당 DB 비용 0. 상한을 넘은 그 순간에만
  세션 주인을 한 번 조회해 기록하고, 같은 시간대에는 다시 안 남김.
- 지난 시간대 기록은 버려 사전이 무한정 자라지 않게 함.
- 상한은 API_CALL_HOURLY_LIMIT(기본 20000, .env 로 조정).

빌드 껍데기는 이미 단단해 손대지 않음 — 실측 확인: 배포 산출물에 소스맵 0개,
식별자 뭉개짐(import{$ as e,C as t,...}), computeCrossDesign 등 원본 함수명
검색 결과 0건. 난독화 도구 추가는 얻는 것 대비 비용이 큼.

시험 tmp/tests/test_call_volume_watch.py 5건. 전체 484 통과.
실서버 확인 — 정상 사용 흐름에서 감시 로그 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:45:12 +09:00
eomsangdonandClaude Opus 5 d0ba8ed812 fix(main): 응답 압축 강도를 1로 — 기본 9가 요청을 9.4초로 늘림
등고선 도엽 36.8MB 를 매 요청 level 9 로 눌러 9.4초가 걸렸음(실측).
level 1 은 줄어드는 양이 거의 같고(66MB 기준 24.0 vs 23.9MB) 시간만 1/3.

실측(8001, IPv4) — 등고선 도엽 739ms · 전송 11.3MB(전 65.9MB 무압축),
종횡단 상세 485ms · 전송 0.33MB(전 1.56MB), 배수유역 전송 22KB(전 151KB).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:24:21 +09:00
eomsangdonandClaude Opus 5 da2130c988 perf(B04): 응답 압축 + 좌표 자릿수 정리로 내려보낼 양 축소
DB·파일에는 뜻 없는 자릿수가 붙어 있고(1.4000000000000001 꼴) 서버는 응답을
전혀 압축하지 않고 있었음. B05 한 번 진입에 118MB 수신(2026-09-06 실측).

- main.py: 압축 미들웨어(1KB 이상). 3D 예상형상(/corridor)은 예외 —
  숫자 배열이라 절반만 줄면서 압축에 493ms 듦(18.6->9.4MB).
- common_util_json: round_floats(value, digits) + LONLAT_DIGITS 7(1.1cm) ·
  METRE_DIGITS 6. 저장 파일은 그대로 두고 내려보낼 때만 줄임.
  브라우저가 다시 계산에 넣는 값(종횡단 지반선)은 줄이면 안 된다고 머리에 경고 적음.
- 도엽 GeoJSON: 자릿수만 줄인 표시용 사본(.display.geojson)을 한 번 만들어 서빙.
  원본이 새로 깔리면 자동 재생성, 실패하면 원본 그대로.
  등고선 도엽 65.9->36.8MB, 압축까지 11.3MB.
- 배수유역 응답: 위경도 7자리. 151->100KB, 압축 22KB.

ruff 통과, 테스트 390 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:13:05 +09:00
eomsangdonandClaude Opus 5 8e6a1e27eb feat(B05): 계획노선 두 벌 + 노선 갈아 끼우기·초기화 서버 경로
노선을 예상노선(원본)·계획노선(수정본) 두 벌로 나눔 (PLAN 0-7 사용자 확정).

- 예상노선 정본 `B05_Profile/route/expected_route.csv` 신설 — 자동 체인이 한 번
  씀. 초기값 스냅샷 안에도 같은 CSV 가 있으나 그 폴더는 재확정 체인이 지우므로
  스냅샷 밖에 한 벌 둠.
- 계획노선 수정본 `B05_Profile/route/planned_route.csv` — 설계 계통(`load_design_route`)이
  수정본 → 예상노선 → 스냅샷 순으로 읽음. 노선 초기화는 수정본을 지우는 것.
- `GET /route/plan` 두 노선 정점 반환(모달이 점선·실선으로 그림).
- `POST /route/replan` 고친 노선을 수정본에 쓰고(조밀화) 재확정 체인 재사용 —
  배수유역 다시 → 관 새로 → 계획선·종횡단 재생성 → 옛 측점 설계 누가거리 이월.
- `POST /route/replan/reset` 수정본 삭제 후 같은 재계산.
- 횡단배수 지점은 노선이 바뀌면 저장분을 버리고 새로 계산(사용자 확정).
  구조물은 프로젝트 정본이라 옛 측점값 그대로 남음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:00:34 +09:00
eomsangdonandClaude Opus 5 d0d3f2a311 feat(B07): 자리표 마우스 크기조절·기본 도각 칸 정렬·도면명 미리보기
- 칸이 있는 글자와 그림에 모서리 그립 추가, 모서리를 끌어 칸 크기를 바꾸는 조작 신설 (다른 도면 요소에도 동일 적용)
- 기본 도각(00_template_A1)의 글자 24개에 표제란 칸을 계산해 부여 — 칸 기준 가로·세로 가운데 정렬
- 도각 편집 시 도면명·도면번호도 보던 도면 값으로 미리보기 제공
- 복제 시 미리보기 값 유지 (그립 편집 후 자리표가 토큰으로 되돌아가던 문제)
- CAD index.html 을 캐시하지 않도록 처리 — 빌드 후에도 옛 화면이 남던 문제 해소

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:52:36 +09:00
eomsangdonandClaude Opus 5 2b6e76cd1c feat(B07): 도면 DXF/DWG 내보내기 추가
- 내보내기 엔진 신설 — 캐드 도면(JSON)을 ezdxf 로 DXF 작성(선·폴리선·글자·점·원·호, 도면층 유지), DWG 는 LibreDWG dxf2dwg 를 별도 프로세스로 호출
- POST /{project_id}/drawing-export 신설, 한글 파일명은 RFC 5987 방식으로 전달, 담지 못한 그림 수는 X-Aislo-Skipped 헤더로 통지
- CAD 출력 리본에 「DXF 내보내기」·「DWG 내보내기」 추가 — 캐드가 부모에 요청하면 부모가 서버 파일을 받아 내려받기
- 도각·내보내기 엔드포인트를 B07_DesignDetail_Router_Frame 으로 분리 (700줄 제한)
- 그림(Image)은 DXF 외부 참조 방식이라 제외하고 개수를 안내

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 14:38:55 +09:00
eomsangdonandClaude Opus 5 5502f95040 feat(B01): 시스템 회사 자동 권한·회사관리자 권한 확대·재신청 방어
- 시스템 관리 회사(.env 관리자 계정 소속 회사) 소속은 전원 시스템관리자 — 서버 시작 시 일괄 반영, 가입 승인·팀원 등록 시에도 자동 부여
- 이미 소속이 있는 사용자의 회사 가입 재신청 차단 (기존: 신청 즉시 소속 해제·승인대기 전락)
- 회사관리자에게 자기 회사 역할 변경·프로젝트 삭제 허용, 마지막 관리자 강등 차단
- 역할 변경 시 is_master 동반 조정 (권한 판정 불일치 제거)
- 일반사용자도 소속 회사 프로젝트 전체 열람 (수정 권한은 별개)
- 대시보드 시스템 로그 섹션 최하단 이동
- PLAN.md 에 B01 정비 계획 반영

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:16:25 +09:00
eomsangdon 3a161aa18d refactor(B05): 경로 확정·초기화 엔드포인트 분리 (722→491줄)
- `B05_Profile_Router_Lifecycle.py`(256줄) 신설 — `/route/confirm` · `/route/reset`
  두 엔드포인트를 자체 APIRouter 로 옮김. URL·응답 스키마 불변.
- `main.py` 가 새 라우터를 함께 등록 (`b05_route_lifecycle_router`).
- 자동설계 체인이 쓰던 `B05_Profile_Router.confirm_latest_route` 경로는 재수출로 유지.
- 검증: 라우트 8개 그대로(openapi.json 실측), 백엔드 재기동 후 200, pytest 359 passed.
2026-09-02 16:20:17 +09:00
eomsangdonandClaude Opus 5 f82e60492e fix(security): 프로젝트 경로가 붙는 API가 그 프로젝트의 회사를 대조하게 한다
B03~B07 라우터는 로그인·회사 소속만 확인하고 URL의 project_id가 누구 것인지는 보지
않았다. 프로젝트 id만 알면 남의 회사 자료를 읽고 쓸 수 있었고, B07 도각 저장(PUT)은
그 회사의 공용 양식을 덮어쓴다.

require_project_access를 protected_with_company에 함께 걸어 경로에 project_id가 있는
요청만 한 곳에서 검사한다. 다른 회사면 403, 없는 프로젝트면 404, 시스템 관리자는 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 16:11:24 +09:00
eomsangdonandClaude Fable 5 6e195afb69 feat(B07,B08): 워크플로 순서 교환 — 상세설계를 수량산출 앞으로
횡단설계(B06) 다음을 상세설계 → 수량산출 → 설계도서 순으로 재배열하고,
폴더 번호가 흐름과 일치하도록 이름을 맞바꾼다.

- B08_DesignDetail → B07_DesignDetail, B07_Quantity → B08_Quantity
  (파일 접두어·식별자·라우트·locale 키 전량 스왑)
- STAGE_KEYS 4=DESIGN_DETAIL, 5=QUANTITY 스왑 + 라우터 stage 리터럴 교체
- CAD 마운트 /b08-cad → /b07-cad (main.py·vite proxy·iframe URL),
  openwebcad Toolbar 라벨 B07로 수정 후 재빌드
- 유지: openwebcad postMessage 프로토콜 aislo:b08:*·패키지명(내부 식별자)
- 기존 프로젝트 storage 폴더 rename + project_manifest 갱신,
  DB project_workflow_stages stage_no 4↔5 행 스왑 완료

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 16:16:46 +09:00
eomsangdonandClaude Opus 5 3b997963e3 feat(B05): 3D 예상형상 — 계획노선 코리도 서피스·원지반 경계 재절단·영구저장
- Corridor_Build: design_line을 차도/노견/측구/절토/성토 5종으로 분류,
  catch point(설계선-지반 교차)로 점유 폭 산출, 노선 폴리라인 2m 세분
  중간 프레임 보간 (계산식 재구현 없이 백엔드 design_line 소비만)
- Corridor_Mesh: 종류별 색 리본 BufferGeometry 조합(멀티 스윕)
- Corridor_Clip: catch line 스트립으로 지형 삼각형 경계 재절단(셀 단위
  판정·정점색 보간), 원본 보존 — [예상형상] 토글 OFF 시 원지반 완전체 복원
- Viewer: 원본/클리핑 지형 두 벌 스왑, 비탈 최외곽 z 지형 투영(심 보정),
  클립 rAF 비동기, 흑백·dispose 연동
- Panel: [예상형상] 토글 (기본 ON)
- Corridor 엔트리: 설계 입력 FNV-1a 해시 버전, base64 Float32 JSON 직렬화,
  최초 빌드 즉시 저장 + 임시저장·페이지 이동 시 dirty 저장, SPA 메모리 캐시
- Router_Corridor(신규)+main 등록: GET/PUT corridor 파일 보관
  (B05_Profile/corridor/, atomic, 64MB 상한)
- 검증: tsc·ruff·pytest 5/5, 공용 브라우저 실조작(토글·B06 변경 왕복·
  저장 왕복 네트워크 실측), 저장본 리본 수치 대조

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 12:28:37 +09:00
eomsangdonandClaude Fable 5 9bc13b0bc8 fix(main): 포트 정리 netstat 인코딩 — 한글 Windows에서 UnicodeDecodeError로 죽던 문제
`_free_dev_port`의 netstat 호출이 text=True만 주고 encoding을 지정하지 않아
기본 utf-8로 읽었다. 한글 Windows의 netstat는 콘솔 코드페이지(cp949)로 찍으므로
한글 헤더의 0xbc 바이트에서 디코딩이 터졌고, 그 예외가 subprocess 읽기 스레드에서
발생해 try/except로도 잡히지 않았다.

시스템 기본 인코딩으로 읽고 깨진 바이트는 대체 문자로 넘긴다 — 이 함수가 보는 값은
포트 번호와 PID뿐이라 한글 헤더는 읽지 못해도 무방하다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 23:05:56 +09:00
eomsangdonandClaude Fable 5 50e5626a95 feat(B05): 구조물 타입 레지스트리·정본 CRUD 백엔드 추가
임도 구조물 전체(구조물군 B~G)를 수동 배치할 수 있도록 백엔드 기반을 만든다.
타입 정의를 코드가 아닌 데이터(JSON 레지스트리)로 두어, 구조물 목록이 바뀌어도
코드를 고치지 않고 화면 폼까지 따라오게 했다.

- B05_Profile_Structure_Types.json: 타입 레지스트리 정본 (B~G 32종 + 호환 4종).
  배치형태(point/interval/site)·옵션 스키마·마크 스타일·drawing_views 메타 포함.
- B05_Profile_Structures_Schema.py: 타입·인스턴스 Pydantic 모델.
  배치형태별 위치 필드 배타 검증, side·offset_m·placement_source·status·revision.
- B05_Profile_Structures_Repository.py: structures.json 단일 정본 읽기·쓰기.
  atomic_write_json 재사용, base_revision 불일치 시 충돌 예외,
  설계 영향 변경만 후속 단계를 무효화하도록 판정하는 지문 비교.
- B05_Profile_Structures_Migration.py: 기존 비정규 측점 이관.
  배관은 pipe_points.json 정본이라 제외, 기성막이→기슭막이, 대피로→대피소 통합,
  나머지는 이름을 살려 기타. 같은 입력이면 같은 결과.
- B05_Profile_Structures_Router.py: GET /structure-types, 구조물 목록 조회·저장.
  판번호 충돌 409, 미등록·타 정본 관리 타입 400, 저장 후 B06 이후 STALE 전파.

테스트 45건(tmp/tests) 통과. 기존 배관·배수유역·종단 편집 코드는 건드리지 않았다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 22:19:00 +09:00
eomsangdonandClaude Fable 5 17834d8189 feat(B01,B03): 프로젝트 생성 전 임시 보관함 (temp upload)
라이다 원본은 업로드에 오래 걸려 프로젝트 정보 확정 전에 미리 올릴 수 있어야 한다.
계정에 묶인 임시 보관함을 만들고, 나중에 만든 프로젝트로 자료를 옮겨 쓴다.

저장·DB
- storage/tmp/{user_id}/{batch_id}/ 아래에 프로젝트 저장소와 동일한 구조를 써서
  청크 저장·병합 엔진(resolve_upload_destination/merge_upload_chunks)을 그대로 재사용
- 010_temp_upload.sql: temp_upload_batches / temp_upload_files 신설,
  upload_sessions.project_id NULL 허용 + temp_batch_id 추가(FK명 조회 후 재생성)
- config: TEMP_UPLOAD_DIR_NAME / TEMP_UPLOAD_RETENTION_DAYS(30) /
  TEMP_UPLOAD_CLEANUP_INTERVAL_HOURS(6)

백엔드
- B03_FileInput_Router_Temp.py: 묶음 생성·목록·삭제, 일반/청크 업로드, finalize,
  이어올리기 상태 조회, 프로젝트 연결(attach)
- attach: 파일 이동 후 input_files 등록, stage 0 완료, WF1·자동 설계 체인 트리거
- common_util_temp_cleanup.py: 완료 시각 기준 만료분 주기 삭제(서버 시작 시 1회 포함)

프론트엔드
- B01 대시보드 임시 보관함 섹션: 프로젝트 등록과 같은 폼 + 보관 목록.
  진행률은 모달이 아니라 리스트 행에 표시, 새로고침 후 이어올리기 지원
- B03 업로드 컨테이너 내부 불러오기 버튼과 선택 모달.
  완료된 묶음만 노출하고, 선택 후 업로드를 누르면 이동과 분석으로 이어짐

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 12:55:14 +09:00
eomsangdonandClaude Fable 5 ec9943417c feat(B07,B09): B07_Quantity 셸 페이지 신설 + B09_Estimation 접두사 정리 + 단계 순서 반영
- B09_wf6_Estimation -> B09_Estimation (폴더·파일·라우트 키/슬러그)
- B07_Quantity_UI_Page.ts 신설: 워크플로우 셸 + 좌측 [확정] 버튼
  (renderPendingWorkflow에 leftPanel 옵션 추가로 공용 셸 재사용)
- B07_Quantity_Router.py 신설: POST /api/projects/{id}/quantity/confirm
  -> complete_stage(4) 전이만 수행(본문 미구현), main.py 등록
- STAGE_KEYS 재정의: FILE_INPUT/PREPROCESS/PROFILE/SECTION/QUANTITY/DESIGN_DETAIL/ESTIMATION
- 스텝바 라벨·아이콘 4↔5 스왑(수량산출이 4차, 상세설계가 5차), A02 소개 문구 스왑
- 라우터 테이블에 b07-quantity 등록(플레이스홀더 제거), locale 키 5종 추가

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 10:15:07 +09:00
eomsangdonandClaude Fable 5 2a248ff742 refactor(B07,B08): 도면 코드 B08_DesignDetail 이동 + 수량 슬롯 B07_Quantity 확보
- B07_wf4_DesignDetail(8파일+openwebcad) -> B08_DesignDetail로 git mv (이력 보존)
- B08_wf5_Quantity -> B07_Quantity (구 수량 백업 zip 보관 폴더)
- 파생 문자열 일괄 전환: /b07-cad -> /b08-cad 정적 서빙, CAD 레이어 b07-* -> b08-*,
  postMessage aislo:b07:* -> aislo:b08:*, 패키지명 aislo-b08-cad, CSS .b08-*,
  라우트 키/슬러그(B08_DESIGN_DETAIL/b08-design-detail, B07_QUANTITY/b07-quantity)
- 상세설계 워크플로우 stage 4 -> 5 (확정/무효화 전이 3곳), 확정 완료 시 B09로 이동
- WORKFLOW_STEP_ROUTES 순서 재배열: index4=수량(B07), index5=상세설계(B08)
- [임시] B08 이동 테스트 버튼 제거, openwebcad dist 재빌드
- 주석 의미 정렬: 수량 인계 주석 B08->B07, 도면 참조 B07->B08

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 10:10:10 +09:00
eomsangdonandClaude Fable 5 54954a05e5 refactor(B05,B06): B05_wf2_Route -> B05_Profile, B06_wf3_ProfileCross -> B06_Section 동시 개명
- 한몸으로 동작하는 두 페이지라 한 커밋으로 처리 (상호 참조 다수)
- B05 37파일 + B06 20파일 접두사 개명 (git mv, 이력 보존)
- 참조 치환 91파일: import 경로, 라우트 슬러그(b05-profile/b06-section),
  라우트 키(B05_PROFILE/B06_SECTION), B03 자동 체인, storage 상수, pyproject 제외 경로
- 로직 변경 없음. typecheck·백엔드 import 검증 통과

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 10:03:11 +09:00
eomsangdonandClaude Fable 5 f7528a4aa4 refactor(B04): B04_wf1_Surface -> B04_PreProcess 전면 개명
- 폴더·내부 파일 51개 접두사 개명 (git mv, 이력 보존)
- 저장소 전체 참조 치환 67파일: import 경로, 라우트 슬러그(b04-preprocess),
  라우트 키(B04_PREPROCESS), storage 경로 상수, locale, SQL 주석
- 로직 변경 없음 (기계적 치환). typecheck·백엔드 import 검증 통과

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 10:01:36 +09:00