diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff.py b/B08_Quantity/B08_Quantity_Engine_Handoff.py index 8f1535a5..553881af 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff.py @@ -186,7 +186,7 @@ def build_handoff( str(row.get("ground_class")) for row in work_items if row.get("ground_class") - and row.get("ground_class") != "토사" + and row.get("ground_class") not in ("토사", "암") # 「암」은 구성비가 빠진 것 and row.get("work_item_code") is None and row.get("origin") == ORIGIN_EARTHWORK } diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Mapping.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Mapping.py index cda537d3..40b2be44 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Mapping.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Mapping.py @@ -121,6 +121,12 @@ ORIGIN_PIPE = "pipe" #: 갈래 이름(연암·보통암…)만으로는 공종을 못 고른다(2026-09-07 일감 9 실서버에서 드러남). METHOD_TO_GROUND = {"ripping": "리핑암", "blasting": "발파암"} NOTE_METHOD_MISSING = "시공법 미지정으로 공종을 못 고름" +#: 암 총량이 갈래로 안 나뉘어 한 줄 「암」으로 선 경우 — 시공법은 **갈래마다** 고르므로 이 줄은 +#: 시공법만 골라서는 안 풀린다. 빠진 입력은 구성비다(2026-09-14 흙깎기·측구터파기 암). +NOTE_ROCK_RATIO_MISSING = ( + "암 갈래 구성비(%)가 아직 없어 암 총량이 한 줄 「암」으로 섬 — 산출 조건 「암 갈래 구성비」를 " + "넣고 갈래마다 시공법(리핑·발파)을 고르면 공종이 섬" +) #: 철근으로 보는 성분 이름 조각. **정확한 낱말이 아니라 앞머리**로 본다 — #: 「이형철근 D13」·「원형철근」처럼 규격이 뒤에 붙기 때문이다. `철근콘크리트`는 성분 이름이 diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py index d353c8c7..fd748b2b 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py @@ -20,6 +20,7 @@ from B08_Quantity.B08_Quantity_Engine_Handoff_Mapping import ( METHOD_TO_GROUND, NOTE_HAUL_IN_SUMMARY, NOTE_METHOD_MISSING, + NOTE_ROCK_RATIO_MISSING, ORIGIN_EARTHWORK, ORIGIN_HAUL, ORIGIN_SLOPE, @@ -58,6 +59,8 @@ def _mapping_ground(ground: str | None, methods: dict[str, str | None]) -> tuple """ if ground is None or ground == "토사": return ground, "" + if ground == "암": + return None, NOTE_ROCK_RATIO_MISSING # 갈래로 안 나뉜 총량 — 시공법만으론 안 풀림 method = methods.get(ground) mapped = METHOD_TO_GROUND.get(method or "") if mapped: diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities.py b/B09_Estimation/B09_Estimation_BillOfQuantities.py index 9a73aa88..6f67830c 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities.py @@ -559,12 +559,18 @@ def build_bill( if item.origin == "structure" else "공종을 못 골랐습니다 — B08 인계에 공종코드가 없습니다." ) + # ⚠ B08 이 막힘 까닭을 실어 보냈으면 **그 문구를 그대로** — 「코드가 없다」로 덮으면 + # 입력하면 풀리는 자리(암 갈래 구성비·시공법)가 매핑 구멍으로 읽힘(2026-09-14 흙깎기 암). + if item.blocked_reason: + kind = item.blocked_kind or _NOT_OUR_ROW + reason = f"{_BLOCKED_LABELS.get(kind, '막힘')} — {item.blocked_reason}" result.missing.append( { "name": item.display_name, "unit": item.unit, "quantity": str(item.quantity), "reason": reason, + **({"blocked_kind": item.blocked_kind} if item.blocked_kind else {}), } ) diff --git a/resources/tester/test_b08_handoff.py b/resources/tester/test_b08_handoff.py index 9e95f95f..c31fc7e2 100644 --- a/resources/tester/test_b08_handoff.py +++ b/resources/tester/test_b08_handoff.py @@ -367,6 +367,20 @@ def test_시공법을_안_정하면_찍지_않고_드러냄() -> None: assert any("시공법" in item for item in handoff["unmatched_work_items"]) +def test_갈래로_안_나뉜_암은_구성비가_빠졌다고_말함() -> None: + """2026-09-14 — 한 줄 「암」은 시공법만 골라서는 안 풀림. B09 내역도 이 까닭을 그대로 실음.""" + handoff = build_handoff(summary_table=집계표(집계줄("흙깎기", "암", 100.0))) + row = handoff["work_items"][0] + assert row["work_item_code"] is None and row["blocked_kind"] == "input_missing" + assert "암 갈래 구성비" in row["blocked_reason"] + assert handoff["missing_method_classes"] == [] # 시공법 안내에 「암」을 안 띄움 + + from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill + + bill = build_bill(handoff) + assert any("입력이 필요합니다 — 암 갈래 구성비" in m["reason"] for m in bill.missing) + + def test_토사는_시공법이_필요없음() -> None: handoff = build_handoff(summary_table=집계표(집계줄("흙깎기", "토사", 100.0))) assert handoff["work_items"][0]["work_item_code"] == "FP-09-03-02"