# -*- coding: utf-8 -*- """701-1 — 석축 · 옹벽 길이 → B08 구조물 집계표 열 · B09 설계내역서 구조물 줄(owners 수량). 견본: 옹벽 H=2.0 구간 100~112(12 m) · 돌쌓기(찰) H=2.0 구간 196~204(옛 길이 칸 99 안 씀) · 돌쌓기(찰) 높이 빈칸(null · 금액 안 듦 = 값 빈칸) · 큰돌쌓기 사본 없음(안 셈 · 미연결) """ from __future__ import annotations import json from pathlib import Path import pytest from B08_Quantity import B08_Quantity_Router_Summary as summary from B09_Estimation.B09_Estimation_Bill_Items import structure_items from M02_MasterTemplete.StructOptions import M02_SO_Definition as definition def _item(key: str) -> dict: return {"layer": "system", "owner": None, "key": key, "name": key} STRUCTURES = [ { "structure_id": "s-wall", "type_id": "retaining_wall", "placement": "interval", "chainage_m": 106, "start_m": 100, "end_m": 112, "options": {"height_m": 2}, "summary_item": _item("SS000201"), }, { "structure_id": "s-wet", "type_id": "masonry_wet", "placement": "interval", "chainage_m": 200, "start_m": 196, "end_m": 204, "options": {"height_m": 2.0, "length_m": 99}, "summary_item": _item("SS000202"), }, { "structure_id": "s-wet-blank", "type_id": "masonry_wet", "placement": "interval", "chainage_m": 300, "start_m": 295, "end_m": 305, "options": {"height_m": None}, "summary_item": _item("SS000202"), }, { "structure_id": "s-boulder", "type_id": "boulder_masonry", "placement": "interval", "chainage_m": 400, "start_m": 395, "end_m": 405, "options": {"height_m": 3}, "summary_item": _item("SS000203"), }, ] @pytest.fixture def out(tmp_path: Path) -> dict: root = tmp_path / "proj" route = root / "B05_Profile/route" route.mkdir(parents=True) (route / "structures.json").write_text( json.dumps({"revision": 1, "structures": STRUCTURES}, ensure_ascii=False), encoding="utf-8", ) names = definition.read()["종류"]["pipe"]["이름"] linked = {"s-wall": {}, "s-wet": {}, "s-wet-blank": {}} # 큰돌쌓기 = 사본 없음 return summary.pipe_summary(root, [], names, 6.0, linked=linked) def _col(out: dict, cid: str) -> dict: return next(c for c in out["columns"] if c["id"] == cid) def test_옹벽_석축_높이마다_길이_열(out): wall = _col(out, "pipe:wall|retaining_wall|2.0") assert wall["group"] == ["옹벽", "H=2.0"] and wall["label"] == "m" assert wall["combos"] == ["system||SS000201"] assert wall["owners"] == {"s-wall": 12.0} # 구간 시작~종료 wet = _col(out, "pipe:wall|masonry_wet|2.0") assert wet["owners"] == {"s-wet": 8.0} # 구간이 옛 길이 칸을 이김 assert out["total"]["pipe:wall|retaining_wall|2.0"] == 12.0 def test_빈_높이_미입력_열_값_빈칸(out): blank = _col(out, "pipe:wall|masonry_wet|-") assert blank["group"] == ["돌쌓기(찰)", "미입력"] assert blank["owners"] == {} and "pipe:wall|masonry_wet|-" not in out["total"] def test_사본_없는_벽_안_셈_미연결(out): assert not any("boulder_masonry" in c["id"] for c in out["columns"]) assert [f["id"] for f in out["unlinked"]] == ["s-boulder"] assert {"retaining_wall", "masonry_wet", "boulder_masonry"} <= set(out["types"]) def test_B09_구조물_줄_기타구조물_수량(out): rows = {r["key"]: r for r in structure_items(out)} wall = rows["st:pipe:wall|retaining_wall|2.0"] assert (wall["section"], wall["name"], wall["spec"], wall["unit"]) == ( "2.3", "옹벽", "H=2.0", "m", ) assert wall["qty"] == 12.0 and wall["owners"] == {"s-wall": 12.0} assert rows["st:pipe:wall|masonry_wet|-"]["qty"] is None def test_B09_금액_길이_곱하기_대가(out): from B09_Estimation.B09_Estimation_Calc_Bill import compute_estimation price = {"no": "대가1", "key": "blk:w", "labor": 96868, "material": 11309, "expense": 16417} inputs = {"bill": {"items": structure_items(out)}} sheets = compute_estimation(inputs, {}, {"s-wall": price}.get) row = next(r for r in sheets["bill"]["rows"] if r["key"].endswith("#blk:w")) assert (row["qty"], row["sum"], row["note"]) == (12.0, 1495128, "대가1 · 마스터")