PR-A 베이스라인 사전 작업. 후속 백엔드/프론트 PR 의 의존성.
DDL (400_mbom.sql):
- mbom_header (21 cols) — PK objid varchar(64), UNIQUE mbom_no
FK source_ebom_objid → part_bom_report.objid (SET NULL)
FK source_mbom_objid → mbom_header.objid (SET NULL, self)
- mbom_detail (47 cols) — PK objid varchar(64)
FK mbom_header_objid → mbom_header.objid (CASCADE)
FK part_objid → part_mng.objid (SET NULL)
- 인덱스 11개 (project/source/status/header/parent/part 등)
운영DB vs RPS 타입 함정 정정:
- 운영 part_mng.objid varchar(64) → RPS bigint (feedback_createobjid_pattern.md)
- mbom_detail.part_objid 도 bigint 로 통일해 FK 호환
- numeric/varchar 모두 bigint 와 FK 불가 — 정확한 타입 매칭 필요
운영 sample 이관 (01_mbom_sync.sql):
- mbom_header 3 / mbom_detail 95 → RPS 전이
- staging 테이블 패턴 (LIKE INCLUDING DEFAULTS, FK·PK 제거)
- FK 매칭 안 되는 source_ebom_objid / part_objid 는 NULL 처리
- mbom_header 에 없는 mbom_header_objid 행은 DELETE (orphan 방지)
- part_objid varchar(64) → bigint 형변환 (운영 String.hashCode 결과)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 페이지 검색 폼이 단순 Input + 품번/품명 LIKE 였음. 운영판 wace 는 class="select2-part"
자동완성 (initPartSelect2Ajax) + 한쪽 선택 시 다른 쪽 자동 채움.
수정 페이지 (모두 DevPartSelect 적용):
- part-regist/page.tsx (M1 PART 등록 — wace partMngTempList.jsp)
- part-search/page.tsx (M2 PART 조회 — wace partMngList.jsp)
- ebom-regist/page.tsx (M3 E-BOM 등록 — wace structureList.jsp)
동작:
- 품번/품명 양쪽 DevPartSelect (part_mng IS_LAST='1' 캐시)
- 품번 선택 → 품명 자동 채움 (row.part_name)
- 품명 선택 → 품번 자동 채움 (row.part_no)
- setFilter 함수형 업데이트로 동시 setState 충돌 방지
ebom-search 와 동일 패턴 통일 — DevPartSelect 1개 컴포넌트로 4 페이지 공유.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E-BOM 조회 트리 화면 검증용 긴 BOM 1건을 운영DB 에서 RPS 로 복사 (1회성).
선정 기준 : bom_part_qty 행 수 + 트리 깊이 큼.
대상 BOM : part_bom_report.objid = '1038014721'
part_no = '21008-0109' / part_name = 'BS030-120H4A11-EN'
구조 : 126 행 / 4 레벨 (L1=1 / L2=37 / L3=62 / L4=26)
파일:
- 03_long_bom_sample.sql : TEMP staging + ON CONFLICT DO NOTHING INSERT
· pbr_stage / bpq_stage 두 TEMP 테이블에 \copy 로 적재 후 INSERT FROM SELECT
· 재실행 안전 (ON CONFLICT 시 skip)
- pbr_long.csv : part_bom_report 1행 (운영DB export, CSV HEADER)
- bpq_long.csv : bom_part_qty 126행 (운영DB export, CSV HEADER, seq ORDER BY)
- README.md : 02_sequences.sql / 03_long_bom_sample.sql 섹션 추가
용도:
- 동적 LEVEL 컬럼 (L1..L4) "*" 표시 검증
- 토글 -/+ 버튼 동작 검증 (자식 보유 행 식별 + 자손 hide 체인)
- search_level 1~5 필터 검증
- 정전개 엑셀 다운로드 검증
운영DB OBJID 그대로 사용 — RPS part_mng 가 운영DB와 동일 OBJID 보유 (이전 part_mng_sync
로 보장) 라서 PART 정보 매핑 정상.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(1) 정전개 트리 화면 운영판 wace structureAscendingList.jsp 1:1 정정:
- L1..LMaxLevel 컬럼 — row.lev 와 일치하는 컬럼에만 "*" 표시 (이전엔 품번 표시)
- 별도 품번 컬럼 1개 (모든 행 part_no)
- 3D/2D/PDF — renderType: "folder" (wace fnc_getFolderIcon 1:1)
- 컬럼 운영판 1:1 : 품번/품명/수량/항목수량/3D/2D/PDF/재료/열처리경도/열처리방법/표면처리/메이커/범주이름/비고
- 제거 : 변경일/REV/규격/중량 (운영판 미사용)
(2) 토글 -/+ 버튼 추가 (wace 트리 1:1):
- 첫 컬럼 __toggle — 자식 있는 행만 − / + 표시, 클릭 시 자식 숨김/표시
- collapsedChildIds Set<string> 상태로 접힘 관리
- ancestor 체인: parent_objid → 부모 행 child_objid 추적 (cycle guard)
- 가시 행 필터: ancestor 중 하나라도 collapsed Set 에 있으면 hide → 자손 전체 숨김
- 새 조회 시 collapsed Set 초기화 (모두 펼침)
(3) 품번 셀 클릭 → PartDetailDialog (wace partMngDetailPopUp 1:1):
- row.part_no = part_mng.objid::varchar 이므로 그대로 detail dialog 의 objid 로 전달
- ebom-search 페이지에 PartDetailDialog 임포트 + state
(4) 검색 필터 anchor 정정 (사용자 검증: 1행만 나오고 자식 안 풀림):
- 이전: search_part_no/search_part_name 을 결과 단계 WHERE PM.part_no LIKE ... 로 적용
→ 매칭 행 1개만 살아남고 자식 잘림
- 정정: anchor 단계에서 매칭된 PART 가 들어있는 bom_report_objid 전체를 startWhere 로
→ 재귀 CTE 가 자식 모두 풀어냄 (운영판 1:1)
- search_level (1~5) 은 결과 단계 유지 (트리 깊이 제한)
- ascending / ascendingForExcel 양쪽 동일 패턴
(5) ascending SELECT 풀 컬럼 보강:
- 추가 : item_qty(p_qty), heat_treatment_hardness/method, surface_treatment,
maker, part_type, part_type_title (comm_code.code_name)
- TREE CTE 컬럼에 item_qty 추가
- BomTreeRow 타입 동기 (lib/api/devBom.ts)
(6) 상태변경 시 확정일(DEPLOY_DATE) 처리 — 사용자 요청:
- status = 'Y' 변경 시 DEPLOY_DATE = TO_CHAR(NOW(), 'YYYY-MM-DD') 채움 (varchar)
- 'N' 변경 시 기존 DEPLOY_DATE 보존
- $5 prepared statement 타입 추론 충돌 (varchar vs unknown) → $5::varchar 명시 캐스팅
- STATUS_TITLE 매핑은 운영판 1:1 — CREATE/CHANGEDESIGN/DEPLOY 만 라벨, Y/N 등은 raw 표시
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
사용자 검증으로 발견된 5가지 함정 일괄 정정.
(1) ebom-search 검색폼 운영판 1:1 — wace structureAscendingList.jsp 노출 필드만:
- 제거: 프로젝트 OBJID (raw input), UNIT_CODE (raw input)
운영판도 고객사/프로젝트번호/유닛명 모두 주석 처리되어 노출 안 됨
- 유지: 품번 / 품명 / 표시 레벨 (1~5 select)
- BomTreeFilter.search_level 추가 + ascending/descending CTE 에 T.lev <= $search_level::int
(2) 품번/품명 자동완성 (wace select2-part 1:1):
- 영업관리 PartSelect 는 item_info 마스터 기반 → 개발관리(part_mng)용 별도 컴포넌트 신설
- backend GET /api/development/part/options : IS_LAST='1' part_mng 전체 (영업관리 sales/parts 패턴)
- frontend DevPartSelect.tsx : SmartSelect 캐시 + mode partNo/partName 분리
- ebom-search 페이지 단순 Input → DevPartSelect 교체
- 품번 선택 시 품명 자동 채움 / 품명 선택 시 품번 자동 채움 (운영판 select2-part 1:1)
(3) BomReportStatusDialog 운영판 1:1 재작성 — wace structureStatusChangePopup.jsp:
- 잘못된 점: read-only 박스 + 상태 select(create/changeDesign/deploy 3옵션)
- 정정: 5필드 모두 편집 가능 (CommCodeSelect 제품구분 / 품번 input / 품명 input /
Version input / 상태 Y/N 라디오) — 운영 매퍼 updateStructureStatus 5컬럼 UPDATE 1:1
- 헤더 파란 바 + 4컬럼 테이블(25%/75%) + 저장/닫기 중앙 배치 (운영판 스타일 1:1)
(4) DataGrid id 매핑 — 체크박스 ID 키 불일치 함정:
- DataGrid 는 row.id 로 체크박스 ID 관리, 백엔드 응답은 row.objid (postgres lowercase)
- 결과: checkedIds[0] 가 undefined → 상태변경/수정/삭제 다이얼로그가 objid=undefined 로 열려
detail 호출 안 됨 → 빈 폼 표시 (사용자 지적 "기본 정보 표시 안됨")
- 일괄 수정 (3 페이지) : ebom-regist / part-regist / part-search
gridRows = useMemo(() => rows.map(r => ({ ...r, id: r.objid })), [rows])
영업관리 페이지 동일 패턴 1:1
(5) STATUS_TITLE 매핑 운영판 1:1 — 운영 그리드는 'Y'/'N' 글자 그대로 표시:
CASE UPPER(T.STATUS)
WHEN 'CREATE' THEN '등록중'
WHEN 'CHANGEDESIGN' THEN '설계변경미배포'
WHEN 'DEPLOY' THEN '배포완료'
ELSE COALESCE(T.STATUS, '') END AS STATUS_TITLE
- 운영 매퍼는 ELSE '' 이지만 RPS 는 raw fallback (사용자 화면에서 식별 가능)
- 'Y'/'N' 매핑 라벨 추가 → 운영 스크린샷 확인 후 제거 (운영판은 raw)
미해결 (별 작업):
- 확정일 (DEPLOY_DATE) 표시 — 운영판은 별도 "배포" 액션 (deployBomReport 매퍼) 으로 채움.
RPS ebom-regist 에 배포 버튼 미구현 → 신규 BOM 확정일 빈값. 별 PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CSV 파싱 함정 정정:
사용자 검증 중 운영판 wace 에서 3건만 파싱되는 CSV 가 RPS 에서 4건으로 파싱되며 4번째
행이 깨진 채 들어가는 문제 발견. 단순 line.split(",") 로는 RFC4180 따옴표 처리 실패.
backend-node:
- csv-parse ^6.2.1 추가
- devBomExcelImportService.parseAndValidate :
text.split(/\r\n|\r|\n/).map(line => line.split(",")) 단순 split →
parseCsvSync(text, { relax_quotes, relax_column_count, skip_empty_lines: false })
· 따옴표 내부 콤마/줄바꿈, "" 이스케이프 모두 안전 처리
· 운영판 사용자가 만든 비정형 quote 도 relax_quotes 로 관대 처리
· 1차 스캔(자품번 수집) 도 동일 allRows 재사용
- getCsvValue 헬퍼는 보존 (csv-parse 후에도 안전 trim/quote-strip 으로 유지)
시퀀스 누락 (별 함정):
저장 시 "relation seq_bom_qty does not exist" 에러 발생. wace 매퍼에서 사용하는
nextval('seq_*') 시퀀스 5종 중 RPS DB 에 seq_ecr_no 만 존재. 나머지 4종 신규 생성.
02_sequences.sql (data-sync 디렉토리에 보존):
- seq_bom_qty 200,000 (운영 179,258 + 여유)
- seq_as_no 1,000 (운영 109 + 여유)
- seq_comm_code 10,000 (운영 1,839 + 여유)
- seq_eo_no 1,000 (운영 62 + 여유)
- seq_ecr_no (RPS 기존 보존, 운영 33)
운영 last_value 보다 충분히 큰 값으로 setval — 향후 운영 데이터 sync 시 PK 충돌 방지.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PartDetailDialog 재작성 (운영판 부적합 → 정정):
- 기존: 임의 섹션 분리("기본정보/크기·형상/분류·단위/Y-N 플래그") + 운영판 없는 필드
(두께/너비/높이/외경/내경/길이/단위/수량/후가공/공급업체) 노출
- 정정: PartFormDialog 와 동일 레이아웃을 readonly 박스(Ro)로 표시
· 운영판 22필드 그대로 (운영판 없는 9필드 제거)
· 부속 행 추가 (운영 detail 만 표시) : EO No / EO Date / EO구분(CHANGE_TYPE) / EO사유(CHANGE_OPTION)
· CAD Data 3행 : 3D / 2D(Drawing) / 2D(PDF) — 첨부 카운트 표시 (실제 파일 다운로드는 DEV-7 별 PR)
· 헤더: 파란색 바 "품목 상세" (운영판 헤더 1:1)
· 코드값 → 라벨 매핑 : ODRFG (구매/생산/Phantom), LOT_FG (미사용/사용),
USE_YN (미사용/사용), QC_FG (무검사/검사), SETITEM/REQ (부/여)
colgroup 비율 정정 (Form + Detail 공통):
- 기존: 4컬럼 단순 (라벨 110px / input / 라벨 110px / input) → 첫 input 25% 좁음
- 정정: 운영판 JSP colgroup 1:1 (12% / 12% / 25% / 12% / *) + 매 행 첫 Td 에 colSpan={2}
· 결과 input1 ≈ 37%, input2 ≈ 39% — 양쪽 input 비율 거의 동일 (운영판 일치)
· 규격/비고 행 colSpan={4} (운영 JSP colspan=4)
· CAD Data 행 colSpan={3} (작은 라벨 1칸 + 컨텐츠 3칸)
· Th 의 고정 너비 w-[110px] 제거 — colgroup 이 폭 결정
· table-fixed 추가로 colgroup 비율 강제 적용
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
운영판 wace 재확인 결과 BOM 등록은 XLSX가 아니라 CSV 가 진짜 입력 포맷이었음.
근거:
· openBomReportExcelImportPopUp.jsp : 제목 "PART 및 구조등록 CSV upload",
Drop Zone "Drag & Drop CSV 템플릿", fnc_setFileDropZone(..., "csv") 로 CSV 만 허용
· /partMng/parsingExcelFile.do 가 .csv 분기에서 별도 함수 parsingCsvFile() 호출
· CSV 11컬럼 : 수준 / 품번 / 품명 / 수량 / 항목수량 / 재료 / 열처리경도 / 열처리방법 /
표면처리 / 공급업체(MAKER) / 범주이름(PART_TYPE)
· CSV 는 PARENT_PART_NO 컬럼이 없고 "수준" 으로 부모-자식 자동 결정
· 숫자("1","2","3"): 깊이 D 의 부모 = D-1 의 최신 품번
· 점 표기법("1","1.1","1.4.1"): 마지막 점 이전 수준이 부모
backend (devBomExcelImportService.ts):
· XLSX(xlsx 라이브러리) 파싱 → CSV(iconv-lite) 파싱으로 완전 재작성
· 인코딩 자동 감지 1:1 — CP949 → UTF-8 → EUC-KR → MS949 순서, � 카운트로 베스트 선택,
UTF-8 BOM() 자동 제거
· CSV 영문 범주명 자동 변환 — Assy/ASSY→조립품, Buy/BUY→구매품, Make/MAKE→부품
· 범주별 ACCTFG/ODRFG 자동 설정 — 조립품(0001813)·부품(0001812) → ACCTFG=4·ODRFG=1,
구매품(0000063) → ACCTFG=7·ODRFG=0
· 기본값 일괄 — UNIT_DC=0001400(EA) · UNITMANG_DC=0001400 · UNITCHNG_NB=1 ·
LOT_FG=1 · USE_YN=1 · QC_FG=0 · SETITEM_FG=0 · REQ_FG=0
· 검증 1:1 — rowIndex > 2 에서만 모품번 자품번 목록 존재 검사, NOTE 누적
· 저장 로직(savePartBomMaster)은 동일 유지 — 자식 PART updatePartInfoFromCsv UPDATE /
insertpartInfo INSERT, 부모 PART 존재 시 lookup·없으면 "" (INSERT 안 함),
bom_part_qty INSERT 시 ACCTFG/ODRFG/UNIT_DC/UNITCHNG_NB 등 모든 컬럼 동기
frontend:
· BomExcelRow → BomCsvRow (LEVEL 컬럼 + ACCTFG/ODRFG/UNIT_DC/UNITCHNG_NB/LOT_FG/USE_YN/
QC_FG/SETITEM_FG/REQ_FG 자동 채움 필드 추가). BomExcelRow 는 호환 type alias 로 보존
· BomReportExcelImportDialog : 제목 "PART 및 구조등록 CSV upload", accept=".csv", Drop Zone
안내 텍스트 변경, 그리드 컬럼 — 결과/수준/모품번(자동)/품번/품명/수량/항목수량/재료/
열처리경도/열처리방법/표면처리/공급업체/범주/계정구분(자동)/조달구분(자동)
· 파싱 결과의 encoding 정보 표시 (CP949/UTF-8 등)
정적 자산:
· 운영 BOM_REPORT_EXCEL_IMPORT_TEMPLATE.xlsx 제거 (운영판도 실제 사용 안 함)
· 신규 BOM_REPORT_CSV_IMPORT_TEMPLATE.csv 추가 (11컬럼 헤더 + 4행 샘플)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 배치(REST API→DB) 저장 시 매핑 payload 에 from_connection_id 추가
— 등록된 연결 선택값이 DB 에 저장 안 돼 수정 화면 재진입 시 manual 로 표시되던 문제 해결
- 배치 실행 호출만 timeout 30s → 5분(300000ms) override
— 외부 API + 대량 INSERT 시 자주 끊기던 AxiosError 해결
- 권한관리 좌측 권한 카드에서 authCode 행 제거
— authName 만 노출(코드값은 hover tooltip 으로 유지). 검색은 authCode 도 매칭 유지
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
· wace fn_openSaleRegPopup(PROJECT_NO, "detail") 의도 재해석 — read-only 상세 조회 모드
· 행 전체 클릭(onRowClick) → PROJECT_NO 컬럼 셀 클릭(cellClick)으로 좁힘 (wace 1:1)
· 판매관리 페이지 라우팅 폐기 → ProjectInfoDialog 신설 (같은 탭, list row 직접 사용, 추가 API 호출 0)
· 표시 항목: 프로젝트번호/영업번호/주문유형/제품구분/국내해외/고객사/유무상/품번/품명/S/N/수주수량/접수일/요청납기/발주일/프로젝트명/작성자
· 영업관리 변경 롤백 (SaleListFilter.project_no, useSearchParams 자동 선택, 초기화 핸들러)
· 01-progress / 01-progress-verify 문서 갱신
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 시스템 관리 > 시퀀스 관리 신규 메뉴 + 페이지(채번 룰 빌더)
- ensureSequenceMngMenu 부팅 시드
- 테이블 타입관리 → 채번 룰 드롭다운 + 의존성 자동 검증
- 표시명/코멘트 UX 개선
- 설계 문서 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
- salesOrderMgmtService에 assertNoProjectExists(client, contractObjid) 신설. wace 매퍼 getProjectListBycontractObjid:3909와 동일하게 SELECT 1 FROM project_mgmt WHERE contract_objid=$1 LIMIT 1 (status 필터 없음).
- salesEstimateService.remove + salesOrderMgmtService.remove 둘 다 BEGIN 직후 가드 호출. 차단 시 AppError("프로젝트가 생성된 건은 삭제할 수 없습니다.", 409) throw.
- 컨트롤러 remove 두 곳에서 error.statusCode ?? 500으로 응답 → 409 정확히 전달.
- DB 검증: 26C-0698(project 11건) 차단, project 없는 contract 통과.
G7(contract_item UPSERT)는 사실상 이미 구현 — upsertItems 두 서비스 모두 INSERT ... ON CONFLICT (objid) DO UPDATE 패턴으로 OBJID 유지 보장 중. 별도 코드 변경 없음.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ECR 관리: wace 의 ecrList/Form/Detail JSP 와 동일하게 5개 필터(연도/기종/요청/작성자/상태),
변경전/후 2분할 모달, 작성중(0000100)만 삭제·수정 허용, 컬럼 순서/라벨 wace 일치
- ECR 스키마 wace 풀세트 동기화: ecr_mng 컬럼폭 확장, product_mgmt 16컬럼, part_mng 52컬럼,
user_info 22컬럼, comm_code 보강(id/code_cd/ext_val), code_name(varchar) 함수,
seq_ecr_no setval(33) 정렬
- wace_plm public.comm_code 733행 시드: src/seed/wace_comm_code.sql 추출 + 부팅 시 자동 적재
(writer='system-seed' placeholder 자동 정리, 무중단 재적재 엔드포인트 /comm-code-seed)
- wace_plm 데이터 import 풀스키마: PRODUCT(7→16), PART(6→52), USER_INFO·COMM_CODE 신규
- 공통코드 관리 화면: 제목/설명 축소, 카테고리·코드 카드 → 컴팩트 리스트, 활성 토글 점,
계층 배지 톤다운, hover 시 액션 노출
- 테이블 타입 관리 — 좌측: 한 줄 리스트 + 알파벳 인덱스 sticky 헤더
- 테이블 타입 관리 — 우측: 타입 카드 그리드 → 그룹 셀렉트(기본/참조/자동/첨부/표시변형),
표시이름 제거 + 코멘트(description) Textarea 신설(화면관리에서 기본 라벨로 활용),
시스템 자동 생성 컬럼(id/company_code/writer/created_date/updated_date) 잠금,
표시옵션/고급설정(필수·읽기·기본값·최대길이) 제거 — 화면관리로 이관
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1) getById 응답 구조 fix
- salesEstimateService.getById는 { ...contract_mgmt 헤더, items } 평면 객체 반환
- 페이지가 contractInfo.header.customer_objid로 잘못 접근해 고객사·환율·통화 자동 채움이 안 됐던 버그
- contractInfo.customer_objid 직접 접근으로 수정 (template1·template2 동일)
2) /auth/me 응답에 cellPhone/tel 추가
- AuthService.getUserInfo는 이미 두 필드 반환하나 authController가 응답에서 누락
- 견적서 연락처가 wace MailUtil 패턴(cell_phone 우선 → tel 폴백)으로 자동 채워지도록 노출
3) 시행일자 picker 표시 형식 정정
- input[type="date"]는 한국 Chrome이 "YYYY. MM. DD." 강제 (CSS 변경 불가)
- text input + hidden type=date 조합: 표시는 YYYY-MM-DD, 클릭 시 hidden picker showPicker() 트리거
- template1 executor + template2 executor_date 모두 적용
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
신규 작성 시 useAuth로 로그인 사용자 정보 자동 채움:
- 담당자 = "{deptName} {userName}" (wace: connectUserDeptName + connectUserName)
- 연락처 = user.tel (wace: cell_phone || tel)
조건: templateObjid 없는 신규 작성 + 사용자가 아직 입력하지 않은 빈 값에만 채움.
기존 견적서 수정 모드는 DB 값 우선 (manager_name/manager_contact).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- frontend deps: html2canvas-pro@^2.0.2, jspdf@^3.0.4
- template1 페이지 "PDF 다운로드" 버튼 → wace fn_generatePdf 1:1 포팅
· 클라이언트 dynamic import (SSR 회피)
· html2canvas-pro 캡처 (scale 2, JPEG 0.85) → jsPDF A4 페이지 분할 → save({estimateNo}.pdf)
· onclone 콜백에서 input/textarea/select → 텍스트 노드로 교체 (글자 잘림 방지)
· .no-print, .btn-area, 삭제버튼은 클론에서 숨김
- html2canvas-pro 사용 이유: Tailwind 4 oklab/oklch CSS color function 지원
(기존 html2canvas 1.4.1은 "Attempting to parse an unsupported color function 'oklab'" 에러)
장비 견적서(template2)는 wace 원본부터 PDF 다운로드 버튼이 없어 그대로 유지.
메일 발송용 PDF 합본은 G6 SMTP 묶음에서 처리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
이전 커밋에서 메인 등록 Dialog에만 onInteractOutside 추가 — 자식(S/N)·손자(연속번호) Dialog가 닫힐 때 이벤트가 메인까지 도미노로 전파되며 여전히 메인이 닫혔음.
해결: nested Dialog 4개(견적/주문 각각 S/N + 연속번호) 모두에 onInteractOutside={(e) => e.preventDefault()} 추가.
사용자는 X / 취소 / 확인 / 생성 버튼으로만 명시적으로 각 Dialog 닫음.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Radix Dialog 알려진 이슈 — nested Dialog 닫힐 때 PointerDown 이벤트가 부모로 전파되어 outside click으로 인식, 부모 Dialog도 함께 닫힘.
수정:
<DialogContent onInteractOutside={(e) => e.preventDefault()}>
→ 외부 클릭으로 부모 닫힘 차단. 사용자는 X / 취소 / 저장 버튼으로만 닫음.
ESC는 그대로 — Radix focus stack이 자식만 먼저 닫음.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>