- hopyo_lines(sheets) = 산근 블록(145-1 sheets['sg']) · 대가 블록(145-2 daega_blocks) 줄 → 호표 구성 - 산근 → 하위 산근 → 중기 겹 전개 · % 줄 · 빈칸 줄은 자원 아님 - 수량 · 호표 · 호표 구성 없는 내역 항목 = 전개 안 함 · summary.unlinked · 화면 안내 「연결 없음 n」 - 시험 = 엑셀 노무 석공 · 보통인부 · 재료 폭약 · 중기 굴삭기+브레카 반올림 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
89 lines
3.4 KiB
TypeScript
89 lines
3.4 KiB
TypeScript
/* =============================================================================
|
||
* B09_Estimation_UI_Tabs_Summary.ts
|
||
* B09 집계표 넷(PLAN 145-5) — 재료비 · 노무비 · 경비 수량금액집계표 · 중기시간금액집계표(실무 엑셀 꼴).
|
||
*
|
||
* 값 = 서버 단독 역전개(`B09_Estimation_Summary` · GET 응답 `summary`) — 조작 중엔 안 바뀜 · [저장] 뒤 정본
|
||
* 끝수 = 총수량 ROUND 2 · 금액 ROUND(수량 × 단가, 0) · 줄 누름 = 단가를 낸 목록표 줄
|
||
* ========================================================================== */
|
||
|
||
import type { DataRow } from "@ui/ui_template_data_table";
|
||
import type { Row } from "./B09_Estimation_Calc_Base";
|
||
import {
|
||
money,
|
||
qty,
|
||
text,
|
||
type TabBuild,
|
||
type TabId,
|
||
type TabTable,
|
||
} from "./B09_Estimation_UI_Tabs";
|
||
|
||
const HINT =
|
||
"총수량 = 설계내역서 수량 × 호표(산근 · 대가) 구성 역전개(서버 · [저장] 뒤 다시 셈) · ROUND 2 · 금액 = ROUND(수량 × 단가, 0) · 줄 누름 = 목록표";
|
||
|
||
/** 안내 + 「연결 없음 n」(전개 안 한 내역 항목 — 조합 밖 토공 · 운반 산근 등) */
|
||
const hint = (payload: Row, extra = ""): string => {
|
||
const n = (payload.summary || {}).unlinked || 0;
|
||
return `${HINT}${extra}${n ? ` · 연결 없음 ${n} — 전개 안 함(연결 점검 탭)` : ""}`;
|
||
};
|
||
|
||
function resourceTab(id: string, list: TabId, unitPrice: string): 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);
|
||
return {
|
||
columns: [
|
||
qty("no", "호표", 0),
|
||
text("name", "명칭", 200),
|
||
text("spec", "규격", 140),
|
||
qty("qty", "수량", 2),
|
||
text("unit", "단위", 56),
|
||
money("price", unitPrice),
|
||
money("amount", "금액"),
|
||
text("note", "비고", 120),
|
||
],
|
||
rows,
|
||
total: rows.length ? { name: "계", amount: sum } : null,
|
||
frozen: 2,
|
||
hint: hint(payload),
|
||
link: (i) => (rows[i] ? { tab: list, key: String(rows[i].key) } : null),
|
||
};
|
||
};
|
||
}
|
||
|
||
export const buildSumMaterial = resourceTab("sum_material", "material", "단가");
|
||
export const buildSumLabor = resourceTab("sum_labor", "labor", "단가");
|
||
export const buildSumExpense = resourceTab("sum_expense", "expense", "단가");
|
||
|
||
export const buildSumMachine: TabBuild = ({ payload }): TabTable => {
|
||
const rows: DataRow[] = ((payload.summary || {}).sum_machine || []).map((r: Row) => ({ ...r }));
|
||
const add = (key: string) =>
|
||
rows.reduce((acc, r) => acc + (typeof r[key] === "number" ? (r[key] as number) : 0), 0);
|
||
return {
|
||
columns: [
|
||
qty("no", "호표", 0),
|
||
text("name", "명칭", 200),
|
||
text("spec", "규격", 120),
|
||
qty("qty", "수량", 2),
|
||
text("unit", "단위", 56),
|
||
money("sum", "합계"),
|
||
money("labor", "노무비"),
|
||
money("material", "재료비"),
|
||
money("expense", "경비"),
|
||
text("note", "비고", 120),
|
||
],
|
||
rows,
|
||
total: rows.length
|
||
? {
|
||
name: "계",
|
||
sum: add("sum"),
|
||
labor: add("labor"),
|
||
material: add("material"),
|
||
expense: add("expense"),
|
||
}
|
||
: null,
|
||
frozen: 2,
|
||
hint: hint(payload, " · 비목마다 ROUND(시간 × 시간당, 0)"),
|
||
link: (i) => (rows[i] ? { tab: "machine", key: String(rows[i].key) } : null),
|
||
};
|
||
};
|