# 01. 견적관리 이식 상세

> 원본: `/contractMgmt/estimateList_new.do` (estimateList_new.jsp, 47KB)
> 대상: `app/(main)/COMPANY_16/sales/estimate/page.tsx`

## 1. 화면 구조 (wace_plm 원본)

### 1.1 검색 폼 (`#plmSearchZon`)

**활성 7개** (estimateList_new.jsp line 1110~1170):

| 필드 | name/id | 타입 | 비고 |
|---|---|---|---|
| 주문유형 | `category_cd` | select2 (공통코드) | |
| 고객사 | `customer_objid` | select2 (`SUPPLY_MNG`+`CLIENT_MNG`) | `'C_'` 접두사로 두 테이블 구분 |
| 품번 | `search_partNo` | select2-part (검색어 → API) | hidden `search_partObjId` 동행 |
| 품명 | `search_partName` | select2-part | |
| 시리얼 | `search_serialNo` | text | |
| 결재상태 | `appr_status` | select2 (대기/상신중/완료/반려/불필요) | |
| 접수일 from~to | `receipt_start_date` / `receipt_end_date` | date_icon | |

**비활성 (JSP line 1174~1203 `<!-- 주석처리된 검색필터 - 필요시 활성화 -->` 블록 안)** — 이식 대상 아님:

| 필드 | name/id |
|---|---|
| 제품 | `product` |
| 지역 | `area_cd` |
| 유/무상 | `paid_type` |
| 요청납기 from~to | `due_start_date` / `due_end_date` |

### 1.2 버튼 영역 (`.plm_btn_wrap`)

| 버튼 | id | 동작 |
|---|---|---|
| 조회 | `btnSearch` | `fn_search()` → `/contractMgmt/estimateGridList.do` |
| 삭제 | `btnDelete` | 선택행 → `/contractMgmt/deleteEstimateMgmtInfo.do` |
| 견적요청등록 | `.btnRegist` | 팝업: `/contractMgmt/estimateRegistFormPopup.do?actionType=regist` |
| 견적작성 | `btnEstimate` | 일반/장비 견적서 분기 → `fn_openEstimateTemplate(objId, "1"|"2")` |
| 결재상신 | `btnApproval` | 결재불필요 분기 → `/contractMgmt/checkApprovalRequired.do` → `fn_openAmaranthApproval` |
| 메일발송 | `btnMail` | 결재완료/불필요만 → `/contractMgmt/estimateMailFormPopup.do` |

### 1.3 그리드 (`_tabulGrid` = Tabulator)

데이터 소스: `POST /contractMgmt/estimateGridList.do` (검색 폼 직렬화)

| # | 컬럼 | field | 정렬 | 비고 |
|---|---|---|---|---|
| 0 | EST_OBJID (hidden) | EST_OBJID | | |
| 1 | 영업번호 | CONTRACT_NO | center | 클릭 → `fn_projectConceptDetail(OBJID)` |
| 2 | 주문유형 | CATEGORY_NAME | center | |
| 3 | 접수일 | RECEIPT_DATE | center | |
| 4 | 요청납기 | EARLIEST_DUE_DATE | center | "{date} 외 {n}건" 포맷 |
| 5 | 고객사 | CUSTOMER_NAME | left | |
| 6 | 품명 | ITEM_SUMMARY | left | "{itemName} 외 {n}건" 포맷 |
| 7 | 견적수량 | ESTIMATE_QUANTITY | right | toLocaleString |
| 8 | 유/무상 | PAID_TYPE | center | |
| 9 | 공급가액 | EST_TOTAL_AMOUNT | right | 통화기호 변환 (CONTRACT_CURRENCY_NAME) |
| 10 | 원화환산공급가액 | EST_TOTAL_AMOUNT_KRW | right | |
| 11 | 견적현황 | EST_STATUS | center | 클릭 → `fn_showEstimateList(OBJID)` |
| 11-1 | 추가견적(PDF 첨부수) | ADD_EST_CNT | center | 📎 클릭 → `fn_openAddEstimatePdf` |
| 12 | AMARANTH_STATUS (hidden) | AMARANTH_STATUS | | 아마란스 결재상태 |
| 13 | 결재상태 | APPR_STATUS | center | 색상 표시 |

