Files
hjjeong b17d7b063d PR-D G11 견적 결재상신 — Amaranth 직행 (wace estimateList_new.jsp btnApproval 1:1)
G11 수주 결재상신(905d5c09)과 동일 패턴을 견적관리에 확장. target_type='CONTRACT_ESTIMATE',
target_objid=estimate_template.objid(최신 차수), formId='1162' (수주 1161과 별도 양식).

- 백엔드: salesEstimateService.startEstimateApproval + POST /sales/estimate/:id/amaranth-approval
- 견적 list SQL: LEFT JOIN amaranth_approval(CONTRACT_ESTIMATE) + APPR_STATUS 4단계 한글 라벨 + approval_required='N' fallback (wace contractMgmt.xml:513~522 1:1)
- 프론트: 견적관리 placeholder 토스트 → handleAmaranthApproval 핸들러 + sky-600 Send 버튼 (수주 페이지와 통일)
- docker-compose 3개: AMARANTH_OUT_PROCESS_CODE_CONTRACT_ESTIMATE + AMARANTH_FORM_ID_CONTRACT_ESTIMATE=1162 추가
- 가드: 행 미선택 / est_objid 없음(견적서 미작성) / inProcess+complete / notRequired+approval_required='N'
- 사전판정(checkApprovalRequired)은 G4 영역으로 분리 — 이번 PR은 단순 SSO 흐름만

검증: BEGIN/ROLLBACK으로 26C-0712(est_objid=-452406811) 4단계 상태(create→inProcess→complete→reject)
+ amaranth row 삭제 시 approval_required='N' fallback 모두 한글 라벨 정상. 문서 08-estimate-approval-verify.md.

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

