diff --git a/resources/tester/test_est_quantity_sheets.py b/resources/tester/test_est_quantity_sheets.py index 5984aa335..9b2b5082a 100644 --- a/resources/tester/test_est_quantity_sheets.py +++ b/resources/tester/test_est_quantity_sheets.py @@ -141,3 +141,16 @@ def test_estx_export_refuses_marker_edit(): str(MAIN), ) assert result.startswith("산출 줄의 수량 지정·해제는 ESTX") + + +def test_sheet_search_ignores_spaces_bars_and_brackets(): + result = node(""" + const sheet = { Code: 'N1', Name: '토 공(터파기)', items: [{ Name: '잔토|처리', Size: '0.7㎥' }] }; + process.stdout.write(JSON.stringify([ + run("EstQuantitySheets.matches(" + JSON.stringify(sheet) + ", '토공터파기')"), + run("EstQuantitySheets.matches(" + JSON.stringify(sheet) + ", '잔토 처리')"), + run("EstQuantitySheets.matches(" + JSON.stringify(sheet) + ", '포장')"), + run("EstQuantitySheets.matches(" + JSON.stringify(sheet) + ", '')"), + ])); + """) + assert result == [True, True, False, True] diff --git a/tools/est-test/PLAN.md b/tools/est-test/PLAN.md index d27004612..fc28e92a1 100644 --- a/tools/est-test/PLAN.md +++ b/tools/est-test/PLAN.md @@ -481,6 +481,8 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD - [x] 기타경비 계산·산업안전보건관리비 비교(원본 TfrmWongaDiget 모드 4·1, 0xd0bc80·0xd0b298 — 디스어셈블 근거, 내장 요율표 없음) — 작은 기초금액 식 선택·요율 입력·비교 줄(CalcIndex 2), 대상액(직접공사비 또는 재료비 소계+비과세+직접노무비)·관급/1.1·1.2배·기초금액(기본 5,349,000 원본 상수). - 검증완료: `test_est_wonga_tools.py` 2개(샘플에서 원본이 적어 둔 식 `(A + 4) × 0.0315 × 1.2`·비교 `((A + 4) + J/1.1) × 0.0315` 그대로 재현·오류 0, 기타경비 작은 쪽·비교 줄·잘못된 입력 거부) 통과. ORCA 브라우저에서 안전관리비 비교 → 같은 식·도급액 유지, 기타경비는 직접공사비 행 없는 샘플에서 원본처럼 거부, 샘플 복구. - 남음: 구성비·요율 전환(CalcBase), 약어 표시(SName), 제비율 기준 문서 보기(원본 한글 파일). + - [x] 수량산출서 검색·전체보기(원본 TFRMMULLANG btnSerch·btnAllDisplay — 빈칸·|·괄호 무시 포함 검색) — 고르기 줄을 `native-quantity-sheets.js` 로 옮겨 700줄 제한 유지. + - 검증완료: `test_est_quantity_sheets.py` 검색 시험 포함 수량 시험 22개 통과. ORCA 브라우저에서 「집수」 검색 → 항목에 집수정이 있는 「배수」만·선택 이동, 전체보기 → 3개 복귀, 샘플 복구. - 대조 합계(CAD 제외 882개): 구현 283 · 부분 193 · 없음 273 · 제외 133. - [ ] 남은 핵심(실무 빈도순): 3. 수량산출서 — 누계·4방향 합계, 제출용 전체 인쇄. diff --git a/tools/est-test/README.md b/tools/est-test/README.md index e4e23bebf..86f85ae37 100644 --- a/tools/est-test/README.md +++ b/tools/est-test/README.md @@ -58,6 +58,8 @@ 산출 줄 옆 `수량 지정`은 줄 글에서 드래그해 고른 숫자를 결과 표지로 감싸고 수량 항목을 새로 만듭니다. 품명·규격·단위 표지가 있으면 항목 이름에 옮깁니다. `지정 해제`는 커서가 놓인 결과 표지를 풀고, 다른 줄에서 쓰지 않는 항목을 지웁니다. 내역에 연결된 항목은 먼저 연결을 비워야 합니다. 이 변경이 있는 작업은 ESTX 수정 저장을 거부합니다. +수량산출 화면의 `검색`은 원본처럼 산출서 이름과 항목의 명칭·규격에서 빈칸·`|`·괄호를 빼고 찾아 목록을 좁히고, `전체보기`로 되돌립니다. + 수량산출 화면의 `산출서 복사`는 현재 산출서를 바로 아래에 복사하고, 내역에 두 번 반영되지 않도록 복사본의 내역 연결을 비웁니다. `산출서 위로`·`아래로`로 순서를 바꿉니다. 산출서 추가·순서 변경이 있는 작업은 ESTX 수정 저장을 거부하고 JSON 저장을 안내합니다. 수량산출 화면의 수량 항목마다 `끝수 처리`에서 AUTO(산출서 기본)·절사·올림·반올림 0~5자리를 고릅니다. 원본처럼 결과 코드 뒤에 방식과 자리수를 붙이며(`r000001` → `r00000131`), 산출 행의 결과 표지도 함께 바꿉니다. 이 변경이 있는 작업은 ESTX 수정 저장을 거부하고 JSON 저장을 안내합니다. diff --git a/tools/est-test/native-quantity-sheets.js b/tools/est-test/native-quantity-sheets.js index b799e50b8..d02a6c609 100644 --- a/tools/est-test/native-quantity-sheets.js +++ b/tools/est-test/native-quantity-sheets.js @@ -106,8 +106,53 @@ const EstQuantitySheets = (() => { return editor; } + // 원본 수량산출 검색(TFRMMULLANG btnSerch·btnAllDisplay): 명칭·규격에서 빈칸·|·괄호를 빼고 포함 검색. + const loose = (text) => + String(text ?? "") + .replace(/[\s|()[\]{}]/g, "") + .toLowerCase(); + function matches(sheet, query) { + const q = loose(query); + if (!q) return true; + return [sheet.Name, sheet.Code, ...(sheet.items || []).flatMap((i) => [i.Name, i.Size])].some( + (t) => loose(t).includes(q), + ); + } + + // 수량산출서 고르기 줄(원본 공종 목록 + 검색·전체보기). + function selector(state) { + const { esc } = EstApp; + const options = (state.project.quantitySheets || []) + .filter((sheet) => matches(sheet, state.quantityQuery) || sheet.Code === state.quantitySheet) + .map( + (sheet) => + ``, + ) + .join(""); + return `
`; + } + function handle(name, state, node) { const code = state.quantitySheet; + if (name === "quantity-sheet-search" || name === "quantity-sheet-all") { + state.quantityQuery = + name === "quantity-sheet-all" + ? "" + : (document.querySelector("[data-quantity-query]")?.value ?? ""); + const first = (state.project.quantitySheets || []).find((s) => + matches(s, state.quantityQuery), + ); + if (state.quantityQuery && !first) throw Error("찾는 수량산출서가 없습니다."); + if ( + first && + !matches( + state.project.quantitySheets.find((s) => s.Code === code) || {}, + state.quantityQuery, + ) + ) + state.quantitySheet = first.Code; + return true; + } if (name === "quantity-sheet-copy") { EstApp.change((project) => { state.quantitySheet = copy(project, code, EstApp.uid()); @@ -176,5 +221,5 @@ const EstQuantitySheets = (() => { .join(""); } - return { copy, move, mark, free, excelView, handle }; + return { copy, move, mark, free, excelView, matches, selector, handle }; })(); diff --git a/tools/est-test/native-quantity.cjs b/tools/est-test/native-quantity.cjs index 3c21105c9..ea9bb7925 100644 --- a/tools/est-test/native-quantity.cjs +++ b/tools/est-test/native-quantity.cjs @@ -223,8 +223,7 @@ const EstQuantity = (() => { return `Q${index}`; } function selector(state) { - const { esc } = EstApp; - return `
`; + return typeof EstQuantitySheets === "undefined" ? "" : EstQuantitySheets.selector(state); } function validExcelAddress(address) { const parts = typeof address === "string" && address.match(/^([A-Z]{1,3})([1-9]\d*)$/);