영업관리 GAP 분석 문서 신설 + README 다음 작업 갱신

00-gap.md: wace_plm 원본 흐름 vs vexplor_rps 이식본 GAP 매트릭스. 다음 PR 우선순위(A: 수주확정→프로젝트 자동생성, B: 직접등록 통합폼, C: 결재·메일·PDF) 합의 문서.
README.md: §7 다음 작업 항목을 완료 처리하고 00-gap.md 우선 합의로 재정렬.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hjjeong
2026-05-08 14:48:24 +09:00
parent 2d9f30ebab
commit b7a6816ef2
2 changed files with 122 additions and 3 deletions
+118
View File
@@ -0,0 +1,118 @@
# 영업관리 이식 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 신설. 외부 amaranth SSO는 RPS 결재 모듈 결정 후 |
| 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 | 🟡 | 견적요청 → 견적작성 라인 자동 복제 UI | (원본은 사용자가 contract_item에서 수동 선택) | 미구현 — 견적 작성 시 매번 라인 재입력 | "이전 라인 복제" 버튼 + contract_item → estimate_template_item 일괄 복사 |
| G10 | 🟢 | 환율 마스터 + EXCHANGE_RATE 자동 변환 | `contractBase` SQL EST_TOTAL_AMOUNT_KRW 환산식 | 환산식만 있고 환율 마스터 미구축 | 환율 테이블 신설(또는 ECOS API 동기화) |
| G11 | 🟢 | 결재 모듈 (amaranth_approval / 자체) | 외부 amaranth + APPR_STATUS 라벨 | RPS 결재 정책 미정 | vexplor `approvalController` 매핑 vs `amaranth_approval` 도입 결정 |
## 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)
+4 -3
View File
@@ -141,9 +141,10 @@ import { useAuth } from "@/hooks/useAuth";
## 7. 다음 작업
1. **운영 DB 접속해서 누락 테이블 DDL 추출** (`estimate_template`, `estimate_template_item`, `sales_registration`)
2. [01-estimate.md](./01-estimate.md) 견적관리 상세 매핑 작성 → 코드 시작
3. 마스터 매핑 테이블 설계 (`legacy_id_map`)
1. ~~운영 DB DDL 추출~~ 완료 (2026-05-07)
2. ~~01~04 상세 매핑 + 1차 이식~~ 완료 (2026-05-08)
3. **[00-gap.md](./00-gap.md) 우선** — 원본 흐름 10단계 vs 이식본 GAP 매트릭스. 다음 PR(A: 수주확정→프로젝트 자동생성, B: 직접등록 통합폼, C: 결재·메일·PDF) 합의 문서.
4. PR-A부터 착수: `salesOrderMgmtService.updateStatus`에 project_mgmt 자동생성 + project_no 채번 로직 이식.
## 8. 공통 UX 규칙 (검색 폼 / 영업관리 4개 메뉴 동일 적용)