/* ============================================================================= * B09_Estimation_UI_Tabs_Labor.ts * B09 대가 탭 둘(PLAN 145-2) — 일위대가목록표 · 일위대가표(실무 엑셀 꼴 · 값 = `B09_Estimation_Calc_Labor`). * * 목록표 = 호표 한 줄(합계 · 노무비 · 재료비 · 경비) · 줄 누름 = 그 블록으로 * 일위대가표 = 공용 일위대가 격자(704-3 · `ui_template_hopyo_grid` · 머리 · [품셈 인용] · 줄 · 합계 · 줄 꼬리표) · * 줄 누름 = 단가를 낸 탭 · 줄(대가 줄 = 그 블록) * 줄 꼴(155-2) = 머리 · 합계 굵게(`is-title`) · 인용 · 줄 = 들여쓰기 「ㄴ」(2단 · 겹 대가는 안 펼침) * 연결된 구조물 조합 사본이 없으면 빈 표 「연결 없음」 * 빈 줄(166-6) = 합계 = 아는 줄 합 · 옆 「빈 줄 n」 · 풍선 = 빈 줄 이름 · 내역 단가엔 안 넘김 * ========================================================================== */ import type { DataRow } from "@ui/ui_template_data_table"; import type { Row } from "./B09_Estimation_Calc_Base"; import { blockLevel, money, text, type Link, type TabBuild, type TabId, type TabTable, } from "./B09_Estimation_UI_Tabs"; import { raw } from "./B09_Estimation_UI_Tabs_Sangeun"; import { HOPYO_TAG, hopyoCellClass, hopyoGrid } from "@ui/ui_template_hopyo_grid"; const EMPTY = "연결 없음 — 연결된 구조물 조합 사본(일위대가)이 없음 · B05 · B06 에서 조합을 고르고 [저장]"; const BLANK = "빈 줄은 금액을 모르는 줄(합계는 아는 줄만)"; /** 「빈 줄 n」 작은 글 · 풍선 = 빈 줄 이름 */ const blankNote = (b: Row): string => (b.blank ? `빈 줄 ${b.blank}` : ""); const blankTip = (b: Row | undefined): string | null => b?.blank ? `빈 줄 — ${(b.blank_names as string[]).join(" · ")}` : null; /** 블록 줄 → 단가를 낸 탭 */ const LINE_TAB: Record = { labor: "labor", material: "material", machine: "machine", daega: "dg", sangeun: "sg", }; export const buildDaegaList: TabBuild = ({ sheets }): TabTable | string => { const rows: DataRow[] = (sheets.daega || []).map((r: Row) => ({ ...r, no: `대가${r.no}`, blank_note: blankNote(r), })); if (!rows.length) return EMPTY; return { columns: [ text("no", "호표", 80), text("kind", "꼴", 70), text("name", "명칭", 240), text("spec", "규격", 200), text("unit", "단위", 56), money("sum", "합계"), text("blank_note", "", 64), money("labor", "노무비"), money("material", "재료비"), money("expense", "경비"), ], rows, frozen: 3, hint: `줄을 누르면 그 블록으로 이동 · ${BLANK}`, link: (i: number): Link | null => (rows[i] ? { tab: "dg", key: String(rows[i].key) } : null), tip: (i, key) => key === "spec" ? raw(rows[i]?.spec, rows[i]?.spec_raw) : key === "blank_note" || key === "sum" ? blankTip(rows[i]) : null, }; }; export const buildDaegaBlocks: TabBuild = ({ sheets }): TabTable | string => { const blocks: Row[] = sheets.daega_blocks || []; if (!blocks.length) return EMPTY; // 704-3 — 공용 일위대가 격자(M02 카드 · 상세와 같은 열 · 줄 꼴) · 값 = 서버 `Calc_Labor` 그대로 const grid = hopyoGrid( blocks.map((b) => ({ key: b.id, no: b.no, name: b.name, spec: b.spec, spec_raw: b.spec_raw, unit: b.unit, kind: b.kind, cite: b.cite, total: { sum: b.sum, labor: b.labor, material: b.material, expense: b.expense }, blank: b.blank ?? 0, blank_names: b.blank_names ?? [], lines: b.lines.map((ln: Row) => ({ name: ln.name || "", spec: ln.spec || "", spec_raw: ln.spec_raw, qty: ln.qty, unit: ln.unit || "", prices: { sum: ln.price, ...ln.prices }, amounts: { sum: ln.amount, ...ln.amounts }, note: ln.ref_no || ln.why || "", tag: HOPYO_TAG.pum, })), })), ); const { rows, titles } = grid; const links: Array = rows.map((_, i) => { const at = grid.at.get(i); const ln: Row | undefined = at && blocks[at[0]].lines[at[1]]; const tab = ln && LINE_TAB[ln.kind]; return tab && ln.key ? { tab, key: String(ln.key) } : null; }); return { columns: grid.columns, rows, frozen: 1, hint: `줄을 누르면 단가를 낸 탭으로 이동 · ${BLANK}`, link: (i: number) => links[i] ?? null, rowClass: (i: number) => (titles.has(i) ? "is-title" : null), cellClass: (i: number, key: string) => hopyoCellClass(rows, i, key), level: blockLevel("name", (i) => titles.has(i)), tip: (i: number, key: string) => key === "spec" ? raw(rows[i]?.spec, rows[i]?.spec_raw) : key === "note" || key === "sum" ? blankTip(rows[i]) : null, }; };