- 보고서: 원본 STMATE 엑셀과 셀 대조(목록표·자재단가대비·중기·일위대가·산근 목록·설계내역서·원가계산서) · 조달청 XML 공식 XSD 검증 · PM_Prt 화면 - 제잡비: 원본 계산 순서 4형식(T/S/TA/SA) · 실무 7건 저장 결과키 전수 1원 · 요율표 조회 · 선택지 계산 · 제잡비·제비율 화면 - 식언어·수량: 함수·조건식·금액변수·문법 검사·수량변수·QCODE · 산근·구성·중기공식 편집 화면 · 순환 알림 - 화면: 원본 메뉴 체계·단축키 · 설계내역서 편집(행 넣기·지우기·이동·레벨·보관함) · 초기값 · 단가목록표 - 저장: 초기값(Start8x.Ini) 바뀐 키 저장 · 노임 환산 직종 = 초기값 CONLD · 산근·구성 결과에 적용단가 칸 채우지 않음(원본 0) - DBF 글 칸 앞 빈칸 보존 · 시험 도우미 표준입력 utf8(한글 조각 깨짐 원인) - 실무 5건 재계산 후 저장 = 바뀐 칸 0 · 시험 332건 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
151 lines
6.3 KiB
JavaScript
151 lines
6.3 KiB
JavaScript
/* -*- 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 { 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 = po.remarkHopyo && !po.codeMode ? lbl : str(r.BQCODE).trim();
|
|
if (rm) cells.Q = rm;
|
|
C.push(sh, cells);
|
|
});
|
|
return sh;
|
|
}
|
|
|
|
// 시트 차례 — 원본 STMATE 엑셀 내보내기 순서(목차 번호)
|
|
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");
|
|
const all = Object.assign({}, unit, boq, { 자재단가대비표: pc });
|
|
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 book = [tocSheet(names, pname), guideSheet(boq.stage.kanjub)].concat(sheets);
|
|
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": "단가산출근거목록표",
|
|
"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;
|