**㉣ 작업효율을 두 곳에 넣지 않는다 — 계획서에 없던 네 번째 규칙**
- 품셈은 작업효율 `E` 를 **작업량 산정**에 넣음(`Q = n·q·f·E`, 8-1-4). 시간당 사용료는
**1일 8시간**으로 나눔(8-1-6 「관리비는 1일 8시간 초과해도 8시간으로 계산」).
**양쪽에 넣으면 같은 효율이 두 번 곱해짐.**
- 실무 관측이 효율을 사용료 쪽에 미리 넣어 두어(굴착기 0.7㎥ 노무 55,700/hr =
일당 ÷ 5.09h), 그 값을 보고 나눗수를 고치는 사고가 정확히 이 자리에서 남.
- `reject_efficiency_in_hourly_rate()` · `check_operator_hours_basis()` 추가하고
**`hourly_machine_cost()` 안에서 실제로 호출** — 함수만 있고 안 부르면 없는 것과 같음.
- PLAN 9-6 의 금지 규칙을 셋 → **넷**으로 늘리고 근거·경위 기록.
기계 사용료 11 % 차이는 **「설명된 차이」**임을 표로 못 박음(맞추려 손대지 말 것).
**`B09_Estimation_UnitPrice.py` 신설 — 층을 실제로 쌓음**
- `S 취득가 · L 노임 · M 연료 → X 시간당 사용료 → B 일위대가`.
**시간당 사용료를 미리 계산해 넣지 않음** — 쌓아야 화면이 「무엇으로 이루어졌나」를
보일 수 있음(8-13 계산 과정을 감추지 않음).
- 조종원 수량 = `인/일 ÷ 8` 로 상세에 들어가 ㉣이 데이터로도 드러남.
- 자원이 하나도 안 붙은 공종은 **빈 줄로 안 세움** — 0 원 일위대가가 내역에 서면 안 됨.
- ㉠ 가드를 `verify_surcharge_once()` 로 실제 호출 — 일위대가 재료비는 **할증 전** 값.
산출 — 제목 **189**(일위대가 64 · 기계 층 3 · 노임·연료), 건너뛴 공종 0.
실측 `X-0201-0070` 굴착기 0.7㎥ = 재 26,130 + 노 35,412 + 경 24,554.
자체검증 — 신규 5건 포함 `pytest tmp/tests/ -q` **116 passed** · ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
**빈 두 성분을 채움** — `mach_fuel_rate`·`mach_operator_map` 0건이라 시간당 중기사용료의
재료비·노무비가 비어 있던 자리(손료만 있으면 실제의 4분의 1). 건설품셈 **8-4 운전경비
산정** 표에서 뽑아 **92 기종** 확보.
⚠ **표가 열 단위로 뭉쳐 옴** — PDF 추출 탓에 `분류번호` 한 칸에 코드 31개,
`주연료` 한 칸에 값 31개가 공백으로 이어 붙어 있음. **위치로 짝짓되 개수가 안 맞으면
그 줄을 통째로 버림**(10건 버림, 목록으로 남김). 어긋난 채 짝지으면 **다른 기종의
연료값이 조용히 붙는** 자리라 억지로 안 맞춤.
- 코드는 앞자리를 이어받음 — `0201-0012 0020 0040` → `0201-0020`·`0201-0040`.
- ⚠ **이름 칸은 못 씀** — 표 전체 기종명이 한 덩어리로 옴. **코드로 613 기종
카탈로그에서 이름·규격을 가져옴**(카탈로그에 없는 코드는 버리고 목록에).
**검증(굴착기 무한궤도 0.7㎥)** — 주연료 11.6 ℓ/hr · 잡재료 22 % · 조종원 1인/일.
시간당 = 재료 26,130(연료 21,418 + 잡재료 4,712) + 노무 35,412 + 경비 24,554 = **86,096**.
STC 2024 관측 96,829 과 **11 % 차** — 손료·재료는 근사하고 **노무가 갈림**
(관측 55,700/hr = 445,600/일로 어느 직종보다 높음). **맞추려 손대지 않음** — 관측값은
대조용이고 우리는 법대로 감. ⚠ 조종원 직종 확인 항목으로 올림.
- **잠정 표시** — 조종원 **직종명이 품셈 8-4 에 없어** `aliases` 기반 규칙(트럭 계열 =
화물차운전사, 그 밖 = 건설기계운전사)으로 매김. 결과에 `operator_mapping_is_provisional`
로 드러남. TODO(미결 9-6).
- **유가는 전국평균 잠정**, 지역 파라미터 자리(`region`)만 뚫어 둠 (품셈 8-1-7 5호).
- **파생 파일로 냄** — `resources/data_cost_machine_operating/`, 기준자료 안 건드림.
`derived_from` 에 `mach_base` 판의 세 쪽을 적어 낡으면 드러나게 함.
자체검증 — 신규 6건 포함 `pytest tmp/tests/ -q` **110 passed** · ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
**`B09_Estimation_MachineCost.py` 신설 — 취득가 → 시간당 사용료 두 단계**
- `mach_base_2026.json` 실사: `mach_price` **613 기종**(`machine_code`+`specification`),
`mach_loss_coef` 387건(시간당 손료계수·내용시간·연간표준시간).
- **시간당 손료 = 취득가 × 손료계수.** 취득가가 **천원 단위**라 환산을 놓치면 1,000배
틀리는 자리 — 테스트로 박음. 굴착기(무한궤도) 0.7㎥ → **24,554원/hr**
(STC 2024 관측 23,128 과 같은 자릿수, 취득가 판 차이로 설명됨).
- ⚠ **빠진 성분을 0 으로 안 때움** — `mach_fuel_rate`·`mach_operator_map` 이 **0건**이라
연료(재료비)·운전사(노무비)를 못 채움. `HourlyMachineCost.gaps` 로 **공백을 알림**.
실측 비중이 노무 53·재료 20·경비 27 % 라 손료만 쓰면 **4분의 1**이 됨 — 조용히
총액에 섞이면 안 되는 자리.
- 손료계수 없는 기종 **230건**도 0 으로 안 때우고 멈춤.
**자원 축에 기종 매칭 추가**
- 기종 셀은 괄호 안에 **형식과 규격이 섞여** 있음 — 「굴착기(무한궤도, 0.7㎥)」는
이름 `굴착기(무한궤도)` + 규격 `0.7`. 형식은 이름의 일부이고 숫자 조각만 규격임.
- **규격이 옆 칸에 있는 표**가 많아(`['굴착기 (무한궤도)','굴착기(무한궤도,0.2㎥)','hr',…])
뒷 칸에서 규격 후보를 찾아 고름. 규격 없이 매칭 성공으로 치지 않는 규칙은 그대로.
- 산출 갱신 — 매칭 86 → **91행**(노무 86 + 기종 5), 미매칭 274 → **269건**.
자체검증 — 신규 7건 포함 `pytest tmp/tests/ -q` **104 passed** · ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
메인 창의 공종 축(`resources/data_work_item_master/`)은 **읽기만** 하고, 결과는
**내 소관 경로** `resources/data_cost_resource_axis/` 에 냄 — 메인이 품셈을 다시
돌리면 그 폴더가 덮이므로 안에 섞으면 사라짐.
- ⚠ **`pum_form` 을 먼저 봄** — `productivity` = 1 ÷ 값 · `requirement` = 값 ÷ 기준수량.
**뒤집으면 20배 틀림.** 형태를 모르는 표(`undetermined`)는 **값을 안 씀**, 한쪽으로
단정하지 않고 멈춤. 테스트가 뒤집힘을 자릿수로 잡음.
- `coefficient`(19)·`reference`(94)는 공종이 아니므로 **일위대가 항목으로 안 세움**.
- **규격이 없으면 매칭 성공으로 안 침** — 「굴착기」와 「굴착기 0.7㎥」는 단가가 다름.
이름이 여럿이면 규격 없이는 못 고르고 `unmatched` 로 감.
- **못 맞춘 것은 빈칸이 아니라 목록** — 다만 표 머리글·소계·기호(`f`·`E`)·규격 구간
(`10∼12`·`50이상`)·절번호는 자원이 아니므로 먼저 걷어냄. 안 걸렀을 때 1,550건이던
목록이 **274건의 실제 공백**으로 줄어 쓸 수 있는 목록이 됨.
산출 — 매칭 **86행**(노무, 7직종: 보통인부 59·특별인부 19·조경공 4 등) ·
못 맞춘 **274건**(상위: 치즐소모량·자재·굴착기(무한궤도) 등).
⚠ **노임 카탈로그만 있어 노무만 맞음** — 기계·자재 카탈로그가 아직 없어 그 계열은
전부 못 맞춘 목록으로 감. 사유 문구에 그 사실을 적어 「이름이 틀림」과 구분함.
자체검증 — 신규 12건 포함 `pytest tmp/tests/ -q` **97 passed** · ruff 통과 · 409줄.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
**단수 처리를 「출력 위치」에 바인딩** (`B09_Estimation_Rounding.py`)
- `단수처리_규칙.md` §1 — 같은 수량×단가라도 **내역서 본체는 절사, 집계표는 반올림**.
함수가 항목이 아니라 **표 종류**에 붙음. 자리를 안 대고 부르는 길을 안 둠(기본값 없음).
- ⚠ **집계표와 본체의 합이 어긋나는 것이 정상** — 「합계가 1원 틀린다」에 계산을 고치지
않도록 `summary_vs_body_gap()` 으로 차이를 값으로 드러내고 화면 문구를 상수로 둠.
- 계산은 전정밀, 자르는 것은 표 그리는 자리에서. 값은 원문에서 복사 안 하고 문서를 가리킴.
**단가 계층 — 표를 세 벌 만들지 않음** (`B09_Estimation_PriceBook.py`)
- 「제목 + 상세」 한 쌍 + 종류 구분. 층은 `S 중기취득가 → X 시간당사용료 → B 일위대가 →
D 단가산출`, 바닥은 `M 자재`·`L 노임`, `W 일식·견적`은 **단가 0**(무대처리, 8-7 ㉡).
- 금액은 어느 층이든 **재료·노무·경비 3분할**, 합계 = 셋의 합.
- **단가 슬롯 6개** — 번호 고정, **이름은 프로젝트 설정**. 기본 채택 6번(`JUKNM=6`).
슬롯별 **근거 쪽수**(STC `PG_` 열) 자리도 둠.
- 안전장치: 채택 슬롯이 비면 **0 으로 안 때우고 멈춤** · 없는 코드는 `unmatched_codes()`
로 **목록으로 냄** · 참조 순환 감지.
**프로젝트 스냅샷** (`B09_Estimation_Storage.py`)
- 채택 단가를 `<project_root>/B09_Estimation/v1/` 에 사본으로. 기준자료가 갱신돼도
**옛 프로젝트 결과가 안 바뀜**. `dataset_version` 은 `dataset_id`+`effective_date`+
`sha256` **세 쪽**. 사본이 바뀌면 지문 불일치로 멈춤. 차수(당초·1~3차) 자리 비워 둠.
자체검증 — 신규 13건 포함 `pytest tmp/tests/ -q` **85 passed** · ruff 통과 ·
파일 최대 534줄. 중기 실측값(`X00005` 96,829 = 노 55,700 + 재 18,001 + 경 23,128)으로
3분할 유지·층 쌓임 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`단수처리_규칙.md` §3 원문을 읽다 **입력 의미를 잘못 잡은 것**을 찾음.
실무 서류의 「관급자재대 69,850,000」은 이미
`ROUNDUP(순자재대 69,474,220 + 조달수수료 375,160, −3)` 한 값인데,
그 합계를 순자재대 자리에 넣고 있었음. 그래서 안전관리비 관급항이 부풀었고,
그 어긋남을 「조달수수료 차감 옵션」으로 덮고 있었음.
- **입력을 순자재대·수수료로 나눠 넣는 것으로 정정.** 그러면 ㉮ 관급자재대 천원 올림
㉯ 안전관리비 관급항 `순자재대 ÷ 1.1` 이 **둘 다 저절로 맞음**.
- ⇒ `deduct_procurement_fee_for_safety` **옵션 제거**(엔진·법정경비·라우터).
8-10 의 「차감은 규정 문구가 아님」과 결론은 같되 이유가 더 단순함 —
**애초에 수수료는 관급금액에 안 들어감.**
- 잘못 넣는 사고를 테스트로 박음 — `test_owner_supplied_input_is_net_material_not_total`
(합계를 넣으면 안전관리비가 8,629 원 = 375,160 ÷ 1.1 × 2.53 % 커짐).
- 필드 주석에 「실무 서류의 관급자재대를 그대로 넣지 말 것」 경고 추가.
자체검증 — **관행 옵션 없이 기본값만으로** 두 벌 재현:
2024 요율 → 안전 16,586,996 · 총공사비 1,201,879,000 / 현행 요율 → 안전 **18,586,091**.
화면 실측(5174) — 안전관리비 A 18,586,091 채택 · 관급자재대 69,850,000.
pytest 72 passed · ruff 통과 · tsc 통과 · 백엔드 재시작 후 200.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `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>
- 근거 = 국가계약법 시행령 제7조 1호 「공사계약의 경우에는 **관급자재로 공급될 부분의
가격을 제외한 금액**」. 우리 계산의 그 값은 **총원가**(부가세 전·관급 밖)임.
- 씨앗은 직접공사비로 두되 **거기서 멈추지 않음** — 나온 총원가로 구간을 다시 판정해
구간 지문이 굳을 때까지 되풀이(최대 5회). 진동하면 **높은 쪽 구간 채택**하고
결과 `notes` 에 남김. `estimated_price_krw` 명시 시 반복 없음.
- 구간 지문 = 일반관리비·이윤·고용보험·하도급보증 구간 + 적용 하한(환경·퇴직) 충족 여부.
- ⚠ 잠재 결함 하나 같이 고침 — 하도급보증 30억 이상 구간이 공종(토목·산업설비/건축)으로
갈려 **구간 2개가 겹쳐 멈추던 자리**. `select_bracket(prefer_suffix=…)` 로 공종을
부르는 쪽이 대게 함 (조용한 기본값 아님).
- 자체검증: 32 passed. 씨앗 47억(8 %) → 총원가 50억 초과 → **6.5 % 로 재판정**되는
경계 넘기 사례를 테스트로 박음.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `B09_Estimation_Statutory.py` 신설(법정경비 14비목) — 700줄 제한 대비 분리.
**비목 목록을 코드에 안 박음**: 그 해 요율 데이터에 변수가 있는 것만 유효(8-13·8-14).
- `B09_Estimation_Guards.py` 신설 — 이중계상 거울 테스트 3종을 함수로 둠(8-7 ㉠㉡㉢).
할증 두 번 · 무대 줄 단가 · 배합 두 번 쪼개기를 **수치로 잡아 멈춤**.
- 엔진: 이윤 3줄(조정 전·조정액·조정 후) · 관급자재대 천원 올림 · 폐기물처리비 실비 슬롯 ·
`formula_text`(줄마다 제 산식 — 실무 원문의 A식 복사 오류를 안 따라감) ·
`proposed_profit_adjustment`(필요액을 보여만 주고 적용은 명시로, ★법대로 8-10).
- 요율 로더에 `load_rate_dataset_from_path` 추가 — 옛 연도 재현 검산 전용, 지문이 없어
정본이 아님이 결과에 드러남.
- 검산 고정값 두 벌(`tmp/tests`, git 밖):
· 2024 요율 → 울진 공통 금액 사슬 **전건 재현**(안전 16,586,996 · 이윤 108,109,955 ·
총원가 1,029,117,273 · 총공사비 1,201,879,000)
· 현행 요율 → 안전 18,586,091. **같은 입력·같은 코드, 요율만 교체** — 연도 교체 구조 증명
· 거창 A/B min **양방향**(울진 A 채택 · 거창 B 채택)
- 자체검증: `pytest tmp/tests/ -q` **68 passed** · `ruff check` 통과 · 파일 최대 441줄.
- 잠정 반영(TODO 주석 + 계획서 항목번호): 환경보전비 임도 요율 0.9 % 잠정 ·
폐기물처리비 자리 미확정 · 조달수수료 차감은 기본 꺼짐(옛 서류 재현용 옵션).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `B09_Estimation_Rates.py` — 요율 데이터(`resources/data_cost_input_value/rates_*.json`)
로더 + 구간 조회. 요율을 코드에 안 박음. 구간 라벨의 `billion` = 십억 원.
구간 판정 실패 시 기본값으로 안 때우고 `RateLookupError` 로 멈춤.
- `B09_Estimation_Engine_Cost.py` — ⑤ 공사원가계산서 계산. 순공사비를 받아
법정경비·일반관리비·이윤·부가세를 얹음. 수량·단가와 무관하게 홀로 돎.
- 지킨 것 (PLAN 8-9·8-10, 실무 원가계산서 재현으로 확인된 것만)
· 모든 줄 원 단위 버림 · 밑수가 항목마다 갈림(직노 / 직노+간노 / 건강보험료 / …)
· 안전관리비 A·B 두 값 중 작은 쪽 — 대상액이 구간 경계를 넘으면 A 가 더 커짐
· 이윤 수동 조정액은 설계자 명시 입력일 때만 · 비목 목록을 코드에 안 박음
- 검사 16건 별도(`tmp/tests/test_b09_cost_engine.py`, git 밖) 전건 통과.
실무 실측 두 방향(울진 A 채택 · 거창 B 채택)을 고정값으로 씀.
- 기존 파일 무수정 · DB 미사용(기준자료는 파일, PLAN 8-5·9-2).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>