summaryrefslogtreecommitdiff
path: root/lib/soap/mdg
diff options
context:
space:
mode:
Diffstat (limited to 'lib/soap/mdg')
-rw-r--r--lib/soap/mdg/send/vendor-master/action.ts203
1 files changed, 124 insertions, 79 deletions
diff --git a/lib/soap/mdg/send/vendor-master/action.ts b/lib/soap/mdg/send/vendor-master/action.ts
index a7b4d8a4..8bb2f633 100644
--- a/lib/soap/mdg/send/vendor-master/action.ts
+++ b/lib/soap/mdg/send/vendor-master/action.ts
@@ -1,7 +1,7 @@
'use server'
import db from "@/db/db";
-import {
+import {
VENDOR_MASTER_BP_HEADER,
VENDOR_MASTER_BP_HEADER_ADDRESS,
VENDOR_MASTER_BP_HEADER_ADDRESS_AD_EMAIL,
@@ -17,7 +17,7 @@ import {
VENDOR_MASTER_BP_HEADER_BP_VENGEN_BP_PORG_ZVPFN
} from "@/db/schema/MDG/mdg";
import { eq, sql, desc } from "drizzle-orm";
-import { withSoapLogging } from "@/lib/soap/utils";
+import { withSoapLogging } from "../../../utils";
import { XMLBuilder } from 'fast-xml-parser';
import { CSV_FIELDS } from './csv-fields';
@@ -34,7 +34,7 @@ const MDG_ENDPOINT_URL = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap
function generateSAPXICompatibleXML(supplierMaster: Record<string, string>): string {
// XML 선언을 별도로 처리
const xmlDeclaration = '<?xml version="1.0" encoding="UTF-8"?>\n';
-
+
// SOAP Envelope 구조 정의
const soapEnvelope = {
'soap:Envelope': {
@@ -62,14 +62,14 @@ function generateSAPXICompatibleXML(supplierMaster: Record<string, string>): str
tagValueProcessor: (name, val) => val, // 값 처리기
attributeValueProcessor: (name, val) => val // 속성 처리기
});
-
+
const xmlBody = builder.build(soapEnvelope);
-
+
// XML 선언과 Body 결합
const completeXML = xmlDeclaration + xmlBody;
-
+
console.log('🔍 생성된 XML (전체):', completeXML);
-
+
return completeXML;
}
@@ -100,10 +100,10 @@ async function sendXMLToMDG(xmlData: string): Promise<{
} else {
console.warn('⚠️ MDG SOAP 인증 정보가 환경변수에 설정되지 않았습니다.');
}
-
+
console.log('📤 MDG 전송 시작');
console.log('🔍 전송 XML (첫 500자):', xmlData.substring(0, 500));
-
+
const res = await fetch(MDG_ENDPOINT_URL, {
method: 'POST',
headers,
@@ -111,7 +111,7 @@ async function sendXMLToMDG(xmlData: string): Promise<{
});
const text = await res.text();
-
+
console.log('📥 MDG 응답 수신:', res.status, res.statusText);
console.log('🔍 응답 XML (첫 500자):', text.substring(0, 500));
@@ -150,11 +150,11 @@ async function fetchVendorData(vendorCode: string) {
.from(VENDOR_MASTER_BP_HEADER)
.where(eq(VENDOR_MASTER_BP_HEADER.VNDRCD, vendorCode))
.limit(1);
-
+
if (!vendorHeader) {
return null;
}
-
+
const [
addresses,
adEmails,
@@ -182,7 +182,7 @@ async function fetchVendorData(vendorCode: string) {
db.select().from(VENDOR_MASTER_BP_HEADER_BP_VENGEN_BP_PORG).where(eq(VENDOR_MASTER_BP_HEADER_BP_VENGEN_BP_PORG.VNDRCD, vendorCode)),
db.select().from(VENDOR_MASTER_BP_HEADER_BP_VENGEN_BP_PORG_ZVPFN).where(eq(VENDOR_MASTER_BP_HEADER_BP_VENGEN_BP_PORG_ZVPFN.VNDRCD, vendorCode))
]);
-
+
return {
vendorHeader,
addresses,
@@ -198,7 +198,7 @@ async function fetchVendorData(vendorCode: string) {
bpPorgs,
zvpfns
};
-
+
} catch (error) {
console.error(`VENDOR ${vendorCode} 데이터 조회 실패:`, error);
throw error;
@@ -259,15 +259,15 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
}> {
try {
console.log(`🚀 VENDOR_MASTER 송신 시작: ${vendorCodes.length}개 벤더`);
-
+
const results: Array<{ vendorCode: string; success: boolean; error?: string }> = [];
-
+
for (const vendorCode of vendorCodes) {
try {
console.log(`📤 VENDOR ${vendorCode} 데이터 조회 중...`);
-
+
const vendorData = await fetchVendorData(vendorCode);
-
+
if (!vendorData) {
results.push({
vendorCode,
@@ -276,13 +276,13 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
});
continue;
}
-
+
const supplierMaster = buildSupplierMasterData(vendorData);
console.log(`📄 VENDOR ${vendorCode} 데이터 생성 완료`);
-
+
const generatedXML = generateSAPXICompatibleXML(supplierMaster);
const result = await sendXMLToMDG(generatedXML);
-
+
if (result.success) {
console.log(`✅ VENDOR ${vendorCode} MDG 전송 성공`);
results.push({
@@ -297,7 +297,7 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
error: result.message
});
}
-
+
} catch (error) {
console.error(`❌ VENDOR ${vendorCode} 전송 실패:`, error);
results.push({
@@ -307,18 +307,18 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
});
}
}
-
+
const successCount = results.filter(r => r.success).length;
const failCount = results.length - successCount;
-
+
console.log(`🎉 VENDOR_MASTER 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`);
-
+
return {
success: failCount === 0,
message: `전송 완료: 성공 ${successCount}개, 실패 ${failCount}개`,
results
};
-
+
} catch (error) {
console.error('❌ VENDOR_MASTER 송신 중 전체 오류 발생:', error);
return {
@@ -328,6 +328,39 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
}
}
+// 필수 필드 검증 함수
+function validateMandatoryFields(formData: Record<string, string>): {
+ isValid: boolean;
+ missingFields: string[];
+ errorMessage: string;
+} {
+ const missingFields: string[] = [];
+
+ // CSV_FIELDS에서 mandatory가 true인 필드들만 필수 필드로 체크
+ CSV_FIELDS.forEach(field => {
+ if (field.mandatory) {
+ const value = formData[field.field];
+ if (!value || value.trim() === '') {
+ missingFields.push(field.field);
+ }
+ }
+ });
+
+ if (missingFields.length > 0) {
+ return {
+ isValid: false,
+ missingFields,
+ errorMessage: `필수 필드가 누락되었습니다: ${missingFields.join(', ')}`
+ };
+ }
+
+ return {
+ isValid: true,
+ missingFields: [],
+ errorMessage: ''
+ };
+}
+
// 테스트용 폼 데이터 송신 함수
export async function sendTestVendorDataToMDG(formData: Record<string, string>): Promise<{
success: boolean;
@@ -337,7 +370,19 @@ export async function sendTestVendorDataToMDG(formData: Record<string, string>):
}> {
try {
console.log('🚀 테스트용 VENDOR 데이터 송신 시작');
-
+
+ // 필수 필드 검증
+ const validation = validateMandatoryFields(formData);
+ if (!validation.isValid) {
+ console.error('❌ 필수 필드 누락:', validation.missingFields);
+ return {
+ success: false,
+ message: validation.errorMessage,
+ responseData: undefined,
+ generatedXML: undefined
+ };
+ }
+
const seen = new Set<string>();
const uniqueFields = CSV_FIELDS.filter(f => {
if (seen.has(f.field)) return false;
@@ -351,18 +396,18 @@ export async function sendTestVendorDataToMDG(formData: Record<string, string>):
});
const generatedXML = generateSAPXICompatibleXML(supplierMaster);
-
+
console.log('📄 SAP XI 호환 XML 생성 완료');
-
+
const result = await sendXMLToMDG(generatedXML);
-
+
return {
success: result.success,
message: result.success ? '테스트 송신이 완료되었습니다.' : result.message,
responseData: result.responseText,
generatedXML
};
-
+
} catch (error) {
console.error('❌ 테스트 송신 실패:', error);
return {
@@ -387,44 +432,44 @@ export async function sendAllVendorsToMDG() {
const vendors = await db
.select({ VNDRCD: VENDOR_MASTER_BP_HEADER.VNDRCD })
.from(VENDOR_MASTER_BP_HEADER);
-
+
const vendorCodes = vendors.map(v => v.VNDRCD);
-
+
if (vendorCodes.length === 0) {
return {
success: false,
message: '송신할 VENDOR 데이터가 없습니다.'
};
}
-
+
console.log(`⚠️ 전체 VENDOR 송신 요청: ${vendorCodes.length}개`);
-
+
const batchSize = 10;
const results: Array<{ vendorCode: string; success: boolean; error?: string }> = [];
-
+
for (let i = 0; i < vendorCodes.length; i += batchSize) {
const batch = vendorCodes.slice(i, i + batchSize);
console.log(`📦 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`);
-
+
const batchResult = await sendVendorMasterToMDG(batch);
if (batchResult.results) {
results.push(...batchResult.results);
}
-
+
if (i + batchSize < vendorCodes.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
-
+
const successCount = results.filter(r => r.success).length;
const failCount = results.length - successCount;
-
+
return {
success: failCount === 0,
message: `전체 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`,
results
};
-
+
} catch (error) {
console.error('전체 VENDOR 송신 중 오류:', error);
return {
@@ -442,9 +487,9 @@ export async function sendModifiedVendorsToMDG(): Promise<{
}> {
try {
console.log('🔍 수정된 VENDOR 데이터 조회 중...');
-
+
const modifiedVendors = await db
- .select({
+ .select({
VNDRCD: VENDOR_MASTER_BP_HEADER.VNDRCD,
createdAt: VENDOR_MASTER_BP_HEADER.createdAt,
updatedAt: VENDOR_MASTER_BP_HEADER.updatedAt
@@ -453,9 +498,9 @@ export async function sendModifiedVendorsToMDG(): Promise<{
.where(
sql`EXTRACT(EPOCH FROM ${VENDOR_MASTER_BP_HEADER.updatedAt}) - EXTRACT(EPOCH FROM ${VENDOR_MASTER_BP_HEADER.createdAt}) > 1`
);
-
+
const vendorCodes = modifiedVendors.map(v => v.VNDRCD);
-
+
if (vendorCodes.length === 0) {
console.log('📝 수정된 VENDOR 데이터가 없습니다.');
return {
@@ -463,37 +508,37 @@ export async function sendModifiedVendorsToMDG(): Promise<{
message: '수정된 VENDOR 데이터가 없습니다.'
};
}
-
+
console.log(`📋 수정된 VENDOR ${vendorCodes.length}개 발견:`, vendorCodes);
-
+
const batchSize = 10;
const results: Array<{ vendorCode: string; success: boolean; error?: string }> = [];
-
+
for (let i = 0; i < vendorCodes.length; i += batchSize) {
const batch = vendorCodes.slice(i, i + batchSize);
console.log(`📦 수정 데이터 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`);
-
+
const batchResult = await sendVendorMasterToMDG(batch);
if (batchResult.results) {
results.push(...batchResult.results);
}
-
+
if (i + batchSize < vendorCodes.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
-
+
const successCount = results.filter(r => r.success).length;
const failCount = results.length - successCount;
-
+
console.log(`🎯 수정된 VENDOR 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`);
-
+
return {
success: failCount === 0,
message: `수정된 VENDOR 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`,
results
};
-
+
} catch (error) {
console.error('❌ 수정된 VENDOR 송신 중 오류:', error);
return {
@@ -505,7 +550,7 @@ export async function sendModifiedVendorsToMDG(): Promise<{
// 테스트용 N건 송신
export async function sendNVendorsToMDG(
- count: number,
+ count: number,
startFrom: number = 0
): Promise<{
success: boolean;
@@ -519,35 +564,35 @@ export async function sendNVendorsToMDG(
message: '송신할 건수는 1 이상이어야 합니다.'
};
}
-
+
console.log(`🧪 테스트용 VENDOR 송신: ${count}건 (${startFrom}번째부터)`);
-
+
const vendors = await db
.select({ VNDRCD: VENDOR_MASTER_BP_HEADER.VNDRCD })
.from(VENDOR_MASTER_BP_HEADER)
.limit(count)
.offset(startFrom);
-
+
const vendorCodes = vendors.map(v => v.VNDRCD);
-
+
if (vendorCodes.length === 0) {
return {
success: false,
message: `${startFrom}번째부터 ${count}건의 VENDOR 데이터가 없습니다.`
};
}
-
+
console.log(`📋 테스트 대상 VENDOR ${vendorCodes.length}개:`, vendorCodes);
-
+
const result = await sendVendorMasterToMDG(vendorCodes);
-
+
console.log(`🧪 테스트 송신 완료: ${vendorCodes.length}개 처리`);
-
+
return {
...result,
message: `테스트 송신 완료 (${vendorCodes.length}개): ${result.message}`
};
-
+
} catch (error) {
console.error('❌ 테스트 송신 중 오류:', error);
return {
@@ -567,9 +612,9 @@ export async function sendRecentModifiedVendorsToMDG(
}> {
try {
console.log(`🕒 최근 수정된 VENDOR ${count}건 조회 중...`);
-
+
const recentVendors = await db
- .select({
+ .select({
VNDRCD: VENDOR_MASTER_BP_HEADER.VNDRCD,
updatedAt: VENDOR_MASTER_BP_HEADER.updatedAt
})
@@ -579,28 +624,28 @@ export async function sendRecentModifiedVendorsToMDG(
)
.orderBy(desc(VENDOR_MASTER_BP_HEADER.updatedAt))
.limit(count);
-
+
const vendorCodes = recentVendors.map(v => v.VNDRCD);
-
+
if (vendorCodes.length === 0) {
return {
success: true,
message: '최근 수정된 VENDOR 데이터가 없습니다.'
};
}
-
- console.log(`📋 최근 수정된 VENDOR ${vendorCodes.length}개:`,
+
+ console.log(`📋 최근 수정된 VENDOR ${vendorCodes.length}개:`,
recentVendors.map(v => `${v.VNDRCD}(${v.updatedAt?.toISOString()})`));
-
+
const result = await sendVendorMasterToMDG(vendorCodes);
-
+
console.log(`🕒 최근 수정 데이터 송신 완료`);
-
+
return {
...result,
message: `최근 수정된 ${vendorCodes.length}개 송신 완료: ${result.message}`
};
-
+
} catch (error) {
console.error('❌ 최근 수정 데이터 송신 중 오류:', error);
return {
@@ -621,14 +666,14 @@ export async function getVendorSendStatistics(): Promise<{
const [totalResult] = await db
.select({ count: sql<number>`count(*)` })
.from(VENDOR_MASTER_BP_HEADER);
-
+
const [modifiedResult] = await db
.select({ count: sql<number>`count(*)` })
.from(VENDOR_MASTER_BP_HEADER)
.where(
sql`EXTRACT(EPOCH FROM ${VENDOR_MASTER_BP_HEADER.updatedAt}) - EXTRACT(EPOCH FROM ${VENDOR_MASTER_BP_HEADER.createdAt}) > 1`
);
-
+
const [lastModifiedResult] = await db
.select({ updatedAt: VENDOR_MASTER_BP_HEADER.updatedAt })
.from(VENDOR_MASTER_BP_HEADER)
@@ -637,7 +682,7 @@ export async function getVendorSendStatistics(): Promise<{
)
.orderBy(desc(VENDOR_MASTER_BP_HEADER.updatedAt))
.limit(1);
-
+
const [oldestUnmodifiedResult] = await db
.select({ createdAt: VENDOR_MASTER_BP_HEADER.createdAt })
.from(VENDOR_MASTER_BP_HEADER)
@@ -646,14 +691,14 @@ export async function getVendorSendStatistics(): Promise<{
)
.orderBy(VENDOR_MASTER_BP_HEADER.createdAt)
.limit(1);
-
+
return {
total: totalResult.count,
modified: modifiedResult.count,
lastModified: lastModifiedResult?.updatedAt || undefined,
oldestUnmodified: oldestUnmodifiedResult?.createdAt || undefined
};
-
+
} catch (error) {
console.error('통계 조회 실패:', error);
throw error;