diff --git a/resources/tester/test_est_progress_workflow.py b/resources/tester/test_est_progress_workflow.py index 29d90e7c9..6b88b1989 100644 --- a/resources/tester/test_est_progress_workflow.py +++ b/resources/tester/test_est_progress_workflow.py @@ -121,3 +121,22 @@ def test_progress_preserves_zero_wonga_total_and_falls_back_only_when_absent(): assert result["zero"]["rows"][0]["GumTC"] == 200 assert result["zero"]["total"] == 0 assert result["absent"]["total"] == 200 + + +def test_progress_preserves_prior_costs_for_multiple_blank_codes(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.options.SourceRefresh=false; + p.tables.EstMainDat=[{PkID:1,Code:'R',GN:10,GU:100,NU:50},{LinkIndex:54}]; + p.tables.Wonga=[{Gubun:'',Name:'material',BaseField:'GC'}, + {Gubun:'',Name:'labor',BaseField:'NC'}]; + p.progress=[{id:'one',inputs:{'1':{Gum:2}}}]; + const q=EstProgress.lock(EstNative.calculate(p),'one'); + q.progress.push({id:'two',inputs:{'1':{Gum:3}}}); + return EstProgress.calculate(q,'two'); + })()""", + ) + assert result["errors"] == [] + assert [row["JunCost"] for row in result["costs"]] == [200, 100] + assert [row["TotalCost"] for row in result["costs"]] == [500, 250] diff --git a/tools/est-test/README.md b/tools/est-test/README.md index 74ca5417f..cf96d3d26 100644 --- a/tools/est-test/README.md +++ b/tools/est-test/README.md @@ -61,7 +61,7 @@ Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요 2026-11-06 지정 범위인 설계변경·기성·물가변동·연차의 웹앱 구현을 완료했습니다. 설계변경은 당초 보존·재계산 0/1/2와 신규·삭제·변경 비교를 제공합니다. 삭제된 당초/기성 행 번호를 새 행에 재사용하지 않습니다. -기성은 수량·금액·비율·비목별 입력, 사정 승인, 전회·금회·누계 원가를 계산합니다. 확정 회차는 당시 계약 자료를 보존하고 서버 저장 시 결과를 재검산합니다. 전회 수정은 이후 회차의 확정도 해제하며, 전회 미확정 상태에서 다음 회차를 확정할 수 없습니다. +기성은 수량·금액·비율·비목별 입력, 사정 승인, 전회·금회·누계 원가를 계산합니다. 원가 기호가 없는 항목은 이름과 같은 이름의 순서로 전회 금액을 대응합니다. 확정 회차는 당시 계약 자료를 보존하고 서버 저장 시 결과를 재검산합니다. 전회 수정은 이후 회차의 확정도 해제하며, 전회 미확정 상태에서 다음 회차를 확정할 수 없습니다. 물가변동은 같은 행 구조의 계약·입찰 자료(JSON/ESTX)를 불러와 조사·계약·변동 단가를 나눕니다. 원본 레코드 순서 방식에 따라 행 순서·코드·레벨이 다른 자료는 거부합니다. 등락 분기, 조사금액 기준 조정률, 자동 최대수량/수동 적용제외, 간접비 포함 적용대가와 기성 누계 불러오기를 제공합니다. 기성과 물가변동은 원가계산서의 명시적인 0원 도급액을 보존합니다. 연차는 수식 참조 오류를 차단하며, 미리보기 뒤 선택한 차수만 독립 사본으로 엽니다. diff --git a/tools/est-test/native-progress.js b/tools/est-test/native-progress.js index 6afced713..c71f624f7 100644 --- a/tools/est-test/native-progress.js +++ b/tools/est-test/native-progress.js @@ -224,11 +224,20 @@ const EstProgress = (() => { row[prefix + field] = children.reduce((n, child) => n + num(child[prefix + field]), 0); } const acceptedCost = round.approved ? sa : gum; - const priorCosts = new Map( - (previous?.costs || previous?.wonga || []).map((row) => [row.Gubun ?? row.Name, row]), - ); + const costKeys = (items) => { + const occurrences = new Map(); + return items.map((row) => { + const identity = JSON.stringify(row.Gubun ? ["code", row.Gubun] : ["name", row.Name || ""]); + const occurrence = occurrences.get(identity) || 0; + occurrences.set(identity, occurrence + 1); + return JSON.stringify([identity, occurrence]); + }); + }; + const oldCosts = previous?.costs || previous?.wonga || []; + const priorCosts = new Map(costKeys(oldCosts).map((key, index) => [key, oldCosts[index]])); + const currentKeys = costKeys(acceptedCost.rows); const costs = acceptedCost.rows.map((row, index) => { - const previous = priorCosts.get(row.Gubun ?? row.Name); + const previous = priorCosts.get(currentKeys[index]); const jun = num(previous?.TotalCost ?? previous?.Cost); return { ...row,