- 원본 처리 근거대로 당초금액 복사, 도급액 고정 연결 해제, (감)/(증) 차액 행 - 작성/해제 토글과 원가 화면 당초금액·정산금액 열, 재계산마다 차액 갱신 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
"""EST 테스트 앱 정산원가계산서(원본 당초금액 초기화·정산원가계산서 작성/해제)."""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
FILES = [
|
|
"native-math.js",
|
|
"native-formula.js",
|
|
"native-machine.js",
|
|
"native-rows.js",
|
|
"native-text.js",
|
|
"native-quantity.cjs",
|
|
"native-wonga.js",
|
|
"native-jungsang.js",
|
|
"native-engine.js",
|
|
"native-row-match.js",
|
|
"native-escalation.js",
|
|
"native-progress.js",
|
|
"native-business.js",
|
|
"native-review.js",
|
|
"native-resources.js",
|
|
"native-schedule.js",
|
|
"native-model.js",
|
|
"native-verify.js",
|
|
]
|
|
|
|
|
|
def run_js(code: str):
|
|
runner = f"""
|
|
const fs = require('fs'), vm = require('vm');
|
|
const ctx = vm.createContext({{ structuredClone, console }});
|
|
for (const f of {json.dumps(FILES)})
|
|
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, {{ filename: f }});
|
|
ctx.source = JSON.parse(fs.readFileSync('tools/est-test/sample.json', 'utf8'));
|
|
process.stdout.write(JSON.stringify(vm.runInContext({json.dumps(code)}, ctx)));
|
|
"""
|
|
return json.loads(subprocess.check_output(["node", "-e", runner], cwd=ROOT).decode("utf-8"))
|
|
|
|
|
|
def test_difference_text_and_row_reuse():
|
|
result = run_js(
|
|
"""(() => {
|
|
const rows = [{ Name: '순공사비', Cost: 100, SC: 120 }, { Name: '', Cost: 0 }, { Name: '', Cost: 0 }];
|
|
const less = EstJungSang.difference(rows);
|
|
rows[0].Cost = 150;
|
|
const more = EstJungSang.difference(rows);
|
|
return { less, more, at: rows.findIndex((r) => r.LinkIndex === 66), size: rows.length, row: rows[1] };
|
|
})()"""
|
|
)
|
|
assert result["less"] == "(감)20" and result["more"] == "(증)30"
|
|
assert result["at"] == 1 and result["size"] == 3
|
|
assert result["row"]["Name"] == "." and result["row"]["EndLineIndex"] == 1
|
|
|
|
|
|
def test_toggle_copies_original_amounts_and_recalc_rewrites_difference():
|
|
result = run_js(
|
|
"""(() => {
|
|
const calc = (p) => EstNative.calculate(EstModel.validate(p));
|
|
let p = calc(structuredClone(source));
|
|
const before = p.tables.Wonga.map((r) => Number(r.Cost) || 0);
|
|
const fixed = p.tables.Wonga.findIndex((r) => Number(r.LinkIndex) === 55);
|
|
EstJungSang.toggle(p);
|
|
const copied = p.tables.Wonga.every((r, i) => r.LinkIndex === 66 || Number(r.SC) === (before[i] ?? 0));
|
|
p = calc(p);
|
|
const zero = p.tables.Wonga.find((r) => r.LinkIndex === 66).VSusic;
|
|
const row = p.tables.EstMainDat.find((r) => Number(r.GN) > 0 && Number(r.TU) > 0 && !Number(r.Level));
|
|
row.GN = Number(row.GN) * 2;
|
|
p = calc(p);
|
|
const diff = p.tables.Wonga.find((r) => r.LinkIndex === 66).VSusic;
|
|
const errors = p.calculation.errors.length;
|
|
EstJungSang.toggle(p);
|
|
const off = { show: p.options.JungSangShow, has66: p.tables.Wonga.some((r) => r.LinkIndex === 66),
|
|
keptSC: p.tables.Wonga.some((r) => Number(r.SC) > 0) };
|
|
return { copied, released: Number(p.tables.Wonga[fixed].LinkIndex), zero, diff, errors, off,
|
|
columns: EstJungSang.columns({ options: { JungSangShow: true } }, ['Name', 'VSusic', 'Cost']) };
|
|
})()"""
|
|
)
|
|
assert result["copied"] is True
|
|
assert result["released"] == 0
|
|
assert re.match(r"^(0|\((증|감)\)\d+)$", result["zero"])
|
|
assert result["diff"].startswith("(증)")
|
|
assert result["errors"] == 0
|
|
assert result["off"] == {"show": False, "has66": False, "keptSC": True}
|
|
assert result["columns"] == ["Name", "VSusic", "SC", "Cost"]
|