Files
hjjeong 489fa50d11 영업관리 4개 메뉴 검색폼 wace 일치 + 공통 UX(초기화·date input) 정비
- 검색 폼 정합성: wace JSP `<!-- 주석처리된 검색필터 -->` 블록까지 잘못 이식했던 부분 정정
  - 견적: 11→7개 (제품구분/국내해외/유무상/요청납기 제거)
  - 주문: 13→9개 (제품구분/국내해외/유무상/견적환종 제거)
  - 매출: 10→11개 (출하지시상태 제거 + 제품구분·국내/해외 추가, JSP 순서로 재배치)
  - 판매: 변경 없음 (원본 그대로 일치)
- 매출 백엔드: SaleListFilter에 productType/nation 추가, getRevenueList에 partObjId/serialNo/orderDate/productType/nation 5개 필터 처리
- 공통 UX
  - 초기화 버튼을 4개 메뉴 동일하게 통일 (variant=ghost, 버튼 영역 끝)
  - <Input type="date">는 빈 값 placeholder 숨김 + 캘린더 아이콘 숨김 + 영역 클릭으로 picker 자동(showPicker)
- 신규 공통 컴포넌트: CommCodeSelect/CustomerSelect/CustomerSearchDialog/PartSelect/ItemSearchDialog + backend salesCommonRoutes
- 문서: 01/02/04 검색 폼 표를 활성/비활성 분리 형식으로 정정, README에 8. 공통 UX 규칙 섹션 신설

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:42:16 +09:00

195 lines
11 KiB
Markdown

