diff --git a/B09_Estimation/B09_Estimation_UnitPrice.py b/B09_Estimation/B09_Estimation_UnitPrice.py index 5005265c..e025561f 100644 --- a/B09_Estimation/B09_Estimation_UnitPrice.py +++ b/B09_Estimation/B09_Estimation_UnitPrice.py @@ -1100,6 +1100,7 @@ def build_unit_prices( attach_work_items_ax(build) # 기계 수송비 — 기계경비의 셋째 몫(손료·운전경비·**수송비**). 거리가 있어야 선다. + from B09_Estimation.B09_Estimation_Transport import WORK_ITEM_CODE as TRANSPORT_CODE from B09_Estimation.B09_Estimation_Transport import attach_transport build.transport_notes = attach_transport( @@ -1108,6 +1109,13 @@ def build_unit_prices( distance_km=transport_distance_km, road_key=transport_road, ) + # ⚠ 줄이 하나도 안 서면 **사유를 그 공종에 붙인다** — 안 그러면 수송비가 조용히 0 으로 빠진다 + # (2026-09-16 조사: 거리가 비어 모든 프로젝트에서 안 서고 있었고 사유도 안 떴다). + # 문구는 아래 층 것 **그대로** 쓴다 — 여기서 새로 짓지 않는다(까닭은 그대로 올림). + if build.transport_notes and not any( + code.startswith(f"B-{TRANSPORT_CODE}") for code in build.book.titles + ): + build.component_gaps[TRANSPORT_CODE] = build.transport_notes[0] # 덤프 운반 — 운반거리(B08 유토곡선·사토장)마다 한 벌(산림품셈 10-12 「2. 운반」). attach_dump_hauls(build, master, dump_haul_m) diff --git a/resources/tester/test_b09_transport_reason.py b/resources/tester/test_b09_transport_reason.py new file mode 100644 index 00000000..24c47929 --- /dev/null +++ b/resources/tester/test_b09_transport_reason.py @@ -0,0 +1,39 @@ +"""B09 기계 수송비 — 거리가 비면 **사유가 붙은 빈 줄**이 선다(2026-09-16 브레인 · 사용자 지시). + +기계경비는 손료 + 운전경비 + **수송비**(건설품셈 8-1-6의 1)인데, 수송비는 거리(인근 시·군·구청 +소재지 → 현장)가 설계 입력이라 안 넣으면 줄이 안 섰다. ⚠ 그때 **사유도 안 떠서** 모든 프로젝트에서 +수송비가 조용히 0 이었다(2026-09-16 조사) — 「조용히 빠짐」은 우리가 내내 막아 온 자리다. +⇒ 금액은 지어내지 않는다. **사유만** 세운다 — 문구는 아래 층(`B09_Estimation_Transport`) 것 그대로. +""" + +from __future__ import annotations + +from decimal import Decimal + +from B09_Estimation.B09_Estimation_Transport import WORK_ITEM_CODE +from B09_Estimation.B09_Estimation_UnitPrice import cached_build + + +def test_거리가_비면_사유가_서고_금액은_안_섬() -> None: + build = cached_build() + assert f"B-{WORK_ITEM_CODE}" not in build.book.titles # 금액을 지어내지 않음 + gap = build.component_gaps.get(WORK_ITEM_CODE) or "" + assert "거리" in gap and "산출 조건" in gap + assert gap in build.transport_notes # 아래 층 문구 그대로 — 새로 짓지 않음 + + +def test_거리를_넣으면_줄이_서고_사유는_사라짐() -> None: + build = cached_build(transport_distance_km="12", transport_road="paved") + codes = [c for c in build.book.titles if c.startswith(f"B-{WORK_ITEM_CODE}#")] + assert codes, "거리를 넣었는데 수송비 줄이 안 섬" + assert WORK_ITEM_CODE not in build.component_gaps + detail = build.book.details[codes[0]][0] + assert detail.quantity > Decimal(0) + + +def test_한_갈래만_서면_사유는_그_갈래에만_남음() -> None: + """고속4차선은 원문에 트레일러 속도가 「-」 — 덤프 줄은 서고 트레일러만 사유.""" + build = cached_build(transport_distance_km="12", transport_road="expressway_4") + assert [c for c in build.book.titles if c.startswith(f"B-{WORK_ITEM_CODE}#")] + assert build.transport_notes and any("원문에 없습니다" in n for n in build.transport_notes) + assert WORK_ITEM_CODE not in build.component_gaps # 줄이 섰으면 공종 사유는 안 붙음