summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/login/login-form-shi.tsx2
-rw-r--r--components/login/login-form.tsx2
-rw-r--r--lib/shi-api/shi-api-utils.ts2
-rw-r--r--lib/soap/ecc/mapper/po-mapper.ts18
-rw-r--r--lib/soap/ecc/send/cancel-rfq.ts10
-rw-r--r--lib/soap/ecc/send/create-po.ts7
-rw-r--r--lib/soap/ecc/send/pcr-confirm.ts7
-rw-r--r--lib/soap/ecc/send/rfq-info.ts7
-rw-r--r--lib/soap/mdg/mapper/project-mapper.ts22
-rw-r--r--lib/soap/sender.ts20
10 files changed, 65 insertions, 32 deletions
diff --git a/components/login/login-form-shi.tsx b/components/login/login-form-shi.tsx
index 1a554cbc..0be6709a 100644
--- a/components/login/login-form-shi.tsx
+++ b/components/login/login-form-shi.tsx
@@ -130,7 +130,7 @@ export function LoginFormSHI({
}
} else {
// callbackUrl이 없으면 기본 대시보드로 리다이렉트
- router.push(`/${lng}/evcp/report`);
+ router.push(`/${lng}/evcp/dashboard`);
}
} else {
diff --git a/components/login/login-form.tsx b/components/login/login-form.tsx
index 6aca755f..67d9f8ac 100644
--- a/components/login/login-form.tsx
+++ b/components/login/login-form.tsx
@@ -85,7 +85,7 @@ export function LoginForm() {
}
} else {
// callbackUrl이 없으면 기본 대시보드로 리다이렉트
- router.push(`/${lng}/partners/report`);
+ router.push(`/${lng}/partners/dashboard`);
}
}
}, [status, session, router, lng, searchParams]);
diff --git a/lib/shi-api/shi-api-utils.ts b/lib/shi-api/shi-api-utils.ts
index 7ea582f6..955cddd9 100644
--- a/lib/shi-api/shi-api-utils.ts
+++ b/lib/shi-api/shi-api-utils.ts
@@ -133,7 +133,7 @@ export const getAllNonsapUser = async () => {
await db.insert(users)
.values(mappedChunk)
.onConflictDoUpdate({
- target: users.nonsapUserId,
+ target: users.email,
set: {
name: users.name,
employeeNumber: users.employeeNumber,
diff --git a/lib/soap/ecc/mapper/po-mapper.ts b/lib/soap/ecc/mapper/po-mapper.ts
index a6363a87..6e282b98 100644
--- a/lib/soap/ecc/mapper/po-mapper.ts
+++ b/lib/soap/ecc/mapper/po-mapper.ts
@@ -98,10 +98,20 @@ export async function mapECCPOHeaderToBusiness(
}
};
+ // projectId와 vendorId 필수 체크
+ if (!projectId) {
+ debugError('프로젝트를 찾을 수 없어 매핑을 건너뜁니다', { pspid: eccHeader.PSPID });
+ throw new Error(`프로젝트를 찾을 수 없습니다: PSPID=${eccHeader.PSPID}`);
+ }
+ if (!vendorId) {
+ debugError('벤더를 찾을 수 없어 매핑을 건너뜁니다', { lifnr: eccHeader.LIFNR });
+ throw new Error(`벤더를 찾을 수 없습니다: LIFNR=${eccHeader.LIFNR}`);
+ }
+
// 매핑
const mappedData: ContractData = {
- projectId: projectId || 1, // TODO: 기본값 설정, 실제로는 유효한 projectId 필요
- vendorId: vendorId || 1, // TODO: 기본값 설정, 실제로는 유효한 vendorId 필요
+ projectId,
+ vendorId,
contractNo: eccHeader.EBELN || '',
contractName: eccHeader.ZTITLE || eccHeader.EBELN || '',
status: eccHeader.ZPO_CNFM_STAT || 'ACTIVE',
@@ -249,14 +259,14 @@ export async function mapECCPODetailToBusiness(
const calculatedTaxAmount = (unitPriceNum * quantity * taxRateNum) / 100;
taxAmount = calculatedTaxAmount.toString();
} catch (error) {
- debugError('세액 계산 오류', { unitPrice, taxRate, quantity, error });
+ debugError('세액(taxAmount) 계산 오류((unitPriceNum * quantity * taxRateNum) / 100)', { unitPrice, taxRate, quantity, error });
}
}
// 매핑
const mappedData: ContractItemData = {
contractId,
- itemId: itemId || 1, // TODO: 기본값 설정, 실제로는 유효한 itemId 필요
+ itemId: itemId!, // 아이템이 없으면 자동 생성되므로 null이 될 수 없음
description: eccDetail.MAKTX || null,
quantity,
unitPrice,
diff --git a/lib/soap/ecc/send/cancel-rfq.ts b/lib/soap/ecc/send/cancel-rfq.ts
index fcddddf8..b26ca38b 100644
--- a/lib/soap/ecc/send/cancel-rfq.ts
+++ b/lib/soap/ecc/send/cancel-rfq.ts
@@ -2,8 +2,8 @@
import { sendSoapXml, type SoapSendConfig, type SoapLogInfo, type SoapSendResult } from "@/lib/soap/sender";
-// ECC RFQ 취소 엔드포인트
-const ECC_CANCEL_RFQ_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3016_SO";
+// ECC RFQ 취소 엔드포인트 (WSDL에 명시된 P2038_D 사용)
+const ECC_CANCEL_RFQ_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3016_SO&QualityOfService=ExactlyOnce";
// RFQ 취소 요청 데이터 타입
export interface CancelRFQRequest {
@@ -70,7 +70,8 @@ async function sendCancelRFQToECC(rfqData: CancelRFQRequest): Promise<SoapSendRe
soapAction: 'http://sap.com/xi/WebService/soap1.1',
timeout: 30000, // RFQ 취소는 30초 타임아웃
retryCount: 3,
- retryDelay: 1000
+ retryDelay: 1000,
+ namespace: 'http://shi.samsung.co.kr/P2_MM/MMM' // ECC MM 모듈 네임스페이스
};
// 로그 정보
@@ -83,6 +84,9 @@ async function sendCancelRFQToECC(rfqData: CancelRFQRequest): Promise<SoapSendRe
const rfqNumbers = rfqData.T_ANFNR.map(item => item.ANFNR).join(', ');
console.log(`📤 RFQ 취소 요청 전송 시작 - RFQ Numbers: ${rfqNumbers}`);
console.log(`🔍 취소 대상 RFQ ${rfqData.T_ANFNR.length}개`);
+ console.log(`🌐 엔드포인트: ${ECC_CANCEL_RFQ_ENDPOINT}`);
+ console.log(`📋 네임스페이스: ${config.namespace}`);
+ console.log(`🔐 SOAPAction: ${config.soapAction}`);
// SOAP XML 전송
const result = await sendSoapXml(config, logInfo);
diff --git a/lib/soap/ecc/send/create-po.ts b/lib/soap/ecc/send/create-po.ts
index 3bd28057..d44f091b 100644
--- a/lib/soap/ecc/send/create-po.ts
+++ b/lib/soap/ecc/send/create-po.ts
@@ -3,8 +3,8 @@
import { sendSoapXml, type SoapSendConfig, type SoapLogInfo, type SoapSendResult } from "@/lib/soap/sender";
import { getCurrentSAPDate, getCurrentSAPTime } from "@/lib/soap/utils";
-// ECC PO 생성 엔드포인트
-const ECC_PO_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3015_SO";
+// ECC PO 생성 엔드포인트 (WSDL에 명시된 P2038_D 사용)
+const ECC_PO_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3015_SO&QualityOfService=ExactlyOnce";
// PO 헤더 데이터 타입
export interface POHeaderData {
@@ -154,7 +154,8 @@ async function sendPOToECC(poData: POCreateRequest): Promise<SoapSendResult> {
soapAction: 'http://sap.com/xi/WebService/soap1.1',
timeout: 60000, // PO 생성은 60초 타임아웃
retryCount: 3,
- retryDelay: 2000
+ retryDelay: 2000,
+ namespace: 'http://shi.samsung.co.kr/P2_MM/MMM' // ECC MM 모듈 네임스페이스
};
// 로그 정보
diff --git a/lib/soap/ecc/send/pcr-confirm.ts b/lib/soap/ecc/send/pcr-confirm.ts
index 7ac2d931..46d1a909 100644
--- a/lib/soap/ecc/send/pcr-confirm.ts
+++ b/lib/soap/ecc/send/pcr-confirm.ts
@@ -3,8 +3,8 @@
import { sendSoapXml, type SoapSendConfig, type SoapLogInfo, type SoapSendResult } from "@/lib/soap/sender";
-// ECC PCR 확인 엔드포인트
-const ECC_PCR_CONFIRM_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3019_SO";
+// ECC PCR 확인 엔드포인트 (WSDL에 명시된 P2038_D 사용)
+const ECC_PCR_CONFIRM_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3019_SO&QualityOfService=ExactlyOnce";
// PCR 확인 요청 데이터 타입
export interface PCRConfirmRequest {
@@ -167,7 +167,8 @@ async function sendPCRConfirmToECC(pcrData: PCRConfirmRequest): Promise<SoapSend
soapAction: 'http://sap.com/xi/WebService/soap1.1',
timeout: 30000, // PCR 확인은 30초 타임아웃
retryCount: 3,
- retryDelay: 1000
+ retryDelay: 1000,
+ namespace: 'http://shi.samsung.co.kr/P2_MM/MMM' // ECC MM 모듈 네임스페이스
};
// 로그 정보
diff --git a/lib/soap/ecc/send/rfq-info.ts b/lib/soap/ecc/send/rfq-info.ts
index 43fe821f..d313a74b 100644
--- a/lib/soap/ecc/send/rfq-info.ts
+++ b/lib/soap/ecc/send/rfq-info.ts
@@ -19,8 +19,8 @@
import { sendSoapXml, type SoapSendConfig, type SoapLogInfo, type SoapSendResult } from "@/lib/soap/sender";
import { getCurrentSAPDate } from "@/lib/soap/utils";
-// ECC RFQ 정보 전송 엔드포인트
-const ECC_RFQ_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3014_SO";
+// ECC RFQ 정보 전송 엔드포인트 (WSDL에 명시된 P2038_D 사용)
+const ECC_RFQ_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_D&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3014_SO&QualityOfService=ExactlyOnce";
// RFQ 헤더 데이터 타입
export interface RFQHeaderData {
@@ -145,7 +145,8 @@ async function sendRFQToECC(rfqData: RFQInfoRequest): Promise<SoapSendResult> {
soapAction: 'http://sap.com/xi/WebService/soap1.1',
timeout: 60000, // RFQ 정보 전송은 60초 타임아웃
retryCount: 3,
- retryDelay: 2000
+ retryDelay: 2000,
+ namespace: 'http://shi.samsung.co.kr/P2_MM/MMM' // ECC MM 모듈 네임스페이스
};
// 로그 정보
diff --git a/lib/soap/mdg/mapper/project-mapper.ts b/lib/soap/mdg/mapper/project-mapper.ts
index 112dd4f5..6de2b38a 100644
--- a/lib/soap/mdg/mapper/project-mapper.ts
+++ b/lib/soap/mdg/mapper/project-mapper.ts
@@ -76,10 +76,9 @@ export async function mapAndSaveMDGProjectData(
/**
* MDG 프로젝트 데이터를 비즈니스 테이블 projects 구조로 변환
- * TODO: 실제 매핑 로직은 사용자가 추가할 예정
*
id: serial("id").primaryKey(), << 자동
- code: varchar("code", { length: 50 }).notNull(), <<
+ code: varchar("code", { length: 50 }).notNull(),
name: text("name").notNull(),
type: varchar("type", { length: 20 }).default("ship").notNull(),
pspid: char('pspid', { length: 24 }).unique(), // 프로젝트ID (ECC), TODO: 매핑 필요
@@ -94,12 +93,15 @@ function mapMDGToProject(mdgProject: MDGProjectData): ProjectData | null {
return null;
}
- // TODO: 사용자가 실제 매핑 로직을 추가할 예정
// 현재는 기본 구조만 제공
const mappedProject: ProjectData = {
code: mdgProject.PROJ_NO || '',
- name: mdgProject.PROJ_NM || mdgProject.PROJ_NO || '',
- type: 'ship', // 기본값, 필요시 매핑 로직 추가
+ name: mdgProject.PROJ_DSC || '',
+ type: checkProjectType(mdgProject.TYPE || ''), // 기본값, 필요시 매핑 로직 추가
+ // type 매핑 방법: TYPE 네임스페이스에서 null 값을 수신한 경우 조선(ship)이고, 그 외는 해양(plant)이다.
+ // 수신할 수 있는 경우의 수는 H, T, H/T, null 인데, 필요시 hull / top 구분 처리하자. 현재는 조선/해양 구분만 필요하고, 기존 코드들도 plant 값인지 ship 값인지만 따지고 있다.
+ // 의미상으로는 H = 해양 Hull, T = 해양 Top, H/T = 해양 Hull과 Top 둘 다 하는 프로젝트 인데, 일단은 조선/해양 구분만 ship/plant 값을 넣어주는 것으로 하자.
+ // TODO: pspid를 구해야 하는데 mdg에서 안주는 것 같음. 나프로에게 문의함.
pspid: mdgProject.PROJ_NO || null, // ECC 프로젝트 ID와 연결
// id, createdAt, updatedAt는 자동 생성
};
@@ -167,3 +169,13 @@ async function saveProjectsToDatabase(mappedProjects: ProjectData[]): Promise<vo
throw error;
}
}
+
+// === Utility Functions ===
+
+function checkProjectType(type: string) {
+ if (type == 'H' || type == 'T' || type == 'H/T') {
+ return 'plant';
+ } else {
+ return 'ship';
+ }
+} \ No newline at end of file
diff --git a/lib/soap/sender.ts b/lib/soap/sender.ts
index 580d0c5a..5a61462e 100644
--- a/lib/soap/sender.ts
+++ b/lib/soap/sender.ts
@@ -12,11 +12,12 @@ export interface SoapAuthConfig {
// SOAP 전송 설정 타입
export interface SoapSendConfig {
endpoint: string;
- envelope: Record<string, any>;
+ envelope: Record<string, unknown>;
soapAction?: string;
timeout?: number;
retryCount?: number;
retryDelay?: number;
+ namespace?: string; // 네임스페이스를 동적으로 설정할 수 있도록 추가
}
// 로깅 정보 타입
@@ -64,8 +65,8 @@ function createXmlBuilder() {
// SOAP Envelope 생성
function createSoapEnvelope(
namespace: string,
- bodyContent: Record<string, any>
-): Record<string, any> {
+ bodyContent: Record<string, unknown>
+): Record<string, unknown> {
return {
'soap:Envelope': {
'@_xmlns:soap': 'http://schemas.xmlsoap.org/soap/envelope/',
@@ -77,7 +78,7 @@ function createSoapEnvelope(
// XML 생성
export async function generateSoapXml(
- envelope: Record<string, any>,
+ envelope: Record<string, unknown>,
xmlDeclaration: string = '<?xml version="1.0" encoding="UTF-8"?>\n'
): Promise<string> {
const builder = createXmlBuilder();
@@ -95,9 +96,10 @@ export async function sendSoapXml(
// 인증 정보 설정 (기본값 사용)
const authConfig = auth || getDefaultAuth();
- // XML 생성
+ // XML 생성 (네임스페이스를 동적으로 설정)
+ const namespace = config.namespace || 'http://shi.samsung.co.kr/P2_MD/MDZ';
const soapEnvelope = createSoapEnvelope(
- 'http://shi.samsung.co.kr/P2_MD/MDZ',
+ namespace,
config.envelope
);
@@ -249,7 +251,7 @@ export async function sendSoapXmlWithRetry(
// 간단한 SOAP 전송 함수 (기본 설정 사용)
export async function sendSimpleSoapXml(
endpoint: string,
- bodyContent: Record<string, any>,
+ bodyContent: Record<string, unknown>,
logInfo: SoapLogInfo,
options?: {
namespace?: string;
@@ -263,6 +265,7 @@ export async function sendSimpleSoapXml(
envelope: bodyContent,
soapAction: options?.soapAction,
timeout: options?.timeout || 30000, // 기본 30초
+ namespace: options?.namespace, // 네임스페이스 옵션 추가
};
const auth = options?.auth || getDefaultAuth();
@@ -272,7 +275,7 @@ export async function sendSimpleSoapXml(
// MDG 전용 SOAP 전송 함수 (기존 action.ts와 호환)
export async function sendMdgSoapXml(
- bodyContent: Record<string, any>,
+ bodyContent: Record<string, unknown>,
logInfo: SoapLogInfo,
auth?: SoapAuthConfig
): Promise<SoapSendResult> {
@@ -281,6 +284,7 @@ export async function sendMdgSoapXml(
envelope: bodyContent,
soapAction: 'http://sap.com/xi/WebService/soap1.1',
timeout: 60000, // MDG는 60초 타임아웃
+ namespace: 'http://shi.samsung.co.kr/P2_MD/MDZ', // MDG 전용 네임스페이스 명시
};
return await sendSoapXml(config, logInfo, auth);