/* -*- coding: utf-8 -*- */ /** * 보고서 묶음 (report_book.js) — 자재단가대비표(0105) · 목차 · 안내 · 시트 차례 · SET_REP 서식 표 * 근거: 지식DB STmate/old/20_분석 20 §2.3(가격지 슬롯 이름 JMOK · 축약 JMK) · §3(SET_REP 75종) * 19 §3(목차·안내 시트) · old/30_원자료/print_form_map.txt(서식 번호 ↔ 출력 함수) * 정답 대조: resources/tester/test_stmate_report_sheets.py */ const StmateReportBook = (() => { const g = typeof globalThis !== "undefined" ? globalThis : {}; const C = g.STMATE_REPORT_CORE || require("./report_core.js"); const U = g.STMATE_REPORT_UNIT || require("./report_unit.js"); const B = g.STMATE_REPORT_BOQ || require("./report_boq.js"); const Co = g.STMATE_REPORT_COLL || require("./report_coll.js"); const Sa = g.STMATE_REPORT_SAN || require("./report_san.js"); const In = g.STMATE_REPORT_INIT || require("./report_init.js"); const { num, str, code } = C; /** * 자재단가대비표(0105) — 재료비(M) 전수 · 가격지 5칸(단가·페이지) · 적용 단가와 적용 가격지 축약 * 가격지 이름 = Start8x.Ini JMOK1~6 · 축약 = JMK10~50 · 적용 슬롯 = COSTN PG_1 */ function priceCompareSheet(ctx) { const { dbf, idx, ini, po } = ctx; const sh = C.newSheet("자재단가대비표", "자재단가 대비표", C.projectName(dbf), "R"); const slots = ["E", "G", "I", "K", "M"]; const h3 = { A: po.codeMode ? "코드번호" : "호표", B: "명 칭", C: "규 격", D: "단위", }; const h4 = { _k: "head" }; slots.forEach((c, i) => { h3[c] = str(ini[`JMOK${i + 1}`] || "").trim() || `가격${i + 1}`; h4[c] = "단 가"; h4[String.fromCharCode(c.charCodeAt(0) + 1)] = "페이지"; }); Object.assign(h3, { O: str(ini.JMOK6 || "적용 단가").trim(), Q: "비 고", _k: "head" }); Object.assign(h4, { O: "단 가", P: "페이지" }); C.push(sh, h3); C.push(sh, h4); idx.ofPrefix("M").forEach((r, i) => { const c = code(r); const lbl = po.codeMode ? c : po.padNo ? String(i + 1).padStart(4) : String(i + 1); const cells = { A: lbl, B: str(r.NAME), C: str(r.SIZE), D: C.unitText(r.UNIT) }; Object.assign(cells, { _k: "item", _code: c }); slots.forEach((col, j) => { cells[col] = num(r[`AMT_${j + 1}1`]); const pg = str(r[`PG_${j + 1}1`]).trim(); if (pg) cells[String.fromCharCode(col.charCodeAt(0) + 1)] = pg; }); const p = idx.price(c, 1); cells.O = p.M + p.L + p.S; const s = num(r.PG_1); if (s >= 1 && s <= 5) cells.P = str(ini[`JMK${s}0`] || "").trim(); const rm = U.remarkOf(po, c, lbl, r); if (rm) cells.Q = rm; C.push(sh, cells); }); return sh; } // 시트 차례 — 원본 STMATE 엑셀 내보내기 기본 순서(영월·울진 출력 목차) · 인쇄순서(F1-52)로 바꿈 const ORDER = [ "공사원가계산서", "총괄내역서", "설계내역서", "일위대가목록표", "일위대가표", "일위대가수량금액집계표", "단가산출근거목록표", "단가산출근거", "단가산출근거수량금액집계표", "환율및기초자료", "중기목록표", "중기사용료", "재료비목록표", "노무비목록표", "경비목록표", "일식견적목록표", "표준시장목록표", "실적단가목록표", "자재단가대비표", "재료비수량금액집계표", "노무비수량금액집계표", "경비수량금액집계표", "일식견적수량금액집계표", "표준시장수량금액집계표", "중기시간금액집계표", ]; // 목차 시트(〓 목 차 〓) — 시트 이름과 바로가기(엑셀에서 하이퍼링크) function tocSheet(names, pname) { const sh = { name: "〓 목 차 〓", title: "목 차", rows: [], cols: {} }; C.put(sh, 2, { B: "목 차", D: "※ 본 엑셀자료는 STMATE 재현 앱에서 만든 출력본입니다.", _k: "title", }); C.put(sh, 3, { D: ` 공사명 : ${pname}` }); ["※※안내※※"] .concat(names) .forEach((n, i) => C.put(sh, 4 + i, { B: `${i + 1}. ${n}`, D: `${n} →`, _link: n })); return sh; } // 안내 시트(※※안내※※) — 원본 안내문 요지 + 금액 확인 줄 function guideSheet(k) { const sh = { name: "※※안내※※", title: "안내", rows: [], cols: {} }; C.put(sh, 2, { A: "목차 →", B: "=== 엑셀파일에 작성에 대한 안내문(반드시 읽어보세요) ===", _link: "〓 목 차 〓", }); C.put(sh, 4, { B: "◈ 금액은 STMATE 재현 앱 계산 엔진(calc.js) 결과를 값으로 적었습니다." }); C.put(sh, 5, { B: "◈ 각 시트의 비목 열 순서는 설정(BMKNM)을 따릅니다." }); C.put(sh, 6, { B: "◈ [목차] 및 [안내] 시트의 이름을 바꾸지 마십시요." }); if (k) { C.put(sh, 8, { B: "※※※ 아래의 내용(안내 및 오류)을 확인/검토 하세요 ※※※" }); C.put(sh, 9, { C: `【총괄내역서】 총공사비 = ${C.commas(k.grandTotal)}원 (이윤 보정액 ${C.commas(k.pretaxProfitAdj || 0)}원)`, }); } return sh; } /** * 전체 묶음 — dbf(불러온 자료) · state(sim_state) · computed(calculateAll) · n 차수 * 돌려줌: { sheets:[시트 모형…], byName } */ function buildBook(dbf, state, computed, n = 1, opts = {}) { const unit = U.buildUnitSheets(dbf); const boq = B.buildBoqSheets(dbf, state, computed, n, opts); const pc = C.applyForm(priceCompareSheet(unit.ctx), dbf, "0105"); // 집계표(0501~0508) — 변경 차수면 당초·변경 두 줄 양식 · 단가산출근거(0402) 펼침 const base = n > 1 ? B.boqTree(dbf, B.stageOf(state, computed, 1).boq, 1) : null; const coll = Co.buildCollSheets( unit.ctx, boq.설계내역서._tree, Object.assign({ baseTree: base }, opts), ); const san = C.applyForm(Sa.sanSheet(unit.ctx, opts), dbf, "0402"); const all = Object.assign({}, unit, boq, coll, { 자재단가대비표: pc, 단가산출근거: san }); const order = opts.order && opts.order.length ? opts.order : ORDER; const sheets = order.filter((id) => all[id]).map((id) => Object.assign(all[id], { id })); const names = sheets.map((s) => s.name); const pname = C.projectName(dbf); const init = In.initialSheet(dbf, state, sheets, n, opts); names.push(init.name); const book = [tocSheet(names, pname), guideSheet(boq.stage.kanjub)].concat(sheets, [init]); for (const s of book) delete s._tree; return { sheets: book, byName: Object.fromEntries(book.map((s) => [s.name, s])) }; } // SET_REP 서식 → 시트 이름(구현된 것) — 없으면 null const FORM_SHEET = { "0101": "재료비목록표", "0102": "노무비목록표", "0103": "경비목록표", "0105": "자재단가대비표", "0201": "환율및기초자료", "0202": "중기목록표", "0203": "중기사용료", "0301": "일위대가목록표", "0302": "일위대가표", "0401": "단가산출근거목록표", "0402": "단가산출근거", "0104": "일식견적목록표", "0106": "표준시장목록표", "0206": "실적단가목록표", "0501": "재료비수량금액집계표", "0502": "노무비수량금액집계표", "0503": "경비수량금액집계표", "0504": "일식견적수량금액집계표", "0505": "중기시간금액집계표", "0506": "일위대가수량금액집계표", "0507": "단가산출근거수량금액집계표", "0508": "표준시장수량금액집계표", "0601": "총괄내역서", "0603": "설계내역서", "0608": "공사원가계산서", }; return { buildBook, priceCompareSheet, tocSheet, guideSheet, ORDER, FORM_SHEET }; })(); if (typeof window !== "undefined") window.STMATE_REPORT_BOOK = StmateReportBook; if (typeof module !== "undefined" && module.exports) module.exports = StmateReportBook;