# 02. 주문서관리 이식 상세
> 원본: `/contractMgmt/orderMgmtList.do` (orderMgmtList.jsp, 45KB)
> 대상: `app/(main)/COMPANY_16/sales/order/page.tsx` (기존 골격 대체 또는 신규 경로)
## 1. 화면 구조 (wace_plm 원본)
### 1.1 검색 폼 (`#plmSearchZon`)
**활성 9개** (orderMgmtList.jsp line 1109~1182):
| 필드 | name | 타입 | 비고 |
|---|---|---|---|
| 주문유형 | `category_cd` | select2 (공통코드) | |
| 발주번호 | `search_poNo` | text | |
| 고객사 | `customer_objid` | select2 (`SUPPLY_MNG`+`CLIENT_MNG`) | `'C_'` 접두사 |
| 품번 | `search_partNo` | select2-part | hidden `search_partObjId` 동행 |
| 품명 | `search_partName` | select2-part | |
| 시리얼 | `search_serialNo` | text | |
| 수주상태 | `contract_result` | select2 (공통코드) | |
| 발주일 from~to | `order_start_date` / `order_end_date` | date_icon | |
| 요청납기 from~to | `due_start_date` / `due_end_date` | date_icon | |
**비활성 (JSP line 1184~ `<!-- 주석처리된 검색필터 -->` 블록 안)** — 이식 대상 아님:
| 필드 | name |
|---|---|
| 제품 | `product` |
| 지역 | `area_cd` |
| 유/무상 | `paid_type` |
| 환종 | `contract_currency` |
### 1.2 버튼 영역
| 버튼 | id | 동작 |
|---|---|---|
| 조회 | `btnSearch` | `fn_search()``/contractMgmt/contractGridList.do` |
| 수주복사 | `btnCopy` | `/contractMgmt/copyEstimateAndOrderInfo.do` |
| 수주입력 | `.btnRegist` | 팝업: `/contractMgmt/estimateAndOrderRegistFormPopup.do` 또는 `orderRegistFormPopup.do` |
| 수주확정 | `btnOrderConfirm` | (녹색) — 견적 → 수주 확정 처리 |
| 수주취소 | `btnOrderCancel` | (빨강) — `/contractMgmt/saveOrderCancelQty.do` (수량별 취소) |
| 결재상신 | `btnApproval` | 결재 워크플로우 |
### 1.3 그리드 (`_tabulGrid` = Tabulator)
데이터 소스: `POST /contractMgmt/contractGridList.do`
| # | 컬럼 | field | 정렬 | 비고 |
|---|---|---|---|---|
| 1 | 영업번호 | CONTRACT_NO | center | frozen, anchor → `fn_projectConceptDetail(OBJID)` |
| 2 | 주문유형 | CATEGORY_NAME | center | |
| 3 | 발주일 | ORDER_DATE | center | |
| 4 | 발주번호 | PO_NO | center | |
| 5 | 요청납기 | EARLIEST_DUE_DATE | center | "{date} 외 {n}건" 포맷 |
| 6 | 고객사 | CUSTOMER_NAME | left | |
| 7 | 품명 | ITEM_SUMMARY | left | "{itemName} 외 {n}건" |
| 8 | 수주수량 | ORDER_QUANTITY | right | |
| 9 | 수주취소 | CANCEL_QTY_SUM | right | |
| 10 | 유/무상 | PAID_TYPE | center | |
| 11 | 수주상태 | CONTRACT_RESULT_NAME | left | |
| 12 | 공급가액 | ORDER_SUPPLY_PRICE_SUM | right | 통화기호 |
| 13 | 부가세 | ORDER_VAT_SUM | right | |
| 14 | 총액 | ORDER_TOTAL_AMOUNT_SUM | right | |
| 15 | 원화총액 | ORDER_TOTAL_AMOUNT_KRW | right | |
| 16 | 주문서첨부 | CU01_CNT | center | 📎 클릭 → `/contractMgmt/FileRegistPopup.do` |
| 17 | 주문서 | HAS_ORDER_DATA | center | 클릭 → `/contractMgmt/orderFormView.do?objId=...` |
| 18 | 고객사요청사항 | CUSTOMER_REQUEST | left | |
| 19 | 결재상태 | ORDER_APPR_STATUS | center | |
| 20 | 환종 | CONTRACT_CURRENCY_NAME | center | |
| 21 | 환율 | EXCHANGE_RATE | right | |
| 22 | S/N | SERIAL_NO | center | |
| 23 | 품번 | PART_NO | center | |
| 24 | 작성자 | WRITER_NAME | center | |
| 25 | 제품구분 | PRODUCT_NAME | center | |
| 26 | 국내/해외 | AREA_NAME | center | |
| 27 | 접수일 | RECEIPT_DATE | center | |
> 일부 셀 클릭은 `contractList.jsp`로 form submit되어 다른 화면 이동 (line 194 참고).
## 2. 백엔드 endpoint 매핑
| wace_plm endpoint | 메서드 | 용도 | vexplor_rps 신규 endpoint |
|---|---|---|---|
| `/contractMgmt/orderMgmtList.do` | GET | 페이지 진입 | (Next.js page) |
| `/contractMgmt/contractGridList.do` | POST | 그리드 데이터 | `GET /api/sales/order/list?...` |
| `/contractMgmt/getPagingContractList.do` | POST | 페이징 조회 (legacy) | 위와 통합 |
| `/contractMgmt/orderRegistFormPopup.do` | GET | 수주 등록/수정 팝업 | (Next.js Dialog) |
| `/contractMgmt/estimateAndOrderRegistFormPopup.do` | GET | 견적+수주 통합 팝업 | (Next.js Dialog with mode) |
| `/contractMgmt/saveOrderInfo.do` | POST | 수주 저장 | `POST /api/sales/order` / `PUT /api/sales/order/:id` |
| `/contractMgmt/saveEstimateAndOrderInfo.do` | POST | 견적+수주 통합 저장 | 위와 통합 (mode 파라미터) |
| `/contractMgmt/copyEstimateAndOrderInfo.do` | POST | 수주 복사 | `POST /api/sales/order/:id/copy` |
| `/contractMgmt/updateOrderStatus.do` | POST | 수주 상태 변경(확정/취소 등) | `PATCH /api/sales/order/:id/status` |
| `/contractMgmt/saveOrderCancelQty.do` | POST | 라인별 취소 수량 저장 | `POST /api/sales/order/:id/cancel-qty` |
| `/contractMgmt/getContractItems.do` | POST | 수주 라인 조회 | `GET /api/sales/order/:id/items` |
| `/contractMgmt/getAllSerialNumbers.do` | POST | 라인 전체 시리얼 조회 | `GET /api/sales/order/:id/serials` |
| `/contractMgmt/orderFormView.do` | GET | 주문서 양식 화면 | `app/(pop)/sales/order/[id]/form/page.tsx` |
| `/contractMgmt/getOrderFormData.do` | POST | 주문서 양식 데이터 | `GET /api/sales/order/:id/form-data` |
| `/contractMgmt/getOrderTotalAmount.do` | POST | 합계 계산 | `GET /api/sales/order/:id/totals` |
| `/contractMgmt/orderMgmtGrodList.do` (typo) | POST | (구버전 그리드) | (사용 안함) |
| `/contractMgmt/getContractItemList.do` | POST | 계약 라인 조회 | 위 `/items`와 통합 |
| `/contractMgmt/contracMgmtReviewFormPopup.do` (typo) | GET | 계약 리뷰 팝업 | (Next.js Dialog) |
| `/contractMgmt/saveContractMgmtReviewInfo.do` | POST | 리뷰 정보 저장 | `POST /api/sales/order/:id/review` |
| `/contractMgmt/overlapOrder.do` | POST | 중복 수주 체크 | `GET /api/sales/order/check-duplicate?...` |
| `/contractMgmt/checkApprovalRequired.do` | POST | 결재 필요 여부 | (견적과 공유) |
| `/contractMgmt/deleteContractMngInfo.do` | POST | 계약(수주) 삭제 | `DELETE /api/sales/order/:id` |
| `/contractMgmt/FileRegistPopup.do` | GET | 첨부파일 팝업 | (공유 컴포넌트) |
| `/contractMgmt/getCustomerManagerList.do` | POST | 고객사 담당자 목록 | `GET /api/customers/:id/contacts` |
| `/contractMgmt/checkProjectExists.do` | POST | 프로젝트 존재 체크 | (견적과 공유) |
> `OrdersMgmtController` (다른 컨트롤러, `/ordersMgmt/*`)는 별개 모듈로 보임 (입출고/미납 관리 위주). 본 메뉴와는 무관.
## 3. DB 테이블 (이식 대상)
| 테이블 | 역할 | DDL 출처 |
|---|---|---|
| `contract_mgmt` | 수주(계약) 헤더 — **주문서의 메인 테이블** | `db/dbexport.pgsql:2488` |
| `contract_item` | 수주 라인 (품번/수량/단가/공급가/부가세/총액 + ORDER_* 컬럼들) | `database/contract_item_tables.sql` + `database/add_order_columns_to_contract_item.sql` |
| `contract_item_serial` | 라인별 S/N | `database/contract_item_tables.sql` |
| `contract_mgmt_option` | 수주 옵션 | `db/dbexport.pgsql:2899` |
| `contract_base_data` | 수주 기준 데이터 (공통 마스터) | (운영 DB 추출 필요) |
| `attach_file_info` | 주문서 첨부 (`doc_type='contractMgmt01'`) | `db/dbexport.pgsql:1387` |
| `approval` | 결재 | `db/dbexport.pgsql:507` |
### 3.1 contract_item 핵심 컬럼 (DDL 합본)
```sql
CREATE TABLE CONTRACT_ITEM (
OBJID VARCHAR(50) PRIMARY KEY,
CONTRACT_OBJID VARCHAR(50) NOT NULL REFERENCES CONTRACT_MGMT(OBJID) ON DELETE CASCADE,
SEQ INTEGER NOT NULL,
PART_NO VARCHAR(100) NOT NULL, -- → vexplor_rps item_info 매핑
PART_NAME VARCHAR(200) NOT NULL,
QUANTITY INTEGER NOT NULL DEFAULT 1, -- 견적 수량
DUE_DATE VARCHAR(10),
CUSTOMER_REQUEST TEXT,
-- 수주 컬럼 (add_order_columns 추가)
ORDER_QUANTITY VARCHAR(50),
ORDER_UNIT_PRICE VARCHAR(50),
ORDER_SUPPLY_PRICE VARCHAR(50),
ORDER_VAT VARCHAR(50),
ORDER_TOTAL_AMOUNT VARCHAR(50),
-- 공통
REGDATE TIMESTAMP NOT NULL DEFAULT NOW(),
WRITER VARCHAR(50),
CHGDATE TIMESTAMP,
CHG_USER_ID VARCHAR(50),
STATUS VARCHAR(20) DEFAULT 'ACTIVE'
);
CREATE TABLE CONTRACT_ITEM_SERIAL (
OBJID VARCHAR(50) PRIMARY KEY,
ITEM_OBJID VARCHAR(50) NOT NULL REFERENCES CONTRACT_ITEM(OBJID) ON DELETE CASCADE,
SEQ INTEGER NOT NULL,
SERIAL_NO VARCHAR(200) NOT NULL,
REGDATE TIMESTAMP NOT NULL DEFAULT NOW(),
WRITER VARCHAR(50),
STATUS VARCHAR(20) DEFAULT 'ACTIVE',
UNIQUE (ITEM_OBJID, SERIAL_NO)
);
```
### 3.2 contract_mgmt 주요 컬럼 (참고)
`db/dbexport.pgsql:2488-2540` 참조. 주문서에서 자주 쓰는 컬럼:
- `objid` (PK), `contract_no` (영업번호 = CONTRACT_NO)
- `customer_objid` (고객사 — `'C_'` prefix로 client_mng 분기)
- `category_cd`, `area_cd`, `product`, `paid_type` (공통코드)
- `po_no` (발주번호), `contract_date` (발주일), `req_del_date` (요청납기)
- `contract_currency`, `contract_currency_name`, `exchange_rate`(?)
- `result_cd``contract_result_name` (수주상태)
- `pm_user_id`, `salesman` (담당자)
- `regdate`, `writer`, `chg_user_id`
## 4. 구현 순서
1. **마이그레이션 SQL 작성**: `db/migrations/101_create_order_tables.sql` (contract_mgmt + contract_item + contract_item_serial + contract_mgmt_option + contract_base_data)
2. **백엔드**:
- `backend-node/src/routes/orderRoutes.ts`
- `backend-node/src/controllers/orderController.ts`
- `backend-node/src/services/orderService.ts` (트랜잭션 필수: 헤더 + 라인 + 시리얼 동시 저장)
3. **프론트엔드**:
- `frontend/app/(main)/COMPANY_16/sales/order/page.tsx` (기존 파일 백업 후 신규 작성, 또는 새 라우트 `sales/contract-order`)
- 수주 등록/수정 Dialog (`components/sales/OrderRegistDialog.tsx`)
- 수주확정/취소 모달 (수량 입력)
- 라인별 시리얼 관리 모달
- 주문서 양식 출력 페이지 (PDF/인쇄)
## 5. 주의사항
- **견적 → 수주 전환**: `estimateAndOrderRegistFormPopup`은 견적 OBJID를 받아 수주 헤더를 생성하면서 라인을 복사. 트랜잭션 + 라인 컬럼 매핑(견적 QUANTITY → ORDER_QUANTITY) 필요.
- **라인별 부분 취소**: `saveOrderCancelQty`는 라인의 ORDER_QUANTITY 일부만 취소하는 시나리오. UI에서 취소 수량 입력 → DB는 별도 취소이력 테이블 사용 가능성 (확인 필요).
- **주문서 양식 (orderFormView)**: 인쇄용 별도 화면. PDF 다운로드 또는 인쇄 다이얼로그.
- **계약 리뷰 (Review)**: 사내 검토 단계 — 별도 결재 또는 메모/체크리스트. `contracMgmtReviewFormPopup` (typo) 분석 필요.
- **vexplor_rps 기존 `sales/order/page.tsx`** ([order/page.tsx](../../../frontend/app/(main)/COMPANY_16/sales/order/page.tsx))는 이 도메인이 아닐 가능성. 백업 후 신규 작성 권장.