# 영업관리 G9 — 수주복사 검증 (BEGIN/ROLLBACK)

> 작성: 2026-05-11 / 작성자: hjjeong
> 목적: wace `copyEstimateAndOrderInfo` 1:1 이식한 vexplor_rps의 수주복사가 contract_mgmt 헤더 + contract_item 라인 + contract_item_serial 시리얼을 정확히 복제하는지 확인.

## 원본 매퍼 출처
`wace_plm/src/com/pms/salesmgmt/mapper/contractMgmt.xml`
- `getNextContractNo` (6118)
- `copyContractMgmt` (6127)
- `copyContractItems` (6184)
- `copyContractItemSerials` (6230)
- `getActiveItemObjIds` (6254)

## 이식 위치
- 서비스: `backend-node/src/services/salesOrderMgmtService.ts:copyOrder`
- 컨트롤러: `backend-node/src/controllers/salesOrderMgmtController.ts:copyOrder`
- 라우트: `POST /api/sales/order-mgmt/:id/copy`
- 프론트 API: `frontend/lib/api/salesOrderMgmt.ts:copyOrder`
- 프론트 UI: `frontend/app/(main)/COMPANY_16/sales/order/page.tsx` (수주복사 버튼 + handleCopyOrder)

## 복사 정책 (wace 1:1)
- 새 영업번호: `{YY}C-{NNNN}` (MAX+1)
- 헤더 복사 시 갱신: `objid`, `contract_no`, `writer`, `regdate(NOW())`, `contract_result=''`, `is_direct_order='Y'`
- 라인 복사 시 갱신: `objid`, `contract_objid`, `writer`, `regdate(NOW())`, `status='ACTIVE'`
- 시리얼 복사: `objid = prefix || seq`, 나머지 컬럼 그대로

> 복사본은 항상 `is_direct_order='Y'`로 들어오므로 견적관리에 노출되지 않고 주문관리에만 노출됨 (wace orderMgmtList 동작).

## 검증 SQL (수동 BEGIN/ROLLBACK)

```sql
BEGIN;

-- 1. 새 영업번호 채번
SELECT TO_CHAR(NOW(),'YY') || 'C-' || LPAD((
  COALESCE(MAX(SUBSTRING(contract_no FROM 5)::integer), 0) + 1
)::VARCHAR, 4, '0') AS new_contract_no
FROM contract_mgmt
WHERE contract_no LIKE TO_CHAR(NOW(),'YY') || 'C-%';

-- 2. 헤더 복사 (수동 테스트 키)
INSERT INTO contract_mgmt (
  objid, category_cd, customer_objid, product, area_cd,
  customer_equip_name, customer_project_name, customer_production_no, mechanical_type,
  paid_type, receipt_date, req_del_date, contract_result,
  po_no, order_date, contract_currency, exchange_rate,
  regdate, writer, contract_no, is_direct_order,
  order_supply_price, order_vat, order_total_amount
)
SELECT
  'CM-TEST-COPY-001', category_cd, customer_objid, product, area_cd,
  customer_equip_name, customer_project_name, customer_production_no, mechanical_type,
  paid_type, receipt_date, req_del_date, '',
  po_no, order_date, contract_currency, exchange_rate,
  NOW(), 'TEST_USER', '26C-9999', 'Y',
  order_supply_price, order_vat, order_total_amount
FROM contract_mgmt
WHERE objid = '<SOURCE_OBJID>';

-- 3. 활성 라인 복사
INSERT INTO contract_item (
  objid, contract_objid, seq, part_objid, part_no, part_name,
  quantity, due_date, customer_request, return_reason,
  regdate, writer, status,
  order_quantity, order_unit_price, order_supply_price, order_vat, order_total_amount
)
SELECT
  'CI-TEST-COPY-' || seq::varchar, 'CM-TEST-COPY-001', seq, part_objid, part_no, part_name,
  quantity, due_date, customer_request, return_reason,
  NOW(), 'TEST_USER', 'ACTIVE',
  order_quantity, order_unit_price, order_supply_price, order_vat, order_total_amount
FROM contract_item
WHERE contract_objid='<SOURCE_OBJID>' AND status='ACTIVE';

-- 4. 검증: BEFORE/AFTER 비교
SELECT '원본 라인' AS what, count(*) FROM contract_item WHERE contract_objid='<SOURCE_OBJID>' AND status='ACTIVE';
SELECT '복사 라인' AS what, count(*) FROM contract_item WHERE contract_objid='CM-TEST-COPY-001' AND status='ACTIVE';

ROLLBACK;
```

## 검증 결과 (2026-05-11, 26C-0800 → 시뮬레이션)

| 단계 | 원본 (26C-0800) | 복사본 (26C-9999) | 비고 |
|---|---|---|---|
| 새 영업번호 | — | `26C-0803` | 현재 MAX=`26C-0802` → +1 ✓ |
| 헤더 컬럼 (category_cd/customer_objid/product/paid_type/receipt_date/contract_currency 등) | 동일 | 동일 | wace 24개 컬럼 1:1 복사 확인 |
| contract_result | (빈값) | `''` | 강제 빈문자열 ✓ |
| is_direct_order | (빈값) | `Y` | 강제 'Y' ✓ |
| writer | `khy1022` | `TEST_USER` | 현재 사용자로 갱신 ✓ |
| 활성 라인 수 | 3 | 3 | seq 보존 ✓ |
| 시리얼 | — | — | (해당 행에는 시리얼 없음 — 별도 시리얼 보유 행으로 추가 검증 필요 시) |

## API 호출 (인증 토큰 필요)

```bash
curl -X POST 'http://localhost:8080/api/sales/order-mgmt/<SOURCE_OBJID>/copy' \
  -H 'Authorization: Bearer <TOKEN>' \
  -H 'Content-Type: application/json'

# 응답: {"success": true, "data": {"newObjid": "CM-...", "newContractNo": "26C-0803"}, "message": "수주가 복사되었습니다."}
```

## UI 동작 (wace 1:1)
1. 주문관리 그리드에서 행 1개 선택
2. "수주복사" 버튼 클릭
3. 확인 다이얼로그: `[26C-0800] 수주를 복사하시겠습니까?`
4. 확인 → API 호출
5. 성공 토스트: `복사가 완료되었습니다. (영업번호: 26C-0803)`
6. 목록 새로고침 → 복사본 그리드 상단에 표시

## 미선택 시 분기 (wace 1:1)
- 0건 선택: `복사할 행을 선택해주십시오.`
- 2건 이상 선택: (현 vexplor UI는 단일선택만 지원 — wace의 "한번에 한개의 수주만 복사 가능합니다." 분기는 불필요)
