diff options
Diffstat (limited to 'lib/soap')
| -rw-r--r-- | lib/soap/mdg/send/vendor-master/action.ts | 203 |
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; |
