diff --git a/B09_Estimation/B09_Estimation_UI_Tabs_Summary.ts b/B09_Estimation/B09_Estimation_UI_Tabs_Summary.ts index ecf5fd0ad..27d21fa0b 100644 --- a/B09_Estimation/B09_Estimation_UI_Tabs_Summary.ts +++ b/B09_Estimation/B09_Estimation_UI_Tabs_Summary.ts @@ -4,6 +4,7 @@ * * 값 = 서버 단독 역전개(`B09_Estimation_Summary` · GET 응답 `summary`) — 조작 중엔 안 바뀜 · [저장] 뒤 정본 * 끝수 = 총수량 ROUND 2 · 금액 ROUND(수량 × 단가, 0) · 줄 누름 = 단가를 낸 목록표 줄 + * 경비 집계표 빈 까닭(166-5) = 산근 · 대가 어느 길도 경비 자원 줄을 안 냄(기계 → 중기 · 구역화물 → 자재) * ========================================================================== */ import type { DataRow } from "@ui/ui_template_data_table"; @@ -26,7 +27,10 @@ const hint = (payload: Row, extra = ""): string => { return `${HINT}${extra}${n ? ` · 연결 없음 ${n} — 전개 안 함(연결 점검 탭)` : ""}`; }; -function resourceTab(id: string, list: TabId, unitPrice: string): TabBuild { +/** 줄이 없을 때 표 위 맨 앞 안내 */ +const EXPENSE_NONE = "경비 갈래 자원 없음 — 중기 · 자재로 셈 · 경비 로직이 들어오면 채워짐"; + +function resourceTab(id: string, list: TabId, unitPrice: string, none = ""): TabBuild { return ({ payload }): TabTable => { const rows: DataRow[] = ((payload.summary || {})[id] || []).map((r: Row) => ({ ...r })); const sum = rows.reduce((acc, r) => acc + (typeof r.amount === "number" ? r.amount : 0), 0); @@ -44,7 +48,7 @@ function resourceTab(id: string, list: TabId, unitPrice: string): TabBuild { rows, total: rows.length ? { name: "계", amount: sum } : null, frozen: 2, - hint: hint(payload), + hint: !rows.length && none ? `${none} · ${hint(payload)}` : hint(payload), link: (i) => (rows[i] ? { tab: list, key: String(rows[i].key) } : null), }; }; @@ -52,7 +56,7 @@ function resourceTab(id: string, list: TabId, unitPrice: string): TabBuild { export const buildSumMaterial = resourceTab("sum_material", "material", "단가"); export const buildSumLabor = resourceTab("sum_labor", "labor", "단가"); -export const buildSumExpense = resourceTab("sum_expense", "expense", "단가"); +export const buildSumExpense = resourceTab("sum_expense", "expense", "단가", EXPENSE_NONE); export const buildSumMachine: TabBuild = ({ payload }): TabTable => { const rows: DataRow[] = ((payload.summary || {}).sum_machine || []).map((r: Row) => ({ ...r })); diff --git a/resources/tester/test_145_5_b09_summary.py b/resources/tester/test_145_5_b09_summary.py index 8c780ca8c..2a6375f82 100644 --- a/resources/tester/test_145_5_b09_summary.py +++ b/resources/tester/test_145_5_b09_summary.py @@ -8,6 +8,7 @@ """ import asyncio +import json import sys from pathlib import Path @@ -54,7 +55,7 @@ LINES = { {"kind": "machine", "key": "X1", "qty": 4.295}, {"kind": "hopyo", "key": "B", "qty": 0.5}, {"kind": "material", "key": "M4", "qty": 0.354}, - {"kind": "expense", "key": "E16", "qty": 1.103}, + {"kind": "expense", "key": "E16", "qty": 1.103}, # 합성 줄 — 산근 · 대가는 경비 줄을 안 냄(166-5) ]}, "B": {"lines": [{"kind": "labor", "key": "L1", "qty": 1.658}, {"kind": "labor", "key": "L9", "qty": 1}]}, @@ -155,3 +156,26 @@ def test_산근_블록_역전개_엑셀_반올림(): machine = s["sum_machine"][0] # 중기시간금액집계표 10 줄 노무비 assert (machine["qty"], machine["labor"], machine["sum"]) == (1436.14, 79992998, None) assert s["unlinked"] == 3 # 토공 · 대가 전 구조물 · 수량 빈칸 = 전개 안 함 + + +# ── 166-5 경비 집계표 — 실제 블록 꼴은 경비 자원 0 → 표 위 안내 ── +from test_155_b09_tabs import _node # noqa: E402 + +EXPENSE_NONE = "경비 갈래 자원 없음 — 중기 · 자재로 셈 · 경비 로직이 들어오면 채워짐" + + +def _expense_tab(summary): + return _node( + f"\nconst t = buildSumExpense({{ payload: {{ summary: {json.dumps(summary)} }} }});" + "\nconsole.log(JSON.stringify({ n: t.rows.length, hint: t.hint }));", + "UI_Tabs_Summary", + ) + + +def test_166_5_실자료_경비_0_이면_안내(): + s = compute_summary(SG_SHEETS, hopyo_lines(SG_SHEETS)) + assert s["sum_expense"] == [] # 산근 블록 꼴 = 경비 갈래 줄 없음 + out = _expense_tab(s) + assert out["n"] == 0 and out["hint"].startswith(EXPENSE_NONE) + filled = _expense_tab(compute_summary(SHEETS, LINES)) # 합성 경비 줄이 있으면 안내 없음 + assert filled["n"] == 1 and EXPENSE_NONE not in filled["hint"]