feat(B09): 설계내역서 연결 고르기 서고 구조물조합 · 일위대가 사전조합 — 프로젝트 사본 들여와 새 호표 · 같은 것 재사용 · 저장 때 안 쓰는 사본 정리 (204-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
This commit is contained in:
2026-10-10 14:39:12 +09:00
co-authored by Claude Opus 5.5
parent 694ca5fee3
commit 49b3e79c2a
10 changed files with 558 additions and 31 deletions
+28 -3
View File
@@ -26,6 +26,7 @@ from decimal import Decimal
from pathlib import Path
from typing import Any
from B09_Estimation import B09_Estimation_Bill_Library as library
from B09_Estimation import B09_Estimation_Sangeun as sg
from B09_Estimation import B09_Estimation_Sangeun_Text as st
from M02_MasterTemplete import M02_Item_Prices as item_prices
@@ -282,6 +283,19 @@ class Builder:
}
)
def library(self, folder: Path, manifest: dict[str, Any]) -> str | None:
"""설계내역서 서고 사본(204-2) — 구조물조합 = 조합 블록 · 사전조합 = 겹 대가 블록 그대로."""
if manifest.get("종류") != "precombo":
return self.combo(folder)
labor = read_json(folder / LABOR_FILE)
ref = (manifest.get("원본") or {}).get("참조")
copies = (labor or {}).get("참조사본") or {}
doc = copies.get(refs.ref_id(ref)) if isinstance(ref, dict) else None
if not isinstance(doc, dict):
return None
whole = item_prices.load_prices(read_json(folder / PRICE_FILE) or {})
return self.precombo(whole, ref, doc, copies, 1)
def _material(whole: Any, mat: dict, qty: Any) -> dict[str, Any]:
"""자재 줄 — 조합 사용자 자재 · 사전조합 자재(175-2) 한 꼴 · 이름 · 규격 · 단위 = 사본 단가."""
@@ -327,9 +341,11 @@ def _plain(value: Any) -> Any:
def daega_inputs(project_root: str | Path) -> dict[str, Any]:
"""대가 셈 입력 — `{blocks: [...], owners: {구조물 id: 블록 id}}` · 사본 없으면 빈 것.
"""대가 셈 입력 — `{blocks: [...], owners: {구조물 id: 블록 id}}` · 사본 없으면 빈 것 ·
설계내역서 서고 사본(204-2)이 있으면 `libs` {폴더: 블록 id}.
차례 = 조합(구조물 id 차례) → 사전조합 → 일위대가화(품셈 · 처음 본 차례) — 호표 번호 = 이 차례.
차례 = 조합(구조물 id 차례 → 서고 사본) → 사전조합 → 일위대가화(품셈 · 처음 본 차례) —
호표 번호 = 이 차례.
"""
base = structures_dir(project_root)
build = Builder()
@@ -343,6 +359,15 @@ def daega_inputs(project_root: str | Path) -> dict[str, Any]:
bid = None # 한 사본이 막혀도 나머지 블록은 셈 — 그 구조물은 없음
if bid:
owners[owner] = bid
libs: dict[str, str] = {}
for folder, manifest in library.copies(project_root):
try:
bid = build.library(folder, manifest)
except (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError):
bid = None
if bid:
libs[folder.name] = bid
order = {"조합": 0, "사전조합": 1, "품셈": 2}
blocks = sorted(build.blocks.values(), key=lambda b: order[b["kind"]])
return _plain({"blocks": blocks, "owners": owners})
out = {"blocks": blocks, "owners": owners}
return _plain({**out, "libs": libs} if libs else out)