PR-D G9 수주복사 + G11 Amaranth 수주 결재상신 (wace 1:1)
[G9 수주복사 — wace copyEstimateAndOrderInfo 1:1]
- salesOrderMgmtService.copyOrder: 새 영업번호({YY}C-{NNNN}) 채번 +
contract_mgmt 23컬럼 INSERT-SELECT(contract_result='', is_direct_order='Y' 강제) +
contract_item + contract_item_serial 통째 복제
- POST /api/sales/order-mgmt/:id/copy 라우트
- 주문관리 그리드 "수주복사" 버튼 (Copy 아이콘) + handleCopyOrder
[G11 Amaranth 수주 결재상신 — wace ApprovalService.getAmaranthSsoUrl 1:1]
- chpark의 amaranthApprovalClient(HMAC-SHA256 + AES-128-CBC) 재사용
- amaranth_approval만 사용(자체 approval 미경유, wace 운영 패턴 동일)
- target_type='CONTRACT_ORDER', formId='1161', compSeq='1000'
- approKey 분기: 신규 / reject·delete·create는 새 approKey UPDATE / 그 외 재사용
- salesOrderMgmtService.startOrderApproval: user_info.emp_seq 조회 →
라인 0건 가드 → 매핑 분기 → SSO URL 발급 → INSERT/UPDATE → fullUrl 반환
- POST /api/sales/order-mgmt/:id/amaranth-approval 라우트
- 주문관리 그리드 "결재상신" 버튼 (Send 아이콘, sky-600) + handleAmaranthApproval
- getList SQL에 LEFT JOIN amaranth_approval AMR_ORDER 추가 +
order_appr_status(작성중/결재중/결재완료/반려 한글) + order_amaranth_status 노출
[DB 스키마]
- amaranth_approval.target_objid BIGINT → VARCHAR(80) (wace 운영 1:1)
· 출처: wace 매퍼 T.OBJID::VARCHAR = AMR_ORDER.TARGET_OBJID
· 사유: contract_mgmt.objid가 'CM-' prefix varchar라 bigint cast 불가
· 데이터 0건 무손실, ECR/CS는 bigint→varchar 자동 cast로 무영향
- approvalTableMigration.ts 동기화
[운영 배포 환경변수 — wace Constants 1:1 default 박힘]
- AMARANTH_OUT_PROCESS_CODE=RPSPLM_00001 (wace Constants.java:81)
- AMARANTH_FORM_ID_CONTRACT_ORDER=1161 (wace orderMgmtList.jsp:558)
- AMARANTH_COMP_SEQ=1000 (wace orderMgmtList.jsp:559)
- 3개 docker-compose(deploy/onpremise, docker/deploy, docker/prod) 모두
${VAR:-default} 형식으로 매핑 — 호스트 .env 미설정 시 default 동작
[검증]
- G9: BEGIN/ROLLBACK으로 26C-0800(라인 3건) 복사 시뮬레이션 — 헤더 23컬럼 1:1,
채번 26C-0803, 라인 3→3건 + seq 보존
- G11: 4단계 상태 라벨(create→inProcess→complete→reject) 모두 정상,
VARCHAR PK(CM-... prefix) JOIN도 정상
- 문서: docs/migration/sales/06-copy-order-verify.md, 07-amaranth-approval-verify.md
- GAP: G9 ✅, G10 ❌(영업 GAP 아님 — Admin 도메인), G11 ✅
[운영 트러블슈팅 노트 — 07-verify.md 트러블슈팅 섹션]
dev에서 amaranth 측이 "API Proxy 호출 시 유효한 레디스 값이 존재하지 않습니다"로
거부. 우리 코드는 정상 — 'Amaranth - 결재' accessToken을 amaranth 서버 측
Redis에 등록받아야 동작. chpark/RPS ERP 담당자 협조 영역(코드 변경 없음).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,14 +47,14 @@
|
||||
| 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 결재 모듈 결정 후 |
|
||||
| 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 | 🟡 | 견적요청 → 견적작성 라인 자동 복제 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` 도입 결정 |
|
||||
| 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). 백로그: 첨부파일 원챔버 업로드, 견적관리 결재(`CONTRACT_ESTIMATE` 동일 패턴) |
|
||||
|
||||
## 3. 코드/SQL 정합성 메모
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# 영업관리 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의 "한번에 한개의 수주만 복사 가능합니다." 분기는 불필요)
|
||||
@@ -0,0 +1,138 @@
|
||||
# 영업관리 G4/G11 — 수주 결재상신 (Amaranth 직행) 검증
|
||||
|
||||
> 작성: 2026-05-11 / 작성자: hjjeong
|
||||
> 목적: wace 영업관리 결재 흐름(외부 Amaranth SSO 직행)을 vexplor_rps 주문관리에 1:1 이식. chpark의 `amaranthApprovalClient`(자바 `AmaranthApprovalApiClient` 포팅) 재사용.
|
||||
|
||||
## 원본 출처
|
||||
- 프론트: `wace_plm/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp:132~175` (btnApproval 가드)
|
||||
- 프론트: 같은 파일 `:547~576` (`fn_openAmaranthApproval` → SSO URL 호출 → window.open)
|
||||
- 백엔드: `wace_plm/src/com/pms/service/ApprovalService.java:1782~1909` (`getAmaranthSsoUrl`)
|
||||
- 매퍼: `wace_plm/src/com/pms/salesmgmt/mapper/contractMgmt.xml:523~530, 661~663` (ORDER_APPR_STATUS 라벨 + LEFT JOIN AMARANTH_APPROVAL)
|
||||
|
||||
## 이식 위치
|
||||
- 백엔드 서비스: `backend-node/src/services/salesOrderMgmtService.ts:startOrderApproval`
|
||||
- 백엔드 컨트롤러: `backend-node/src/controllers/salesOrderMgmtController.ts:startApproval`
|
||||
- 라우트: `POST /api/sales/order-mgmt/:id/amaranth-approval`
|
||||
- 프론트 API: `frontend/lib/api/salesOrderMgmt.ts:startApproval`
|
||||
- 프론트 UI: `frontend/app/(main)/COMPANY_16/sales/order/page.tsx:handleAmaranthApproval` + "결재상신" 버튼 + `order_appr_status` 컬럼
|
||||
- 재사용: `backend-node/src/services/amaranthApprovalClient.ts:getSsoUrl` (chpark)
|
||||
|
||||
## DB 스키마 변경
|
||||
- `amaranth_approval.target_objid` BIGINT → **VARCHAR(80)** (wace 운영 패턴 1:1)
|
||||
- 출처: wace 매퍼 `T.OBJID::VARCHAR = AMR_ORDER.TARGET_OBJID`
|
||||
- 이유: vexplor_rps `contract_mgmt.objid`가 varchar(`CM-...` prefix 형식)라 bigint cast 불가
|
||||
- 영향: 해당 테이블 데이터 0건이라 무손실. ECR/CS는 bigint값을 varchar에 INSERT해도 자동 cast → 무영향
|
||||
- 마이그레이션 파일 `approvalTableMigration.ts`도 동기화
|
||||
|
||||
## 결재 정책 (wace 1:1)
|
||||
- target_type: `CONTRACT_ORDER` (영업관리 주문서)
|
||||
- formId: `1161` (운영 amaranth 양식 ID)
|
||||
- compSeq: `1000` (운영 회사 시퀀스)
|
||||
- mod: `W` (Write)
|
||||
- empSeq 출처: `user_info.emp_seq` (PersonBean.getEmpseq 동등) — 미설정 시 명시적 에러
|
||||
- approKey 분기:
|
||||
- 신규: `UB_` + Date.now().toString(36).toUpperCase()
|
||||
- 기존 reject/delete/create: 새 approKey + amaranth_approval UPDATE (재상신)
|
||||
- 기존 inProcess/complete: 기존 approKey 재사용 (프론트에서 차단되지만 백엔드 방어)
|
||||
|
||||
## 환경변수 (운영 배포 시 주입)
|
||||
| 변수 | 기본값 | 비고 |
|
||||
|---|---|---|
|
||||
| `AMARANTH_OUT_PROCESS_CODE_CONTRACT_ORDER` | (없음) | 수주 결재 전용 코드. 미설정 시 `AMARANTH_OUT_PROCESS_CODE` fallback |
|
||||
| `AMARANTH_FORM_ID_CONTRACT_ORDER` | `1161` | wace 운영값 |
|
||||
| `AMARANTH_COMP_SEQ` | `1000` | wace 운영값 |
|
||||
|
||||
> Amaranth 외부 커넥션의 인증 정보(`baseUrl`/`groupSeq`/`callerName`/`accessToken`/`hashKey`/`aesKey`)는 chpark이 'Amaranth - 결재' 외부 커넥션 시드로 자동 주입 (별도 환경변수 불필요).
|
||||
|
||||
## 가드 (프론트 + 백엔드 동시)
|
||||
| 조건 | 메시지 | 처리 |
|
||||
|---|---|---|
|
||||
| 행 미선택 | "결재상신할 행을 선택해주십시오." | 프론트 toast |
|
||||
| `has_order_data === 0` | "수주 품목을 먼저 등록해주세요." | 프론트 toast + 백엔드 400 |
|
||||
| `order_amaranth_status === 'inProcess'` | "결재 진행중인 건은 상신할 수 없습니다." | 프론트 toast |
|
||||
| `order_amaranth_status === 'complete'` | "결재 완료된 건은 상신할 수 없습니다." | 프론트 toast |
|
||||
| 사용자 emp_seq 미설정 | "empSeq 정보가 없습니다." | 백엔드 400 |
|
||||
| 원본 contract_mgmt 부재 | "주문서를 찾을 수 없습니다." | 백엔드 404 |
|
||||
| SSO API resultCode != 0 | "결재 연동 오류: ..." | 백엔드 502 |
|
||||
|
||||
## 검증 SQL (BEGIN/ROLLBACK)
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT INTO amaranth_approval
|
||||
(objid, target_objid, target_type, appro_key, status, form_id, comp_seq, emp_seq, writer, sso_url, regdate)
|
||||
VALUES (9999999999, '1256462102', 'CONTRACT_ORDER', 'UB_TEST', 'create', '1161', '1000', '999', 'test', 'http://test', NOW());
|
||||
|
||||
SELECT T.objid, T.contract_no,
|
||||
CASE WHEN AMR.status='complete' THEN '결재완료'
|
||||
WHEN AMR.status='inProcess' THEN '결재중'
|
||||
WHEN AMR.status='reject' THEN '반려'
|
||||
WHEN AMR.status='create' THEN '작성중'
|
||||
ELSE '' END AS order_appr_status
|
||||
FROM contract_mgmt T
|
||||
LEFT JOIN amaranth_approval AMR
|
||||
ON AMR.target_objid = T.objid AND AMR.target_type='CONTRACT_ORDER'
|
||||
WHERE T.objid='1256462102';
|
||||
ROLLBACK;
|
||||
```
|
||||
|
||||
### 검증 결과 (2026-05-11)
|
||||
| target_objid | status | order_appr_status (한글) |
|
||||
|---|---|---|
|
||||
| `1256462102` | create | 작성중 |
|
||||
| `1256462102` | inProcess | 결재중 |
|
||||
| `1256462102` | complete | 결재완료 |
|
||||
| `1256462102` | reject | 반려 |
|
||||
| `CM-1778464096341-756` (varchar PK) | inProcess | 결재중 ✓ |
|
||||
|
||||
- VARCHAR PK 호환 확인 — ALTER 효과 정상.
|
||||
- ROLLBACK 후 운영 데이터 영향 없음.
|
||||
|
||||
## API 호출
|
||||
```bash
|
||||
curl -X POST 'http://localhost:8080/api/sales/order-mgmt/<CONTRACT_OBJID>/amaranth-approval' \
|
||||
-H 'Authorization: Bearer <TOKEN>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"approvalTitle":"주문서 결재 - 26C-0800"}'
|
||||
|
||||
# 성공: {"success":true,"data":{"fullUrl":"https://...","approKey":"UB_...","status":"create"}}
|
||||
# 실패: {"success":false,"message":"empSeq 정보가 없습니다. ..."}
|
||||
```
|
||||
|
||||
## UI 동작
|
||||
1. 주문관리 그리드에서 행 1개 선택
|
||||
2. "결재상신" 버튼 클릭 (수주복사 옆, 하늘색 sky-600)
|
||||
3. 가드 통과 → 확인 다이얼로그: "결재상신 하시겠습니까?"
|
||||
4. 확인 → API 호출 → `window.open(fullUrl, "amaranthApproval", "width=1200,height=900,...")`
|
||||
5. 외부 Amaranth 결재 페이지에서 사용자가 양식 작성 + 상신
|
||||
6. 목록 새로고침 → "결재상태" 컬럼이 '작성중' → '결재중' → '결재완료' 순으로 변화
|
||||
|
||||
## 미구현 (백로그)
|
||||
- **첨부파일 원챔버 업로드** — wace `uploadOrderFilesToOneChamber` (영업관리 첨부 흐름 별도 작업 후 연동)
|
||||
- **견적 결재** (target_type=`CONTRACT_ESTIMATE`) — 같은 패턴, 견적관리 페이지에 추가만 하면 됨 (이번 PR 범위 외 — 현재 [estimate/page.tsx:474](../../../frontend/app/(main)/COMPANY_16/sales/estimate/page.tsx#L474) placeholder 토스트만 있음)
|
||||
- **결재 콜백** — amaranth가 우리 시스템에 결재 결과를 통보하는 webhook (운영에서는 폴링 또는 amaranth_approval 수동 갱신)
|
||||
|
||||
## 트러블슈팅 — Amaranth 운영 측 토큰 등록 (2026-05-11 확인)
|
||||
|
||||
dev 환경에서 wace 계정(emp_seq=379) + 코드/HMAC 서명 모두 정상이지만 amaranth 서버가 다음 메시지로 거부:
|
||||
|
||||
```
|
||||
인증 토큰 발급 실패: API Proxy 호출 시 유효한 레디스 값이 존재하지 않습니다.
|
||||
```
|
||||
|
||||
**진단**:
|
||||
- 우리 코드 흐름 정상 (`getAuthToken` → `api99u01A01` 호출까지 도달)
|
||||
- amaranth 서버가 `{resultCode:비-0, resultMsg:"...레디스..."}` 응답 → 토큰을 Redis 캐시에서 찾지 못함
|
||||
- 7개 amaranth 커넥션 모두 같은 callerName(`API_gcmsAmaranth40578`)/groupSeq(`gcmsAmaranth40578`) 공유, accessToken만 도메인별로 다름
|
||||
- 'Amaranth - 결재' 커넥션 시드 시점: 2026-05-08 12:16 (chpark 시드). 마지막 테스트: 2026-05-08 16:44
|
||||
|
||||
**가능한 원인 (운영 측 조치 필요)**:
|
||||
1. 'Amaranth - 결재' 토큰만 amaranth 측 Redis 캐시에 등록 안 됨 (cron 배치로 매일 호출되는 다른 7개 커넥션은 정상 동작 가능성)
|
||||
2. callerName이 wace_plm 운영과 공유되어 동시 사용 시 한쪽이 무효화
|
||||
3. 결재 전용 토큰의 별도 갱신 주기
|
||||
|
||||
**대응**:
|
||||
- chpark에게 5/8 시드 시 amaranth 운영 측에 결재 토큰 등록을 마저 요청했는지 확인
|
||||
- 또는 RPS ERP 담당자에게 결재 토큰 Redis 재등록 요청
|
||||
- 우회 검증: 다른 amaranth cron 배치(예: 매일 03:10 사원 동기화)가 잘 도는지 확인 → 다른 건 성공이면 결재 토큰만 별도 등록 필요 확정
|
||||
|
||||
**코드 변경 없음** — 운영 협조로 해결되는 영역. 토큰 등록 완료되면 같은 흐름이 그대로 동작.
|
||||
Reference in New Issue
Block a user