diff options
| author | joonhoekim <26rote@gmail.com> | 2025-07-09 12:16:43 +0000 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-07-09 12:25:40 +0000 |
| commit | ad4855620f4aa80841c4d7b1aa39d19ab2205f0e (patch) | |
| tree | 24dabc8e98390937b75967faa6c6e25a23a0f894 /lib | |
| parent | e328f73e5c23df7af5096d31e80fd74d9070ebc7 (diff) | |
(김준회) 벤더 상세페이지 - 기본정보 구현중 (구매 정의서)
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/soap/mdg/send/vendor-master/action.ts | 122 | ||||
| -rw-r--r-- | lib/vendors/service.ts | 121 |
2 files changed, 228 insertions, 15 deletions
diff --git a/lib/soap/mdg/send/vendor-master/action.ts b/lib/soap/mdg/send/vendor-master/action.ts index e96b93fc..ae0c2c89 100644 --- a/lib/soap/mdg/send/vendor-master/action.ts +++ b/lib/soap/mdg/send/vendor-master/action.ts @@ -267,34 +267,34 @@ async function fetchVendorData(vendorCode: string) { // SOAP 데이터 생성 (WSDL 구조에 맞춤) function buildSoapData(vendorData: NonNullable<Awaited<ReturnType<typeof fetchVendorData>>>) { - const { vendorHeader, addresses, adEmails, adFaxes, adPostals, adTels, adUrls, bpTaxnums, bpVengens } = vendorData; + const { vendorHeader, addresses, adFaxes, adPostals, adTels, bpTaxnums, bpVengens } = vendorData; // 값 추출 매핑 ------------------------------------ const mapping: Record<string, string | undefined> = { // Header BP_HEADER: vendorHeader?.VNDRCD, ZZSRMCD: 'EVCP', - TITLE: vendorHeader?.TITLE ?? '', - BU_SORT1: adPostals[0]?.VNDRNM_ABRV_1, - NAME_ORG1: adPostals[0]?.VNDRNM_1, - KTOKK: bpVengens[0]?.ACNT_GRP, + TITLE: '', // vendorHeader에 TITLE 필드가 없음 + BU_SORT1: adPostals[0]?.VNDRNM_ABRV_1 ?? undefined, + NAME_ORG1: adPostals[0]?.VNDRNM_1 ?? undefined, + KTOKK: bpVengens[0]?.ACNT_GRP ?? undefined, MASTERFLAG: 'X', IBND_TYPE: 'U', // Address mandatory (first) ADDRNO: addresses[0]?.ADDRNO, - AD_NATION: adPostals[0]?.INTL_ADR_VER_ID, - COUNTRY: adPostals[0]?.NTN_CD, - LANGU_COM: adPostals[0]?.LANG_KEY, - POST_COD1: adPostals[0]?.CITY_ZIP_NO, - CITY1: adPostals[0]?.VNDRNM_1, - MC_STREET: adPostals[0]?.ADR_1, + AD_NATION: adPostals[0]?.INTL_ADR_VER_ID ?? undefined, + COUNTRY: adPostals[0]?.NTN_CD ?? undefined, + LANGU_COM: adPostals[0]?.LANG_KEY ?? undefined, + POST_COD1: adPostals[0]?.CITY_ZIP_NO ?? undefined, + CITY1: adPostals[0]?.VNDRNM_1 ?? undefined, + MC_STREET: adPostals[0]?.ADR_1 ?? undefined, // Phone/Fax mandatory fields AD_CONSNO: '001', - T_COUNTRY: adTels[0]?.CTRY_CD ?? 'KR', - F_COUNTRY: adFaxes[0]?.CTRY_CD ?? 'KR', + T_COUNTRY: adTels[0]?.NTN_CD ?? 'KR', + F_COUNTRY: adFaxes[0]?.NTN_CD ?? 'KR', // Tax BP_TX_TYP: bpTaxnums[0]?.TX_NO_CTG ?? 'KR2', - TAXNUM: bpVengens[0]?.VAT_REG_NO, + TAXNUM: bpVengens[0]?.VAT_REG_NO ?? undefined, // Default others can be added as needed }; @@ -306,7 +306,7 @@ function buildSoapData(vendorData: NonNullable<Awaited<ReturnType<typeof fetchVe return true; }); - const supplierMaster: Record<string, any> = {}; + const supplierMaster: Record<string, string> = {}; uniqueFields.forEach(f => { supplierMaster[f.field] = mapping[f.field] ?? ''; }); @@ -620,6 +620,98 @@ export async function getVendorSendStatistics(): Promise<{ } } +// 테스트용 폼 데이터 송신 함수 (SOAP 라이브러리 사용) +export async function sendTestVendorDataToMDG(formData: Record<string, string>): Promise<{ + success: boolean; + message: string; + responseData?: any; +}> { + try { + console.log('🧪 테스트용 VENDOR 데이터 SOAP 송신 시작'); + + // CSV 파일 동적 로드 (더 안전함) + let csvFields: CsvField[] = []; + try { + const csvRaw = fs.readFileSync(CSV_PATH, 'utf-8'); + csvFields = parseCsv(csvRaw); + } catch (e) { + console.error('CSV 로딩 실패:', e); + return { + success: false, + message: 'CSV 필드 정의 파일을 로드할 수 없습니다.' + }; + } + + // 필수 필드 검증 + const requiredFields = csvFields.filter(f => f.mandatory).map(f => f.field); + const missingFields = requiredFields.filter(field => !formData[field]?.trim()); + + if (missingFields.length > 0) { + return { + success: false, + message: `필수 필드가 누락되었습니다: ${missingFields.join(', ')}` + }; + } + + // 필드 순서에 따라 데이터 생성 + const seen = new Set<string>(); + const uniqueFields = csvFields.filter(f => { + if (seen.has(f.field)) return false; + seen.add(f.field); + return true; + }); + + const supplierMaster: Record<string, string> = {}; + uniqueFields.forEach(f => { + supplierMaster[f.field] = formData[f.field] ?? ''; + }); + + // SOAP 요청 구조 생성 + const soapData = { + P2MD3007_S: { + SUPPLIER_MASTER: supplierMaster + } + }; + + console.log('📄 테스트 SOAP 데이터 생성 완료'); + + // SOAP 클라이언트로 요청 전송 + const responseData = await withSoapLogging( + 'OUTBOUND', + 'MDG', + 'IF_MDZ_EVCP_VENDOR_MASTER_TEST', + JSON.stringify(soapData), + async () => { + const client = await createSoapClient(); + + return new Promise<SoapResponse>((resolve, reject) => { + client.P2MD3007_AO(soapData, (err: SoapError | null, result: SoapResponse) => { + if (err) { + reject(err); + } else { + console.log('✅ 테스트 MDG 전송 성공'); + resolve(result); + } + }); + }); + } + ); + + return { + success: true, + message: '테스트 송신이 완료되었습니다.', + responseData + }; + + } catch (error) { + console.error('❌ 테스트 송신 실패:', error); + return { + success: false, + message: error instanceof Error ? error.message : 'Unknown error' + }; + } +} + // 직접 XML 전송 함수 (기존 호환성 유지) export async function sendVendorEnvelopeToMDG(envelope: string): Promise<{ success: boolean; diff --git a/lib/vendors/service.ts b/lib/vendors/service.ts index 2328752b..853b3701 100644 --- a/lib/vendors/service.ts +++ b/lib/vendors/service.ts @@ -2584,4 +2584,125 @@ export async function searchVendors(searchTerm: string = "", limit: number = 100 console.error("벤더 검색 오류:", error); return []; } +} + +/** + * 벤더 기본정보 조회 (Basic Info 페이지용) + * vendorsWithTypesView를 사용하여 기본 정보 + contacts + attachments 조회 + */ +export async function getVendorBasicInfo(vendorId: number) { + unstable_noStore(); + + try { + return await db.transaction(async (tx) => { + // 1. 기본 벤더 정보 조회 (vendorsWithTypesView 사용) + const vendor = await tx + .select() + .from(vendorsWithTypesView) + .where(eq(vendorsWithTypesView.id, vendorId)) + .limit(1) + .then(rows => rows[0] || null); + + if (!vendor) { + return null; + } + + // 2. 연락처 정보 조회 + const contacts = await tx + .select() + .from(vendorContacts) + .where(eq(vendorContacts.vendorId, vendorId)) + .orderBy(desc(vendorContacts.isPrimary), asc(vendorContacts.contactName)); + + // 3. 첨부파일 정보 조회 + const attachments = await tx + .select() + .from(vendorAttachments) + .where(eq(vendorAttachments.vendorId, vendorId)) + .orderBy(asc(vendorAttachments.createdAt)); + + // 4. 타입 변환하여 반환 (추후 확장 가능하도록 구조화) + return { + // 기본 벤더 정보 + id: vendor.id, + vendorName: vendor.vendorName, + vendorCode: vendor.vendorCode, + taxId: vendor.taxId, + address: vendor.address, + businessSize: vendor.businessSize || "", // vendorsWithTypesView에 businessSize 필드가 없을 경우 대비 + country: vendor.country, + phone: vendor.phone, + fax: vendor.fax || null, // vendorsWithTypesView에 fax 필드가 없을 경우 대비 + email: vendor.email, + website: vendor.website, + status: vendor.status, + representativeName: vendor.representativeName, + representativeBirth: vendor.representativeBirth, + representativeEmail: vendor.representativeEmail, + representativePhone: vendor.representativePhone, + representativeWorkExperience: vendor.representativeWorkExperience ?? false, // vendorsWithTypesView에 해당 필드가 없을 경우 false로 기본값 + corporateRegistrationNumber: vendor.corporateRegistrationNumber, + creditAgency: vendor.creditAgency, + creditRating: vendor.creditRating, + cashFlowRating: vendor.cashFlowRating, + createdAt: vendor.createdAt, + updatedAt: vendor.updatedAt, + + // 연락처 정보 + contacts: contacts.map(contact => ({ + id: contact.id, + contactName: contact.contactName, + contactPosition: contact.contactPosition, + contactEmail: contact.contactEmail, + contactPhone: contact.contactPhone, + isPrimary: contact.isPrimary, + })), + + // 첨부파일 정보 + attachments: attachments.map(attachment => ({ + id: attachment.id, + fileName: attachment.fileName, + filePath: attachment.filePath, + attachmentType: attachment.attachmentType, + createdAt: attachment.createdAt, + })), + + // 추가 정보는 임시로 null (나중에 실제 데이터로 교체) + additionalInfo: { + businessType: vendor.vendorTypeId ? `Type ${vendor.vendorTypeId}` : null, + employeeCount: 0, // 실제 데이터가 있을 수 있으므로 유지 + mainBusiness: null, + }, + + // 매출 정보 (구현 예정 - 나중에 실제 테이블 연결) + salesInfo: null, // 구현 시 { "2023": { totalSales: "1000", totalDebt: "500", ... }, "2022": { ... } } 형태로 연도별 키 사용 + + // 추가 정보들 (구현 예정 - 나중에 실제 테이블 연결) + organization: null, + + factoryInfo: null, + + inspectionInfo: null, + + evaluationInfo: null, + + classificationInfo: { + vendorClassification: null, + groupCompany: null, + preferredLanguage: "한국어", // 기본값으로 유지 + industryType: "제조업", // 기본값으로 유지 + isoCertification: null, + }, + + contractDetails: null, + + capacityInfo: null, + + calculatedMetrics: null, // 구현 시 { "20231231": { debtRatio: 0, ... }, "20221231": { ... } } 형태로 YYYYMMDD 키 사용 + }; + }); + } catch (error) { + console.error("Error fetching vendor basic info:", error); + return null; + } }
\ No newline at end of file |
