Files
wace_rps/docs/migration/sales/07-amaranth-approval-verify.md
T
hjjeong 905d5c0976 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>
2026-05-11 17:44:54 +09:00

8.0 KiB

영업관리 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)

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 호출

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 placeholder 토스트만 있음)
  • 결재 콜백 — amaranth가 우리 시스템에 결재 결과를 통보하는 webhook (운영에서는 폴링 또는 amaranth_approval 수동 갱신)

트러블슈팅 — Amaranth 운영 측 토큰 등록 (2026-05-11 확인)

dev 환경에서 wace 계정(emp_seq=379) + 코드/HMAC 서명 모두 정상이지만 amaranth 서버가 다음 메시지로 거부:

인증 토큰 발급 실패: API Proxy 호출 시 유효한 레디스 값이 존재하지 않습니다.

진단:

  • 우리 코드 흐름 정상 (getAuthTokenapi99u01A01 호출까지 도달)
  • 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 사원 동기화)가 잘 도는지 확인 → 다른 건 성공이면 결재 토큰만 별도 등록 필요 확정

코드 변경 없음 — 운영 협조로 해결되는 영역. 토큰 등록 완료되면 같은 흐름이 그대로 동작.