diff --git a/B09_Estimation/B09_Estimation_Calc_Bill.py b/B09_Estimation/B09_Estimation_Calc_Bill.py index efce2981a..161fcefba 100644 --- a/B09_Estimation/B09_Estimation_Calc_Bill.py +++ b/B09_Estimation/B09_Estimation_Calc_Bill.py @@ -126,6 +126,8 @@ def split_by_block(item: dict[str, Any], price_of: PriceOf) -> list[tuple[dict, "note": " · ".join(t for t in (tag, item.get("note") or "") if t), "owners": {o: item["owners"][o] for o in group["owners"]}, } + if group["price"] and group["price"].get("key"): # 대가 블록(145-2) — 이동 · 집계표 역전개 + part["ref"] = {"tab": "dg", "key": group["price"]["key"]} out.append((part, group["price"])) return out diff --git a/B09_Estimation/B09_Estimation_Calc_Bill.ts b/B09_Estimation/B09_Estimation_Calc_Bill.ts index 0661db921..e20791f66 100644 --- a/B09_Estimation/B09_Estimation_Calc_Bill.ts +++ b/B09_Estimation/B09_Estimation_Calc_Bill.ts @@ -119,6 +119,8 @@ export function splitByBlock(item: Row, priceOf: PriceOf): Array<[Row, Row | nul note: [tag, item.note || ""].filter((t) => t).join(" · "), owners: Object.fromEntries(group.owners.map((o: string) => [o, item.owners[o]])), }; + // 대가 블록(145-2) — 이동 · 집계표 역전개 + if (group.price?.key) part.ref = { tab: "dg", key: group.price.key }; out.push([part, group.price]); } return out; diff --git a/B09_Estimation/B09_Estimation_Calc_Labor.py b/B09_Estimation/B09_Estimation_Calc_Labor.py index 98af00488..f5e4cf273 100644 --- a/B09_Estimation/B09_Estimation_Calc_Labor.py +++ b/B09_Estimation/B09_Estimation_Calc_Labor.py @@ -193,5 +193,6 @@ def daega_price(sheets: dict[str, Any], owner: str) -> dict[str, Any] | None: return None return { "no": f"대가{row['no']}", + "key": bid, **{k: row[k] for k in ("labor", "material", "expense", "sum")}, } diff --git a/B09_Estimation/B09_Estimation_Calc_Labor.ts b/B09_Estimation/B09_Estimation_Calc_Labor.ts index 9aa219000..5a1044be5 100644 --- a/B09_Estimation/B09_Estimation_Calc_Labor.ts +++ b/B09_Estimation/B09_Estimation_Calc_Labor.ts @@ -176,6 +176,7 @@ export function daegaPrice(sheets: Row, owner: string): Row | null { if (!row) return null; return { no: `대가${row.no}`, + key: bid, labor: row.labor, material: row.material, expense: row.expense, diff --git a/resources/tester/test_145_2_b09_labor_calc.py b/resources/tester/test_145_2_b09_labor_calc.py index 52c7792a1..78e877bbf 100644 --- a/resources/tester/test_145_2_b09_labor_calc.py +++ b/resources/tester/test_145_2_b09_labor_calc.py @@ -124,6 +124,7 @@ def test_엑셀_대가_블록_검산(): } # fmt: skip assert daega_price(out, "pipe@100.0") == { "no": "대가1", + "key": "DG14", "labor": 77755, "material": 9519, "expense": 14248, @@ -362,3 +363,52 @@ def test_집계표_이음_꼴(): ] assert {"kind": "machine", "key": "X00044", "qty": 0.0416} in lines["DG2"]["lines"] assert all(ln["kind"] != "pct" for ln in lines["DG2"]["lines"]) # % 줄 = 자원 아님 + + +def test_한_바퀴_내역_대가_산근_중기_집계표(tmp_path): + """이음 끝 확인 — 설계내역서 구조물 줄(B08 열 owners) → 대가 블록마다 나눔 → 대가 줄 → + 산근(145-1 · 승격 줄) → 중기 → 집계표(145-5) 역전개 · 짝 셈 한 벌 = Router.compute_sheets.""" + from B09_Estimation.B09_Estimation_Calc_Base import rounddown + from B09_Estimation.B09_Estimation_Router import compute_sheets + from B09_Estimation.B09_Estimation_Summary import compute_summary, hopyo_lines + + root = _project(tmp_path / "loop", [(100, "SS000901"), (400, "SS000902")]) + _snap(root, "pipe@100.0", "SS000901", LINES, _values(STONE)) + promoted = [ + {"로직": "GF000119", "승격": True, "수량": {"손": 1}}, # 노체포설 = 산근 · 중기 줄 + {"참조": {"층": "system", "주인": None, "키": "SC-T"}, "수량": {"손": 4}}, + ] + _snap(root, "pipe@400.0", "SS000902", promoted, _values(STONE), SC) + inputs = prices.build_inputs(None, linked_resource_keys(root)) + inputs["daega"] = daega_inputs(root) + inputs["hopyo"] = {} + inputs["bill"] = { + "items": [ + {"key": "st:revet", "section": "2.1", "name": "돌기슭막이", "spec": "", "unit": "m", + "qty": 10, "ref": {"tab": "dg_list", "key": "revet"}, "note": "", + "owners": {"pipe@100.0": 4, "pipe@400.0": 6}}, + ] + } # fmt: skip + sheets = compute_sheets(root, inputs, {}) + owners = sheets["daega_owners"] + rows = [r for r in sheets["bill"]["rows"] if r.get("kind") == "item"] + assert [r["ref"] for r in rows] == [ + {"tab": "dg", "key": owners["pipe@100.0"]}, + {"tab": "dg", "key": owners["pipe@400.0"]}, + ] + for row in rows: + block = _block(sheets, row["ref"]["key"]) + assert row["price_labor"] == block["labor"] and block["sum"] is not None + assert row["labor"] == rounddown(row["qty"] * block["labor"], 0) + # 산근 이음 — 대가 줄 키 = 145-1 산근 키 · 단가가 들어옴 + combo = _block(sheets, owners["pipe@400.0"]) + sg_line = combo["lines"][0] + sg_row = next(r for r in sheets["sg_list"] if r["key"] == sg_line["key"]) + assert sg_line["kind"] == "sangeun" and sg_line["prices"]["labor"] == sg_row["labor"] + # 집계표 역전개 — 대가 · 사전조합 · 품셈 · 산근 끝까지 + summary = compute_summary(sheets, hopyo_lines(sheets)) + assert summary["unlinked"] == 0 + labor = {r["key"]: r["qty"] for r in summary["sum_labor"]} + assert labor["LB000033"] == 25.6 # 4 × 2 × 0.32 + 6 × (4 × 3 × 0.32) + machine = {r["key"]: r["qty"] for r in summary["sum_machine"]} + assert machine.get("EQ000020", 0) > 0.08 # 품셈 4 × 0.5 × 0.0412 + 산근 6 × (중기 시간)