feat(M02): 구조물 조합 일위대가 합계 칸 상태 색 · 머리와 합계 칸 한 상자 (101-1 · 101-2)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
2026-10-08 16:24:04 +09:00
co-authored by Claude Sonnet 5.5
parent 96c7fecd41
commit 31fa81174f
9 changed files with 151 additions and 11 deletions
+23 -4
View File
@@ -237,20 +237,39 @@ def _total(sums: Any) -> float | None:
return None
def _sum_state(calc: dict[str, Any]) -> dict[str, Any]:
"""합계 칸 색 상태(101-1) — 붉음 `error` > 노랑 `short` > 초록 `ok` + 까닭 글.
붉음 = 기준이 안 나온 돈 줄(까닭 있음) · 기준 단가 없는 자재 / 노랑 = 수량 없는 줄 · 자재 /
돈 아닌 로직 줄(`결과`)은 판정에서 뺌."""
bad = short = 0
for one in (*(calc.get("줄") or []), *(calc.get("자재") or [])):
if not isinstance(one, dict):
continue
if one.get("기준", one.get("기준단가")) is None:
bad += 1 if one.get("까닭") else 0
elif one.get("수량") is None:
short += 1
why = [f"{t} {n}" for t, n in (("기준 단가 없음", bad), ("수량 없는 줄", short)) if n]
return {
"합계상태": "error" if bad else "short" if short else "ok",
"합계까닭": " · ".join(why) or "모두 반영",
}
def _labor_sum(doc: dict[str, Any]) -> dict[str, Any]:
"""일위대가 합계 칸(86-3) — 반영합 `계`(0 아님) · 없으면 기준합 `계` + `합계기준` 참 ·
담은 로직 0 · 계산값 없음 = 빈 칸 (목록 `labor` 합계와 같은 규칙)."""
담은 로직 0 · 계산값 없음 = 빈 칸 (목록 `labor` 합계와 같은 규칙) · `합계상태`(101-1)."""
if not doc.get("담은로직"):
return {}
calc = doc.get("계산값") or {}
if total := _total(calc.get("반영합")):
return {"일위대가합계": total, "합계기준": False}
return {"일위대가합계": total, "합계기준": False, **_sum_state(calc)}
base = _total(calc.get("기준합"))
return {} if base is None else {"일위대가합계": base, "합계기준": True}
return {} if base is None else {"일위대가합계": base, "합계기준": True, **_sum_state(calc)}
def structure_numbers() -> dict[str, dict[str, Any]]:
"""`{열 id: {도번, 일위대가번, 면적번, (일위대가합계, 합계기준)}}` — 집계표 줄 · 좌측 줄이 한 번에
"""`{열 id: {도번, 일위대가번, 면적번, (일위대가합계, 합계기준, 합계상태, 합계까닭)}}` — 집계표 줄 · 좌측 줄이 한 번에
받음(번호 없는 쪽은 빈 글 · 합계는 일위대가 계산값이 있을 때만)."""
out: dict[str, dict[str, Any]] = {}
for kind, key, _, _ in PARTS: