- 원본 처리 근거대로 내역서가 쓰는 자료만 하위까지 따라가 남기고 나머지 삭제 - 내역서 첫 등장 순서 재배열, 묶음 앞 빈 줄 정리, 미연결 수량산출서 삭제 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.3 KiB
Python
103 lines
4.3 KiB
Python
"""EST 테스트 앱 무관자료 삭제(원본 mnuNotLinkDelete·DelSort·MulDelete)."""
|
|
|
|
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.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-cleanup.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_unused_rows_removed_and_totals_unchanged():
|
|
result = run_js(
|
|
"""(() => {
|
|
const calc = (p) => EstNative.calculate(EstModel.validate(p));
|
|
const p = calc(structuredClone(source));
|
|
p.tables.StuffcDat.push({ Code: 'MZZZ', Name: '안 쓰는 자재', Index: 999, Cost1: 1, ValueIndex: 0 });
|
|
p.tables.HeadMainDat.push({ Index: 900, Code: 'UZZZ', Name: '안 쓰는 일위', TitleCode: 'UZZZ' },
|
|
{ Code: 'MZZZ', LinkIndex: 5, GN: 1 });
|
|
const before = calc(p), total = before.calculation.total, direct = before.calculation.direct.TC;
|
|
const counts = Object.fromEntries(['StuffcDat','LaborcDat','HeadMainDat','CostMainDat','MachMainDat'].map((t) => [t, p.tables[t].length]));
|
|
const removed = EstCleanup.clean(p, false);
|
|
const after = calc(p);
|
|
return { total: after.calculation.total === total, direct: after.calculation.direct.TC === direct,
|
|
errors: after.calculation.errors.length,
|
|
gone: !p.tables.StuffcDat.some((r) => r.Code === 'MZZZ') && !EstNative.groups(p.tables.HeadMainDat).has('UZZZ'),
|
|
removedStuff: removed.StuffcDat >= 1, removedHead: removed.HeadMainDat >= 1,
|
|
estimate: p.tables.EstMainDat.length === before.tables.EstMainDat.length };
|
|
})()"""
|
|
)
|
|
assert result == {
|
|
"total": True,
|
|
"direct": True,
|
|
"errors": 0,
|
|
"gone": True,
|
|
"removedStuff": True,
|
|
"removedHead": True,
|
|
"estimate": True,
|
|
}
|
|
|
|
|
|
def test_sorted_mode_follows_first_use_and_keeps_codes():
|
|
result = run_js(
|
|
"""(() => {
|
|
const calc = (p) => EstNative.calculate(EstModel.validate(p));
|
|
const p = calc(structuredClone(source));
|
|
const order = EstCleanup.used(p);
|
|
const total = p.calculation.total;
|
|
const codes = new Set(p.tables.HeadTitleDat.map((r) => r.Code));
|
|
EstCleanup.clean(p, true);
|
|
const after = calc(p);
|
|
return { heads: [...EstNative.groups(p.tables.HeadMainDat).keys()].join() === order.get('HeadMainDat').join(),
|
|
stuff: p.tables.StuffcDat.map((r) => r.Code).join() === order.get('StuffcDat').join(),
|
|
codes: p.tables.HeadTitleDat.every((r) => codes.has(r.Code)),
|
|
total: after.calculation.total === total, errors: after.calculation.errors.length };
|
|
})()"""
|
|
)
|
|
assert result == {"heads": True, "stuff": True, "codes": True, "total": True, "errors": 0}
|
|
|
|
|
|
def test_blank_rows_before_heads_and_quantity_sheets():
|
|
result = run_js(
|
|
"""(() => {
|
|
const rows = [{ Index: 1, Code: 'A' }, { Name: 'x', GN: 1 }, {}, {}, { Index: 2, Code: 'B' }, {}];
|
|
const trimmed = EstCleanup.trimBeforeHeads(rows, 'HeadMainDat').map((r) => r.Code || r.Name || '-');
|
|
const p = { tables: { EstMainDat: [{ PkID: 7, Code: 'D1' }] }, quantitySheets: [
|
|
{ Code: 'Q1', items: [{ TargetKey: 'id:7' }] }, { Code: 'Q2', items: [{ TargetCode: 'NONE' }] }, { Code: 'Q3', items: [] }] };
|
|
const gone = EstCleanup.cleanQuantity(p);
|
|
return { trimmed, gone, left: p.quantitySheets.map((s) => s.Code) };
|
|
})()"""
|
|
)
|
|
assert result == {"trimmed": ["A", "x", "B", "-"], "gone": 2, "left": ["Q1"]}
|