# 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))는 이 도메인이 아닐 가능성. 백업 후 신규 작성 권장.