Files
Aislo/resources/tester/test_est_code_change.py
T
eomsangdonandClaude Opus 5.5 5e6c2988fc feat(est-test): 엑셀형 산출서 편집 동작과 조달청 표준공사코드 적용
- 엑셀형 산출서: 범위 고르기, 행·열 삽입/삭제(수식 참조·병합·수량 연결 이동, 지운 칸 #REF!), 잘라내기·복사·붙여넣기(상대 참조), 내용지우기, 굵게·기울임·밑줄·정렬, 병합·분리, 대각선, 다시 계산, 품명·규격·단위로 지정, 원본 .xls 병합 정보 읽기
- 조달청 표준공사코드 적용(원본 TfrmCodeChange): 7종 탭, 코드변경·검사 색·변경취소·적용(참조 행 포함, 재계산 없음), 코드일괄변경(.estx), 자동변경, 다산코드정리, 사용자정의코드, 검색. 조달청 목록은 사용자가 고른 PPS_*.dat 를 /api/pps-table 로 읽음

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

114 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""EST 테스트 앱 조달청 표준공사코드 적용(원본 TfrmCodeChange — 코드변경·변경취소·적용·일괄변경·자동변경·다산정리·사용자정의코드)."""
import json
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
JODAL = Path("C:/Dasansoft/Est Plus 2/System/MainSys/JodalCodeDB.estx")
PPS = Path("C:/Dasansoft/Est Plus 2/System/MainSys/PPS_STUFFC.dat")
def run_js(code: str, *args: str):
runner = f"""
const fs = require('fs'), vm = require('vm');
const ctx = vm.createContext({{ structuredClone, console, require, process }});
for (const f of ['native-autolink.js', 'native-code-change.js'])
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, {{ filename: f }});
process.stdout.write(JSON.stringify(vm.runInContext({json.dumps(code)}, ctx)));
"""
return json.loads(
subprocess.check_output(["node", "-e", runner, *args], cwd=ROOT).decode("utf-8")
)
def test_input_check_cancel_apply():
out = run_js(
r"""(() => {
const C = EstCodeChange;
const p = { tables: {
StuffcDat: [{ Index: 1, Code: 'M0000000001', Name: '휘발유', Size: '', Unite: 'ℓ' },
{ Index: 2, Code: '1510150620282202', Name: '경유', Size: '', Unite: 'ℓ' }],
LaborcDat: [{ Index: 7, Code: 'L001010101000001', Name: '작업반장', Size: '' }],
EstMainDat: [{ LinkIndex: 5, Code: 'M0000000001', Index: 0 }, { LinkIndex: 6, Code: 'M0000000001' }],
CostMainDat: [{ LinkIndex: 5, Code: 'M0000000001', Index: 1 }, { LinkIndex: 5, Code: 'M0000000001', Index: 0 }],
MachTitleDat: [], MachMainDat: [{ TitleCode: 'E0000000001', Code: 'E0000000001' }] } };
const rows = C.load(p, 5);
const bad = C.validate(p, 5, rows);
const err = (code) => { try { C.input(p, 5, rows, 0, code); return ''; } catch (e) { return e.message; } };
const out = { bad, flags: rows.map((r) => r.Flag), dup: err('1510150620282202'),
other: err('L001010101000001'), short: err('123'), est: err('M0000000009') };
C.input(p, 5, rows, 0, '1510150620282999');
C.cancel(rows, 0);
out.cancel = rows[0].Code;
C.input(p, 5, rows, 0, '1510150620282999');
out.applied = C.apply(p, 5, rows);
out.codes = [p.tables.StuffcDat[0].Code, p.tables.EstMainDat.map((r) => r.Code), p.tables.CostMainDat.map((r) => r.Code)];
const m = [{ Code: 'E0000000001', Name: '불도저' }];
p.tables.MachTitleDat = m;
const mr = C.load(p, 105);
C.input(p, 105, mr, 0, '0000010100070000');
C.apply(p, 105, mr);
out.mach = [m[0].Code, p.tables.MachMainDat[0]];
const s = [{ Code: 'OB010.11001' }, { Code: 'OB010.11001' }, { Code: '' }];
C.auto(s);
out.auto = s.map((r) => r.Code);
const d = [{ Code: 'x' }, { Code: '' }];
C.dasan(d, 5);
out.dasan = d.map((r) => r.Code);
out.user = [C.userCode(p, 6, C.load(p, 6), 'L001010101000001'), C.userCode(p, 103, [], 'CAA16123000S'), C.userCode(p, 105, [], '00000101000700000')];
out.search = C.search([{ Name: '강관 지주', Size: '6m' }, { Name: '강관', Size: '' }], '강관&6 m').length;
return out;
})()"""
)
assert out["bad"] == 1 and out["flags"] == [1, 0]
assert out["dup"] == "같은 코드가 있습니다. 확인하세요."
assert out["other"].startswith('노임"7.작업반장()" ')
assert out["short"].startswith("코드자리수가 틀립니다")
assert out["est"] == "조달청 코드가 아닙니다."
assert out["cancel"] == "M0000000001"
assert out["applied"] == 3
assert out["codes"] == [
"1510150620282999",
["1510150620282999", "M0000000001"],
["M0000000001", "1510150620282999"],
]
assert out["mach"] == [
"00000101000700000",
{"TitleCode": "00000101000700000", "Code": "00000101000700000"},
]
assert out["auto"] == ["OB01011001", "COB010110010", ""]
assert out["dasan"] == ["M0000000000", "M0000000001"]
assert out["user"] == ["L0010101010Z0000", "CAA16123Z000", "00000101000Z00000"]
assert out["search"] == 1
def test_bulk_from_installed_jodal_db_and_pps_list():
if not JODAL.exists() or not PPS.exists():
pytest.skip("installed EST code DB unavailable")
out = run_js(
r"""(() => {
const { importEstx, table } = require(process.cwd() + '/tools/est-test/import-estx.cjs');
const fs = require('fs');
const source = importEstx(fs.readFileSync(process.argv[1]));
const p = { tables: { LaborcDat: [
{ Index: 1, Code: 'L0000000001', Name: '작업반장', Size: '', Unite: '인' },
{ Index: 2, Code: 'L0000000002', Name: '없는직종', Size: '', Unite: '인' },
{ Index: 3, Code: 'L001010101000001', Name: '작업반장', Size: '', Unite: '인' } ] } };
const rows = EstCodeChange.load(p, 6);
rows.splice(2, 1);
const n = EstCodeChange.bulk(p, 6, rows, source);
const pps = table(fs.readFileSync(process.argv[2]), null, []);
return { n, codes: rows.map((r) => r.Code), checked: rows.map((r) => r.Checked), pps: pps.length,
found: EstCodeChange.search(pps, '작업반장').some((r) => r.Code === 'L001010101000001') };
})()""",
str(JODAL),
str(PPS),
)
assert out["n"] == 1 and out["checked"] == [True, False]
assert out["codes"][0] == "L001010101000001"
assert out["codes"][1] == "L0000000002"
assert out["pps"] > 50000 and out["found"]