diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py index fd748b2b..c4ddcfb6 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py @@ -288,9 +288,28 @@ def _haul_rows( "origin": ORIGIN_HAUL, } ) + if equipment == "dump_truck" and in_bill: + # 덤프 운반은 **싣는 일이 따로** — 산림품셈 10-12 「1. 적재」 + # (굴착기 0.7㎥ · ㎝ 22초·180°). + # 흙깎기 9-3-2(㎝ 20초·135°, 깎아 옆에 둠)에 안 들어 있어 운반과 한 벌로 짝 줄을 냄 + # (2026-09-14 브레인 판정). 거리와 무관 · 수량은 운반량 그대로. + rows.append( + { + **rows[-1], + "name": "덤프 적재", + "haul_distance_m": None, + "haul_equipment": LOADING_EQUIPMENT, + "spec_detail": f"{state_note} · 덤프 운반량만큼 싣기(10-12 「1. 적재」)", + } + ) return rows, unmatched +#: 인계 「덤프 적재」 짝 줄의 수단 표지 — +#: B09 `MachineProductivity_Dump.LOADING_EQUIPMENT` 와 같은 글. +LOADING_EQUIPMENT = "dump_loading" + + def blocked_of( structure: dict[str, Any], class_basis: str = "", has_code: bool = False ) -> tuple[str | None, str]: diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities.py b/B09_Estimation/B09_Estimation_BillOfQuantities.py index 6f67830c..86b1c528 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities.py @@ -628,7 +628,15 @@ def build_bill( ), _ZERO, ) - haul_total = sum((item.quantity for item in work_items if item.haul_equipment), _ZERO) + # 「덤프 적재」 짝 줄은 운반이 아니라 싣기 — 같은 흙을 운반량으로 두 번 세지 않게 뺌. + haul_total = sum( + ( + item.quantity + for item in work_items + if item.haul_equipment and item.haul_equipment != "dump_loading" + ), + _ZERO, + ) if cut_total > 0 and haul_total > 0: check_haul_volume_within_cut( haul_volume_total_m3=haul_total, diff --git a/docs/raw/verification/2026-09-13_축C_명세.md b/docs/raw/verification/2026-09-13_축C_명세.md index bcb21055..a9e4d7bd 100644 --- a/docs/raw/verification/2026-09-13_축C_명세.md +++ b/docs/raw/verification/2026-09-13_축C_명세.md @@ -358,7 +358,7 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 | 종목 | 단위 | 지위 | 비고 | 우리 실측과 | | --- | --- | --- | --- | --- | -| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **아직 확인 안 됨** — 데스크탑 서브가 재는 중 | +| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **우리는 안 버리고 있었음** — 실무 6건은 여섯 다 `000`, 우리는 `373·695·578·654·341·639`. 기본을 켜는 것으로 판정(2026-09-14) | | 설계서의 소계 | 원 | 1 | 미만버림 | ✅ | | 설계서의 **금액란** | 원 | 1 | 미만버림 | ✅ 「내역에 실릴 때 정수 절사」와 같음 | | **일위대가표의 계금** | 원 | 1 | 미만버림 | ✅ 「호표 안 성분 소계 절사」와 같음 | @@ -366,6 +366,15 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 ⇒ **STmate 를 재서 얻은 규칙이 법 조문이었음.** 경쟁사를 베낀 것이 아니라 둘 다 같은 고시를 따른 것임. +⚠ **총액 줄만 우리가 안 따르고 있었음** (2026-09-14 데스크탑 서브 실측). 기본값이 꺼져 있어(`cut_basis=""`) +실무 6건의 총공사비 끝 세 자리가 우리 엔진에서만 남음. **기본을 켜되 화면에 근거를 보일 것** — +2026-09-08 「조용히 깎지 않음」 합의는 근거가 없을 때 정한 것이고, 지금은 고시가 근거임. +곁에 버그 하나 — 절사 자동보정이 둘째 걸음부터 ÷1.1 을 안 해 경계를 지나쳐 **진동**함 +(울진 신설 `654→999→900→910→909…`). 다섯 건은 첫 걸음에 앉아 안 드러났음. + +⚠ 「설계서의 소계 1원 미만버림」과 우리 「집계표 행 금액 `ROUND`(279/279)」는 **다른 자리**로 봄 — +집계표는 설계서 소계가 아님. 같은 자리로 읽을 근거가 나오면 다시 잴 것. + **같은 고시 1-2-2 「1. 수량의 단위표준」 [주]**: ``` diff --git a/resources/tester/test_b08_handoff.py b/resources/tester/test_b08_handoff.py index c31fc7e2..016d73dc 100644 --- a/resources/tester/test_b08_handoff.py +++ b/resources/tester/test_b08_handoff.py @@ -188,6 +188,11 @@ def test_무대는_넘기되_내역에는_안_섬() -> None: assert dump["in_bill"] is True assert dump["work_item_code"] == "FP-10-12" assert dump["haul_distance_m"] == pytest.approx(1200.0) + # 2026-09-14 — 덤프 운반마다 「덤프 적재」 짝 줄(10-12 「1. 적재」) · 거리 없음 · 수량 같음 + loading = 줄(handoff, "덤프 적재") + assert loading["haul_equipment"] == "dump_loading" and loading["haul_distance_m"] is None + assert loading["quantity"] == dump["quantity"] and loading["variant_value"] == "토사" + assert not any(r["name"] == "덤프 적재" and r["spec"] != "토사" for r in handoff["work_items"]) def test_합계_줄은_내역에_안_섬() -> None: @@ -600,9 +605,11 @@ def 운반표() -> dict: def test_운반계획이_있으면_네_줄이_실림() -> None: handoff = build_handoff(haul_table=운반표()) haul_rows = [row for row in handoff["work_items"] if row["origin"] == "haul"] - assert len(haul_rows) == 4 + # 운반 넷 + 덤프 운반 둘의 적재 짝 줄 둘(2026-09-14 · 10-12 「1. 적재」 — 거리 없음) + moves = [row for row in haul_rows if row["haul_equipment"] != "dump_loading"] + assert len(moves) == 4 and len(haul_rows) == 6 assert all(row["haul_equipment"] for row in haul_rows) - assert all(row["haul_distance_m"] for row in haul_rows) + assert all(row["haul_distance_m"] for row in moves) def test_무대가_함께_와야_운반_검산이_걸림() -> None: @@ -612,7 +619,7 @@ def test_무대가_함께_와야_운반_검산이_걸림() -> None: assert free["in_bill"] is False assert free["quantity"] == pytest.approx(871.0) assert handoff["excluded_row_count"] == 1 - assert handoff["bill_row_count"] == 3 + assert handoff["bill_row_count"] == 5 # 도자 · 덤프 운반 둘 · 덤프 적재 짝 줄 둘 def test_운반계획이_없으면_줄이_없음() -> None: diff --git a/resources/tester/test_b09_verify_guards.py b/resources/tester/test_b09_verify_guards.py index 941371ef..b82f081a 100644 --- a/resources/tester/test_b09_verify_guards.py +++ b/resources/tester/test_b09_verify_guards.py @@ -53,10 +53,14 @@ def test_v16_불도저_운반이_갈래마다_금액으로_선다() -> None: assert build.book.resolve(code).total > 0, code -def test_v16_덤프_운반은_아직_안_선다() -> None: - """⚠ 안 서는 것도 사실대로 못 박는다 — 운반거리(설계 입력)를 기다리는 자리다.""" +def test_v16_덤프_운반은_거리가_와야_선다() -> None: + """⚠ 안 서는 것도 사실대로 못 박는다 — 운반은 운반거리(설계 입력)가 와야 선다. + + 2026-09-14 — 적재(10-12 「1. 적재」)는 거리 무관이라 늘 서고, 운반(`#L…m`)은 거리가 온 조립에만. + """ build = cached_build() - assert not [code for code in build.book.titles if code.startswith("B-FP-10-12")] + dump = [code for code in build.book.titles if code.startswith("B-FP-10-12")] + assert dump and all(code.endswith("#적재") for code in dump) # ── V-17 ──────────────────────────────────────────────────────────── diff --git a/resources/tester/test_work_item_master_variants.py b/resources/tester/test_work_item_master_variants.py index 3e0983c8..9a46aa3d 100644 --- a/resources/tester/test_work_item_master_variants.py +++ b/resources/tester/test_work_item_master_variants.py @@ -23,6 +23,10 @@ def test_단가표_갈래_제목은_전부_마스터_갈래_키에_있다() -> N for code in cached_build().book.titles: if not code.startswith("B-") or "#" not in code: continue + if code.startswith("B-FP-10-12-"): + # 덤프 운반·적재(10-12)는 **표가 아니라 본문 식**이라 마스터 표에 갈래가 없음 — + # 갈래(`#적재`·`#L…m`)는 식이 냄(2026-09-14 · `MachineProductivity_Dump`). + continue work_item, variant = code[2:].split("#", 1) keys = { normalize_variant_key(k) for k in nodes.get(work_item, {}).get("variant_keys") or []