/* -*- coding: utf-8 -*- */ /** * 보고서 양식 변형 (report_forms.js) — 원본 출력 견본이 없는 SET_REP 서식 * 0303 일위대가표(수공양식) · 0605 분할내역서 · 0607 사·관급내역서 · 0609 총괄집계표 · 0613·0614 견적내역서 · * 0615 견적단가대비표 · 0606·0654·0656 내역서 변형 · 0651·0655 구.시도 · 0652·0653·0672 총괄 변형 · * 0675~0677 재료비·노무비·중장비 집계표 · 0671·0678·0679 투입계획 · 0695 간이공사비 산출명세서 * 근거: 지식DB STmate/REA 분석 35(보고서양식_수량산출서) — ST_Prt 인쇄 분기(0x44fc34 · 서식 번호 → Prn_* 함수) · * 인쇄 양식 DFM(TPM_*: 머리 칸 글·차례·DB 칸 이름) · Prn_Won_A 구성비(T_PER = 금액 ÷ 총액 × 100 · 0x48c6ab) · * old/20_분석 20 §3(SET_REP FLD_N·FLD_W) · 24 §2.4(AMTTOHAN) * 판정: 부분 — 열 차례·머리 글 = 인쇄 양식 DFM · 값 출처 일부(분할 금회 수량 · 견적·기준 단가 · 회사분)는 후보 */ const StmateReportForms = (() => { const g = typeof globalThis !== "undefined" ? globalThis : {}; const C = g.STMATE_REPORT_CORE || require("./report_core.js"); const B = g.STMATE_REPORT_BOQ || require("./report_boq.js"); const Co = g.STMATE_REPORT_COLL || require("./report_coll.js"); const In = g.STMATE_REPORT_INIT || require("./report_init.js"); const { num, str, code, half, trunc } = C; /** * 인쇄 분기(ST_Prt 0x44fc34 · 분기 값 = 20000 + 서식 번호) — 서식 → [인쇄 함수, 엑셀 함수, 모듈] * 0106 = 0104 와 같은 갈래(Prn_iLMok·Prn_SanMok 중 설정 +0x173f) · 0671·0678·0679 = Prn_Nae_Plan_{A|K}_{X|M|L} */ const FORM_PROC = { "0106": ["Prn_iLMok|Prn_SanMok", "Prn_iLMok_EXL|Prn_SanMok_EXL", "ST_P_List"], "0206": ["Prn_AList", "Prn_AList_EXL", "ST_P_List"], "0303": ["Prn_iLWi_S", "Prn_iLWi_S_EXL", "ST_P_iLWi"], "0605": ["Prn_Nae_B", "Prn_Nae_B_EXL", "ST_P_Nae"], "0606": ["Prn_Nae_D", "Prn_Nae_P_EXL", "ST_P_Nae"], "0607": ["Prn_Bqm", "Prn_Bqm_EXL", "ST_P_Nae"], "0608": ["Prn_Wonga", "Prn_Wonga_EXL", "ST_P_Nae"], "0609": ["Prn_Won_A", "Prn_Won_A_EXL", "ST_P_Nae"], "0613": ["Prn_Nae|Prn_NaeW", "Prn_Nae_EXL", "ST_P_Nae"], "0614": ["Prn_Nae_P|Prn_NaeW_P", "Prn_Nae_P_EXL", "ST_P_Nae"], "0615": ["Prn_Nae_Est", "Prn_Nae_EST_EXL", "ST_P_ConEst"], "0651": ["Prn_Nae_Ex1_CT", "Prn_Nae_Ex1_CT_EXL", "ST_P_Excav"], "0652": ["Prn_Nae_Ex1_C1", "Prn_Nae_Ex1_C1_EXL", "ST_P_Excav"], "0653": ["Prn_Nae_Ex1_C2", "Prn_Nae_Ex1_C2_EXL", "ST_P_Excav"], "0654": ["Prn_Nae_Ex1_0", "Prn_Nae_Ex1_0_EXL", "ST_P_Excav"], "0655": ["Prn_Nae_Ex1_T", "Prn_Nae_Ex1_T_EXL", "ST_P_Excav"], "0656": ["Prn_Nae_Ex1_1", "Prn_Nae_Ex1_1_EXL", "ST_P_Excav"], "0671": ["Prn_Nae_Plan_{A|K}_X", "Prn_Nae_PLNX", "ST_P_Nae"], "0672": ["Prn_Jab", "Prn_Jab_EXL", "ST_P_Nae"], "0675": ["Prn_Nae_ColM", "Prn_Nae_ColM", "ST_P_Nae"], "0676": ["Prn_Nae_ColL", "Prn_Nae_ColL", "ST_P_Nae"], "0677": ["Prn_Nae_ColX", "Prn_Nae_ColX", "ST_P_Nae"], "0678": ["Prn_Nae_Plan_{A|K}_M", "Prn_Nae_PLNM", "ST_P_Nae"], "0679": ["Prn_Nae_Plan_{A|K}_L", "Prn_Nae_PLNL", "ST_P_Nae"], "0695": ["Prn_Boq_Not", "Prn_Boq_Not", "ST_P_Nae"], }; // SET_REP 칸 이름(FLD_N1~) function fields(dbf, indx) { const r = ((dbf && dbf.set_rep_rows) || []).find((x) => String(x.INDX).trim() === indx); const out = []; if (r) for (let i = 1; i <= 11; i++) out.push(str(r[`FLD_N${i}`]).trim()); return out; } const form = (sh, dbf, indx) => C.applyForm(sh, dbf, indx); const T4 = (s) => s.M + s.L + s.S; const excluded = (n) => { for (let p = n.parent; p; p = p.parent) if (p.excluded) return true; return false; }; const won = (n) => C.commas(n); /** * 0303 일위대가표(수공양식 · TPM_ILWI_S) — 호표마다 머리 세 줄 * 1줄 「항 목 번 호 :」 · 「제 n 호표」 · 비고 칸 = 코드 / 2줄 「공 종 :」 명칭 · 규격 · 재료비·노무비·경비·총액 / * 3줄 「단 위 :」 단위 — 아래 구성 줄 = 명칭 · 규격 · 수량 · 단위 · 재·노·경 단가·금액 · 비고(코드) · 총액 칸 없음 */ function iLwiHandSheet(u) { const { dbf, idx, evals, hopyoNo } = u; const f = fields(dbf, "0303"); const sh = C.newSheet("일위대가표수공", "일 위 대 가 표", C.projectName(dbf), "K"); C.push(sh, { A: "명 칭", B: "규 격", C: "수 량", D: "단위", E: "재 료 비", G: "노 무 비", I: "경 비", K: "비고", _k: "head", }); C.push(sh, { E: "단 가", F: "금 액", G: "단 가", H: "금 액", I: "단 가", J: "금 액", _k: "head", }); for (const r of idx.ofPrefix("B")) { const c = code(r); const ev = evals[c]; const tot = ev ? ev.tot : { M: 0, L: 0, S: 0 }; C.push(sh, { A: f[0] || "항 목 번 호 :", B: `${f[1] || "제"} ${hopyoNo[c]} ${f[2] || "호표"}`, K: c, _k: "hopyo", }); C.push(sh, { A: f[3] || "공 종 :", B: str(r.NAME), C: str(r.SIZE), E: `재 료 비 ${won(tot.M)}`, F: `노 무 비 ${won(tot.L)}`, H: `경 비 ${won(tot.S)}`, I: `총 액 ${won(ev ? ev.T : 0)}`, _k: "parent", }); C.push(sh, { A: f[4] || "단 위 :", B: C.unitText(r.UNIT), _k: "parent" }); if (!ev) continue; for (const ln of ev.lines) { if (ln.text) continue; const ref = ln.ref || {}; const cells = { A: ref.name || ln.code, B: ref.spec || "" }; Object.assign(cells, { C: ln.qty, D: ln.pct ? "%" : ref.unitDisp || ln.unit }); Object.assign(cells, { E: C.fix(ln.u.M), F: C.fix(ln.a.M, 1) }); Object.assign(cells, { G: C.fix(ln.u.L), H: C.fix(ln.a.L, 1) }); Object.assign(cells, { I: C.fix(ln.u.S), J: C.fix(ln.a.S, 1), K: ln.code, _k: "line" }); C.push(sh, cells); } } return form(sh, dbf, "0303"); } /** * 0605 분할내역서(TPM_NAE_B) — 공종 · 명칭 · 규격 · 수량(전체·금회) · 단위 · 단가(총액) · 금액(전체·금회) · 비고 * 금회 수량 출처는 미확인 — opts.splitRate %(소수 셋째 자리 사사오입)로 대신 · 금액 = 단가 × 수량 원 미만 절사 */ function splitSheet(dbf, tree, opts = {}) { const f = fields(dbf, "0605"); const rate = opts.splitRate == null ? 100 : num(opts.splitRate); const sh = C.newSheet("분할내역서", "분 할 내 역 서", C.projectName(dbf), "J"); const head = { A: "공 종", B: "명 칭", C: "규 격", D: "수 량", F: "단위" }; C.push(sh, Object.assign(head, { G: "단 가", H: "금 액", J: "비고", _k: "head" })); const sub = { D: f[0] || "전 체", E: f[1] || "금 회", H: f[0] || "전 체", I: f[1] || "금 회" }; C.push(sh, Object.assign(sub, { _k: "head" })); let tot = 0, cur = 0; const cutOf = new Map(); for (const n of tree.out) { if (n.kind !== "item") continue; const q2 = half((n.qty * rate) / 100, 3); const a2 = n.pct ? trunc((T4(n.amt) * rate) / 100) : trunc(T4(n.unitP) * q2); cutOf.set(n, { q2, a2 }); // 묶음 금회 합 — 제외(C) 묶음에서 멈춤(설계 묶음 합과 같은 범위) for (let p = n.parent; p && p.row; p = p.parent) { p._cut = (p._cut || 0) + a2; if (p.excluded) break; } if (!excluded(n)) ((tot += T4(n.amt)), (cur += a2)); } for (const n of tree.out) { if (n.kind === "T") { const cells = { A: str(n.row.ITNUM).trim(), B: str(n.row.NAME1), C: str(n.row.SIZE1) }; C.push(sh, Object.assign(cells, { H: T4(n.sum), I: n._cut || 0, _k: `T${n.level}` })); continue; } if (n.kind !== "item") continue; const { q2, a2 } = cutOf.get(n); const cells = { B: str(n.row.NAME1), C: str(n.row.SIZE1), D: n.qty, E: q2 }; Object.assign(cells, { F: C.unitText(n.row.UNIT1), G: T4(n.unitP), H: T4(n.amt), I: a2 }); C.push(sh, Object.assign(cells, { J: n.code, _k: "item", _code: n.code })); } C.push(sh, { B: "합 계", H: tot, I: cur, _k: "final" }); return form(sh, dbf, "0605"); } /** * 0609 총괄집계표(Prn_Won_A · TPM_WON_A) — 공사원가계산서와 같은 임시 표의 줄(NAME · T_AMT · T_PER · S_DET/BIGO) * 칸: 구분 · 금액('#,#') · 구성비(T_PER = 금액 ÷ 총공사비 × 100 · 표시 '0.000 %' · 0 은 빈칸) · 비고(S_DET 없으면 BIGO) */ function totalSheet(dbf, stage) { const f = fields(dbf, "0609"); const sh = C.newSheet("총괄집계표", "총 괄 집 계 표", C.projectName(dbf), "E"); C.push(sh, { A: f[0] || "구 분", B: f[1] || "금 액", C: f[2] || "구 성 비", D: f[3] || "비 고", _k: "head", }); const w = B.wonSheet(dbf, stage, null); for (const r of w.rows.slice(4)) { if (r.D == null) continue; const lbl = [r.A, r.B, r.C].filter((x) => x && String(x).trim()).pop(); C.push(sh, { A: String(lbl || "").trim(), B: r.D, C: r.D ? half(num(r.E), 3) : "", D: r.F || "", _k: "cost", }); } C.push(sh, { A: In.amtToHan(stage.kanjub.grandTotal), _k: "final" }); return form(sh, dbf, "0609"); } /** * 0615 견적단가대비표(Prn_Nae_Est · TPM_NAE_EST) — 내역 품목 줄 · 공종 · 명칭 · 규격 · 단위 · * 재료비·노무비·경비·총액 마다 (견적단가 · 기준단가) · 비고(코드) * 대상·값 후보: 견적(W) 코드 줄 · 견적단가 = 내역 단가 · 기준단가 = 그 코드 CODEA(변환 전 코드) 단가(없으면 빈칸) */ function estimateCompareSheet(u, tree) { const { dbf, idx } = u; const f = fields(dbf, "0615"); const sh = C.newSheet("견적단가대비표", "견 적 단 가 대 비 표", C.projectName(dbf), "M"); const head = { A: "공 종", B: "명 칭", C: "규 격", D: "단위", E: "재 료 비" }; C.push( sh, Object.assign(head, { G: "노 무 비", I: "경 비", K: "총 액", M: "비고", _k: "head" }), ); const e = f[0] || "견적단가", b = f[1] || "기준단가"; C.push(sh, { E: e, F: b, G: e, H: b, I: e, J: b, K: e, L: b, _k: "head" }); for (const n of tree.out) { if (n.kind !== "item" || n.code[0] !== "W") continue; const src = idx.by[n.code]; const base = src && str(src.CODEA).trim() ? idx.price(str(src.CODEA).trim(), 1) : null; const p = n.unitP; const cells = { A: "", B: str(n.row.NAME1), C: str(n.row.SIZE1) }; Object.assign(cells, { D: C.unitText(n.row.UNIT1), E: p.M, G: p.L, I: p.S, K: T4(p) }); if (base) Object.assign(cells, { F: base.M, H: base.L, J: base.S, L: T4(base) }); C.push(sh, Object.assign(cells, { M: n.code, _k: "item", _code: n.code })); } return form(sh, dbf, "0615"); } /** * 0651·0655 구.시도 양식 — 금액을 시기금·구기금으로 나눔(opts.fundRate = 시기금 %, 기본 100) · 합계 * 0655 = 품목 줄(Prn_Nae_Ex1_T) · 0651 = 공종 묶음 줄(Prn_Nae_Ex1_CT) · 나눔 비율 출처는 미확인 */ function fundSheet(dbf, tree, indx, opts = {}) { const f = fields(dbf, indx); const rate = opts.fundRate == null ? 100 : num(opts.fundRate); const items = indx === "0655"; const id = items ? "설계내역서구시도" : "총괄내역서구시도"; const title = items ? "설 계 내 역 서 (구.시도)" : "총 괄 내 역 서 (구.시도)"; const sh = C.newSheet(id, title, C.projectName(dbf), "J"); const head = { A: "공 종", B: "명 칭", C: "규 격", D: items ? "수 량" : "" }; const tail = { E: f[0] || "시 기 금", F: f[1] || "구 기 금", G: f[2] || "합 계", _k: "head", }; C.push(sh, Object.assign(head, tail)); for (const n of tree.out) { if (!(n.kind === "T" || (items && n.kind === "item"))) continue; const v = T4(n.kind === "T" ? n.sum : n.amt); const si = trunc((v * rate) / 100); const cells = { A: n.kind === "T" ? str(n.row.ITNUM).trim() : "", B: str(n.row.NAME1), C: str(n.row.SIZE1), }; if (n.kind === "item") cells.D = n.qty; Object.assign(cells, { E: si, F: v - si, G: v, _k: n.kind === "T" ? `T${n.level}` : "item" }); C.push(sh, cells); } return form(sh, dbf, indx); } // 공종 묶음(opts.level 단 · 기본 2) 별 품목 → 투입 자원 전개(report_coll) function groupBlocks(tree, level) { const groupOf = (n) => { let p = n.parent; while (p && p.row && p.level > level) p = p.parent; return p && p.row ? p : null; }; const blocks = new Map(); for (const n of tree.out) { if (n.kind !== "item" || n.pct || excluded(n)) continue; const gnode = groupOf(n); const key = gnode ? gnode.row._recno : "-"; if (!blocks.has(key)) blocks.set(key, { node: gnode, items: [] }); blocks.get(key).items.push(n); } return blocks; } function usedOf(dbf, it, prefix) { const ex = Co.explode(dbf, [{ code: it.code, qty: it.qty }]); const used = Object.keys(ex.qty) .filter((c) => c[0] === prefix && c !== it.code) .sort(); if (it.code[0] === prefix) used.unshift(it.code); return used.map((c) => [c, half(ex.qty[c], 2)]); } /** * 0675 재료비 · 0676 노무비(Prn_Nae_ColM·ColL · TPM_NAE_COL_ML) — 공종(번호+명칭) · 작업명 · 구분(명칭·규격) · * 수량 · 단위 · 단가 · 금액 · 비고 / 0677 중장비(Prn_Nae_ColX · TPM_NAE_COL) — 단가·금액이 재·노·경·총액 네 쌍 */ function collSheet(u, tree, indx, prefix, opts = {}) { const { dbf, idx } = u; const f = fields(dbf, indx); const rep = ((dbf && dbf.set_rep_rows) || []).find((x) => String(x.INDX).trim() === indx) || {}; const title = str(rep.HEADTITLE).trim() || indx; const four = indx === "0677"; const sh = C.newSheet( title.replace(/[\s,/]+/g, ""), title, C.projectName(dbf), four ? "O" : "I", ); const head = { A: f[0] || "공 종", B: f[1] || "작 업 명", C: f[2] || "구 분" }; Object.assign(head, { E: "수 량", F: "단위" }); if (four) { Object.assign(head, { G: "재 료 비", I: "노 무 비", K: "경 비", M: "총 액", O: "비 고", }); C.push(sh, Object.assign(head, { _k: "head" })); const sub = { G: "단가", H: "금 액", I: "단가", J: "금 액", K: "단가", L: "금 액" }; C.push(sh, Object.assign(sub, { M: "단가", N: "금 액", _k: "head" })); } else C.push(sh, Object.assign(head, { G: "단가", H: "금 액", I: "비 고", _k: "head" })); for (const { node, items } of groupBlocks(tree, opts.level || 2).values()) { const gname = node ? `${str(node.row.ITNUM).trim()} ${str(node.row.NAME1).trim()}`.trim() : ""; C.push(sh, { A: gname, _k: "T2" }); for (const it of items) for (const [c, q] of usedOf(dbf, it, prefix)) { const r = idx.by[c]; if (!r) continue; const p = idx.price(c, 1); const cells = { B: str(it.row.NAME1), C: str(r.NAME), D: str(r.SIZE), E: q }; cells.F = C.unitText(r.UNIT); if (four) { Object.assign(cells, { G: p.M, H: half(q * p.M), I: p.L, J: half(q * p.L) }); Object.assign(cells, { K: p.S, L: half(q * p.S), M: T4(p), N: half(q * T4(p)), O: c }); } else { const own = prefix === "L" ? p.L : prefix === "M" ? p.M : T4(p); Object.assign(cells, { G: own, H: half(q * own), I: c }); } C.push(sh, Object.assign(cells, { _k: "item", _code: c })); } } return form(sh, dbf, indx); } /** * 투입계획(0671 장비 X · 0678 자재 M · 0679 인력 L · Prn_Nae_Plan_{A|K}_*) — 설정 +0x1767 로 A/K 갈래 * A(TPM_NAE_PLAN_A) = 명칭 · 규격 · 수량 · 단위 · 비고(코드 합계 줄) * K(TPM_NAE_PLAN_K) = 공종(번호+명칭) · 명칭 · 규격 · 수량 · 단위 · 비고(공종별) * opts.planAll = true → A · 그 밖 K */ function planSheet(u, tree, indx, prefix, opts = {}) { const { dbf, idx } = u; const rep = ((dbf && dbf.set_rep_rows) || []).find((x) => String(x.INDX).trim() === indx) || {}; const title = str(rep.HEADTITLE).trim() || indx; const all = !!opts.planAll; const sh = C.newSheet( title.replace(/[\s,/]+/g, ""), title, C.projectName(dbf), all ? "E" : "F", ); const row = (c, q) => { const r = idx.by[c]; return r ? { name: str(r.NAME), size: str(r.SIZE), unit: C.unitText(r.UNIT), q } : null; }; if (all) { C.push(sh, { A: "명 칭", B: "규 격", C: "수 량", D: "단위", E: "비 고", _k: "head" }); const sum = {}; for (const { items } of groupBlocks(tree, opts.level || 2).values()) for (const it of items) for (const [c, q] of usedOf(dbf, it, prefix)) sum[c] = (sum[c] || 0) + q; for (const c of Object.keys(sum).sort()) { const v = row(c, half(sum[c], 2)); if (v) C.push(sh, { A: v.name, B: v.size, C: v.q, D: v.unit, E: c, _k: "item", _code: c }); } return form(sh, dbf, indx); } C.push(sh, { A: "공 종", B: "명 칭", C: "규 격", D: "수 량", E: "단위", F: "비 고", _k: "head", }); for (const { node, items } of groupBlocks(tree, opts.level || 2).values()) { const sum = {}; for (const it of items) for (const [c, q] of usedOf(dbf, it, prefix)) sum[c] = (sum[c] || 0) + q; const gname = node ? `${str(node.row.ITNUM).trim()} ${str(node.row.NAME1).trim()}`.trim() : ""; C.push(sh, { A: gname, _k: "T2" }); for (const c of Object.keys(sum).sort()) { const v = row(c, half(sum[c], 2)); if (v) C.push(sh, { B: v.name, C: v.size, D: v.q, E: v.unit, F: c, _k: "item", _code: c }); } } return form(sh, dbf, indx); } /** * 0695 간이공사비 산출명세서(Prn_Boq_Not · TPM_BOQ_NOT · 엑셀판 없음) — 고정 칸 양식 * 구분(회사분·도급분·계) × 재료비·노무비·경비·일반관리비·이윤·소계·부가세·합계 · 작성자(인) · * 예정가격산출(%) · 예정가격(결정권자) · 아래 「공사비 예산서 | 공사비 정산서」 머리 * 값 후보: 도급분 = 원가계산서 줄 · 회사분 = 관급자재대(재료비 칸) · 예정가격산출 % 출처 미확인(빈칸) */ function simpleSheet(dbf, stage) { const sh = C.newSheet("간이공사비산출명세서", "간이공사비 산출명세서", C.projectName(dbf), "J"); const k = stage.kanjub || {}; const cols = ["B", "C", "D", "E", "F", "G", "H", "I"]; const head = { A: "구분", B: "재료비", C: "노무비", D: "경비", E: "일 반\n관리비", F: "이윤" }; C.push(sh, Object.assign(head, { G: "소계", H: "부가세", I: "합계", J: "작성자", _k: "head" })); const w = B.wonSheet(dbf, stage, null).rows.slice(4); const subt = w.filter((r) => /^소\s*계$/.test( String(r.C || "") .replace(/\s+/g, " ") .trim(), ), ); const pick = (re) => { const r = w.find((x) => re.test(String(x.A || "").replace(/\s/g, ""))); return r ? num(r.D) : 0; }; const dog = [ subt[0] ? num(subt[0].D) : 0, subt[1] ? num(subt[1].D) : 0, subt[2] ? num(subt[2].D) : 0, pick(/^일반관리비/), pick(/^이윤/), ]; dog.push(pick(/^총원가/), pick(/^부가가치세/), pick(/^도급공사비/)); const gov = num(k.govFinal); const com = [gov, 0, 0, 0, 0, gov, 0, gov]; const line = (lbl, v) => { const cells = { A: lbl, _k: "cost" }; cols.forEach((c, i) => (cells[c] = v[i])); return C.push(sh, cells); }; line("회사분", com); line("도급분", dog); line( "계", dog.map((v, i) => v + com[i]), ); C.push(sh, { A: " 예정가격산출", _k: "pct" }); C.push(sh, { A: " 예정가격(결정권자)", I: "(인)", _k: "pct" }); C.push(sh, { A: "공 사 비 예 산 서", G: "공 사 비 정 산 서", _k: "head", }); const low = { A: "자산단위번호", B: "품 명 및 규 격", D: "단위", E: "수량", F: "단가", G: "금액", }; C.push(sh, Object.assign(low, { H: "수량", I: "단가", J: "금액", K: "증감", _k: "head" })); C.push(sh, { B: '" 설 계 서 붙 임 "', _k: "note" }); C.push(sh, { A: "총공사비(한글)", B: In.amtToHan(stage.kanjub.grandTotal), _k: "final" }); return form(sh, dbf, "0695"); } /** * 0607 사·관급내역서(Prn_Bqm · TPM_BQM) — 설계내역서 칸 그대로 · 머리 띠 「사 급 분」(SET_REP FLD_N1) · * 관급 구역(C 로 닫힌 묶음) 줄 · 끝 = 관급 계 · 아래 표(공종번호 · 명칭 · 구분 · 금액 '#,# 원') */ function govSheet(design, tree, dbf) { const f = fields(dbf, "0607"); const sh = C.newSheet("관급내역서", "관 급 내 역 서", C.projectName(dbf), "O"); for (const r of design.rows.slice(2, 4)) C.push(sh, Object.assign({}, r)); C.push(sh, { A: f[0] || "사 급 분", _k: "band" }); const sum = { M: 0, L: 0, S: 0 }; const grid = []; // 설계내역서 행 중 관급 구역 — 제외 묶음 줄(_ex · 단 L)부터 같은 단 이상 묶음 줄·끝 줄 앞까지 let zone = 0; for (const r of design.rows.slice(4)) { const lv = Number(r._lv) || 0; if (zone && ((lv && lv >= zone && !r._ex) || r._k === "final")) zone = 0; if (!zone && r._ex) zone = lv; if (zone) C.push(sh, Object.assign({}, r, { _f: undefined })); } tree.out.forEach((n) => { if (n.kind === "T" && n.excluded && !excluded(n)) { for (const k of "MLS") sum[k] += n.sum[k]; grid.push({ A: str(n.row.ITNUM).trim(), B: str(n.row.NAME1).trim(), C: "관급" }); grid[grid.length - 1].D = `${won(T4(n.sum))} 원`; } }); const last = { A: f[1] || "계", _k: "final" }; const head = design.rows[2] || {}; const amt = { 합: T4(sum), 노: sum.L, 재: sum.M, 경: sum.S }; for (const [c, lbl] of Object.entries(head)) if (/^[FHJL]$/.test(c)) { const key = String(lbl).replace(/\s/g, "")[0]; last[String.fromCharCode(c.charCodeAt(0) + 1)] = amt[key] ?? 0; } C.push(sh, last); for (const gr of grid) C.push(sh, Object.assign(gr, { _k: "grid" })); return form(sh, dbf, "0607"); } // 제목만 바뀌는 변형 — 같은 시트 모형을 복사해 SET_REP 제목·이름 // (0613·0614 = Prn_Nae·Prn_Nae_P 같은 함수 · 0606 = 엑셀은 종양식 Prn_Nae_P_EXL · 0654·0656 굴착 · 0652·0653·0672 총괄) function retitle(sh, dbf, indx, id) { const copy = { name: sh.name, title: sh.title, cols: {} }; copy.rows = sh.rows.map((r) => Object.assign({}, r)); const rep = ((dbf && dbf.set_rep_rows) || []).find((x) => String(x.INDX).trim() === indx); const title = rep ? str(rep.HEADTITLE).trim() : sh.title; copy.title = title; if (copy.rows[0] && copy.rows[0]._k === "title") copy.rows[0].A = title; copy.name = `${title.replace(/[\s,/().]+/g, "")}${indx}`; copy.id = id; copy.indx = indx; return copy; } /** 변형 묶음 — 보고서 화면 서식 목록에서 고를 때 만듦 · 돌려줌: { id: 시트 모형 } */ function buildFormSheets(u, dbf, state, computed, n = 1, opts = {}) { const stage = B.stageOf(state, computed, n); const design = B.designSheet(dbf, stage, opts); const tree = design._tree; const summary = B.summarySheet(dbf, stage, design, opts); const out = { 일위대가표수공양식: iLwiHandSheet(u), 분할내역서: splitSheet(dbf, tree, opts), 총괄집계표: totalSheet(dbf, stage), 견적내역서: retitle(design, dbf, "0613", "견적내역서"), 견적내역서종양식: retitle(design, dbf, "0614", "견적내역서종양식"), 견적단가대비표: estimateCompareSheet(u, tree), 내역서도공양식: retitle(design, dbf, "0606", "내역서도공양식"), 설계내역서굴착: retitle(design, dbf, "0654", "설계내역서굴착"), 설계내역서시도: retitle(design, dbf, "0656", "설계내역서시도"), 총괄내역서시도: retitle(summary, dbf, "0652", "총괄내역서시도"), 총괄내역서구도: retitle(summary, dbf, "0653", "총괄내역서구도"), 총괄내역서비교: retitle(summary, dbf, "0672", "총괄내역서비교"), 총괄내역서구시도: fundSheet(dbf, tree, "0651", opts), 설계내역서구시도: fundSheet(dbf, tree, "0655", opts), 재료비집계표: collSheet(u, tree, "0675", "M", opts), 노무비집계표: collSheet(u, tree, "0676", "L", opts), 중장비집계표: collSheet(u, tree, "0677", "X", opts), 장비투입계획: planSheet(u, tree, "0671", "X", opts), 자재사용계획: planSheet(u, tree, "0678", "M", opts), 인력투입계획: planSheet(u, tree, "0679", "L", opts), 간이명세서: simpleSheet(dbf, stage), 관급내역서: govSheet(design, tree, dbf), 제잡비요율표: In.rateSheet(dbf, opts), }; for (const [id, sh] of Object.entries(out)) Object.assign(sh, { id }); delete design._tree; return out; } // 서식 번호 → 변형 시트 id const FORM_SHEET = { "0303": "일위대가표수공양식", "0605": "분할내역서", "0609": "총괄집계표", "0613": "견적내역서", "0614": "견적내역서종양식", "0615": "견적단가대비표", "0606": "내역서도공양식", "0654": "설계내역서굴착", "0656": "설계내역서시도", "0652": "총괄내역서시도", "0653": "총괄내역서구도", "0672": "총괄내역서비교", "0651": "총괄내역서구시도", "0655": "설계내역서구시도", "0675": "재료비집계표", "0676": "노무비집계표", "0677": "중장비집계표", "0671": "장비투입계획", "0678": "자재사용계획", "0679": "인력투입계획", "0695": "간이명세서", "0607": "관급내역서", }; return { buildFormSheets, FORM_SHEET, FORM_PROC, fields, splitSheet, totalSheet, collSheet, planSheet, }; })(); if (typeof window !== "undefined") window.STMATE_REPORT_FORMS = StmateReportForms; if (typeof module !== "undefined" && module.exports) module.exports = StmateReportForms;