fix(est): match prior unnamed cost codes

This commit is contained in:
2026-11-07 02:38:42 +09:00
parent c03929e369
commit 19097b7255
3 changed files with 33 additions and 5 deletions
@@ -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]
+1 -1
View File
@@ -61,7 +61,7 @@ Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요
2026-11-06 지정 범위인 설계변경·기성·물가변동·연차의 웹앱 구현을 완료했습니다. 설계변경은 당초 보존·재계산 0/1/2와 신규·삭제·변경 비교를 제공합니다. 삭제된 당초/기성 행 번호를 새 행에 재사용하지 않습니다.
기성은 수량·금액·비율·비목별 입력, 사정 승인, 전회·금회·누계 원가를 계산합니다. 확정 회차는 당시 계약 자료를 보존하고 서버 저장 시 결과를 재검산합니다. 전회 수정은 이후 회차의 확정도 해제하며, 전회 미확정 상태에서 다음 회차를 확정할 수 없습니다.
기성은 수량·금액·비율·비목별 입력, 사정 승인, 전회·금회·누계 원가를 계산합니다. 원가 기호가 없는 항목은 이름과 같은 이름의 순서로 전회 금액을 대응합니다. 확정 회차는 당시 계약 자료를 보존하고 서버 저장 시 결과를 재검산합니다. 전회 수정은 이후 회차의 확정도 해제하며, 전회 미확정 상태에서 다음 회차를 확정할 수 없습니다.
물가변동은 같은 행 구조의 계약·입찰 자료(JSON/ESTX)를 불러와 조사·계약·변동 단가를 나눕니다. 원본 레코드 순서 방식에 따라 행 순서·코드·레벨이 다른 자료는 거부합니다. 등락 분기, 조사금액 기준 조정률, 자동 최대수량/수동 적용제외, 간접비 포함 적용대가와 기성 누계 불러오기를 제공합니다. 기성과 물가변동은 원가계산서의 명시적인 0원 도급액을 보존합니다. 연차는 수식 참조 오류를 차단하며, 미리보기 뒤 선택한 차수만 독립 사본으로 엽니다.
+13 -4
View File
@@ -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,