From 996300752da4c088dfe0a702cebe7d45ccd52db4 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 09:39:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(b08):=20=EC=82=AC=ED=86=A0=20=EC=9A=B4?= =?UTF-8?q?=EB=B0=98=EC=97=90=EB=8F=84=20=E3=80=8C=EB=8D=A4=ED=94=84=20?= =?UTF-8?q?=EC=A0=81=EC=9E=AC=E3=80=8D=20=EC=A7=9D=20=EC=A4=84=20=C2=B7=20?= =?UTF-8?q?=EC=82=AC=ED=86=A0=20=EC=A4=84=EC=9D=B4=20=EA=B0=88=EB=9E=98?= =?UTF-8?q?=EB=A5=BC=20=EC=8B=A4=EC=96=B4=20=EB=B3=B4=EB=83=84=20=E2=80=94?= =?UTF-8?q?=20=EC=A2=85=EC=A0=84=20=EA=B0=88=EB=9E=98=20=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EB=8D=A4=ED=94=84=20=EC=9E=8E?= =?UTF-8?q?=EC=9D=84=20=EB=AA=BB=20=EA=B3=A0=EB=A5=B4=EB=8D=98=20=EA=B2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- .../B08_Quantity_Engine_Handoff_Spoil.py | 36 ++++++++++++++++--- resources/tester/test_b08_spoil_haul.py | 31 ++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Spoil.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Spoil.py index 38036c8e..98d3c228 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Spoil.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Spoil.py @@ -23,6 +23,7 @@ from B08_Quantity.B08_Quantity_Engine_Handoff_Mapping import ( ORIGIN_HAUL, WorkItemMapping, ) +from B08_Quantity.B08_Quantity_Engine_Handoff_Rows import LOADING_EQUIPMENT SPOIL_NAME = "사토 운반" #: ⚠ **사토장 사면 물량은 안 센다**(2026-09-09 세 창 확인). 교본 6장 3절은 「완료 구간 비탈면을 @@ -127,8 +128,33 @@ def spoil_haul_rows( extra=GROUND_UNKNOWN, ) ) - return rows - return [_spoil_row(code, volume, distance, blocked, reason, note, None, GROUND_UNKNOWN)] + return _with_loading(rows) + return _with_loading( + [_spoil_row(code, volume, distance, blocked, reason, note, None, GROUND_UNKNOWN)] + ) + + +def _with_loading(rows: list[dict[str, Any]]) -> list[dict[str, Any]]: + """사토도 싣는 일이 따로 — 흙깎기 운반과 같은 「덤프 적재」 짝 줄(10-12 「1. 적재」). + + 내역에 서는 운반 줄에만 붙음 · 거리 무관 · 수량은 운반량 그대로(2026-09-14 브레인 판정). + 운반토량 검산은 `dump_loading` 을 빼므로 두 번 안 셈. + """ + out: list[dict[str, Any]] = [] + for row in rows: + out.append(row) + if row["in_bill"]: + out.append( + { + **row, + "name": "덤프 적재", + "spec": row["ground_class"] or "", + "haul_distance_m": None, + "haul_equipment": LOADING_EQUIPMENT, + "spec_detail": f"{row['spec_detail']} · 사토량만큼 싣기(10-12 「1. 적재」)", + } + ) + return out def _spoil_row( @@ -165,8 +191,10 @@ def _spoil_row( "structure_kind": None, "blocked_kind": BLOCKED_INPUT_MISSING if blocked else None, "blocked_reason": reason, - "variant_axis": None, - "variant_value": None, + # 갈래는 `variant_value` 한 통로로 — 덤프 운반·적재 단가가 토사·암으로 갈림 + # (종전 None 이라 B09 가 10-12 잎을 못 골라 사토 운반이 「갈래 미지정」으로 막힘). + "variant_axis": "ground_class" if ground else None, + "variant_value": ground, "secondary_axes": None, "spec_class": None, "spec_class_basis": "", diff --git a/resources/tester/test_b08_spoil_haul.py b/resources/tester/test_b08_spoil_haul.py index 86bfc359..50c83b92 100644 --- a/resources/tester/test_b08_spoil_haul.py +++ b/resources/tester/test_b08_spoil_haul.py @@ -167,6 +167,37 @@ def test_사토도_자연상태로_선다() -> None: assert "자연상태" in rows[0]["spec_detail"] +def test_사토에도_적재_짝_줄이_서고_단가가_선다() -> None: + """사토도 싣는 일이 따로(10-12 「1. 적재」) — 내역에 서는 운반 줄에만 붙고, 검산엔 안 셈.""" + from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill + from B09_Estimation.B09_Estimation_MachineProductivity_Dump import dump_haul_distances + from B09_Estimation.B09_Estimation_UnitPrice import cached_build + + payload = build_handoff( + haul_table={ + "rows": [], + "spoil": { + "volume_m3": 100.0, + "distance_m": 800.0, + "by_ground_m3": {"ea_m3": 70.0}, + "ground_unknown_m3": 30.0, + }, + } + ) + rows = payload["work_items"] + loads = [row for row in rows if row["name"] == "덤프 적재"] + assert [(row["variant_value"], row["quantity"]) for row in loads] == [("토사", 70.0)] + assert loads[0]["haul_distance_m"] is None and loads[0]["haul_equipment"] == "dump_loading" + # 갈래 못 붙인 몫은 막힌 줄이라 짝 줄이 없음 + assert len([row for row in rows if row["name"] == "사토 운반"]) == 2 + + bill = build_bill(payload, build=cached_build(dump_haul_m=dump_haul_distances(payload))) + got = {row.name: row for row in bill.rows if row.name in ("사토 운반", "덤프 적재")} + assert got["사토 운반"].price_code.startswith("B-FP-10-12-01#L800") + assert got["덤프 적재"].price_code == "B-FP-10-12-01#적재" + assert got["사토 운반"].amount_krw > 0 and got["덤프 적재"].amount_krw > 0 + + def test_되돌린_값이_없으면_다짐_그대로_두고_사유() -> None: rows = 줄들(volume_m3=61.88, distance_m=800.0, by_ground_m3={"rr_m3": 61.88}) assert rows[0]["quantity"] == 61.88