From 83d3d43f10e276d8fae42dc1e578067ebcd7f50d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 7 Nov 2026 11:04:23 +0900 Subject: [PATCH] feat(est): apply all quantity sheets --- .../tester/test_est_quantity_workflow.py | 20 +++++++++++++++++++ tools/est-test/PLAN.md | 3 +++ tools/est-test/README.md | 2 ++ tools/est-test/index.html | 1 + tools/est-test/native-business-actions.js | 6 +++++- tools/est-test/native-quantity.cjs | 13 ++++++++++-- 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/resources/tester/test_est_quantity_workflow.py b/resources/tester/test_est_quantity_workflow.py index bbe55e142..b2017857d 100644 --- a/resources/tester/test_est_quantity_workflow.py +++ b/resources/tester/test_est_quantity_workflow.py @@ -1,6 +1,26 @@ from test_est_rea_parity import run +def test_apply_all_quantities_sums_sheets_and_rejects_errors_atomically(): + result = run( + "math", + """(()=>{ + const p=EstNative.empty();p.tables.EstMainDat=[{PkID:1,Code:'A',GN:1},{PkID:2,Code:'B',GN:2}]; + p.quantities=[{Code:'Q1',formula:'2',TargetKey:'id:1'}]; + p.quantitySheets=[{Code:'S1',Name:'sheet',options:{},rows:[{LineData:'3 = \\x120\\x12',markers:['a']}], + items:[{Code:'a',TargetKey:'id:1'}]}, + {Code:'S2',Name:'sheet2',options:{},rows:[{LineData:'4 = \\x120\\x12',markers:['b']}], + items:[{Code:'b',TargetKey:'id:2'}]}]; + const values=EstQuantity.allTotals(p);EstQuantity.applyTotals(p,values); + const updated=p.tables.EstMainDat.map(row=>row.GN),before=JSON.stringify(p.tables.EstMainDat); + p.quantitySheets[1].rows[0].LineData='bad';let rejected=false; + try{EstQuantity.applyTotals(p,EstQuantity.allTotals(p))}catch{rejected=true} + return {updated,rejected,unchanged:before===JSON.stringify(p.tables.EstMainDat)}; + })()""", + ) + assert result == {"updated": [5, 4], "rejected": True, "unchanged": True} + + def test_quantity_apply_requires_one_detail_row_per_code(): result = run( "math", diff --git a/tools/est-test/PLAN.md b/tools/est-test/PLAN.md index a0fec1e76..e6a5ce208 100644 --- a/tools/est-test/PLAN.md +++ b/tools/est-test/PLAN.md @@ -1,4 +1,7 @@ ## Codex · EST 독립 테스트 앱 (2026-11-04) +- [x] 모든 수량산출서·직접 수식표의 내역 수량 일괄 합산 반영 → 동일 대상 합산·오류 시 전체 차단 시험. + - 자체 검증: 동일 내역 합산·오류 시 전체 차단 포함 수량·문서 시험 18개 통과, Edge에서 일괄 반영·취소 확인. + - 2026-11-07: 사용자 지시로 EST 작업 계획과 검증 기록을 공유 PLAN.md에서 이 파일로 이관. 업무 회귀 시험 79개 통과, openpyxl 경고 7건. Git push는 보류. diff --git a/tools/est-test/README.md b/tools/est-test/README.md index f032f7c0d..b45022ab8 100644 --- a/tools/est-test/README.md +++ b/tools/est-test/README.md @@ -41,6 +41,8 @@ Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요 재계산 메뉴의 수량 반영도 현재 선택한 산출서 또는 직접 작성 수식표를 사용합니다. 반영 전에 모든 연결 대상과 수식을 검사하며, 기존 원본 연결 데이터는 그대로 보관합니다. +재계산 메뉴의 `모든 수량산출 합산 반영`은 모든 산출서와 직접 작성 수식표의 연결 수량을 내역 행별로 합산합니다. 어느 산출서든 계산 오류가 있거나 연결 대상이 모호하면 전체 반영을 중단합니다. + 수량산출서 인쇄 서식에는 직접 작성한 수식표와 산출서의 산식, 계산 수량, 연결 내역이 함께 표시됩니다. 계산할 수 없는 수식은 오류를 표시합니다. 산출 행의 결과 표지 개수가 연결 정보와 다르면 계산 오류로 표시하며, 기존 저장 수량을 내역에 반영하지 않습니다. 화면이나 Excel에서 수식을 수정할 때 결과 표지와 연결 항목을 유지하세요. diff --git a/tools/est-test/index.html b/tools/est-test/index.html index 11d0c9f39..a6624cdee 100644 --- a/tools/est-test/index.html +++ b/tools/est-test/index.html @@ -96,6 +96,7 @@ +
diff --git a/tools/est-test/native-business-actions.js b/tools/est-test/native-business-actions.js index 20ae840df..65467e791 100644 --- a/tools/est-test/native-business-actions.js +++ b/tools/est-test/native-business-actions.js @@ -45,7 +45,11 @@ const EstBusinessActions = (() => { const { state } = EstApp, project = state.project, round = currentRound(); - if (name === "move-quantity") { + if (name === "apply-all-quantities") { + const totals = EstQuantity.allTotals(project); + if (confirm("모든 수량산출서와 수식표의 연결 수량을 합산해 내역에 반영하시겠습니까?")) + EstApp.change(() => EstQuantity.applyTotals(project, totals)); + } else if (name === "move-quantity") { const index = Number(node.dataset.row), direction = Number(node.dataset.direction), next = index + direction; diff --git a/tools/est-test/native-quantity.cjs b/tools/est-test/native-quantity.cjs index 85a409322..f5040a0cc 100644 --- a/tools/est-test/native-quantity.cjs +++ b/tools/est-test/native-quantity.cjs @@ -141,7 +141,7 @@ const EstQuantity = (() => { }); return { rows, errors }; } - function totals(project, selected) { + function totals(project, selected, allowEmpty = false) { let items; if (selected && selected !== "manual") { const sheet = project.quantitySheets?.find((s) => s.Code === selected); @@ -170,9 +170,17 @@ const EstQuantity = (() => { const index = targets[0].index; quantities.set(index, (quantities.get(index) || 0) + Number(item.GN || 0)); } - if (!quantities.size) throw Error("연결할 내역코드를 먼저 입력하세요."); + if (!quantities.size && !allowEmpty) throw Error("연결할 내역코드를 먼저 입력하세요."); return quantities; } + function allTotals(project) { + const combined = new Map(); + for (const selected of ["manual", ...(project.quantitySheets || []).map((sheet) => sheet.Code)]) + for (const [index, value] of totals(project, selected, true)) + combined.set(index, (combined.get(index) || 0) + value); + if (!combined.size) throw Error("연결할 내역코드를 먼저 입력하세요."); + return combined; + } function applyTotals(project, quantities) { for (const [index, value] of quantities) project.tables.EstMainDat[index].GN = value; } @@ -337,6 +345,7 @@ const EstQuantity = (() => { calculate, manual, totals, + allTotals, applyTotals, targetSelect, newCode,