diff --git a/resources/tester/test_est_business.py b/resources/tester/test_est_business.py index 59cb99937..92211aead 100644 --- a/resources/tester/test_est_business.py +++ b/resources/tester/test_est_business.py @@ -172,3 +172,39 @@ def test_unlinked_text_price_does_not_inherit_the_previous_rows_source(): assert resources["M"]["quantity"] == 1 assert resources["M"]["amount"] == 150 assert resources["MZZZZZZZZZZ"]["amount"] == 100 + + +def test_change_comparison_uses_original_file_values_without_overwriting_a_baseline(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.tables.EstMainDat=[ + {PkID:1,Code:'R',GN:3,TC:150,GN_:2,TC_:100}, + {PkID:2,Code:'Z',GN:1,TC:10,GN_:0,TC_:0}, + {PkID:3,Code:'N',GN:1,TC:20}]; + const stored=EstBusiness.changes(p); + p.baseline={name:'explicit',tables:{EstMainDat:[{PkID:1,Code:'R',GN:4,TC:200}]}}; + return {stored,explicit:EstBusiness.changes(p),unchanged:p.tables.EstMainDat[0].TC_}; + })()""", + ) + assert result["stored"][0]["originalGN"] == 2 + assert result["stored"][0]["originalTC"] == 100 + assert result["stored"][0]["difference"] == 50 + assert result["stored"][0]["originalSource"] == "file" + assert not result["stored"][1]["NewItem"] + assert result["stored"][2]["NewItem"] + assert result["explicit"][0]["originalTC"] == 200 + assert result["explicit"][0]["difference"] == -50 + assert result["explicit"][1]["NewItem"] + assert result["unchanged"] == 100 + + +def test_original_numeric_fields_reject_invalid_saved_values(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.tables.EstMainDat=[{GN:1,GN_:'invalid'}]; + try {EstModel.validate(p);return false;}catch(error){return error.message;} + })()""", + ) + assert "GN_" in result diff --git a/tools/est-test/README.md b/tools/est-test/README.md index d0597d5d6..49a01dec0 100644 --- a/tools/est-test/README.md +++ b/tools/est-test/README.md @@ -28,6 +28,8 @@ 동원 집계는 비목별 낙찰률과 단위환산을 하위 산근·일위대가에도 전달합니다. 연결 단가가 없는 금액 입력분은 원본 전용 코드로 따로 합산하고, 장비 시간으로 바꿀 수 없는 경비는 `제외금액` 단위로 보존합니다. 산근에서 연결을 해제한 단가는 앞 행의 원천을 가져오지 않습니다. +설계변경 비교는 웹에서 확정한 당초 사본이 없을 때 원본 파일의 당초 수량·금액을 사용합니다. 별도 당초 확정 사본이 있으면 그 사본이 우선합니다. 원본 당초 전체 자료와 원가 서식의 활성 선택을 자동 추정하지는 않습니다. + **전체 원본 동등성은 아직 확보하지 못했습니다.** 기성·물가변동·연차 등은 분석 분기와 합성 시나리오를 시험했으며 원본 업무 샘플 대조는 없습니다. 원본 화면 실행 비교는 못했습니다. 원본 페이지·도면 배치, CAD/VEC0650 편집, 원본 인쇄 서식, Excel/XML/JDL 변환, ESTX 수정 저장은 미구현입니다. 원본 대체품의 완료 판정으로 보지 않습니다. 검사: diff --git a/tools/est-test/native-business.js b/tools/est-test/native-business.js index 3f52ee74d..cec72656b 100644 --- a/tools/est-test/native-business.js +++ b/tools/est-test/native-business.js @@ -34,7 +34,12 @@ const EstBusiness = (() => { function changes(project) { const old = project.baseline?.tables.EstMainDat || []; return project.tables.EstMainDat.map((row, index) => { - const original = old.find((r, i) => rowKey(r, i) === rowKey(row, index)); + const saved = old.find((r, i) => rowKey(r, i) === rowKey(row, index)); + const stored = + !project.baseline && ["Code_", "Name_", "GN_", "TC_"].some((key) => row[key] != null) + ? { GN: row.GN_, TC: row.TC_ } + : null; + const original = saved || stored; return { key: rowKey(row, index), Code: row.Code, @@ -45,6 +50,7 @@ const EstBusiness = (() => { originalTC: num(original?.TC), difference: num(row.TC) - num(original?.TC), NewItem: !original, + originalSource: saved ? "baseline" : stored ? "file" : null, }; }); } diff --git a/tools/est-test/native-extra.js b/tools/est-test/native-extra.js index bb2fb6c79..310e0c746 100644 --- a/tools/est-test/native-extra.js +++ b/tools/est-test/native-extra.js @@ -75,7 +75,9 @@ const EstExtra = (() => { ), project.baseline ? `당초: ${esc(project.baseline.name)}. 당초 자료는 별도 사본으로 보존합니다.` - : "현재 작업을 저장한 뒤 당초 내역을 확정하세요.", + : changes.some((row) => row.originalSource === "file") + ? "원본 파일에 저장된 당초 수량·금액과 비교합니다." + : "현재 작업을 저장한 뒤 당초 내역을 확정하세요.", ); } else if (view === "progress") { state.roundId ||= project.progress[0]?.id; diff --git a/tools/est-test/native-model.js b/tools/est-test/native-model.js index 43a7d14e6..b1d57a10f 100644 --- a/tools/est-test/native-model.js +++ b/tools/est-test/native-model.js @@ -269,7 +269,7 @@ const EstModel = (() => { if (["__proto__", "constructor", "prototype"].includes(key)) throw Error("허용하지 않는 필드 이름"); if ( - numeric.has(key) && + (numeric.has(key) || (key.endsWith("_") && numeric.has(key.slice(0, -1)))) && value != null && (typeof value !== "number" || !Number.isFinite(value) ||