fix(est): reject ambiguous progress inputs
This commit is contained in:
@@ -1,6 +1,23 @@
|
||||
from test_est_rea_parity import run
|
||||
|
||||
|
||||
def test_progress_rejects_ambiguous_inputs_without_applying_them_to_multiple_rows():
|
||||
result = run(
|
||||
"math",
|
||||
"""(()=>{
|
||||
const p=EstNative.empty();p.options.SourceRefresh=false;
|
||||
p.tables.EstMainDat=[{Code:'A',GN:10,GU:100},{Code:'A',GN:20,GU:100},
|
||||
{PkID:3,Code:'B',GN:10,GU:100},{LinkIndex:54}];
|
||||
p.progress=[{id:'one',inputs:{A:{Gum:2},'3':{Gum:3}}}];
|
||||
const out=EstProgress.calculate(EstNative.calculate(p),'one');let rejected=false;
|
||||
try{EstProgress.lock(EstNative.calculate(p),'one')}catch{rejected=true}
|
||||
return {out,rejected};
|
||||
})()""",
|
||||
)
|
||||
assert result["rejected"] and result["out"]["errors"]
|
||||
assert [row["GumTC"] for row in result["out"]["rows"][:3]] == [0, 0, 300]
|
||||
|
||||
|
||||
def test_progress_amount_input_preserves_exact_money_and_component_total():
|
||||
result = run(
|
||||
"math",
|
||||
|
||||
@@ -552,6 +552,8 @@ const EstActions = (() => {
|
||||
if (node.matches("[data-progress]")) {
|
||||
const round = project.progress.find((r) => r.id === state.roundId);
|
||||
if (round.locked) throw Error("확정 회차입니다.");
|
||||
if (EstProgress.ambiguousKeys(project.tables.EstMainDat).has(node.dataset.key))
|
||||
throw Error("기성 내역 식별자가 중복되어 입력할 수 없습니다.");
|
||||
const value = node.type === "number" ? Number(node.value) : node.value;
|
||||
if (node.type === "number" && !Number.isFinite(value)) throw Error("수치 입력 오류");
|
||||
EstApp.change(() => {
|
||||
|
||||
@@ -109,9 +109,19 @@ const EstBusinessActions = (() => {
|
||||
},
|
||||
{ name: "ratio", label: "비율 %", type: "number", value: 100 },
|
||||
],
|
||||
(values) =>
|
||||
(values) => {
|
||||
const value = numeric(values.ratio);
|
||||
const ambiguous = EstProgress.ambiguousKeys(project.tables.EstMainDat);
|
||||
const hasAmbiguous = project.tables.EstMainDat.some(
|
||||
(row, index) =>
|
||||
Number(row.Level || 0) <= 0 &&
|
||||
Number(row.CalcIndex || 0) < 2 &&
|
||||
![54, 55, 58, 59, 62, 67].includes(Number(row.LinkIndex)) &&
|
||||
!(values.mode === "full" && Number(row.LinkIndex) === 57) &&
|
||||
ambiguous.has(EstBusiness.rowKey(row, index)),
|
||||
);
|
||||
if (hasAmbiguous) throw Error("기성 내역 식별자가 중복되어 일괄 입력할 수 없습니다.");
|
||||
EstApp.change(() => {
|
||||
const value = numeric(values.ratio);
|
||||
project.tables.EstMainDat.forEach((row, index) => {
|
||||
if (
|
||||
Number(row.Level) > 0 ||
|
||||
@@ -127,7 +137,8 @@ const EstBusinessActions = (() => {
|
||||
round.inputs[key][values.prefix] =
|
||||
values.mode === "full" ? Number(row.GN) || 0 : value;
|
||||
});
|
||||
}, false),
|
||||
}, false);
|
||||
},
|
||||
);
|
||||
} else if (name === "load-progress-exclusion") {
|
||||
const locked = project.progress.filter((round) => round.locked && round.result);
|
||||
|
||||
@@ -8,6 +8,14 @@ const EstProgress = (() => {
|
||||
return Math.max(0, (mantissa.split(".")[1]?.length || 0) - Number(exponent || 0));
|
||||
};
|
||||
const ordinary = (row) => num(row.Level) === 0 && ![54, 55, 58, 59].includes(num(row.LinkIndex));
|
||||
function ambiguousKeys(rows) {
|
||||
const counts = new Map();
|
||||
rows.forEach((row, index) => {
|
||||
const key = EstBusiness.rowKey(row, index);
|
||||
counts.set(key, (counts.get(key) || 0) + 1);
|
||||
});
|
||||
return new Set([...counts].filter(([, count]) => count > 1).map(([key]) => key));
|
||||
}
|
||||
function distribute(amount, row, digits) {
|
||||
const parts = components.map((field) => num(row[field]));
|
||||
const base = sum(row);
|
||||
@@ -31,6 +39,7 @@ const EstProgress = (() => {
|
||||
const errors = [],
|
||||
position = project.progress.indexOf(round),
|
||||
key = EstBusiness.rowKey;
|
||||
const ambiguous = ambiguousKeys(rows);
|
||||
const earlier = project.progress.slice(0, position);
|
||||
if (earlier.some((item) => !item.locked)) errors.push("전회 기성 회차를 먼저 확정하세요.");
|
||||
const previous = earlier.at(-1)?.result,
|
||||
@@ -39,8 +48,10 @@ const EstProgress = (() => {
|
||||
const separated = round.separateComponents !== false;
|
||||
rows.forEach((row, index) => {
|
||||
const id = key(row, index),
|
||||
input = round.inputs?.[id] || {},
|
||||
old = oldRows.get(id);
|
||||
input = ambiguous.has(id) ? {} : round.inputs?.[id] || {},
|
||||
old = ambiguous.has(id) ? null : oldRows.get(id);
|
||||
if (ambiguous.has(id) && (round.inputs?.[id] || oldRows.has(id)))
|
||||
errors.push(`기성 내역 식별자가 중복됩니다: ${id}`);
|
||||
for (const field of ["GN", "TC", "Ratio", ...components])
|
||||
row["Jun" + field] = num(old?.["Total" + field]);
|
||||
if (num(row.Level) > 0 || [54, 59].includes(num(row.LinkIndex))) {
|
||||
@@ -301,5 +312,5 @@ const EstProgress = (() => {
|
||||
}
|
||||
return project;
|
||||
}
|
||||
return { calculate, lock, unlock, rebuild };
|
||||
return { calculate, lock, unlock, rebuild, ambiguousKeys };
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user