- 엑셀형 산출서: 범위 고르기, 행·열 삽입/삭제(수식 참조·병합·수량 연결 이동, 지운 칸 #REF!), 잘라내기·복사·붙여넣기(상대 참조), 내용지우기, 굵게·기울임·밑줄·정렬, 병합·분리, 대각선, 다시 계산, 품명·규격·단위로 지정, 원본 .xls 병합 정보 읽기 - 조달청 표준공사코드 적용(원본 TfrmCodeChange): 7종 탭, 코드변경·검사 색·변경취소·적용(참조 행 포함, 재계산 없음), 코드일괄변경(.estx), 자동변경, 다산코드정리, 사용자정의코드, 검색. 조달청 목록은 사용자가 고른 PPS_*.dat 를 /api/pps-table 로 읽음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
114 lines
5.4 KiB
Python
114 lines
5.4 KiB
Python
"""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"]
|