66 lines
2.9 KiB
JavaScript
66 lines
2.9 KiB
JavaScript
/* 802-4b-3 금액 줄 값 식 빨강 헬퍼 — 화면 `formula_doc_lines.ts` 와 같은 값 목록(기호 값 + 금액 줄 기호 + 비목 합)으로
|
|
* 금액 줄 단가 · 수량의 기호마다 값이 있는지 봄. stdin = [{문서, 값, 줄}] · stdout = [{빨강: [[줄, 기호]], 합: [[줄, 셈, 금액]]}] */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const cache = new Map();
|
|
function load(at) {
|
|
if (cache.has(at)) return cache.get(at);
|
|
const js = ts.transpileModule(fs.readFileSync(at, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(at, box.exports);
|
|
const need = (p) =>
|
|
p.startsWith(".") ? load(path.resolve(path.dirname(at), `${p}.ts`)) : require(p);
|
|
new Function("exports", "module", "require", js)(box.exports, box, need);
|
|
return box.exports;
|
|
}
|
|
const view = load(
|
|
path.join(__dirname, "..", "..", "common_util", "common_util_formula_doc_view.ts"),
|
|
);
|
|
const COSTS = new Set(["노무비", "재료비", "경비"]);
|
|
const names = (n, out = []) => {
|
|
if (!n) return out;
|
|
if (n[0] === "var") out.push(n[1]);
|
|
else if (n[0] === "field") names(n[1], out);
|
|
else if (n[0] === "neg" || n[0] === "pct") names(n[1], out);
|
|
else if (n[0] === "bin") names(n[2], out) && names(n[3], out);
|
|
else if (n[0] === "call") n[2].forEach((x) => names(x, out));
|
|
return out;
|
|
};
|
|
const num = (v) => (typeof v === "string" && v !== "" && !isNaN(Number(v)) ? Number(v) : v);
|
|
const input = JSON.parse(fs.readFileSync(0, "utf8"));
|
|
const out = input.map(({ 문서, 값, 줄 }) => {
|
|
const vals = { ...값 };
|
|
const lines = 문서.금액 || [];
|
|
lines.forEach((l, i) => {
|
|
const a = num((줄[i] || {}).금액);
|
|
if (l.기호 && typeof a === "number") vals[String(l.기호)] = a;
|
|
});
|
|
const red = [];
|
|
const sums = [];
|
|
lines.forEach((l, i) => {
|
|
const amount = num((줄[i] || {}).금액);
|
|
const raw = view.moneyScope(줄, i);
|
|
const scope = { ...vals, ...raw };
|
|
let cost = false;
|
|
for (const t of [l.단가, l.수량]) {
|
|
for (const name of names(view.tryParse(t))) {
|
|
if (COSTS.has(name)) cost = true;
|
|
const v = scope[name];
|
|
const miss = v === null || v === undefined || (typeof v === "object" && l.비목 !== "묶음");
|
|
if (miss && typeof amount === "number") red.push([i, name]);
|
|
}
|
|
}
|
|
if (cost && typeof amount === "number") {
|
|
const p = view.evalNode(view.tryParse(l.단가), scope);
|
|
const q = l.수량 === undefined || l.수량 === null || l.수량 === "" ? 1 : view.evalNode(view.tryParse(l.수량), scope);
|
|
sums.push([i, typeof p === "number" && typeof q === "number" ? p * q : null, amount]);
|
|
}
|
|
});
|
|
return { 빨강: red, 합: sums };
|
|
});
|
|
process.stdout.write(JSON.stringify(out));
|