diff --git a/resources/tester/test_est_change_workflow.py b/resources/tester/test_est_change_workflow.py index 44b9170c6..55258169d 100644 --- a/resources/tester/test_est_change_workflow.py +++ b/resources/tester/test_est_change_workflow.py @@ -1,6 +1,42 @@ from test_est_rea_parity import run +def test_design_change_costs_include_duplicate_new_and_deleted_cost_rows(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.tables.Wonga=[ + {Gubun:'A',Name:'first',Cost:200},{Gubun:'A',Name:'second',Cost:60}, + {Gubun:'C',Name:'new',Cost:25}]; + p.baseline={name:'old',tables:{EstMainDat:[],Wonga:[ + {Gubun:'A',Name:'first',Cost:100},{Gubun:'A',Name:'second',Cost:50}, + {Gubun:'B',Name:'removed',Cost:30}]}}; + const before=JSON.stringify(p), rows=EstBusiness.costChanges(p); + const output=EstForms.data('change-costs',p); + const node={innerHTML:''};globalThis.EstApp={esc:x=>String(x??''),number:x=>String(x??0),byId:()=>node}; + EstExtra.render({project:p,view:'change'}); + return {rows,output,html:node.innerHTML,same:before===JSON.stringify(p)}; + })()""", + ) + assert result["same"] + assert [row["difference"] for row in result["rows"]] == [100, 10, 25, -30] + assert result["output"]["rows"] == result["rows"] + assert "removed" in result["html"] + + +def test_stored_original_costs_remain_attached_to_each_duplicate_row(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.tables.Wonga=[ + {Gubun:'A',Name:'new',Cost:10},{Gubun:'A',Name:'existing',Cost:50,Cost_:30}]; + return EstBusiness.costChanges(p); + })()""", + ) + assert [row["originalCost"] for row in result] == [0, 30] + assert [row["difference"] for row in result] == [10, 20] + + def test_change_view_includes_named_rows_without_codes(): result = run( "math", diff --git a/tools/est-test/native-business.js b/tools/est-test/native-business.js index 2731f2c78..e64fd54b2 100644 --- a/tools/est-test/native-business.js +++ b/tools/est-test/native-business.js @@ -105,6 +105,50 @@ const EstBusiness = (() => { }); return changes; } + function costChanges(project) { + const current = project.tables.Wonga || []; + if (!project.baseline) { + if (!current.some((row) => row.Cost_ != null)) return []; + return current.map((row) => ({ + Gubun: row.Gubun, + Name: row.Name, + originalCost: num(row.Cost_), + changedCost: num(row.Cost), + difference: num(row.Cost) - num(row.Cost_), + })); + } + const old = project.baseline.tables.Wonga || []; + function keyed(rows) { + const counts = new Map(); + return rows.map((row) => { + const identity = JSON.stringify(row.Gubun ? ["code", row.Gubun] : ["name", row.Name || ""]); + const occurrence = counts.get(identity) || 0; + counts.set(identity, occurrence + 1); + return { row, key: JSON.stringify([identity, occurrence]) }; + }); + } + const previous = new Map(keyed(old).map(({ row, key }) => [key, row])); + const rows = keyed(current).map(({ row, key }) => { + const saved = previous.get(key); + previous.delete(key); + return { + Gubun: row.Gubun, + Name: row.Name, + originalCost: num(saved?.Cost), + changedCost: num(row.Cost), + difference: num(row.Cost) - num(saved?.Cost), + }; + }); + for (const row of previous.values()) + rows.push({ + Gubun: row.Gubun, + Name: row.Name, + originalCost: num(row.Cost), + changedCost: 0, + difference: -num(row.Cost), + }); + return rows; + } function bid(project, rates) { const next = copy(project); for (const key of ["HeadMainDat", "EstMainDat"]) @@ -298,6 +342,7 @@ const EstBusiness = (() => { return { baseline, changes, + costChanges, bid, original, progress, diff --git a/tools/est-test/native-extra.js b/tools/est-test/native-extra.js index ca63a3650..66501a6d1 100644 --- a/tools/est-test/native-extra.js +++ b/tools/est-test/native-extra.js @@ -70,7 +70,17 @@ const EstExtra = (() => { number(row.TC), number(row.difference), ]), - ), + ) + + table( + ["원가 구분", "비목", "당초 금액", "변경 금액", "증감"], + EstBusiness.costChanges(project).map((row) => [ + esc(row.Gubun), + esc(row.Name), + number(row.originalCost), + number(row.changedCost), + number(row.difference), + ]), + ), project.baseline ? `당초: ${esc(project.baseline.name)}. 당초 자료는 별도 사본으로 보존합니다.` : changes.some((row) => row.originalSource === "file") diff --git a/tools/est-test/native-forms.js b/tools/est-test/native-forms.js index 170392641..55f52545a 100644 --- a/tools/est-test/native-forms.js +++ b/tools/est-test/native-forms.js @@ -1,6 +1,11 @@ const EstForms = (() => { const common = ["Code", "Name", "Size", "Unite"]; function data(id, project, state = {}) { + if (id === "change-costs") + return { + columns: ["Gubun", "Name", "originalCost", "changedCost", "difference"], + rows: EstBusiness.costChanges(project), + }; if (id === "annual-summary") { const output = EstBusiness.annual(project, false); return { @@ -282,6 +287,8 @@ const EstForms = (() => { TotalCost: "누계 금액", adjustedCost: "조정 금액", excludedCost: "제외 금액", + originalCost: "당초 금액", + changedCost: "변경 금액", TargetCode: "연결 내역", TU: "단가", TC: "금액", diff --git a/tools/est-test/native-report.js b/tools/est-test/native-report.js index daf9b72d6..d062136ac 100644 --- a/tools/est-test/native-report.js +++ b/tools/est-test/native-report.js @@ -141,6 +141,7 @@ const EstReport = (() => { { Index: "escalation-summary", Name: "물가변동 내역서", Name2: "물가변동 내역서" }, { Index: "escalation-costs", Name: "물가변동 원가계산서", Name2: "물가변동 원가계산서" }, { Index: "annual-summary", Name: "연차 배분표", Name2: "연차 배분표" }, + { Index: "change-costs", Name: "설계변경 원가 증감표", Name2: "설계변경 원가 증감표" }, ); const table = EstApp.state.project.tables[EstApp.state.view]; const s = setting(),