/* ============================================================================= * B09_Estimation_UI_Tabs_Sangeun.ts * B09 단가산출근거 · 단가산출근거목록표 탭(PLAN 145-1) — 값 = 서버 `B09_Estimation_Sangeun` * (서버 단독 · 이 탭엔 조작 없음 · [저장] · [단가 갱신] 때 서버가 다시 셈). * * 목록표 = 산근 호표 한 줄(합계 · 노무비 · 재료비 · 경비 · 1 단위) · 줄 누름 = 그 블록 * 풀이표 = 블록 머리(제 n 호표) · 줄(노무 · 중기 노 · 재 · 경 · 자재 · 하위 산근 · % 줄) · 총계 * 줄 누름 = 단가를 낸 탭 · 줄(노무비목록표 · 중기목록표 · 자재단가대비표 · 하위 산근) * ========================================================================== */ import type { DataRow } from "@ui/ui_template_data_table"; import type { Row } from "./B09_Estimation_Calc_Base"; import { money, qty, text, type Link, type TabBuild, type TabId, type TabTable, } from "./B09_Estimation_UI_Tabs"; const PART_NAME: Record = { labor: "노무비", material: "재료비", expense: "경 비", }; const PARTS = ["labor", "material", "expense"] as const; const notes = (sheets: Row): string => ((sheets.sg_notes as string[]) || []).map((n) => ` · ${n}`).join(""); export const buildSgList: TabBuild = ({ sheets }): TabTable => { const rows: DataRow[] = ((sheets.sg_list as Row[]) || []).map((r) => ({ ...r, note: `쓰는 구조물 ${(r.uses || []).length}`, })); return { columns: [ qty("no", "호표", 0), text("name", "명칭", 220), text("spec", "규격(입력값)", 200), text("unit", "단위", 56), money("sum", "합계"), money("labor", "노무비"), money("material", "재료비"), money("expense", "경비"), text("note", "비고", 110), ], rows, frozen: 2, hint: "산근 = 연결된 구조물 조합 사본의 「단가승격」 로직 줄 · 같은 로직 + 같은 입력값 = 한 호표 · 합계 = 블록 총계 · 줄 누름 = 그 블록" + notes(sheets), link: (i) => (rows[i] ? { tab: "sg", key: String(rows[i].key) } : null), }; }; const LINE_TAB: Record = { labor: "labor", machine: "machine", compare: "compare", sg: "sg", }; export const buildSg: TabBuild = ({ sheets }): TabTable | string => { const rows: DataRow[] = []; const links: Array = []; const push = (row: DataRow, link: Link | null = null): void => { rows.push(row); links.push(link); }; for (const b of (sheets.sg as Row[]) || []) { push({ key: b.key, name: `제 ${b.no} 호표 ${b.name}`, spec: b.spec, unit: b.unit, sum: b.sum, labor: b.labor, material: b.material, expense: b.expense, }); if (b.source) push({ key: `${b.key}|출처`, name: ` ${b.source}` }); (b.lines as Row[]).forEach((ln, n) => { const link = ln.ref?.key ? { tab: LINE_TAB[ln.ref.tab] ?? "compare", key: ln.ref.key } : null; // 중기 = 노 · 재 · 경 세 줄(엑셀 꼴) · 그 밖 = 한 줄 const parts = ln.kind === "machine" ? PARTS.filter((p) => ln.price?.[p] !== 0) : [null]; for (const part of parts) { const at = (p: string): number | null => (part === null || part === p ? ln[p] : 0); const price = part ? ln.price?.[part] : (Object.values(ln.price || {})[0] ?? null); push( { key: `${b.key}|${n}${part ? `|${part}` : ""}`, name: ` ${ln.name}${part ? ` ${PART_NAME[part]}` : ""}`, spec: ln.why ? `${ln.spec ? `${ln.spec} · ` : ""}${ln.why}` : ln.spec, qty: ln.qty, unit: ln.unit, price: price as number | null, sum: PARTS.some((p) => at(p) === null) ? null : PARTS.reduce((s, p) => s + (at(p) as number), 0), labor: at("labor"), material: at("material"), expense: at("expense"), }, link, ); } }); push({ key: `${b.key}|총계`, name: " 총 계", spec: b.why ?? "", sum: b.sum, labor: b.labor, material: b.material, expense: b.expense, }); } if (!rows.length) return ((sheets.sg_notes as string[]) || []).join(" · ") || "산근 없음"; return { columns: [ text("name", "공종 · 산출내역", 260), text("spec", "규격 · 까닭", 180), qty("qty", "수량"), text("unit", "단위", 56), money("price", "단가", undefined, 1), money("sum", "합계", undefined, 1), money("labor", "노무비", undefined, 1), money("material", "재료비", undefined, 1), money("expense", "경비", undefined, 1), ], rows, frozen: 1, hint: "수량 = 로직 품(1 단위 · 작업량 Q = ROUND 2) · 단가 = 굳힌 B09 단가 · 줄 ROUNDDOWN 1 · 총계 비목마다 ROUNDDOWN 0 · 자재 적용 단가를 고치면 [저장] 뒤 반영 · 줄 누름 = 단가를 낸 탭" + notes(sheets), link: (i: number) => links[i] ?? null, }; };