/* -*- coding: utf-8 -*- */ /** * 보고서 부속 (report_init.js) — 〓 INITIAL 〓 설정 시트 · 금액 한글 표기(AMTTOHAN) · 제잡비 요율표 인쇄 * 근거: 지식DB STmate/old/20_분석 18 §2.3(INITIAL = 엑셀 → STmate 재등록 메타) · 24 §2.4(AMTTOHAN 「금 일억삼천사백이십만원정」) * · §3(CONV_XLS_2_ST) · 06 §1~2 · 15 §4(제비율표 좌표 — scr_kanjub_rate.js StmateRate) * 정답 대조: resources/tester/test_stmate_report_coll.py(INITIAL — 원본 STMATE 출력 엑셀) */ const StmateReportInit = (() => { const g = typeof globalThis !== "undefined" ? globalThis : {}; const C = g.STMATE_REPORT_CORE || require("./report_core.js"); const { num, str } = C; const tf = (v) => (String(v ?? "").trim() === "1" || v === true ? "True" : "False"); /** * [Sheet정보] 18칸 — 칸 이름 · 시트 id · 열 자리(ITNAME NAME SIZE QTY UNIT SPOS · TAMT…NAMOUNT · INFO) * 열 자리 = 각 시트 양식의 열 차례(재등록 때 읽는 자리) · EPOS = 그 시트 마지막 행 */ const F = (it, nm, sz, q, un, sp, amts, info) => ({ it, nm, sz, q, un, sp, amts, info }); const LIST = F(null, 2, 3, null, 4, 4, [5], 7); const BIMOK = F(null, 2, 3, null, 4, 4, [5, null, 7, null, 6, null, 8], 10); const SLOTS = [ ["재료비목록표", "재료비목록표", LIST], ["노무비목록표", "노무비목록표", LIST], ["경비목록표", "경비목록표", LIST], ["일식목록표", "일식견적목록표", BIMOK], ["환율 및 기초자료", "환율및기초자료", F(2, 3, 4, null, 5, 6, [6], 9)], ["중기목록표", "중기목록표", BIMOK], ["실적단가표", null, null], ["일위대가목록표", "일위대가목록표", BIMOK], ["산출근거목록표", "단가산출근거목록표", BIMOK], [ "자재단가대비표", "자재단가대비표", F(null, 2, 3, null, 4, 5, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], 18), ], ["내역서", "설계내역서", F(1, 2, 3, 4, 5, 5, [6, 7, 10, 11, 8, 9, 12, 13], 15)], ["총괄내역서", "총괄내역서", F(1, 2, 3, null, 5, 4, [4, 9, 13, 14, 15, 16], 8)], ["중기사용료", "중기사용료", F(null, 1, 2, 3, 4, 5, [5, 6, 9, 10, 7, 8, 11, 12], 14)], ["일위대가표", "일위대가표", F(null, 1, 2, 3, 4, 5, [5, 6, 9, 10, 7, 8, 11, 12], 14)], [ "단가산출근거", "단가산출근거", F(1, 2, null, null, null, 5, [3, null, 5, null, 4, null, 6], 7), ], ["", null, null], ["", null, null], ["표준시장목록표", "표준시장목록표", BIMOK], ]; const COLS = "CDEFGHIJKLMNOPQRSTUVWX".split(""); // 차수 목록 — P_ORD [지금 차수, 저장된 차수…] 다섯 칸 function stageList(state, n) { const ks = Object.keys((state && state.kanjubStages) || {}) .map(Number) .filter((k) => k > 0) .sort((a, b) => a - b); const list = ks.length ? ks : [1]; return [n].concat(list, [0, 0, 0, 0, 0]).slice(0, 6).map(String); } /** * 〓 INITIAL 〓 — sheets = 묶음 시트 모형(id·name·rows) · n 차수 · opts.date(출력 일시) · opts.path(파일 자리) * 절 머리(B) 의 D 칸 = 다음 절 머리 행 번호 */ function initialSheet(dbf, state, sheets, n = 1, opts = {}) { const ini = (dbf && dbf.start_ini) || {}; const prj = (dbf && dbf.prjlst) || {}; const sh = { name: "〓 INITIAL 〓", title: "INITIAL", rows: [], cols: {} }; const put = (r, cells) => C.put(sh, r, cells); put(2, { B: "※※ 현재 Sheet를 수정 또는 삭제하지 마십시요[수정/삭제시 STmate 데이타로 변환할 수 없습니다] ※※", }); const byId = {}; for (const s of sheets || []) byId[s.id || s.name] = s; put(4, { B: "[Sheet정보]", C: false, D: 24 }); const hd = { B: "목차", C: "Sheet", D: "FPos", E: "ITNAME", F: "NAME", G: "SIZE", H: "QTY" }; Object.assign(hd, { I: "UNIT", J: "SPOS", K: "EPOS" }); "TAMT TAMOUNT MAMT MAMOUNT LAMT LAMOUNT SAMT SAMOUNT AAMT AAMOUNT NAMT NAMOUNT" .split(" ") .forEach((t, i) => (hd[COLS[9 + i]] = t)); hd.X = "INFO"; put(5, hd); SLOTS.forEach(([label, id, f], i) => { const cells = { B: `${i + 1}. ${label}` }; const s = id && (byId[id] || Object.values(byId).find((x) => x.name === id)); if (s && f) { Object.assign(cells, { C: s.name, D: `${opts.path || ""}[${opts.file || "STMATE"}]${s.name}`, }); const pos = { E: f.it, F: f.nm, G: f.sz, H: f.q, I: f.un, J: f.sp, K: s.rows.length }; f.amts.forEach((v, j) => (pos[COLS[9 + j]] = v)); pos.X = f.info; for (const [k, v] of Object.entries(pos)) if (v != null) cells[k] = v; } put(6 + i, cells); }); // [공사정보] put(24, { B: "[공사정보]", D: 33 }); const info = [ ["M_VER", str(prj.M_VER || "")], ["PNAME", C.projectName(dbf)], ["PCLAS", str(prj.PCLASS ?? "0").trim() || "0"], ["SCLAS", str(prj.SCLASS ?? "0").trim() || "0"], ["K_ORD", str(prj.K_ORD ?? "0").trim() || "0"], ["PRIPR", tf(prj.PRIV_PR === "T" || prj.PRIV_PR === true ? "1" : prj.PRIV_PR)], ["ACTYP", str(prj.ACTTYP ?? "0").trim() || "0"], ["ACTOP", str(prj.ACTOPT ?? "0").trim() || "0"], ]; info.forEach(([k, v], i) => put(25 + i, v === "" ? { B: k } : { B: k, C: v })); // [초기값] — 환율 8칸 = 달러 저·고 · 달러 저·고 · 유로 둘 · 엔 둘(원본 출력 차례) put(33, { B: "[초기값]", D: 52 }); const pOrd = stageList(state, n); put(34, { B: "P_ORD", C: pOrd[0], D: pOrd[1], E: pOrd[2], F: pOrd[3], G: pOrd[4], H: pOrd[5] }); const ex = ["EXLNM1", "EXHNM1", "EXLNM1", "EXHNM1", "EXEUR1", "EXEUR1", "EXYEN1", "EXYEN1"]; const exRow = { B: "EXNM_" }; ex.forEach((k, i) => (exRow[COLS[i]] = num(ini[k]))); put(35, exRow); const init = [ ["EXRNM", tf(ini.EXRNM)], ["RXNM_", str(ini.RXXNM)], ["COND_", str(ini.CONLD)], ["KCDNM", str(ini.KCDNM ?? "1")], ["JCDNM", str(ini.JCDNM ?? "0")], ["ECDNM", str(ini.ECDNM ?? "0")], ["ACDNM", str(ini.ACDNM ?? "1")], ["BPNNM", str(ini.BPNNM ?? "1")], ["ZCDNM", str(ini.ZCDNM ?? "2")], ["CHGNM", tf(ini.CHGNM)], ["CHBNM", tf(ini.CHBNM)], ["CHNNM", tf(ini.CHNNM)], ["CUTNM", tf(ini.CUTNM)], ["OPTNM", str(ini.OPTNM || "SML")], ["S_JDA", tf(ini.S_JDA)], ["SUBNM", str(ini.SUBNM)], ]; init.forEach(([k, v], i) => put(36 + i, { B: k, C: v })); // [인쇄정보] put(52, { B: "[인쇄정보]", D: 75 }); const bm = [1, 2, 3, 4].map((i) => str(ini[`BMK_STR${i}`])); put(53, { B: "BMSTR", C: bm[0], D: bm[1], E: bm[2], F: bm[3] }); put(54, { B: "BMKNM", C: str(ini.BMKNM || "TLMS") }); put(55, { B: "JEMNM", C: str(ini.JEMNM) }); for (let i = 1; i <= 6; i++) { const cells = { B: `JMKN${i}`, C: str(ini[`JMOK${i}`]).trim() }; if (i < 6) cells.D = str(ini[`JMK${i}0`]).trim(); put(55 + i, cells); } const caps = "CODE NO IBRD EQCOD ITNUM NAME SIZE QTY SUNIT AMT PAGE AMOUNT BIGO".split(" "); caps.forEach((k, i) => put(62 + i, { B: `CAP_${k}`, C: str(ini[`CAP_${k}`]) })); // [PREPARE] put(75, { B: "[PREPARE]", D: 79 }); put(76, { B: "PREP_VER", C: `w${str(ini.PRG_V)}` }); put(77, { B: "PREP_COP", C: str(opts.cop || "STMATE 재현 앱") }); put(78, { B: "PREP_DATE", C: str(opts.date || "") }); put(79, { B: "[.끝.]" }); return sh; } /** * 금액 한글 표기(AMTTOHAN) — 「금 일억삼천사백이십만원정」 * 네 자리 묶음(만·억·조·경)마다 천·백·십 · 자리 숫자 1 도 「일」로 적음(금액 고침 막기 · 원본 예 「일억」) */ const DIG = ["", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구"]; const SMALL = ["천", "백", "십", ""]; const BIG = ["", "만", "억", "조", "경"]; function amtToHan(v, opts = {}) { const n = Math.trunc(Math.abs(num(v))); if (!n) return opts.bare ? "영" : "금 영원정"; const s = String(n); const groups = []; for (let i = s.length; i > 0; i -= 4) groups.unshift(s.slice(Math.max(0, i - 4), i).padStart(4, "0")); let out = ""; groups.forEach((gp, gi) => { let part = ""; for (let k = 0; k < 4; k++) if (gp[k] !== "0") part += DIG[gp[k]] + SMALL[k]; if (part) out += part + BIG[groups.length - 1 - gi]; }); const sign = num(v) < 0 ? "마이너스 " : ""; return opts.bare ? sign + out : `금 ${sign}${out}원정`; } /** * 제잡비 요율표 인쇄(PM_KAN_RATE) — 공사규모 묶음 × 공사기간 · 간접노무비·기타경비(공종별) · 일반관리비 · 이윤 * + 부속표(보험·안전관리비) · 값 = STC 안 RATE_R.DBF(행렬 좌표는 StmateRate) */ function rateSheet(dbf, opts = {}) { const R = g.StmateRate || require("./scr_kanjub_rate.js"); const src = opts.table || "RATE_R.DBF"; const rows = ((dbf && dbf.rate_tables) || {})[src]; const m = R.matrixFromRows(rows || []); const sh = C.newSheet("제잡비요율표", "제 잡 비 요 율 표", C.projectName(dbf), "R"); if (!m.length) { C.push(sh, { A: `${src} 없음`, _k: "item" }); return sh; } const kongs = [ [1, "토목"], [2, "산설(토)"], [3, "조경"], [4, "건축"], [5, "산설(건)"], ]; const cols = "CDEFGHIJKLMNOP".split(""); const h3 = { A: "공사규모", B: "공사기간", _k: "head" }; kongs.forEach(([, l], i) => ((h3[cols[i]] = `간노 ${l}`), (h3[cols[i + 5]] = `기타 ${l}`))); Object.assign(h3, { M: "일반관리비", N: "일반관리비(전문)", O: "이윤" }); C.push(sh, h3); for (const b of R.BANDS) { if (b.optional && !kongs.some(([c]) => R.cell(m, b.row, c))) continue; R.KIKAN.forEach((k, p) => { const row = b.row + p; const cells = { A: p === 0 ? b.name : "", B: k, _k: "item" }; kongs.forEach(([c], i) => { cells[cols[i]] = R.cell(m, row, c); cells[cols[i + 5]] = R.cell(m, row, c + 5); }); if (p === 0) Object.assign(cells, { M: R.cell(m, row, 11), N: R.cell(m, row, 15), O: R.cell(m, row, 16), }); C.push(sh, cells); }); } C.push(sh, {}); C.push(sh, { A: "부속표", B: "구분", C: "요율", _k: "head" }); const annex = [ ["산재보험료", 45, 1], ["건강보험료", 46, 1], ["연금보험료", 46, 6], ["노인장기요양보험료", 46, 11], ]; for (const [l, r, c] of annex) C.push(sh, { B: l, C: R.cell(m, r, c), _k: "item" }); for (let gr = 1; gr <= 7; gr++) C.push(sh, { B: `고용보험 ${gr < 7 ? `${gr} 등급` : "7 등급이하"}`, C: R.cell(m, 45, 10 + gr), _k: "item", }); C.push(sh, {}); C.push(sh, { A: "안전관리비", B: "공종", C: "5억 미만", D: "5~50억 미만", E: "기초액(천원)", F: "50억 이상", _k: "head", }); for (const [l, t] of [ ["건축", 0], ["토목", 1], ["중건설", 3], ["특수건설", 4], ]) { const o = R.ANJUN_OFF[t]; const cells = { B: l, C: R.cell(m, 10 + o, 17), D: R.cell(m, 20 + o, 17), _k: "item" }; Object.assign(cells, { E: R.cell(m, 20 + o, 18), F: R.cell(m, 25 + o, 17) }); C.push(sh, cells); } return sh; } return { initialSheet, amtToHan, rateSheet, SLOTS }; })(); if (typeof window !== "undefined") window.STMATE_REPORT_INIT = StmateReportInit; if (typeof module !== "undefined" && module.exports) module.exports = StmateReportInit;