"""B08 운반 행(PLAN 251-1 · 사용자 159번) — 총괄자재표 → 운반 묶음. 거리이정표 행 · B09 운반공 줄의 한 원천. 묶음 = 총괄 줄 중 수량 있는 것 · 같은 묶음(이형철근 · 레미콘 · 깬돌 · 파형강관 · 커플링밴드)은 한 행 · 골재(모래 · 자갈 · 막자갈 · 혼합석) · 그 밖 줄은 따로 · 채집석 공제 뺌 · 장비 두 행(중기 · 기계) 늘. 옛 B09 운반 6줄 키(`haul:1~6` · 중기 · 기계 · 모래 · 자갈 · 시멘트 · 철근)는 그 묶음에 그대로 — 사용자 설계내역서 덮개가 고아가 되지 않게 · 그 여섯은 수량이 없어도 늘 있음. """ from __future__ import annotations from typing import Any from B08_Quantity.B08_Quantity_Material_Calc import DEDUCT_KEY #: 장비 — (묶음 키, 이름, 단위, 옛 번호) EQUIP: list[tuple[str, str, str, int]] = [ ("equip_heavy", "중기", "대", 1), ("equip_machine", "기계", "대", 2), ] #: 옛 B09 운반 줄 번호 — 묶음 키 → n(`haul:n`) LEGACY = { "equip_heavy": 1, "equip_machine": 2, "sand": 3, "gravel": 4, "cement": 5, "rebar": 6, } #: 총괄 줄 키 머리 → (묶음 키, 이름) — 여러 규격 = 한 행 PREFIX = [ ("rebar_", "rebar", "철근"), ("remicon_", "remicon", "레미콘"), ("stone_buy_", "stone", "깬돌"), ("pipe_", "pipe", "파형강관"), ("coupling_", "coupling", "커플링밴드"), ] #: 총괄 고정 줄 이름 · 단위(줄에 이름이 없어도 같은 이름 · 옛 여섯 늘 행) NAMES = { "sand": ("모래", "㎥"), "gravel": ("자갈", "㎥"), "rubble": ("막자갈", "㎥"), "mixed_aggregate": ("혼합석", "㎥"), "cement": ("시멘트", "대"), "rebar": ("철근", "ton"), "wire_mesh": ("와이어메쉬", "㎡"), "sod": ("떼", "㎡"), "revet_block": ("호안블럭", "㎡"), "flume_400c": ("수로관", "본"), "steel_grating": ("스틸그레이팅", "EA"), } def _group(row: dict[str, Any]) -> tuple[str, str]: key = str(row.get("key") or "") for head, gkey, name in PREFIX: if key.startswith(head): return gkey, name if key in NAMES: return key, NAMES[key][0] return key, str(row.get("group") or row.get("spec") or key) # 맞춤 없는 사본 자재 = 자재 이름 def bill_key(gkey: str) -> str: """B09 운반 줄 키 — 옛 여섯은 `haul:n` 그대로 · 새 묶음 = `haul:<묶음 키>`.""" return f"haul:{LEGACY[gkey]}" if gkey in LEGACY else f"haul:{gkey}" def haul_groups(materials: dict[str, Any] | None) -> list[dict[str, Any]]: """총괄자재표(`material.rows`) → 운반 행 [{key, name, unit, qty}]. 장비 둘 + 묶음(차례 = 총괄 줄 차례).""" rows = ((materials or {}).get("material") or {}).get("rows") or [] out: dict[str, dict[str, Any]] = { g: {"key": g, "name": n, "unit": u, "qty": None} for g, n, u, _ in EQUIP } for row in rows: if row.get("key") == DEDUCT_KEY: # 채집석 공제(뺄 수량) continue gkey, name = _group(row) amount = row.get("amount") has = isinstance(amount, (int, float)) and amount > 0 slot = out.get(gkey) if slot is None: if not has and gkey not in LEGACY: continue # 수량 없는 새 묶음 = 행 없음 slot = out[gkey] = { "key": gkey, "name": name, "unit": row.get("unit") or "", "qty": None, } if has and (slot["unit"] in ("", row.get("unit"))): slot["unit"] = row.get("unit") or slot["unit"] slot["qty"] = round((slot["qty"] or 0) + float(amount), 6) for gkey in LEGACY: # 옛 여섯 = 늘 if gkey not in out: name, unit = NAMES[gkey] out[gkey] = {"key": gkey, "name": name, "unit": unit, "qty": None} return list(out.values()) def with_haul_routes(data: dict[str, Any], groups: list[dict[str, Any]]) -> dict[str, Any]: """거리이정표 자료에 운반 행 상자 자동(251-2) — 묶음마다 거래처 상자가 없으면 넣음. 거래처 · 주소 빈칸 · 선 없음(linked false) · 항목 = 자재 이름 · 저장은 [저장] 때(읽기만 합침).""" have = {r.get("haul") for r in data.get("routes") or []} routes = list(data.get("routes") or []) for g in groups: if g["key"] in have: continue routes.append( { "id": f"h-{g['key']}"[:40], "name": "", "address": "", "to": "site", "linked": False, "dir": None, "haul": g["key"], "legs": [{"road": None, "km": None}], "rows": [{"item": g["name"], "dest": "start", "note": ""}], } ) return {**data, "routes": routes} def route_bill_key(route: dict[str, Any], index: int = 0) -> str: """거리이정표 행 → B09 운반공 줄 키(253-1) — 자동 상자 = 옛 키 · 사용자 상자 = `haul:r-{id}` · 한 상자 둘째 행부터 `.{n}`(261-4 자동 상자도 · 경로 id · 묶음 키에 「.」 없음 — 안 겹침).""" tail = f".{index}" if index else "" if route.get("haul"): return bill_key(str(route["haul"])) + tail return f"haul:r-{route.get('id')}" + tail def haul_rows(data: dict[str, Any], table: list[dict[str, Any]]) -> list[dict[str, Any]]: """거리이정표 표 행 하나 = 운반공 줄 하나(253-1 · 261-4 단가연결 무관) — [{key, name, ref, user, haul, L1, L2}] · name = 행 항목명 그대로(없으면 거래처 · 그것도 없으면 빈칸) · L1 = 포장 계(2차 + 1차 + 포장) · L2 = 비포장(`compute_milestone` 결과) · user = 사용자 상자 · haul = 자동 상자 묶음 키(첫 행은 총괄자재 묶음 줄 이름 · 거리).""" routes = {r.get("id"): r for r in data.get("routes") or []} out, seen = [], set() for row in table: route = routes.get(row.get("route")) or {} key = route_bill_key(route, int(row.get("index") or 0)) if key in seen: continue seen.add(key) out.append( { "key": key, "name": str(row.get("item") or "").strip() or str(route.get("name") or "").strip(), "ref": route.get("ref"), "user": not route.get("haul"), "haul": route.get("haul"), "L1": row.get("paved"), "L2": row.get("unpaved"), } ) return out