feat(stmate-test): 3차 화면·데이터 마무리 — 보호표시 · 공종번호 · 직종·노임 · 제비율표 편집 · 차수 칸 저장 · 계약 변환등록 = 울진 계약 STC · 장부 87.6%

- 시험 615 통과 · 브라우저 화면 13개·메뉴 90개 오류 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 21:00:14 +09:00
co-authored by Claude Opus 5.5
parent e8b76dd65b
commit ffc2401870
36 changed files with 1907 additions and 78 deletions
+62
View File
@@ -0,0 +1,62 @@
/**
* STMATE w 8.0 — 단가 성분 검사 (ui_code_check.js) · 화면·DOM 없음(node 시험 가능)
* 근거: 지식DB STmate/old/20_분석 10 §3(검증 조건식 `TAMT1 <> MAMT1 + LAMT1 + SAMT1` 세 모듈 · TAMT = 총계)
* · 코드종류별 성분 — L → LAMT = TAMT · M → MAMT = TAMT · S → SAMT = TAMT · 복합/W → TAMT = M + L + S
* 차수 칸 1~4 마다 · 내역(BOQ) 단가코드 행은 단가 칸(TAMTn)과 금액 칸(T_AMTn) 둘 다
*/
(function (global) {
"use strict";
const txt = (v) => String(v == null ? "" : v).trim();
const num = (v) => {
const n = parseFloat(txt(v).replace(/,/g, ""));
return Number.isFinite(n) ? n : 0;
};
const OWN = { M: "M", L: "L", S: "S" };
const STRUCT = /^(T\d|E\d|C\d|:)$/;
const near = (a, b) => Math.abs(a - b) < 1e-6;
// 한 칸 무리 검사 — pre(단가 "" · 금액 "_")
function checkSlot(r, code, s, pre, where) {
const out = [];
const f = (k) => `${k}${pre}AMT${s}`;
if (!(f("T") in r)) return out;
const v = { M: num(r[f("M")]), L: num(r[f("L")]), S: num(r[f("S")]), T: num(r[f("T")]) };
if (!near(v.T, v.M + v.L + v.S))
out.push({ where, code, slot: s, rule: "sum", msg: `${f("T")} ≠ 재+노+경`, v });
const own = OWN[code[0]];
if (own && ["M", "L", "S"].some((k) => k !== own && v[k] !== 0))
out.push({ where, code, slot: s, rule: "own", msg: `${own} 코드에 다른 비목 금액`, v });
return out;
}
/** COSTN 행 — 차수 칸 1~4 · [{ where, code, slot, rule: sum|own, msg, v }] */
function checkComponents(rows) {
const out = [];
for (const r of rows || []) {
const c = txt(r.CODE).toUpperCase();
if (!c) continue;
for (const s of [1, 2, 3, 4]) out.push(...checkSlot(r, c, s, "", "COSTN"));
}
return out;
}
/** 내역 단가코드 행 — 단가(TAMTn) · 금액(T_AMTn) · % 줄은 금액 칸만 */
function checkBoq(rows) {
const out = [];
for (const r of rows || []) {
const c = txt(r.BDCODE1).toUpperCase();
if (!c || STRUCT.test(c)) continue;
const pct = txt(r.UNIT1).endsWith("%");
for (const s of [1, 2, 3, 4]) {
if (!pct) out.push(...checkSlot(r, c, s, "", `BOQ#${r._recno}`));
out.push(...checkSlot(r, c, s, "_", `BOQ#${r._recno}`));
}
}
return out;
}
const api = { checkComponents, checkBoq };
global.STMATE_UI_CODE_CHECK = api;
if (typeof module !== "undefined" && module.exports) module.exports = api;
})(typeof window !== "undefined" ? window : globalThis);