Files
Aislo/resources/tester/helper_stmate_qty_run.cjs
T
eomsangdonandClaude Opus 5.5 3d8fddfc34 feat(stmate-test): 보고서·제잡비·식언어·수량산출·화면 묶음 합침 · 재계산 후 저장 무변경
- 보고서: 원본 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>
2026-11-13 19:12:04 +09:00

100 lines
3.8 KiB
JavaScript

// STMATE 재현 앱 식 언어·수량산출서 순수 계산 호출 — stdin {calls:[{fn, args}]} → stdout [결과]
// evaluate·check 의 lookup = 단가표 {코드: {M,L,S,unit}} · qty = 수량코드 행([{CODE, QTY0~9, XCODE}])
globalThis.window = globalThis;
const T = "../../tools/stmate-test/";
// smt_engine 이 쓰는 수치 도우미만(calc.js 원가 계산 묶음과 떨어뜨림)
globalThis.STMATE_CALC = globalThis.STMATE_CALC || {
num: (v, d = 0) => {
const n = parseFloat(String(v ?? "").replace(/,/g, ""));
return Number.isFinite(n) ? n : d;
},
fmt: (v) => String(v),
};
const E = require(T + "smt_eval.js");
const X = require(T + "scr_edit_expr.js");
const L = require(T + "scr_edit_lang.js");
const Q = require(T + "scr_qty_sheet.js");
const PR = require(T + "scr_edit_price.js");
require(T + "smt_engine.js");
const ENG = globalThis.STMATE_ENGINE;
const lookupOf = (table) => (c) => (table && table[c] ? table[c] : null);
const qtyOf = (rows) => ENG.qtyResolver({ dbfData: { qcode_rows: rows || [] } });
const FN = {
expr: (src, vars) => X.evaluate(src, vars || {}),
evaluate: (text, table, opts = {}) => {
const o = { ...opts, qty: opts.qcode ? qtyOf(opts.qcode) : undefined };
const ev = E.evaluate(text, lookupOf(table), o);
return {
M: ev.M,
L: ev.L,
S: ev.S,
T: ev.T,
vars: ev.vars,
labels: ev.labels,
errors: ev.errors,
lines: ev.lines,
};
},
composition: (rows, table, opts = {}) => {
const o = { ...opts };
if (o.laborMap) o.labor = new Map(Object.entries(o.laborMap));
return E.evaluateComposition(rows, lookupOf(table), o);
},
devd: (q, unit) => E.devdToOrdinary(q, unit),
normQCode: (c) => E.normQCode(c),
codeLines: (code, row, opts) => L.codeLines(code, row, opts),
stripResults: (t) => L.stripResults(t),
marker: (lv, ex) => L.marker(lv, ex),
laborMap: (ini, sel, fb) => Object.fromEntries(ENG.laborMap(ini, sel, fb)),
cycles: (graph) => ENG.findCycles((c) => graph[c] || [], new Set(Object.keys(graph))),
smtAll: (load) => {
// 실무 STC 의 산근 전부 — 오류 목록과 금액(금액결과 삭제 뒤 금액 포함)
const ctx = { dbfData: load, uiState: {}, state: load.sim_state };
const out = {};
for (const [code, m] of Object.entries(load.costn_memos || {})) {
const a = ENG.evaluateSmtFormulaText(m.text, ctx, code);
const b = ENG.evaluateSmtFormulaText(L.stripResults(m.text), ctx, code);
out[code] = {
errors: a.errors,
sum: [a.sumM, a.sumL, a.sumS],
stripped: [b.sumM, b.sumL, b.sumS],
};
}
return out;
},
cascadeCycles: (load) => {
globalThis.STMATE_VIEW_PRJ_BOQ = globalThis.STMATE_VIEW_PRJ_BOQ || {
syncCostnUnitPrice: () => {},
};
const ctx = { dbfData: load, uiState: {}, state: load.sim_state };
return ENG.cascadeRecalculateAll(ctx).cycles;
},
adopt: (rows, opts) => PR.adoptCandidates(rows, opts),
evalSheet: (sheet) => Q.evalSheet(sheet),
assignFromSheet: (rows, sheet) => Q.assignFromSheet(rows, sheet),
sheetToBoq: (sheet, mode) => Q.sheetToBoq(sheet, mode),
checkQcodes: (rows, codes) => Q.checkQcodes(rows, codes),
nextQcode: (rows) => Q.nextQcode(rows),
qtyByCode: (rows, kind, field) => Q.qtyByCode(rows, kind, field),
rollup: (boq, bd, field) => Q.rollup(boq, bd, field),
roundTrip: (sheet, rows) => Q.deserialize(Q.serialize(sheet, rows)),
deserialize: (t) => Q.deserialize(t),
};
let buf = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (d) => (buf += d));
process.stdin.on("end", () => {
const { calls } = JSON.parse(buf);
const out = calls.map(({ fn, args }) => {
try {
return FN[fn](...(args || []));
} catch (e) {
return { __error: String(e && e.stack ? e.stack : e) };
}
});
process.stdout.write(JSON.stringify(out));
});