- 가져오기가 WPT 표를 버리던 결함 고침: 표 줄(칸·수량 표지·수식·세로병합) 보존, 문서 읽기 native-quantity-wpt.cjs 로 분리 - 표 셀 합·수량 지정·병합/분리·표 만들기·표 모양, 문단·그림·쪽 삽입/삭제·품명/규격 지정·공종명칭 입력·테두리선·산출근거 양식 - 수량 불러오기·수량목록보기·선택수량복사·내역서 수량붙여넣기(연결) - ESTX 저장은 표 줄을 건너 문단만 맞춤, 표·서식 편집은 거부 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
97 lines
3.9 KiB
Python
97 lines
3.9 KiB
Python
"""EST 테스트 앱 기성 원가계산서 편집(원본 TfrmWongaAccomH/V 팝업) — 회차별 원가 서식 사본."""
|
|
|
|
import json
|
|
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-wpt.cjs",
|
|
"native-quantity.cjs",
|
|
"native-wonga.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",
|
|
"native-wonga-tools.js",
|
|
"native-progress-wonga.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_progress_wonga_edits_apply_to_round_copy_only():
|
|
result = run_js(
|
|
r"""(() => {
|
|
const p = EstNative.calculate(EstModel.validate(structuredClone(source)));
|
|
const inputs = {};
|
|
p.tables.EstMainDat.forEach((row, i) => {
|
|
if (row.Code && !Number(row.Level)) inputs[EstBusiness.rowKey(row, i)] = { Gum: 50, GumMode: 'ratio' };
|
|
});
|
|
p.progress = [{ id: 'one', name: '1회', inputs }];
|
|
const round = p.progress[0], ops = EstProgressWonga.ops;
|
|
const calc = () => EstProgress.calculate(p, 'one');
|
|
const plain = (t) => String(t).replace(/\s+/g, '');
|
|
const base = calc();
|
|
const anjun = base.costs.findIndex((r) => plain(r.Name).includes('산업안전') && Number(r.CalcIndex) !== 2);
|
|
const total = base.costs.findIndex((r) => Number(r.LinkIndex) === 55);
|
|
const out = { errors: base.errors, baseTotal: base.total, length: base.costs.length };
|
|
ops.insert(p, round, 1);
|
|
const ins = calc();
|
|
out.inserted = [ins.costs.length, ins.costs[1].Name, ins.costs[1].ContractCost, ins.costs[2].ContractCost === base.costs[1].ContractCost, p.tables.Wonga.length];
|
|
ops.undo(p, round);
|
|
out.undone = ['wonga' in round, 'wongaUndo' in round];
|
|
ops.position(p, round, anjun, 2, 3);
|
|
const cut = calc();
|
|
out.anjun = [base.costs[anjun].GumCost, cut.costs[anjun].GumCost, cut.costs[anjun].ContractCost];
|
|
ops.total(p, round, 4);
|
|
const fixed = calc();
|
|
out.total = [cut.total, fixed.total, fixed.errors];
|
|
ops.clear(p, round, 0);
|
|
const r0 = round.wonga[0];
|
|
out.cleared = [r0.VSusic, r0.Cost, r0.Youl, r0.Diget, r0.Name !== ''];
|
|
ops.init(p, round);
|
|
out.init = ['wonga' in round, 'wongaTotalDigits' in round, calc().total === base.total];
|
|
ops.undo(p, round);
|
|
out.reinit = Array.isArray(round.wonga);
|
|
ops.init(p, round);
|
|
round.locked = true;
|
|
try { ops.append(p, round); } catch (e) { out.locked = e.message; }
|
|
return out;
|
|
})()"""
|
|
)
|
|
assert result["errors"] == []
|
|
assert result["baseTotal"] > 0
|
|
n = result["length"]
|
|
assert result["inserted"] == [n + 1, "", 0, True, n]
|
|
assert result["undone"] == [False, False]
|
|
before, after, contract = result["anjun"]
|
|
assert after == before // 1000 * 1000 and after != before and contract > 0
|
|
cut_total, fixed_total, errors = result["total"]
|
|
assert fixed_total == cut_total // 10000 * 10000 and errors == []
|
|
assert result["cleared"] == ["", 0, "", 0, True]
|
|
assert result["init"] == [False, False, True]
|
|
assert result["reinit"] is True
|
|
assert result["locked"] == "확정한 회차는 고칠 수 없습니다."
|