Files
Aislo/resources/tester/test_est_excel_sheet.py
T
eomsangdonandClaude Opus 5.5 d38d2a2893 feat(est-test): 엑셀형 수량산출서 계산·편집과 원가 행 자리수
- 엑셀형 산출서 BIFF 수식 해석·다시 계산·수식줄 편집·4방향 합·수량으로 지정(가져올 때 값이 같은 합계 셀 추정 연결), 셀 편집 시 ESTX 저장 거부
- 원가 행 계산을 원본 c9d278 끝부분대로: 소수 4자리 반올림 뒤 원 미만 절사, Diget>0 행 자리맞춤(0x11293e8)
- 원가계산서 「선택 행 자릿수 설정」을 원본 선택항목 절하하기 대화로(도급액 행 거부, 입력 행 원금액 기준)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:24:06 +09:00

73 lines
3.5 KiB
Python

"""EST 테스트 앱 엑셀형 수량산출서 계산·편집(원본 TFRMMULEXCEL, .xls BIFF 수식)."""
import json
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
MAIN = Path("C:/Dasansoft/Est Plus 2/Data/ProjectData/main.estx")
def node(script: str, *args: str):
return json.loads(
subprocess.check_output(["node", "-e", script, *args], cwd=ROOT).decode("utf-8")
)
def test_evaluate_and_directional_sum():
out = node("""
const X = require(process.cwd() + '/tools/est-test/native-excel-sheet.cjs');
const cells = { '0,0': { value: '품명' }, '1,0': { value: 2 }, '2,0': { value: 3 }, '3,0': {},
'1,1': { value: 0, formula: '(A2+A3)/2*4' }, '1,2': { value: 0, formula: 'SUM(A2:B2)' },
'4,4': { value: 0, formula: 'ROUND(10/3,2)+ROUNDUP(1.21,1)+ROUNDDOWN(1.29,1)' }, '5,5': { value: 0, formula: 'F6+1' } };
const errors = X.recalc(cells);
const down = X.sumFormula(cells, 3, 0, 'up');
let none = ''; try { X.sumFormula({ '0,0': { value: 'x' } }, 1, 0, 'up'); } catch (e) { none = e.message; }
process.stdout.write(JSON.stringify({ b2: cells['1,1'].value, c2: cells['1,2'].value, e5: cells['4,4'].value,
cycle: [cells['5,5'].value, errors.length], down, none }));
""")
assert out["b2"] == 10 and out["c2"] == 12
assert out["e5"] == 3.33 + 1.3 + 1.2
assert out["cycle"] == ["#오류", 1]
assert out["down"] == "SUM(A2:A3)"
assert out["none"] == "더할 셀이 없습니다."
def test_real_sheets_recompute_and_link_items():
if not MAIN.exists():
pytest.skip("installed EST main sample unavailable")
out = node(
"""
const fs = require('fs'), vm = require('vm');
const imp = require(process.cwd() + '/tools/est-test/import-estx.cjs'), ex = require(process.cwd() + '/tools/est-test/export-estx.cjs');
const p = imp.importEstx(fs.readFileSync(process.argv[1]));
const s = p.quantitySheets.find((x) => x.Code === 'N0000000136');
const formulas = p.quantitySheets.filter((x) => x.excel).flatMap((x) => Object.values(x.excel[0].cells)).filter((c) => c.formula);
const same = Buffer.compare(ex.exportEstx(p), fs.readFileSync(process.argv[1])) === 0;
const ctx = vm.createContext({ structuredClone });
for (const f of ['native-math.js', 'native-formula.js', 'native-text.js', 'native-excel-sheet.cjs', 'native-quantity.cjs'])
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, { filename: f });
ctx.s = s;
const before = vm.runInContext('EstQuantity.calculate(s).items.map((i) => i.GN)', ctx);
vm.runInContext("s.excel[0].cells['6,2'].value = 40; EstExcelSheet.recalc(s.excel[0].cells)", ctx);
const after = vm.runInContext('EstQuantity.calculate(s).items.map((i) => i.GN)', ctx);
s.excelEdited = true;
let message = ''; try { ex.exportEstx(p); } catch (e) { message = e.message; }
process.stdout.write(JSON.stringify({ decoded: formulas.every((c) => typeof c.formula === 'string'), count: formulas.length,
links: Object.fromEntries(Object.entries(s.excelLinks).map(([k, l]) => [k, [l.row, l.col, l.inferred]])), before, after, same, message }));
""",
str(MAIN),
)
assert out["decoded"] is True and out["count"] == 221
assert out["links"] == {
"V000001": [32, 4, True],
"V000002": [32, 6, True],
"V000003": [32, 8, True],
}
assert out["before"][:3] == [17, 12, 58]
assert out["after"][:3] != out["before"][:3]
assert out["same"] is True
assert out["message"].startswith("엑셀형 수량산출서 편집은 ESTX")