feat(est): apply all quantity sheets
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
## Codex · EST 독립 테스트 앱 (2026-11-04)
|
||||
- [x] 모든 수량산출서·직접 수식표의 내역 수량 일괄 합산 반영 → 동일 대상 합산·오류 시 전체 차단 시험.
|
||||
- 자체 검증: 동일 내역 합산·오류 시 전체 차단 포함 수량·문서 시험 18개 통과, Edge에서 일괄 반영·취소 확인.
|
||||
|
||||
|
||||
- 2026-11-07: 사용자 지시로 EST 작업 계획과 검증 기록을 공유 PLAN.md에서 이 파일로 이관. 업무 회귀 시험 79개 통과, openpyxl 경고 7건. Git push는 보류.
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요
|
||||
|
||||
재계산 메뉴의 수량 반영도 현재 선택한 산출서 또는 직접 작성 수식표를 사용합니다. 반영 전에 모든 연결 대상과 수식을 검사하며, 기존 원본 연결 데이터는 그대로 보관합니다.
|
||||
|
||||
재계산 메뉴의 `모든 수량산출 합산 반영`은 모든 산출서와 직접 작성 수식표의 연결 수량을 내역 행별로 합산합니다. 어느 산출서든 계산 오류가 있거나 연결 대상이 모호하면 전체 반영을 중단합니다.
|
||||
|
||||
수량산출서 인쇄 서식에는 직접 작성한 수식표와 산출서의 산식, 계산 수량, 연결 내역이 함께 표시됩니다. 계산할 수 없는 수식은 오류를 표시합니다.
|
||||
|
||||
산출 행의 결과 표지 개수가 연결 정보와 다르면 계산 오류로 표시하며, 기존 저장 수량을 내역에 반영하지 않습니다. 화면이나 Excel에서 수식을 수정할 때 결과 표지와 연결 항목을 유지하세요.
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
<button data-action="recalculate">일건서류 전체 재계산</button
|
||||
><button data-action="recalculate">내역서 재계산</button
|
||||
><button data-action="apply-quantity">수량산출 반영 후 재계산</button>
|
||||
<button data-action="apply-all-quantities">모든 수량산출 합산 반영</button>
|
||||
</div>
|
||||
</details>
|
||||
<details>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user