From 04cba1082c4871adbdd0af1598099e79d852647c Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 12 Nov 2026 13:17:02 +0900 Subject: [PATCH] =?UTF-8?q?feat(est-test):=20=EC=97=91=EC=85=80=ED=98=95?= =?UTF-8?q?=20=EC=82=B0=EC=B6=9C=EC=84=9C=20=EC=85=80=20=EC=86=8C=EC=88=98?= =?UTF-8?q?=EC=9E=90=EB=A6=AC=20=EC=84=9C=EC=8B=9D=EA=B3=BC=20=EC=9D=B8?= =?UTF-8?q?=EC=87=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- tools/est-test/PLAN.md | 4 +++- tools/est-test/native-excel-view.js | 35 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/tools/est-test/PLAN.md b/tools/est-test/PLAN.md index 12b27b373..28abaa9fe 100644 --- a/tools/est-test/PLAN.md +++ b/tools/est-test/PLAN.md @@ -476,7 +476,9 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD - 검증완료: `test_est_excel_sheet.py` 2개(수식·함수·순환 오류·셀 합, 설치 main.estx 수식 221개 해석·토적계산표 항목 V000001~3 추정 연결 E33·G33·I33·수량 17·12·58·거리 바꾸면 수량 변함·원본 재저장 동일·편집 시 ESTX 거부) 통과. 다시 계산한 값이 Excel 저장값(0, 갱신 안 됨)이 아니라 EST 저장 수량(17·12·58·25.16·32.76·15.92·2.17)과 맞음. ORCA 브라우저에서 거리 C7 20→40 → 항목 17→34·12→24·58→116, 위 합 → SUM(E6:E33), 샘플 복구. - [x] 3단계 편집 동작(원본 TFRMMULEXCEL 단추·팝업) — 범위 고르기(Shift), 행·열 삽입/삭제(수식 참조 이동·지운 칸 #REF!·범위 줄임·병합·수량 연결 함께 이동), 잘라내기·복사·붙여넣기(상대 참조 이동, Ctrl+X·C·V·Delete), 내용지우기, 굵게·기울임·밑줄·정렬, 병합·분리, 왼쪽·오른쪽 대각선·삭제, 다시 계산, 품명·규격·단위로 지정(셀 글자를 항목에). 원본 .xls 병합 정보(MERGEDCELLS) 읽어 머리글 그대로 표시. 되돌리기는 앱 취소(↶). - 검증완료: `test_est_excel_sheet.py` 4개(행 넣기·지우기·열 넣기 참조, 붙여넣기 상대 참조, 병합 이동·분리, 서식 켜고 끄기, 설치 main.estx 병합 14개) 등 44개 통과. ORCA 브라우저에서 설치 main.estx 토적계산표: 병합 머리글 14개 표시, 행 삽입·삭제 뒤 수량 연결 E33 유지, 범위 굵게·병합, 복사·붙여넣기 =(F33+F34)/2*E34, 임시 작업 삭제. - - 남음: 원본 셀↔항목 대응 근거, 셀 서식 대화(글꼴·테두리·표시형식), .xls 저장. + - [x] 4단계 — 셀 서식 소수자리(actFormatCell 일부: 0~4자리·해제), 미리보기·인쇄(tbsPreView·tbsPrint — 고른 시트 격자만 새 창에서 인쇄). + - 검증완료: 바인딩 시험 통과. ORCA 브라우저에서 =10/3 → 소수 2자리 「3.33」, 인쇄 창 html 에 표(도구 단추 없음), 샘플 복구. + - 남음: 원본 셀↔항목 대응 근거, 셀 서식의 글꼴·테두리, .xls 저장. - [x] 수량산출 누계(원본 TfrmMulWord ActionAdd Ctrl+U → 0x11155c4, 재계산 0x111c0b0 — 디스어셈블 근거) — 원본 F·a 표지를 `` 쌍·`row.totals` 로 읽고, 위쪽 품명 줄(포함)/앞 누계(제외)부터 줄마다 첫 결과 합을 표지 반올림(F+6자리+방식+자리)으로 매 계산 갱신, 커서 자리 누계 넣기. 원본 TFRMMULNAMEINPUT 「누계계산」 단추는 실제로 지정해제와 같은 처리라 따로 만들지 않음. - 검증완료: `test_est_quantity_total.py`(소계 범위·앞 누계 제외·반올림 표지·값 갱신 6,003·넣기 이름·표지 안 거부·원본 F 표지 읽기) 통과, 수량 시험 포함 26개 통과. ORCA 브라우저에서 「소계」 줄 끝에 누계 → ⟦누계⟧9⟦누계⟧(6+3), 샘플 복구. - [x] 갑지·표지·공사설명서·시방서(원본 TFRMGABGI·TFRMCOVER·TFRMESTEDITOR, DatGabgi/DatCover/DatExplean/DatSiBang.opt — 디스어셈블 근거) — `midas-read.cjs` MIDAS 레코드·WPTools 글자 읽기 → `project.docs`, 편집 창, 공사명↔공사 이름 동기화, 공사금액 자동입력 두 방식·한글금액(일 생략 없음), 설명서·시방서 갑지 붙여넣기, 인쇄 미리보기 8·9·10·11. 문서 편집 작업은 ESTX 저장 거부. diff --git a/tools/est-test/native-excel-view.js b/tools/est-test/native-excel-view.js index a9d1ed104..bd9b6e5cb 100644 --- a/tools/est-test/native-excel-view.js +++ b/tools/est-test/native-excel-view.js @@ -29,6 +29,7 @@ const EstExcelView = (() => { .join(""); return [ btn("excel-recalc", "다시 계산"), + btn("excel-print", "미리보기·인쇄"), btn("excel-cut", "잘라내기"), btn("excel-copy", "복사"), btn("excel-paste", "붙여넣기"), @@ -45,6 +46,7 @@ const EstExcelView = (() => { btn("excel-style", "오른쪽", 'data-name="align" data-value="right"'), btn("excel-merge", "병합"), btn("excel-split", "분리"), + ``, btn("excel-style", "왼쪽대각선", 'data-name="diag" data-value="left"'), btn("excel-style", "오른쪽대각선", 'data-name="diag" data-value="right"'), btn("excel-style", "대각선 삭제", 'data-name="diag" data-value=""'), @@ -104,7 +106,12 @@ const EstExcelView = (() => { cell == null ? "" : typeof cell.value === "number" - ? number(cell.value) + ? s.digits != null && s.digits !== "" + ? cell.value.toLocaleString("ko-KR", { + minimumFractionDigits: Number(s.digits), + maximumFractionDigits: Number(s.digits), + }) + : number(cell.value) : esc(cell.value); const style = [ cell?.formula || s.b ? "font-weight:bold" : "", @@ -173,6 +180,18 @@ const EstExcelView = (() => { EstApp.render(); }); document.addEventListener("change", (event) => { + if (event.target.dataset.excelDigits !== undefined) { + const v = event.target.value, + state = EstApp.state, + sheet = sheetOf(state), + sel = state.excelCell; + if (!v || !sheet?.excel || !sel) return; + EstApp.change(() => { + EstExcelEdit.style(sheet.excel[sel.n], range(sel), "digits", v === "-" ? "" : v); + sheet.excelEdited = true; + }, false); + return; + } if (event.target.dataset.excelBar === undefined) return; const state = EstApp.state, sel = state.excelCell, @@ -217,6 +236,7 @@ const EstExcelView = (() => { "excel-style", "excel-merge", "excel-split", + "excel-print", ]); function handle(name, state, node) { if (!names.has(name)) return false; @@ -266,7 +286,18 @@ const EstExcelView = (() => { edit(() => EstExcelEdit.style(page, box, node.dataset.name, node.dataset.value)); else if (name === "excel-merge") edit(() => EstExcelEdit.merge(page, box)); else if (name === "excel-split") edit(() => EstExcelEdit.split(page, box)); - else { + else if (name === "excel-print") { + // 미리보기·인쇄(tbsPreView·tbsPrint): 고른 시트 격자만 새 창에 + const html = render({ ...sheet, excel: [page], excelLinks: {} }, { excelCell: null }) + .replace(/
[\s\S]*?<\/div>/, "") + .replace(/ data-excel-pick="[^"]*"/g, ""); + const win = open("", "_blank"); + if (!win) throw Error("새 창을 열 수 없습니다. 팝업 차단을 확인하세요."); + win.document.write( + `${EstApp.esc(sheet.Name || page.name)}${html}