"""B09 설계내역서 항목 짜기(PLAN 145-3) — 서버 단독 · B08 수량 → 내역 줄(수량 · 단가 자리). 공종 나무 = 엑셀 기번3 공통 꼴 고정(`B09_Estimation_Calc_Bill.SECTIONS`) · 공구 안 나눔. 토공(1.1~1.6) = B08 토공집계표 줄 짝(`EARTH` — 엑셀 항목 이름 ↔ 토공집계표 구분 · 종류 · 규격) · 단가 = 조합 밖 토공 산근 · 준비공 대가 — 원천 없음 → 빈칸 「연결 없음」(지시) 구조물(2.1~2.3) = B08 구조물 집계표 열(사본 있을 때만 · 143) · 구조물군 → 공종 짝(`STRUCTURE_SECTION`) · 커플링 열은 자재(관급자재대)라 뺌 · 단가 = 대가 호표(145-2 가 `hopyo` 로 줌 · 그전엔 빈칸) 운반(3) = 엑셀 운반공 줄 · 수량 = B08 총괄자재 짝 · 거리 · 단가 = 운반 산근(원천 없음 → 빈칸) 자재대 · 관급자재대 = 화면 짝이 대비표 「별도」 · 「관급」 표시로 짬(사용자 조작 즉시) """ from __future__ import annotations from typing import Any from B08_Quantity.B08_Quantity_Material_Calc import _structure_row WHY_EARTH = "조합 밖 토공 산근 — 연결 없음" WHY_READY = "준비공 대가 — 연결 없음" WHY_HAUL = "운반 산근 — 연결 없음(운반거리 원천 없음)" WHY_STRUCTURE = "대가 호표 — 연결 없음(일위대가 145-2)" #: 토공 항목 — (공종, 엑셀 명칭, 단위, 토공집계표 (구분, 종류, 규격 | None) | None) EARTH: list[tuple[str, str, str, tuple[str, str, str | None] | None]] = [ ("1.1", "규준틀설치 종단규준틀", "개소", None), ("1.1", "규준틀설치 횡단규준틀", "개소", None), ("1.1", "제근", "㎡", None), ("1.2", "절토(토사)", "㎥", ("흙깍기", "토사", None)), ("1.2", "절토(연암)", "㎥", ("흙깍기", "연암", None)), ("1.2", "암발파", "㎥", ("흙깍기", "경암", None)), ("1.3", "구조물터파기(토사)", "㎥", ("구조물터파기", "토사", None)), ("1.3", "구조물터파기(연암)", "㎥", ("구조물터파기", "연암", None)), ("1.3", "구조물 되메우기", "㎥", ("되메우기", "", None)), ("1.4", "측구파기(토사)", "㎥", ("측구 터파기", "토사", None)), ("1.4", "측구파기(연암)", "㎥", ("측구 터파기", "연암", None)), ("1.5", "유용토운반", "㎥", ("유용토 소계", "", None)), ("1.5", "도자운반", "㎥", ("토사운반", "", "19 TON B/D")), ("1.5", "덤프운반", "㎥", ("토사운반", "", "BH+DT+BH")), ("1.6", "사토처리", "㎥", ("잔토처리", "", None)), ("1.6", "성토사면다짐", "㎡", ("성토면다짐", "", None)), ("1.6", "노면정리", "㎡", None), ] #: 구조물 집계표 열 id 머리 → 공종(그 밖 = 2.2 배수공) · 커플링 = 자재라 뺌 STRUCTURE_SECTION = {"pipe:revet|": "2.1", "pipe:chute|": "2.3"} SKIP_COLUMNS = ("pipe:cp|",) #: 운반공 — (명칭, 단위, B08 총괄자재 줄 키(합) | None) HAUL: list[tuple[str, str, tuple[str, ...] | None]] = [ ("중기운반", "대", None), ("기계운반", "대", None), ("모래운반", "㎥", ("sand",)), ("자갈운반", "㎥", ("gravel",)), ("시멘트운반", "대", ("cement",)), ("철근운반", "ton", ("rebar_d13", "rebar_d16", "rebar_d19", "rebar_d22", "rebar_d25")), ] def _same(row: dict[str, Any], want: tuple[str, str, str | None]) -> bool: group, kind, spec = want same = row.get("group") == group and (row.get("kind") or "") == kind return same and (spec is None or row.get("spec") == spec) def earth_items(earthwork: dict[str, Any] | None) -> list[dict[str, Any]]: rows = (earthwork or {}).get("summary") or [] out = [] for n, (section, name, unit, want) in enumerate(EARTH, 1): source = next((r for r in rows if want and _same(r, want)), None) out.append( { "key": f"earth:{n}", "section": section, "name": name, "spec": (source or {}).get("spec") or "", "unit": unit, "qty": source.get("value") if source else None, "note": "B08 토공집계표" if source else "", "ref": None, "why": WHY_READY if section == "1.1" else WHY_EARTH, } ) return out def structure_items(summary: dict[str, Any] | None) -> list[dict[str, Any]]: columns = (summary or {}).get("columns") or [] total = (summary or {}).get("total") or {} out = [] for column in columns: cid = str(column.get("id") or "") if cid.startswith(SKIP_COLUMNS): continue section = next((s for head, s in STRUCTURE_SECTION.items() if cid.startswith(head)), "2.2") name, spec, unit = _structure_row(column) combos = [c for c in column.get("combos") or [] if c] out.append( { "key": f"st:{cid}", "section": section, "name": name, "spec": spec, "unit": unit, "qty": total.get(cid), "note": " · ".join(combos), "ref": {"tab": "dg_list", "key": cid}, "why": WHY_STRUCTURE, } ) return out def haul_items(materials: dict[str, Any] | None) -> list[dict[str, Any]]: rows = ((materials or {}).get("material") or {}).get("rows") or [] amount = {row.get("key"): row.get("amount") for row in rows} out = [] for n, (name, unit, keys) in enumerate(HAUL, 1): qty = None if keys: found = [amount[k] for k in keys if amount.get(k) is not None] qty = round(sum(found), 6) if found else None out.append( { "key": f"haul:{n}", "section": "3", "name": name, "spec": "", "unit": unit, "qty": qty, "note": "B08 총괄자재" if qty is not None else "", "ref": None, "why": WHY_HAUL, } ) return out def bill_items(b08: dict[str, Any]) -> dict[str, Any]: return { "items": [ *earth_items(b08.get("earthwork")), *structure_items(b08.get("summary")), *haul_items(b08.get("materials")), ] } def hopyo_prices(project_root: Any) -> dict[str, dict[str, float]]: """호표 단가 `{호표 자리 키: {labor, material, expense}}` — 145-1 · 2(산근 · 대가)가 들어오면 채움 · 그전엔 빈 묶음(구조물 단가 = 빈칸 「연결 없음」).""" return {}