4007042311
[ECR(Engineering Change Request) 관리]
- ecr_mng / product_mgmt / part_mng 테이블 + seq_ecr_no 시퀀스
- 설변요청 코드(0000090) 자식 코드 시드 (설계오류/품질개선/원가절감/고객요청/법규대응/부품단종)
- API: 목록/상세/등록·수정(merge)/조치완료/삭제 + 옵션(작성자/제품/부품/공통코드)
- 화면: 필터바, 그리드, 등록·수정 모달, 상세 모달, 조치완료 모달
[고객 CS 관리]
- customer_cs_mng 테이블 (기존 customer_mng 거래처 마스터와 충돌 회피)
- 공통코드 5종 카테고리(0000200~0000204) 자동 시드: 관리유형/제품구분/담당자/조치유형/CS상태
- API: 목록/상세/등록·수정/삭제/대시보드(요약·상태별·관리유형별·제품구분별)
- 화면: 요약 카드 4종, 10종 필터바, 14컬럼 그리드, 등록·수정·상세 모달
[결재(APPROVAL) 시스템 — wace_plm ApprovalService 포팅]
- approval / route / inboxtask / approval_target / approval_kind / amaranth_approval 6개 테이블
- 라이프사이클: 상신(startApproval) → 결재함(listInbox) → 승인/반려(approveTask/rejectTask)
→ 다음 결재자 ready 자동 전달, 마지막이면 route+approval+target 일괄 complete
- target_type 별 도메인 status 동기화 훅 (ECR_MNG/CUSTOMER_MNG)
- API: /api/wace-approval/{inbox,start,inbox/:id/approve|reject,by-target,:id,:id/amaranth-link}
[Amaranth(Wehago) 전자결재 RestAPI 연계]
- 외부 커넥션 'Amaranth - 결재' 자동 시드 (auth_config: callerName/accessToken/hashKey/groupSeq/aesKey)
- amaranthApprovalClient: 자바 AmaranthApprovalApiClient 1:1 포팅
HMAC-SHA256 wehago-sign / AES-128-CBC empSeq 암호화 / HTTPS / JSON 파싱
- 6개 endpoint: 인증토큰/결재함/문서목록/문서상세/SSO URL/문서상신
- 하드코딩 0건 — DB 외부 커넥션의 인증 정보를 매 호출 시 로드
- 통신 검증 완료 (dummy empSeq 로 응답 정상 수신)
- 프록시 라우트 /api/amaranth-approval/{box,docs,docs/:id,sso,submit,auth-token}
[wace_plm 데이터 import]
- WacePlmDataImportService: source PG 클라이언트 → 우리 DB ON CONFLICT DO NOTHING idempotent
- 환경변수 WACE_PLM_DB_* 만 읽음 (운영 비번 코드 0건, 미설정 시 명시적 에러)
- /api/wace-import/{all,ecr,cs,masters,approval} (SUPER_ADMIN 전용)
[Bugfix]
- 배치 편집 conditional 매핑 silent drop 방지 — 평가 필드/규칙 누락 시
어느 컬럼이 어떤 이유로 빠졌는지 toast 로 명시 + 저장 차단
- 시드 topup 강화 — extra_auth_config(aesKey 등) 누락된 기존 레코드 자동 보강
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
/**
|
|
* Customer CS(고객 사후관리) API 클라이언트
|
|
*/
|
|
import { apiClient } from "./client";
|
|
|
|
export interface CsItem {
|
|
objid: string;
|
|
mng_number: string;
|
|
mng_type?: string;
|
|
mng_type_title?: string;
|
|
product_division?: string;
|
|
product_division_title?: string;
|
|
project_objid?: string;
|
|
reception_date?: string;
|
|
reception_date_title?: string;
|
|
event_location?: string;
|
|
action_date?: string;
|
|
action_date_title?: string;
|
|
customer_name?: string;
|
|
title: string;
|
|
performer?: string;
|
|
performer_title?: string;
|
|
writer?: string;
|
|
writer_title?: string;
|
|
analysis?: string;
|
|
measure?: string;
|
|
measure_type?: string;
|
|
measure_type_title?: string;
|
|
measure_amount?: string;
|
|
status?: string;
|
|
status_title?: string;
|
|
regdate_title?: string;
|
|
editdate_title?: string;
|
|
}
|
|
|
|
export interface CsListResponse {
|
|
success: boolean;
|
|
list: CsItem[];
|
|
pagination: { page: number; pageSize: number; total: number; totalPages: number };
|
|
summary: { sumMeasureAmount: number };
|
|
}
|
|
|
|
export interface CsListParams {
|
|
searchYear?: string;
|
|
mngType?: string;
|
|
productDivision?: string;
|
|
customerName?: string;
|
|
startReceptionDate?: string;
|
|
endReceptionDate?: string;
|
|
eventLocation?: string;
|
|
performer?: string;
|
|
startActionDate?: string;
|
|
endActionDate?: string;
|
|
measureType?: string;
|
|
status?: string;
|
|
page?: number;
|
|
pageSize?: number;
|
|
}
|
|
|
|
export interface CsCategories {
|
|
MNG_TYPE: string;
|
|
PRODUCT_DIVISION: string;
|
|
PERFORMER: string;
|
|
MEASURE_TYPE: string;
|
|
STATUS: string;
|
|
}
|
|
|
|
export const customerCsApi = {
|
|
list: async (params: CsListParams = {}): Promise<CsListResponse> =>
|
|
(await apiClient.get("/customer-cs", { params })).data,
|
|
|
|
detail: async (objId: string | number): Promise<CsItem> =>
|
|
(await apiClient.get(`/customer-cs/${objId}`)).data.data,
|
|
|
|
merge: async (payload: Partial<CsItem> & { objId?: string | number }) =>
|
|
(await apiClient.post("/customer-cs", payload)).data.data,
|
|
|
|
remove: async (objIds: string[]) =>
|
|
(await apiClient.delete("/customer-cs", { data: { objIds } })).data,
|
|
|
|
dashboard: async (searchYear?: string) =>
|
|
(await apiClient.get("/customer-cs/dashboard", { params: { searchYear } })).data.data as {
|
|
summary: { total_count: number; sum_amount: number; sum_paid_amount: number; sum_free_amount: number };
|
|
byStatus: Array<{ code_id: string; code_name: string; cnt: number }>;
|
|
byMngType: Array<{ code_id: string; code_name: string; cnt: number }>;
|
|
byProductDivision: Array<{ code_id: string; code_name: string; cnt: number }>;
|
|
},
|
|
|
|
categories: async (): Promise<CsCategories> =>
|
|
(await apiClient.get("/customer-cs/options/categories")).data.data,
|
|
|
|
codeOptions: async (parentCodeId: string): Promise<Array<{ value: string; label: string }>> =>
|
|
(await apiClient.get(`/customer-cs/options/codes/${parentCodeId}`)).data.data,
|
|
};
|