From 6aa8001780fddeee19d44745cedb41735411488d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 10:32:37 +0900 Subject: [PATCH] =?UTF-8?q?fix(B09):=20=E3=80=8C=EC=84=B1=EB=B6=84?= =?UTF-8?q?=EC=9D=B4=20=EB=B9=A0=EC=A0=B8=20=EB=AA=BB=20=EC=84=B8=EC=9B=80?= =?UTF-8?q?=E3=80=8D=EC=9D=84=20=E3=80=8C=EC=9D=BC=EC=9C=84=EB=8C=80?= =?UTF-8?q?=EA=B0=80=20=EC=97=86=EC=9D=8C=E3=80=8D=EA=B3=BC=20=EA=B0=88?= =?UTF-8?q?=EB=9D=BC=20=EB=83=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메인 창이 물음 — 종단배수 셋이 같은 모양(밑수 1 m)인데 맹암거만 금액이 서고 산마루측구·소단측구는 안 서는 까닭 **표 성격이 달랐음** — 맹암거(F0345)는 수량이 실제로 적힌 소요량표라 자원 4줄이 붙어 25,492.1원/m 로 섬. 나머지 둘은 **수량 칸이 비어 있고 참조로만** 적힘 ``` FP-12-09-02 철근가공조립 | ton | (빈칸) | 「철근가공조립(간단)의 30%」 FP-12-09-03 거 푸 집 | 회 | (빈칸) ``` ⇒ 값을 지어낼 수 없어 **금액을 안 만드는 것이 맞음**(지금 방침 그대로) - 다만 화면 사유가 **「일위대가 없음」으로 뭉뚱그려지고** 있었음 — 「아직 안 만든 것」과 「성분이 빠져 못 세운 것」은 **할 일이 다름**. 사유를 갈라 적음: 「성분이 빠져 단가를 못 세웠습니다 — 철근가공조립 줄을 못 풀었습니다」 - `UnitPriceBuild.component_gaps` 로 자원 축의 사유를 화면까지 나름 - 짝 시험 — 성분 빠진 쪽은 금액 없음 + 사유 표시, **맹암거는 그대로 금액이 섬** 검증: pytest 213 통과(신규 1). 맹암거 40m × 25,492.1 = 1,019,685원 — 메인 값과 일치 Co-Authored-By: Claude Opus 5 (1M context) --- .../B09_Estimation_BillOfQuantities_Rows.py | 12 ++++++++++-- B09_Estimation/B09_Estimation_UnitPrice.py | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py index d01c0d65..f3544b42 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py @@ -208,8 +208,16 @@ def _leaf_row( row.note = f"이 공종엔 일위대가가 없고 한 층 아래에 있습니다 — 후보: {names}" reason = f"일위대가가 하위 공종에 있음(후보 {len(children)}건)" else: - row.note = "일위대가가 아직 없습니다 — 금액을 0 으로 때우지 않습니다." - reason = "일위대가 없음" + # ⚠ 「아직 안 만든 것」과 「성분이 빠져 못 세운 것」은 **할 일이 다르다**. + # 뭉뚱그리면 사용자가 무엇을 기다려야 하는지 못 읽는다(2026-09-08 산마루측구: + # 표에 수량 칸이 비어 있고 「철근가공조립(간단)의 30 %」처럼 참조로만 적힌 자리). + gap = unit_prices.component_gaps.get(node.code) + if gap: + row.note = f"성분이 빠져 단가를 못 세웠습니다 — {gap}" + reason = f"성분 미확보 — {gap}" + else: + row.note = "일위대가가 아직 없습니다 — 금액을 0 으로 때우지 않습니다." + reason = "일위대가 없음" result.missing.append( { "name": row.name, diff --git a/B09_Estimation/B09_Estimation_UnitPrice.py b/B09_Estimation/B09_Estimation_UnitPrice.py index 19976501..41943249 100644 --- a/B09_Estimation/B09_Estimation_UnitPrice.py +++ b/B09_Estimation/B09_Estimation_UnitPrice.py @@ -78,6 +78,10 @@ class UnitPriceBuild: skipped: list[str] = field(default_factory=list) #: 기계 성분이 비어 있는 채로 쓰인 기종 (연료·조종원 미확보). incomplete_machines: list[str] = field(default_factory=list) + #: 자원은 알아봤는데 **값을 못 읽은 줄**이 있어 단가를 못 세운 공종 — 사유 문구. + #: ⚠ 일위대가가 **아예 안 선** 경우에도 남는다 — 「일위대가 없음」과 「성분이 빠져 + #: 못 세움」은 할 일이 다르므로 화면에서 갈라 보여야 한다(2026-09-08 산마루측구). + component_gaps: dict[str, str] = field(default_factory=dict) #: 배분율 표인데 일부 몫만 붙은 공종 — 「단가가 일부만 섬」. 값은 붙은 몫(%). partial_ratio: dict[str, Decimal] = field(default_factory=dict) #: 시공능력 공식으로 장비 몫을 세운 공종 — 산출근거를 화면에 그대로 보인다. @@ -315,6 +319,7 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild: names = {w["work_item_code"]: w.get("name", "") for w in master.get("work_items", [])} build = UnitPriceBuild() + build.component_gaps = dict(axis.partial_items) missing_basis = load_basis_missing() wages = load_operator_wages() _add_labor_titles(build.book, wages)