"""계곡 통과 시설(`pipe_points.json`)이 원단위 전개·인계에 선다 (A1, 2026-09-14). ⚠ 앞서 전개가 `structures.json` 만 읽어 관 유입·유출부 기슭막이·집수정·독립 기슭막이가 원단위·내역에 한 줄도 안 섰음. 관 줄(품셈 12-11 m당)에는 기슭막이 몫이 없어 **한 번만** 셈. """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402 from B08_Quantity import B08_Quantity_Router_Material as material # noqa: E402 from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff # noqa: E402 from B08_Quantity.B08_Quantity_Engine_Pipe import build_rows, facility_structures # noqa: E402 from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402 NAMES = {"pipe": "배수관", "revetment": "기슭막이", "ford_bridge": "세월교"} def _table(points: list[dict]) -> dict: return build_table(facility_structures(points), NAMES) def test_관_하나에_기슭막이_둘이_기본값_사유와_함께_서고_관_줄에는_없다() -> None: point = {"chainage_m": 100.0, "options": {"pipe_diameter_mm": 1000}} table = _table([point]) names = [s["name"] for s in table["structures"]] assert names == ["배수관 · 유입부 기슭막이", "배수관 · 유출부 기슭막이"] inlet, outlet = table["structures"] assert inlet["options"]["form"] == "돌쌓기(찰)" and outlet["options"]["form"] == "돌쌓기(메)" assert all(s["components"] and s["length_m"] == 10 for s in table["structures"]) assert "등록부 기본값으로 섰음" in inlet["notes"][0] # 관 줄은 관 연장만 — 기슭막이가 거기 섞이지 않음(두 번 안 셈). pipe = build_rows([point], [{"chainage_m": 100.0, "design": {"pipe_length_m": 8}}]) assert [row["quantity"] for row in pipe["rows"]] == [8.0] work = {row["name"]: row for row in build_handoff(unit_quantity_table=table)["work_items"]} assert work["배수관 · 유입부 기슭막이"]["work_item_code"] == "FP-13-04-05" assert work["배수관 · 유출부 기슭막이"]["work_item_code"] == "FP-13-04-02" assert ( work["배수관 · 유입부 기슭막이"]["in_bill"] and work["배수관 · 유입부 기슭막이"]["unit"] == "㎡" ) def test_유입구가_집수정이면_집수정_줄이_사유로_서고_유입_기슭막이는_없다() -> None: table = _table([{"chainage_m": 5.0, "options": {"inlet_type": "집수정"}}]) basin, outlet = table["structures"] assert basin["type_id"] == "pipe_inlet_basin" and not basin["components"] and basin["notes"] assert outlet["name"] == "배수관 · 유출부 기슭막이" def test_유입구가_기슭막이인데_집수정_형식이_남아_있으면_집수정은_안_세고_알린다() -> None: options = {"inlet_type": "기슭막이", "inlet_basin_form": "□형(기본형)"} inlet = _table([{"chainage_m": 5.0, "options": options}])["structures"][0] assert inlet["type_id"] == "revetment" and "집수정은 안 셈" in inlet["notes"][0] def test_독립_기슭막이_한쪽이면_두_칸이_같을_때만_세고_다르면_사유() -> None: same = {"facility": "revetment", "chainage_m": 50.0, "options": {"side": "좌"}} rows = _table([same])["structures"] assert [r["name"] for r in rows] == ["기슭막이 · 좌 벽"] and rows[0]["components"] differ = {**same, "options": {"side": "우", "inlet_revet_height_m": 3.0}} row = _table([differ])["structures"][0] assert not row["components"] and "서버가 못 가림" in row["notes"][-1] def test_산출식_없는_세월교는_사유로_서고_터파기_빠짐_줄을_안_만든다() -> None: table = _table([{"facility": "ford_bridge", "chainage_m": 30.0, "options": {}}]) assert "산출식이 아직 없습니다" in table["structures"][0]["notes"][0] rows = build_handoff(unit_quantity_table=table)["work_items"] assert not any("터파기가 안 선 구조물" in str(row.get("spec_detail")) for row in rows) def test_구조물_목록의_관_지점_종류_옛_저장분은_안_셈(monkeypatch, tmp_path) -> None: old = StructureInstance( structure_id="old", type_id="revetment", placement="point", chainage_m=10.0 ) monkeypatch.setattr(material, "load_structures", lambda root: (1, [old])) targets, _names, skipped = material._collect_structures(str(tmp_path)) assert targets == [] and any("관 지점 정본이 주인" in note for note in skipped)