fix(est): preserve unmatched progress exclusions

This commit is contained in:
2026-11-07 10:36:30 +09:00
parent 5302684c93
commit 448b92276f
3 changed files with 46 additions and 10 deletions
@@ -1,6 +1,23 @@
from test_est_rea_parity import run
def test_progress_exclusions_preserve_new_rows_and_reject_ambiguous_keys():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[{PkID:1,Code:'A',EscProgress:9},
{PkID:2,Code:'B',EscProgress:7}];
const before=JSON.stringify(p), saved={rows:[{PkID:1,Code:'A',TotalGN:3}]};
const updates=EstBusiness.progressExclusions(p,saved);let blocked=0;
try{EstBusiness.progressExclusions(p,{rows:[{Code:'A',TotalGN:1},{Code:'A',TotalGN:2}]})}catch{blocked++}
const q=structuredClone(p);q.tables.EstMainDat=[{Code:'A'},{Code:'A'}];
try{EstBusiness.progressExclusions(q,{rows:[{Code:'A',TotalGN:1}]})}catch{blocked++}
return {updates,blocked,same:before===JSON.stringify(p)};
})()""",
)
assert result == {"updates": [{"index": 0, "value": 3}], "blocked": 2, "same": True}
def test_escalation_preserves_explicit_zero_total_and_falls_back_only_without_wonga():
result = run(
"math",
+7 -10
View File
@@ -142,17 +142,14 @@ const EstBusinessActions = (() => {
choices: locked.map((round) => [round.id, round.name]),
},
],
(values) =>
(values) => {
const result = locked.find((round) => round.id === values.id).result;
const updates = EstBusiness.progressExclusions(project, result);
EstApp.change(() => {
const result = locked.find((round) => round.id === values.id).result;
const rows = new Map(
result.rows.map((row, index) => [EstBusiness.rowKey(row, index), row]),
);
project.tables.EstMainDat.forEach(
(row, index) =>
(row.EscProgress = Number(rows.get(EstBusiness.rowKey(row, index))?.TotalGN) || 0),
);
}, false),
for (const { index, value } of updates)
project.tables.EstMainDat[index].EscProgress = value;
}, false);
},
);
} else if (name === "load-contract" || name === "load-bid") {
purpose = name;
+22
View File
@@ -4,6 +4,27 @@ const EstBusiness = (() => {
const copy = (value) => structuredClone(value);
const rowKey = (row, index) => String(row.PkID ?? (row.Code || `row:${index}`));
const special = (row) => [57, 58, 62, 67].includes(num(row.LinkIndex));
function progressExclusions(project, result) {
const saved = new Map();
result.rows.forEach((row, index) => {
const key = rowKey(row, index);
if (saved.has(key)) throw Error(`기성 내역 식별자가 중복됩니다: ${key}`);
const value = Number(row.TotalGN);
if (!Number.isFinite(value)) throw Error(`기성 누계수량 오류: ${key}`);
saved.set(key, value);
});
const counts = new Map();
project.tables.EstMainDat.forEach((row, index) => {
const key = rowKey(row, index);
counts.set(key, (counts.get(key) || 0) + 1);
});
return project.tables.EstMainDat.flatMap((row, index) => {
const key = rowKey(row, index);
if (!saved.has(key)) return [];
if (counts.get(key) !== 1) throw Error(`현재 내역 식별자가 중복됩니다: ${key}`);
return [{ index, value: saved.get(key) }];
});
}
function moveEstimateRows(rows, selection, direction) {
const indices = [...selection].sort((a, b) => a - b);
if (!indices.length) throw Error("이동할 내역 행을 선택하세요.");
@@ -384,6 +405,7 @@ const EstBusiness = (() => {
annual,
newAnnualName,
rowKey,
progressExclusions,
moveEstimateRows,
importContract,
importBid,