1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
'use server'
import db from "@/db/db"
import { vendors, vendorTypes } from "@/db/schema/vendors"
import { eq } from "drizzle-orm"
import { sendVendorMasterToMDGInternal } from "./action"
import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils'
import { oracleKnex } from '@/lib/oracle-db/db'
/**
* Oracle DB에서 ZZREQID 값 조회
*
* @returns ZZREQID 값 (조회 실패 시 기본값 'HUIUN84.KIM')
*/
async function getZZREQID(): Promise<string> {
const DEFAULT_ZZREQID = 'HUIUN84.KIM'
try {
debugLog(`🔍 [ZZREQID] Oracle DB에서 ZZREQID 조회 시작`)
const query = `
SELECT CDNM
FROM CMCTB_CDNM
WHERE CD_CLF = :cd_clf
AND CD = :cd
AND ROWNUM <= 1
`
const results = await oracleKnex.raw(query, {
cd_clf: 'SRM0BN',
cd: '001'
})
debugLog(`🗄️ [ZZREQID] Oracle 쿼리 결과:`, {
resultType: typeof results,
isArray: Array.isArray(results),
})
// Oracle 결과 파싱 (여러 형태 처리)
let rows
if (Array.isArray(results)) {
// [rows, metaData] 형태 또는 바로 데이터 배열
if (results.length > 0 && typeof results[0] === 'object' && !Array.isArray(results[0])) {
rows = results // 바로 데이터 배열
} else {
rows = results[0] || [] // [rows, metaData] 형태
}
} else if (results && typeof results === 'object' && results.rows) {
rows = results.rows || []
} else {
rows = []
}
if (!Array.isArray(rows) || rows.length === 0) {
debugError(`⚠️ [ZZREQID] Oracle 조회 결과 없음, 기본값 사용: ${DEFAULT_ZZREQID}`)
return DEFAULT_ZZREQID
}
const cdnm = rows[0]?.CDNM || rows[0]?.[0]
if (!cdnm) {
debugError(`⚠️ [ZZREQID] CDNM 컬럼 값 없음, 기본값 사용: ${DEFAULT_ZZREQID}`)
return DEFAULT_ZZREQID
}
debugSuccess(`✅ [ZZREQID] Oracle 조회 성공: ${cdnm}`)
return String(cdnm)
} catch (error) {
debugError(`❌ [ZZREQID] Oracle 조회 실패, 기본값 사용:`, {
error: error instanceof Error ? error.message : '알 수 없는 오류',
defaultValue: DEFAULT_ZZREQID
})
return DEFAULT_ZZREQID
}
}
/**
* 개별 벤더 데이터를 MDG로 전송
*
* @param input.vendorId - vendors 테이블의 ID
* @param input.mode - 'NEW_VENDOR' (신규) 또는 'REGULAR_VENDOR' (정규)
* @returns 전송 결과
*/
export async function sendSingleVendorToMDG(input: {
vendorId: number
mode: 'NEW_VENDOR' | 'REGULAR_VENDOR'
}): Promise<{
success: boolean
message: string
vendorId: number
responseText?: string
requestXml?: string
}> {
try {
debugLog(`📤 [MDG Single] 벤더 ID ${input.vendorId} 전송 시작 (모드: ${input.mode})`)
// 1. 벤더 데이터 조회 (vendor_types 조인)
const [vendorData] = await db
.select({
vendor: vendors,
vendorTypeName: vendorTypes.nameKo,
})
.from(vendors)
.leftJoin(vendorTypes, eq(vendors.vendorTypeId, vendorTypes.id))
.where(eq(vendors.id, input.vendorId))
.limit(1)
if (!vendorData) {
throw new Error(`벤더를 찾을 수 없습니다: ID ${input.vendorId}`)
}
const vendor = vendorData.vendor
const vendorTypeName = vendorData.vendorTypeName
debugLog(`📋 [MDG Single] 벤더 조회 완료: ${vendor.vendorName} (업체 유형: ${vendorTypeName || '없음'})`)
// 2. Oracle DB에서 ZZREQID 조회
const zzreqid = await getZZREQID()
debugLog(`🔑 [MDG Single] ZZREQID 값: ${zzreqid}`)
// 3. MDG 포맷으로 데이터 매핑
const supplierMaster = mapVendorToMDGFormat(vendor, vendorTypeName, zzreqid, input.mode)
debugLog(`🔄 [MDG Single] 데이터 매핑 완료 (${Object.keys(supplierMaster).length}개 필드)`)
// 3. MDG로 전송
const result = await sendVendorMasterToMDGInternal(supplierMaster)
if (!result.success) {
debugError(`❌ [MDG Single] 전송 실패: ${result.message}`)
throw new Error(`MDG 전송 실패: ${result.message}`)
}
debugSuccess(`✅ [MDG Single] 전송 성공: ${vendor.vendorName}`)
return {
success: true,
message: `벤더 '${vendor.vendorName}' MDG 전송 완료`,
vendorId: input.vendorId,
responseText: result.responseText,
requestXml: result.requestXml,
}
} catch (error) {
debugError(`❌ [MDG Single] 전송 중 오류:`, error)
return {
success: false,
message: error instanceof Error ? error.message : 'Unknown error',
vendorId: input.vendorId,
}
}
}
/**
* Vendor DB 데이터를 MDG SUPPLIER_MASTER 포맷으로 매핑
*/
function mapVendorToMDGFormat(
vendor: typeof vendors.$inferSelect,
vendorTypeName: string | null,
zzreqid: string,
mode: 'NEW_VENDOR' | 'REGULAR_VENDOR'
): Record<string, string> {
debugLog(`🗺️ [MDG Mapper] 데이터 매핑 시작: ${vendor.vendorName}`)
// 전화번호로 모바일 여부 판단 (+8210으로 시작하면 1, 아니면 0)
const phoneStr = vendor.phone?.trim() || ''
const isMobile = phoneStr.startsWith('+8210') ? '1' : '0'
// 기본 매핑 (신규 벤더)
const mapping: Record<string, string> = {
// === 필수 필드 (고정값) ===
BP_HEADER: `evcp${vendor.id}`, // 벤더 ID (evcp + ID)
ZZSRMCD: `evcp${vendor.id}`, // SRM 코드 (evcp + ID)
KTOKK: 'LIEF', // 고정값: Vendor account group
MASTERFLAG: 'V', // 고정값
IBND_TYPE: 'I', // 고정값
CONSNUMBER: '1', // 고정값: 단건 전송
// === 업체 기본 정보 ===
SORT1: vendor.vendorName || '', // 검색어 (업체명과 동일)
NAME1: vendor.vendorName || '', // 업체명
// === 대표자 정보 ===
J_1KFREPRE: vendor.representativeName || '', // 대표자명
J_1KFTBUS: vendorTypeName || '', // 사업유형 (vendor_types의 name_ko)
J_1KFTIND: vendorTypeName || '', // 산업유형 (vendor_types의 name_ko)
// === 요청자 정보 ===
ZZREQID: zzreqid, // Oracle DB에서 동적 조회 (CMCTB_CDNM 테이블)
// === 주소 정보 ===
ADDRNO: '', // 빈 문자열 (보내면 안됨)
NATION: '', // 빈 문자열 (보내면 안됨)
COUNTRY: vendor.country || 'KR', // 국가코드 (기본값 KR)
POST_CODE1: vendor.postalCode || '00000', // 우편번호 (없으면 00000)
CITY1: vendor.addressDetail || '상세주소 정보가 없습니다', // 상세주소 (없으면 fallback)
STREET: vendor.address || '주소 정보가 없습니다', // 기본주소 (없으면 fallback)
// === 연락처 정보 ===
TEL_NUMBER: vendor.phone || '', // 전화번호
R3_USER: isMobile, // 모바일 여부 (0: 일반전화, 1: +8210 시작)
URI_ADDR: vendor.website || '', // 홈페이지 주소
SMTP_ADDR: vendor.representativeEmail || '', // 대표자 이메일
// === 세금 정보 ===
TAXTYPE: 'KR2', // 고정값 (한국)
TAXNUM: vendor.taxId?.replace(/-/g, '') || '', // 사업자번호 (하이픈 제거)
BP_TX_TYP: 'KR2', // 고정값 (한국)
STCD3: vendor.corporateRegistrationNumber?.replace(/-/g, '') || '', // 법인등록번호 (하이픈 제거)
// === 기업 정보 ===
ZZIND03: vendor.businessSize || '', // 기업규모 (A=대기업, B=중견기업, C=중소기업, D=소기업)
// === 무시할 필드들 (빈 문자열) ===
LANGU: '', // 언어 키 (무시)
}
// 정규 벤더의 경우 추가 필드 매핑 가능
if (mode === 'REGULAR_VENDOR') {
// TODO: 정규 벤더 전송 시 추가 필드 매핑
// 현재는 신규와 동일하게 처리
debugLog(`ℹ️ [MDG Mapper] 정규 벤더 모드 (추가 필드 없음)`)
}
// 빈 문자열인 필드만 포함 (null/undefined는 제외)
const result: Record<string, string> = {}
Object.entries(mapping).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
result[key] = String(value)
}
})
debugLog(`✅ [MDG Mapper] 매핑 완료: ${Object.keys(result).length}개 필드`)
return result
}
|