120 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 영업관리 이식 GAP 분석 (원본 wace_plm 대비)
> 작성: 2026-05-08 / 작성자: hjjeong
> 목적: vexplor_rps에 이식된 영업관리 4개 메뉴가 wace_plm 원본 흐름과 어디서 어긋나는지 정리하고, 다음 PR 우선순위를 합의하기 위한 단일 문서.
> 참고: [01-estimate.md](./01-estimate.md), [02-order.md](./02-order.md), [feedback_wace_jsp_columns](../../../../.claude/projects/-Users-jhj-vexplor-rps/memory/feedback_wace_jsp_columns.md)
## 0. 한 문장 요약
견적/주문 list와 SQL은 잘 이식됐지만 **상태 전이 트리거**(수주확정 → 프로젝트 자동생성)와 **직접등록 통합폼**, **결재 자동판정**, **PDF·SMTP 실작업**이 통째로 빠져 있어, 사용자가 영업 흐름을 끝까지 돌릴 수 없는 상태.
## 0.1 이식 원칙 (모든 GAP 작업 공통)
> **JSP/Java/매퍼XML 안의 주석 블록(`/* */`, `<!-- -->`, `//`)은 비활성 옛 로직 보존 영역이다 — 절대 이식 대상이 아니다. 활성 코드만, 한 줄 한 줄 직접 따라가서 그대로 이식한다.**
- **운영 화면이 진실의 기준**: waceplm.esgrin.com 운영 화면에 실제 보이는 항목/동작이 활성. 코드만 보면 활성/비활성 구분이 흐려짐.
- **컬럼 정의(`var columns = [...]`)**: `/* 주석처리된 컬럼 - 필요시 활성화 */` 블록 이하는 무시.
- **검색 폼(`#plmSearchZon`)**: `<!-- 주석처리된 검색필터 - 필요시 활성화 -->` 블록 이하는 무시.
- **서비스 메서드**: 주석된 옛 SQL/분기 무시. 호출 그래프(controller → service → mapper)를 한 줄씩 따라가서 활성 경로만 추출.
- **매퍼 XML**: `<!-- ... -->` 블록 안의 SQL fragment는 무시. `<select id="...">`/`<insert id="...">` 단위로 호출되는 것만 사용.
- 자세한 함정 사례: 메모리 [feedback_wace_jsp_columns](../../../../.claude/projects/-Users-jhj-vexplor-rps/memory/feedback_wace_jsp_columns.md) (2026-05-08 영업관리 4개 메뉴 검색폼 사고 기록).
## 1. 원본 견적·주문 흐름 (10단계)
| # | 단계 | wace_plm endpoint | 핵심 테이블 변경 |
|---|---|---|---|
| 1 | 견적 list 조회 | `POST /contractMgmt/estimateGridList.do` | `contract_mgmt`(read, `IS_DIRECT_ORDER!='Y'` 필터) |
| 2 | 견적요청 등록 | `POST /contractMgmt/saveContractMgmtInfo.do` | `contract_mgmt` + `contract_item`(N) + `contract_item_serial`(N) INSERT |
| 3 | 직접등록 통합폼 (견적 없이 주문) | `POST /contractMgmt/saveEstimateAndOrderInfo.do` | 위와 동일 + `IS_DIRECT_ORDER='Y'` 강제 세팅 |
| 4 | 견적요청 → 견적작성 | (식별자 `contract_objid`로 묶임. 명시적 컬럼 복사 없음) | (없음) |
| 5 | 견적 작성 (일반/장비) | `POST /contractMgmt/saveEstimate.do | saveEstimate2.do` | `estimate_template` + `estimate_template_item` INSERT/UPDATE (N건 가능) |
| 6 | 결재 사전판정 | `POST /contractMgmt/checkApprovalRequired.do` | (read only — 신규수주/가격인하 로직) |
| 6a | 결재불필요 자동처리 | `POST /contractMgmt/setApprovalNotRequired.do` | `contract_mgmt.APPROVAL_REQUIRED='N'` UPDATE |
| 6b | 아마란스 결재상신 | `POST /approval/getAmaranthSsoUrl.do` | `amaranth_approval` INSERT (외부 SSO) |
| 7 | 메일 발송 | `POST /contractMgmt/sendEstimateMail.do` | `mail_log` INSERT + 실제 SMTP `MailUtil.sendMailWithAttachFileUTF8` |
| 8 | 주문서 list 조회 | `POST /contractMgmt/contractGridList.do` | `contract_mgmt`(read, 모든 행) — 견적 list와 동일 테이블 |
| 9 | **수주확정 (전이)** | `POST /contractMgmt/updateOrderStatus.do` | `contract_mgmt.CONTRACT_RESULT` UPDATE만 (새 INSERT 없음) |
| 10 | **프로젝트 자동생성** (9의 부수효과) | (9의 service 내부에서 호출) | `project_mgmt` INSERT × N (라인별, Machine은 수량만큼) + `project_no` 채번 |
> 견적관리·주문서관리·판매관리·매출관리 4개 list는 **모두 같은 `contract_mgmt` 행**을 단계별 필터로 보여주는 구조. 단계 전이는 `CONTRACT_RESULT` 코드 변경으로 일어남.
## 2. GAP 매트릭스 (우선순위 순)
> 🔴 = 사용자 흐름 차단 / 🟠 = 핵심 기능 빠짐 / 🟡 = 보완 필요 / 🟢 = 백로그
| # | 우선 | 항목 | 원본 위치 | 이식본 현재 상태 | 권장 작업 |
|---|---|---|---|---|---|
| G1 | 🔴 | **수주확정 시 프로젝트 자동생성** | `ContractMgmtService.updateOrderStatus` (라인 2987~3113) + `project.xml` (7518~7581) | [salesOrderMgmtService.ts:521](../../../backend-node/src/services/salesOrderMgmtService.ts#L521) `updateStatus``CONTRACT_RESULT` UPDATE만 — 프로젝트 생성 호출 없음 | `updateStatus` 트랜잭션 내에서: contract_item 루프 → PRODUCT='0000928'(Machine)이면 quantity만큼 N회, 아니면 1회 → `project_no` 채번 (`{주문유형}-{제품구분}-{YYMMDD}-{순번3자리}`) → `project_mgmt` INSERT |
| G2 | 🔴 | **직접등록 통합폼** (`estimateAndOrderRegistFormPopup`) | `ContractMgmtService.saveEstimateAndOrderInfo` (라인 2664) | endpoint 자체 부재. 주문관리 화면 "신규" 버튼이 견적 없이 주문 등록하는 흐름 미지원 | `POST /api/sales/order/direct` 신설 — `IS_DIRECT_ORDER='Y'` 강제, `contract_mgmt` UPSERT + `contract_item` UPSERT + `contract_item_serial` 다중 INSERT |
| G3 | 🟠 | 견적요청등록 시 contract_item 다중 INSERT | `ContractMgmtService.saveContractMgmtInfo` (라인 544) | [salesEstimateService.ts](../../../backend-node/src/services/salesEstimateService.ts)는 헤더만 INSERT, 라인 입력 누락 | save 트랜잭션에 `contract_item` 다중 UPSERT + `contract_item_serial` 처리 추가 |
| G4 | 🟡 | 결재 자동판정 (`checkApprovalRequired`) | (별도 컨트롤러, 신규수주/가격인하 룰) | 미구현. APPROVAL_REQUIRED='N' 라벨 표시만 | 룰 분석 후 endpoint 신설 (사전판정 룰만 — SSO 흐름은 G11에서 처리됨) |
| G5 | 🟠 | 견적템플릿 일반/장비 분기 + PDF | `ContractMgmtService.saveEstimateTemplate/2` (라인 1501/1591) + SmartEditor `uploadPdfChunk` | 미이식. 추가견적 카운트(시연 시드)만 표시 | template1/template2 popup 라우트 + `puppeteer` 또는 `react-pdf` PDF 생성 → `attach_file_info doc_type='estimate02'` INSERT |
| G6 | 🟠 | SMTP 실제 발송 | `ContractMgmtService.sendEstimateMail` (라인 1774-1968), `MailUtil.sendMailWithAttachFileUTF8` (라인 1925) | [salesEstimateService.ts:618](../../../backend-node/src/services/salesEstimateService.ts#L618)는 mail_log INSERT만 | `mailSendSimpleService`(nodemailer) 통합 + HTML 본문 생성기(`makeEstimateMailContents`) 포팅 + 첨부 결합 |
| G7 | 🟡 | 주문서 수정 시 contract_item UPSERT (OBJID 유지) | `mapper.upsertContractItemWithOrder` (UPSERT 패턴) | 이식본은 단순 UPDATE — 라인 변경 시 OBJID 유지 보장 안 됨 | UPSERT(ON CONFLICT) 패턴 적용 + 삭제된 라인 처리 분기 |
| G8 | 🟡 | 프로젝트 존재 시 견적·주문 삭제 방지 | `ContractMgmtService.deleteContractMngInfo` (라인 794~808) | [salesOrderMgmtService.ts](../../../backend-node/src/services/salesOrderMgmtService.ts) delete는 사전 체크 없음 | delete 전에 `project_mgmt WHERE contract_objid=$1 LIMIT 1` 체크 → 있으면 거부 |
| G9 | ✅ | **수주복사** (헤더 + contract_item + 시리얼 통째로 복제, 새 영업번호 채번) | `ContractMgmtService.copyEstimateAndOrderInfo` (라인 2601) + 매퍼 `copyContractMgmt`/`copyContractItems`/`copyContractItemSerials`/`getNextContractNo` | **완료 (2026-05-11)**`salesOrderMgmtService.copyOrder` + `POST /sales/order-mgmt/:id/copy` + 주문관리 그리드 "수주복사" 버튼. 검증: [06-copy-order-verify.md](./06-copy-order-verify.md) |
| ~~G10~~ | ❌ | ~~환율 마스터 + EXCHANGE_RATE 자동 변환~~ | (영업관리 GAP 아님 — Admin 도메인) | wace 영업관리 화면도 `exchange_rate`는 사용자 직접 입력. `COMM_EXCHANGE_RATE` 테이블·환율관리 화면은 wace AdminController(`4898~4993`) + `admin.xml(8191~8336)` 소속 | 영업관리 GAP에서 제외. Admin 메뉴 이식 시점에 별도로 다룸 |
| G11 | ✅ | **수주 결재상신 (Amaranth 직행)** | wace `ApprovalService.getAmaranthSsoUrl` (라인 1782~1909) + `orderMgmtList.btnApproval` (132~175) | chpark의 `amaranthApprovalClient`(HMAC-SHA256 + AES-128-CBC) 기반. `amaranth_approval` 테이블만 사용, 자체 approval 미경유 (wace 패턴 동일). target_type=`CONTRACT_ORDER`, formId=`1161`, compSeq=`1000` | **완료 (2026-05-11)**`salesOrderMgmtService.startOrderApproval` + `POST /sales/order-mgmt/:id/amaranth-approval` + 주문관리 "결재상신" 버튼 + 결재상태 컬럼(작성중/결재중/결재완료/반려). DB ALTER: `amaranth_approval.target_objid` → VARCHAR. 검증: [07-amaranth-approval-verify.md](./07-amaranth-approval-verify.md). 백로그: 첨부파일 원챔버 업로드 |
| G11E | ✅ | **견적 결재상신 (Amaranth 직행, G11 동일 패턴)** | wace `estimateList_new.jsp:154~270, 868~916` + `ApprovalService.getAmaranthSsoUrl` CONTRACT_ESTIMATE 분기 | G11과 동일 흐름. 차이점: target_type=`CONTRACT_ESTIMATE`, target_objid=`estimate_template.objid`(최신 차수), formId=`1162`. 사전판정(`checkApprovalRequired`)은 G4 영역으로 분리 | **완료 (2026-05-11)**`salesEstimateService.startEstimateApproval` + `POST /sales/estimate/:id/amaranth-approval` + 견적관리 "결재상신" 버튼 + 견적 list SQL `LEFT JOIN amaranth_approval` + 결재상태 4단계 라벨 + `notRequired`/`approval_required='N'` fallback. 검증: [08-estimate-approval-verify.md](./08-estimate-approval-verify.md). 백로그: 첨부파일 원챔버 업로드, 사전판정(G4) |
## 3. 코드/SQL 정합성 메모
### 3.1 견적·주문은 같은 `contract_mgmt` 행
원본은 **상태 전이형 모델**: 같은 `contract_mgmt` 행이 견적단계(`IS_DIRECT_ORDER='N'`) → 수주(`CONTRACT_RESULT='0000964'`) → FCST(`CONTRACT_RESULT='0000968'`) 로 진행. 새 INSERT는 §3 통합폼과 §10 프로젝트 생성에서만 일어남. 우리 이식본은 이 단계 모델에 맞춰져 있어 list 단의 SQL은 정합성 OK.
### 3.2 project_no 채번 룰 (G1의 핵심)
```
형식: {주문유형}-{제품구분}-{YYMMDD}-{순번3자리}
예: R-CT-260507-001
주문유형 매핑 (CATEGORY_CD → 1글자):
오버홀=O, 개조=M, 개발=D, 견적=Q, 수리=R, 판매=S, 기타=T
제품구분 매핑 (PRODUCT → 2글자):
Machine=MC, A/S=AS, D/S=DS, B/S=BS, C/T=CT, A/C=AC, W/M=WM, 기타=원문
순번:
같은 (주문유형 + 제품구분 + 날짜) 조합 내 MAX(순번)+1
없으면 001부터
```
출처: `wace_plm/src/com/pms/projectmgmt/mapper/project.xml:7518-7581`.
RPS의 `project_mgmt` 89건이 모두 이 룰로 채번되어 있으니, 이식 시 **새 행도 같은 룰**을 따라야 일관성 유지됨.
### 3.3 Machine 분기
contract_item 1라인의 PRODUCT가 `0000928`(Machine)이면 **수량만큼 N회** project_mgmt INSERT (각 quantity=1). 그 외(A/S·D/S·C/T 등)는 1회 INSERT (수주수량 그대로). 이 분기 누락 시 시리얼 단위 추적이 깨짐.
### 3.4 첨부 파일 doc_type
| 단계 | doc_type | 그리드 컬럼 |
|---|---|---|
| 견적 PDF | `estimate01` (메인 견적서) | (없음) |
| 추가 견적 | `estimate02` | "추가견적" |
| 주문서 첨부 | `ORDER_DOC` 또는 `FTC_ORDER` | "주문서첨부" (CU01_CNT) |
`02-order.md`에 주문서 첨부 컬럼이 미정의되어 있음 — 보완 필요.
## 4. 다음 PR 후보 (3개로 묶기)
### PR-A: 수주확정 → 프로젝트 자동생성 (G1)
- 단독 PR. SQL·트랜잭션 위주, UI 변경 거의 없음.
- 작업: `salesOrderMgmtService.updateStatus` 안에 project 생성 로직 + project_no 채번 helper + Machine 분기.
- 검증: `0000964`/`0000968` 코드로 상태 변경 시 project_mgmt에 새 행 생기는지, project_no 형식 일치하는지.
### PR-B: 직접등록 통합폼 (G2 + G3)
- 주문관리 화면 "신규" 버튼이 통합 다이얼로그 띄움 → `IS_DIRECT_ORDER='Y'` + contract_item 다중 입력.
- G3(견적요청등록 라인 입력)도 같은 라인 입력 컴포넌트 재사용 가능 → 묶어서.
### PR-C: 결재·메일·PDF (G4 + G5 + G6)
- 분량 큼. RPS 결재 정책 결정(G11) 선행 필요.
- 단계: SMTP(G6) → PDF(G5) → 결재(G4) 순서로 사이즈 작은 것부터.
### 백로그
- G7~G11은 위 3개 끝난 뒤 평가.
## 5. 검증 체크리스트 (PR마다)
- [ ] 원본 endpoint와 이식 endpoint 매핑표 갱신 (이 문서 §1)
- [ ] 영향 받는 테이블의 BEFORE/AFTER row count 기록
- [ ] wace 운영 화면(waceplm.esgrin.com)과 동일 시나리오 비교 스크린샷
- [ ] `project_mgmt` 새 행이 89건 + N 으로 늘어나는지 (G1)
- [ ] `IS_DIRECT_ORDER='Y'` 행 신규 생성 확인 (G2)