diff --git a/B09_Estimation/B09_Estimation_UnitPrice.py b/B09_Estimation/B09_Estimation_UnitPrice.py index 41943249..043e61c4 100644 --- a/B09_Estimation/B09_Estimation_UnitPrice.py +++ b/B09_Estimation/B09_Estimation_UnitPrice.py @@ -333,6 +333,19 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild: for row in axis.rows: by_item.setdefault((row.work_item_code, getattr(row, "variant", "")), []).append(row) + # ⚠ **자원 줄이 하나도 없어도 공식이 온전하면 세운다.** 기계만 쓰는 공종(층따기 9-18 · + # 쇄석 부설 11-4)은 표에 인력 줄이 없어 자원 축이 비고, 그러면 아래 반복문이 그 공종을 + # 아예 안 본다 — 「미판정」으로 남아 있던 것이 실은 **시공능력 공식표**였다 + # (2026-09-08 미판정 77건을 훑다 발견). + for node in master.get("work_items", []): + code = node.get("work_item_code") + if not code or (code, "") in by_item: + continue + for table in node.get("tables", []): + if isinstance(extract_cycle_factors(code, table), CycleFactors): + by_item[(code, "")] = [] + break + for (work_item_code, variant), rows in sorted(by_item.items()): # 갈래 키는 **내부 공백을 지운 것**, 화면 문구는 **원문 그대로** # (2026-09-08 두 창 합의). 원문이 「보 통」·「보 통」으로 들쭉날쭉해 @@ -349,7 +362,8 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild: for row in rows ] attachable = [(row, ref) for row, ref in attachable if ref in build.book.titles] - if not attachable: + if not attachable and not _has_full_formula(master, work_item_code): + # 붙을 상세도 없고 공식도 없으면 **제목도 안 세운다**(0 원 일위대가 금지). build.skipped.append(work_item_code) continue @@ -416,6 +430,20 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild: return build +def _has_full_formula(master: dict, work_item_code: str) -> bool: + """그 공종에 **온전한 시공능력 공식**이 있는가 (기계만 쓰는 공종용).""" + node = next( + (w for w in master.get("work_items", []) if w.get("work_item_code") == work_item_code), + None, + ) + if node is None: + return False + return any( + isinstance(extract_cycle_factors(work_item_code, table), CycleFactors) + for table in node.get("tables", []) + ) + + def _share_of(row) -> Decimal: """그 줄이 차지하는 몫(0~1). 배분율이 없으면 1 — 종전과 같다.""" ratio = getattr(row, "group_ratio_pct", None)