fix(est): reserve deleted Excel link targets

This commit is contained in:
2026-11-07 16:01:55 +09:00
parent 041d4e653e
commit 7d6d9769d5
5 changed files with 44 additions and 1 deletions
+17
View File
@@ -236,6 +236,23 @@ def test_excel_quantity_links_export_and_import_reject_invalid_targets():
assert result == {"links": [link], "rejected": 3, "original": 1}
def test_excel_import_does_not_reuse_deleted_excel_link_target():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[{PkID:1,Code:'A',GN:1}];
const link={target:2,file:'source.xlsx',sheet:'Data',cell:'B2'};p.excelQuantityLinks=[link];
const rows=[{PkID:1,Code:'A',GN:2},{PkID:2,Code:'B',GN:5}];let rejected=0;
for(const data of [{EstMainDat:rows},{EstMainDat:rows,ExcelQuantityLinks:[link]}]){
try{EstExchange.apply(p,data)}catch{rejected++}
}
const cleared=EstExchange.apply(p,{EstMainDat:rows,ExcelQuantityLinks:[]});
return {rejected,original:p.tables.EstMainDat[0].GN,cleared:cleared.excelQuantityLinks.length};
})()""",
)
assert result == {"rejected": 2, "original": 1, "cleared": 0}
def test_excel_imports_manual_quantities_and_schedule_and_rejects_invalid():
result = run(
"math",
@@ -43,6 +43,20 @@ def test_excel_quantity_links_validate_and_survive_project_validation():
assert result["rejected"] == 5
def test_deleted_excel_link_target_id_is_reserved_for_new_estimate_rows():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[{PkID:1,Code:'A'}];
p.excelQuantityLinks=[{target:2,file:'a.xlsx',sheet:'Data',cell:'B2'}];
const row=EstModel.newRow('EstMainDat',p.tables.EstMainDat,p);
const other=EstModel.newRow('StuffcDat',[],p);
return {next:row.PkID,other:other.PkID};
})()""",
)
assert result == {"next": 3, "other": 1}
def test_apply_all_quantities_sums_sheets_and_rejects_errors_atomically():
result = run(
"math",
+2
View File
@@ -1,4 +1,6 @@
## Codex · EST 독립 테스트 앱 (2026-11-04)
- [x] 삭제된 Excel 연결 대상의 행 식별자 재사용 방지 → 새 행·붙여넣기·Excel 가져오기 오연결 차단.
- 자체 검증: 삭제된 2번을 예약해 Edge 추가·붙여넣기가 3·4번을 사용. Excel 가져오기 오연결 차단·연결 제거 후 적용 확인. 관련 시험 35개 통과, openpyxl 경고 8건.
- [x] Excel 수량 연결을 전체 자료 Excel에 포함 → 재가져오기와 잘못된 내역 연결의 전체 적용 차단 확인.
- 자체 검증: Tailscale HTTP Edge에서 실제 Excel 내보내기·재가져오기와 잘못된 내역 연결 전체 차단 확인. 수량·Excel 시험 25개 통과, openpyxl 경고 8건.
- [x] Excel 셀과 내역 행 연결을 JSON에 보관하고 다시 선택한 파일로 일괄 갱신 → 중복·누락·오류 시 전체 변경 차단.
+8
View File
@@ -466,6 +466,14 @@ const EstExchange = (() => {
throw Error(`Excel 연결 내역이 없거나 중복되었거나 상세 행이 아닙니다: ${link.target}`);
}
}
if (Object.hasOwn(tables, "EstMainDat"))
for (const link of project.excelQuantityLinks || [])
if (
(next.excelQuantityLinks || []).some((item) => item.target === link.target) &&
!project.tables.EstMainDat.some((row) => row.PkID === link.target) &&
next.tables.EstMainDat.some((row) => row.PkID === link.target)
)
throw Error(`삭제된 Excel 연결 내역의 식별자를 다시 사용할 수 없습니다: ${link.target}`);
if (Object.hasOwn(tables, "EscalationSettings")) {
const rows = tables.EscalationSettings;
if (
+3 -1
View File
@@ -626,11 +626,13 @@ const EstModel = (() => {
if (table === "EstMainDat")
for (const round of project?.progress || [])
historical.push(round.contract?.rows || [], round.result?.rows || []);
const max = historical.reduce(
let max = historical.reduce(
(maximum, list) =>
list.reduce((value, row) => Math.max(value, Number(row.PkID) || 0), maximum),
0,
);
if (table === "EstMainDat")
for (const link of project?.excelQuantityLinks || []) max = Math.max(max, link.target);
const row = {
PkID: max + 1,
Index: 0,