> 16개 더 있을 수 있음 — 559라인까지 컬럼 정의 이어짐. 구현 시 [estimateList_new.jsp:342-559](../../../wace_plm/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp#L342) 직접 참고.

## 2. 백엔드 endpoint 매핑 (wace_plm → vexplor_rps)

| wace_plm endpoint | 메서드 | 용도 | vexplor_rps 신규 endpoint (제안) |
|---|---|---|---|
| `/contractMgmt/estimateList_new.do` | GET | 페이지 진입 (JSP 렌더) | (없음 — Next.js 페이지) |
| `/contractMgmt/estimateGridList.do` | POST | 그리드 데이터 조회 | `GET /api/sales/estimate/list?...` |
| `/contractMgmt/estimateRegistFormPopup.do` | GET | 견적 등록/수정 팝업 | (Next.js Dialog 컴포넌트) |
| `/contractMgmt/saveEstimateMgmtInfo.do` | POST | 견적 메타 저장 | `POST /api/sales/estimate` / `PUT /api/sales/estimate/:id` |
| `/contractMgmt/deleteEstimateMgmtInfo.do` | POST | 견적 삭제 | `DELETE /api/sales/estimate/:id` |
| `/contractMgmt/createEstimateMgmtInfo.do` | POST | 견적 신규 생성 | `POST /api/sales/estimate` 와 통합 |
| `/contractMgmt/estimateTemplate1.do` | GET | 일반 견적서 작성 화면 | `app/(pop)/sales/estimate/template1/page.tsx` |
| `/contractMgmt/estimateTemplate2.do` | GET | 장비 견적서 작성 화면 | `app/(pop)/sales/estimate/template2/page.tsx` |
| `/contractMgmt/getEstimateTemplateList.do` | POST | 템플릿 목록 | `GET /api/sales/estimate/:id/templates` |
| `/contractMgmt/getEstimateTemplateDataByObjId.do` | POST | 템플릿 단건 | `GET /api/sales/estimate/templates/:templateId` |
| `/contractMgmt/saveEstimate.do` | POST | 일반 견적서 저장 | `POST /api/sales/estimate/templates` |
| `/contractMgmt/saveEstimate2.do` | POST | 장비 견적서 저장 | 위와 통합 (type 파라미터) |
| `/contractMgmt/uploadPdfChunk.do` | POST | PDF 청크 업로드 | `POST /api/sales/estimate/templates/:id/pdf-chunk` |
| `/contractMgmt/sendEstimateMail.do` | POST | 견적 메일 발송 | `POST /api/sales/estimate/:id/mail` |
| `/contractMgmt/sendEstimateMailCustom.do` | POST | 견적 메일 발송(커스텀) | 위와 옵션 통합 |
| `/contractMgmt/estimateMailFormPopup.do` | GET | 메일 발송 팝업 | (Next.js Dialog) |
| `/contractMgmt/getContractInfoForMail.do` | POST | 메일 발송용 데이터 | `GET /api/sales/estimate/:id/mail-context` |
| `/contractMgmt/getCustomerContactInfo.do` | POST | 고객사 연락처 | `GET /api/customers/:id/contacts` (마스터 공용) |
| `/contractMgmt/getEstimateDetail.do` | POST | 견적 단건 상세 | `GET /api/sales/estimate/:id` |
| `/contractMgmt/checkApprovalRequired.do` | POST | 결재 필요 여부 체크 | `GET /api/sales/estimate/:id/approval-check` |
| `/contractMgmt/setApprovalNotRequired.do` | POST | 결재불필요 처리 | `POST /api/sales/estimate/:id/approval-skip` |
| `/contractMgmt/checkProjectExists.do` | POST | 프로젝트 존재 체크 | `GET /api/sales/estimate/check-project?...` |
| `/contractMgmt/searchPartList.do` | POST | 품목 검색 (select2) | `GET /api/items/search?q=...` (마스터 공용) |
| `/contractMgmt/itemPriceCompareList.do` | GET | 가격비교 화면 | `app/(main)/COMPANY_16/sales/estimate/price-compare/page.tsx` |
| `/contractMgmt/getItemPriceCompareGridList.do` | GET | 가격비교 그리드 | `GET /api/sales/item-price-compare?...` |
| `/contractMgmt/FileRegistPopup.do` | GET | 첨부파일 등록 팝업 | (Next.js Dialog + 파일 업로드 컴포넌트) |
| `/contractMgmt/addEstimatePdfPopup.do` | GET | 추가견적 PDF 첨부 팝업 | (Next.js Dialog) |

## 3. DB 테이블 (이식 대상)

### 3.1 핵심 테이블

| 테이블 | 역할 | DDL 출처 |
|---|---|---|
| `estimate_mgmt` | 견적 헤더 (구버전) | `db/dbexport.pgsql:4340` |
| `estimate_template` | 견적 템플릿 / **신규 견적 헤더** | dbexport에 없음 — 운영 DB에서 추출 필요 |
| `estimate_template_item` | 견적 라인 | dbexport에 없음 — 운영 DB에서 추출 필요 |
| `contract_mgmt` | 영업번호(CONTRACT_NO)와 연결되는 영업/계약 헤더 | `db/dbexport.pgsql:2488` |
| `contract_item` | 견적/계약 품목 라인 | `database/contract_item_tables.sql` |
| `contract_item_serial` | 라인별 시리얼 | 위 SQL |
| `contract_mgmt_option` | 옵션 | `db/dbexport.pgsql:2899` |
| `attach_file_info` | 첨부파일 (`doc_type='contractMgmt01'`, `addEstimatePdf` 등) | `db/dbexport.pgsql:1387` |
| `approval` / `amaranth_approval` | 결재 (자체 + 아마란스) | `db/dbexport.pgsql:507` |
| `mail_log` | 견적 메일 이력 | (필요 시 추출) |

### 3.2 마스터 의존 (vexplor_rps에 매핑)

- `SUPPLY_MNG` (objid) + `CLIENT_MNG` (objid, `'C_'` prefix) → vexplor_rps `customer_mng.id`
  - 매핑 테이블: `legacy_customer_map (legacy_objid VARCHAR PK, customer_mng_id INT)`
- `PART_MGMT` (objid) → vexplor_rps `item_info.id`
- `USER_INFO` (user_id) → vexplor_rps `user_info.user_id`
- `DEPT_INFO` (dept_code) → 확인 필요

### 3.3 estimate_mgmt 컬럼 (`db/dbexport.pgsql:4340-4371`)

```sql
CREATE TABLE public.estimate_mgmt (
    objid integer NOT NULL,
    customer_objid character varying,        -- → customer_mng.id (변환)
    category_cd character varying,           -- 공통코드
    product_group character varying,
    product character varying DEFAULT '0',
    product_std character varying,
    qty character varying,
    warranty character varying,
    product_price integer,
    other_price integer,
    total_price integer,
    contract_user_id character varying,      -- → user_info.user_id
    contract_date character varying,
    contract_phone character varying,
    contract_email character varying,
    contract_office_no character varying,
    contract_fax_no character varying,
    est_release_date character varying,
    regdate timestamp without time zone,
    writer character varying,
    estimate_no character varying,           -- 채번 (EST-YYYYMMDD-NNN)
    contract_product_price integer,
    sale character varying,
    final_total_price integer,
    contract_type character varying,
    note character varying,
    cus_request_date character varying,
    delivery_place character varying,
    product_code character varying,
    status character varying
);
-- 시퀀스: estimate_mgmt_seq
```

⚠️ **현재 신규 견적은 `estimate_mgmt`보다 `estimate_template`을 메인으로 쓰는 것으로 보임** (`estimateList_new.jsp` 분석 결과). 운영 DB DDL 확인이 우선.

## 4. 구현 순서

1. **운영 DB에서 누락 DDL 추출**
   ```bash
   pg_dump -h 211.115.91.141 -p 11133 -U postgres -d waceplm \
     --schema-only --table=estimate_template --table=estimate_template_item \
     --table=mail_log > /tmp/estimate_missing_ddl.sql
   ```
2. **마이그레이션 파일 작성**: `db/migrations/100_create_estimate_tables.sql` (estimate_mgmt + estimate_template + estimate_template_item + 매핑 테이블)
3. **백엔드 작성**:
   - `backend-node/src/routes/estimateRoutes.ts`
   - `backend-node/src/controllers/estimateController.ts`
   - `backend-node/src/services/estimateService.ts`
4. **마스터 매핑 유틸**: `backend-node/src/utils/legacyIdMap.ts` (customer/part objid 변환)
5. **프론트엔드 작성**:
   - `frontend/app/(main)/COMPANY_16/sales/estimate/page.tsx` (목록 + 검색 + 그리드)
   - 견적 등록/수정 Dialog 컴포넌트 (`components/sales/EstimateRegistDialog.tsx`)
   - 일반/장비 견적서 작성 페이지 (popup): `frontend/app/(pop)/sales/estimate/template[1|2]/page.tsx`
   - 메일 발송 Dialog
6. **메뉴/권한**: vexplor_rps 메뉴 관리에서 등록 + 권한 매핑
7. **테스트**: 견적 생성 → 일반/장비 견적서 작성 → 결재 → 메일 발송 → 추가견적 PDF 첨부

## 5. 주의사항 / 결정 보류 항목

- **결재 시스템**: wace_plm은 자체 `approval` + 외부 `amaranth_approval` 두 채널. RPS에서 둘 다 살릴지, 자체만 쓸지 결정 필요. (관리자 측에서 amaranth 연동 진행 중이라면 활용 가능)
- **PDF 생성/메일 발송**: wace_plm은 SmartEditor 기반. Next.js에선 `puppeteer` 또는 react-pdf 등으로 대체 검토.
- **`fn_projectConceptDetail`** (영업번호 클릭): 프로젝트 모듈 종속 → 프로젝트관리 이식 후 연결.
- **품목 검색 select2**: vexplor_rps의 `item_info` 마스터에 wace_plm `PART_MGMT` 데이터를 마이그레이션해 두어야 select2 동작.
- **통화/환율**: `CONTRACT_CURRENCY_NAME`, `EST_TOTAL_AMOUNT_KRW` 컬럼 — 환율 마스터 또는 환율 API 필요.
