summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-06 15:50:45 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-06 15:50:45 +0900
commitba43cd261d10c6b0c5218a9da3f946993b21de6e (patch)
treef902939afcd7a2c15359dc0a75eda43dadd540da
parent9d00afc23fca94056ce630dfdd91e16010cad7aa (diff)
(김준회) 결재: 협력업체관리: 가입승인: 가입승인시 결재절차 구현 (템플릿 관련 부분은 미구현)
-rw-r--r--lib/approval/handlers-registry.ts7
-rw-r--r--lib/soap/mdg/send/vendor-master/action.ts13
-rw-r--r--lib/soap/mdg/send/vendor-master/send-single-vendor.ts238
-rw-r--r--lib/vendors/approval-actions.ts119
-rw-r--r--lib/vendors/approval-handlers.ts119
-rw-r--r--lib/vendors/table/approve-vendor-dialog.tsx34
-rw-r--r--public/wsdl/P2MD3007_AO.csv26
7 files changed, 530 insertions, 26 deletions
diff --git a/lib/approval/handlers-registry.ts b/lib/approval/handlers-registry.ts
index 6306f811..7f3261bc 100644
--- a/lib/approval/handlers-registry.ts
+++ b/lib/approval/handlers-registry.ts
@@ -35,7 +35,12 @@ export async function initializeApprovalHandlers() {
// 정규업체 등록 핸들러 등록 (결재 승인 후 실행될 함수 registerVendorInternal )
registerActionHandler('vendor_regular_registration', registerVendorInternal);
- // 4. 계약 승인 핸들러
+ // 4. 벤더 가입 승인 핸들러
+ const { approveVendorWithMDGInternal } = await import('@/lib/vendors/approval-handlers');
+ // 벤더 가입 승인 핸들러 등록 (결재 승인 후 실행될 함수 approveVendorWithMDGInternal)
+ registerActionHandler('vendor_approval', approveVendorWithMDGInternal);
+
+ // 5. 계약 승인 핸들러
// const { approveContractInternal } = await import('@/lib/contract/handlers');
// registerActionHandler('contract_approval', approveContractInternal);
diff --git a/lib/soap/mdg/send/vendor-master/action.ts b/lib/soap/mdg/send/vendor-master/action.ts
index bdb2d579..c73d78c5 100644
--- a/lib/soap/mdg/send/vendor-master/action.ts
+++ b/lib/soap/mdg/send/vendor-master/action.ts
@@ -36,7 +36,7 @@ function createVendorMasterSoapBodyContent(supplierMaster: Record<string, string
}
// MDG로 VENDOR 마스터 SOAP XML 전송하는 함수 (sender.ts 사용)
-async function sendVendorMasterToMDGInternal(supplierMaster: Record<string, string>): Promise<{
+export async function sendVendorMasterToMDGInternal(supplierMaster: Record<string, string>): Promise<{
success: boolean;
message: string;
responseText?: string;
@@ -220,6 +220,17 @@ function buildSupplierMasterData(vendorData: NonNullable<Awaited<ReturnType<type
// 메인 송신 함수들
// ========================================
+/**
+ * @deprecated 이 함수는 더 이상 사용되지 않습니다.
+ * 대신 sendSingleVendorToMDG()를 사용하세요.
+ *
+ * 이유:
+ * - 신규 벤더는 vendorCode가 없음 (MDG에서 생성)
+ * - 개별 벤더 데이터 전송이 필요
+ * - vendors 테이블에서 직접 데이터 조회 필요
+ *
+ * @see lib/soap/mdg/send/vendor-master/send-single-vendor.ts
+ */
// VENDOR 마스터 데이터를 MDG로 송신하는 액션
export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{
success: boolean;
diff --git a/lib/soap/mdg/send/vendor-master/send-single-vendor.ts b/lib/soap/mdg/send/vendor-master/send-single-vendor.ts
new file mode 100644
index 00000000..d10792b7
--- /dev/null
+++ b/lib/soap/mdg/send/vendor-master/send-single-vendor.ts
@@ -0,0 +1,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 || '', // 상세주소
+ STREET: vendor.address || '', // 기본주소
+
+ // === 연락처 정보 ===
+ 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
+}
+
diff --git a/lib/vendors/approval-actions.ts b/lib/vendors/approval-actions.ts
new file mode 100644
index 00000000..69d09caa
--- /dev/null
+++ b/lib/vendors/approval-actions.ts
@@ -0,0 +1,119 @@
+'use server'
+
+import { ApprovalSubmissionSaga } from '@/lib/approval'
+import type { ApprovalConfig } from '@/lib/approval/types'
+import db from '@/db/db'
+import { vendors } from '@/db/schema/vendors'
+import { inArray } from 'drizzle-orm'
+
+/**
+ * 벤더 가입 승인 결재 상신
+ *
+ * @param input - 결재 요청 정보
+ * @returns 결재 상신 결과
+ */
+export async function approveVendorsWithApproval(input: {
+ vendorIds: number[]
+ currentUser: {
+ id: number
+ epId: string | null
+ email?: string
+ }
+ approvers?: string[]
+}) {
+ console.log(`[Vendor Approval Action] 벤더 승인 결재 상신 시작:`, input.vendorIds)
+
+ try {
+ // 1. 입력 검증
+ if (!input.currentUser.epId) {
+ throw new Error('Knox EP ID가 필요합니다. 시스템 관리자에게 문의하세요.')
+ }
+
+ if (input.vendorIds.length === 0) {
+ throw new Error('승인할 벤더를 선택해주세요.')
+ }
+
+ // 2. 벤더 정보 조회
+ const vendorRecords = await db
+ .select({
+ id: vendors.id,
+ vendorName: vendors.vendorName,
+ vendorCode: vendors.vendorCode,
+ email: vendors.email,
+ status: vendors.status,
+ taxId: vendors.taxId,
+ country: vendors.country,
+ })
+ .from(vendors)
+ .where(inArray(vendors.id, input.vendorIds))
+
+ if (vendorRecords.length === 0) {
+ throw new Error(`벤더를 찾을 수 없습니다: ${input.vendorIds.join(', ')}`)
+ }
+
+ // 3. PENDING_REVIEW 상태가 아닌 벤더 확인
+ const invalidVendors = vendorRecords.filter(v => v.status !== 'PENDING_REVIEW')
+ if (invalidVendors.length > 0) {
+ throw new Error(
+ `가입 신청 중(PENDING_REVIEW) 상태의 벤더만 승인할 수 있습니다. ` +
+ `잘못된 상태: ${invalidVendors.map(v => `${v.vendorName}(${v.status})`).join(', ')}`
+ )
+ }
+
+ console.log(`[Vendor Approval Action] ${vendorRecords.length}개 벤더 조회 완료`)
+
+ // 4. 템플릿 변수 준비 (TODO: 실제 템플릿에 맞게 수정 필요)
+ const variables: Record<string, string> = {
+ // TODO: 다음 대화에서 제공될 템플릿에 맞게 변수 매핑
+ '업체수': String(vendorRecords.length),
+ '업체목록': vendorRecords.map(v =>
+ `${v.vendorName} (${v.vendorCode || '코드 미할당'})`
+ ).join('\n'),
+ '요청일': new Date().toLocaleDateString('ko-KR'),
+ '요청자': input.currentUser.email || 'Unknown',
+ }
+
+ console.log(`[Vendor Approval Action] 템플릿 변수 준비 완료`)
+
+ // 5. 결재 상신 (Saga 패턴)
+ const saga = new ApprovalSubmissionSaga(
+ 'vendor_approval', // 핸들러 타입 (handlers-registry에 등록될 키)
+ {
+ vendorIds: input.vendorIds,
+ userId: input.currentUser.id, // 결재 승인 후 실행 시 필요
+ },
+ {
+ title: `벤더 가입 승인 요청 - ${vendorRecords.length}개 업체`,
+ description: `${vendorRecords.map(v => v.vendorName).join(', ')} 의 가입을 승인합니다.`,
+ templateName: '벤더 가입 승인 요청',
+ variables,
+ approvers: input.approvers,
+ currentUser: input.currentUser,
+ } as ApprovalConfig
+ )
+
+ console.log(`[Vendor Approval Action] 결재 상신 실행 중...`)
+
+ const result = await saga.execute()
+
+ console.log(`[Vendor Approval Action] 결재 상신 완료:`, result)
+
+ return {
+ success: true,
+ message: `${vendorRecords.length}개 벤더의 가입 승인 결재가 상신되었습니다.`,
+ approvalId: result.approvalId,
+ pendingActionId: result.pendingActionId,
+ status: result.status,
+ }
+
+ } catch (error) {
+ console.error(`[Vendor Approval Action] 결재 상신 실패:`, error)
+
+ return {
+ success: false,
+ message: error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.',
+ error: error instanceof Error ? error.message : 'Unknown error',
+ }
+ }
+}
+
diff --git a/lib/vendors/approval-handlers.ts b/lib/vendors/approval-handlers.ts
new file mode 100644
index 00000000..bc4d24a6
--- /dev/null
+++ b/lib/vendors/approval-handlers.ts
@@ -0,0 +1,119 @@
+'use server'
+
+import db from "@/db/db"
+import { vendors } from "@/db/schema/vendors"
+import { inArray } from "drizzle-orm"
+import { sendSingleVendorToMDG } from "@/lib/soap/mdg/send/vendor-master/send-single-vendor"
+import { approveVendors } from "./service"
+
+/**
+ * 벤더 가입 승인 핸들러 (결재 승인 후 자동 실행)
+ *
+ * 이 함수는 결재 승인 후 approval-polling-service에 의해 자동으로 호출됩니다.
+ * 직접 호출하지 마세요. 결재 상신은 approveVendorsWithApproval()을 사용하세요.
+ *
+ * @param payload - { vendorIds: number[], userId: number }
+ * @returns 승인 결과
+ */
+export async function approveVendorWithMDGInternal(payload: {
+ vendorIds: number[]
+ userId: number
+}) {
+ console.log(`[Vendor Approval Handler] 벤더 승인 시작:`, payload.vendorIds)
+
+ try {
+ // 1. 벤더 정보 조회
+ const vendorRecords = await db
+ .select({
+ id: vendors.id,
+ vendorName: vendors.vendorName,
+ status: vendors.status,
+ })
+ .from(vendors)
+ .where(inArray(vendors.id, payload.vendorIds))
+
+ if (vendorRecords.length === 0) {
+ throw new Error(`벤더를 찾을 수 없습니다: ${payload.vendorIds.join(', ')}`)
+ }
+
+ console.log(`[Vendor Approval Handler] ${vendorRecords.length}개 벤더 조회 완료`)
+
+ // 2. 각 벤더를 개별적으로 MDG에 전송
+ const mdgResults: Array<{
+ vendorId: number
+ vendorName: string
+ success: boolean
+ error?: string
+ }> = []
+
+ for (const vendor of vendorRecords) {
+ console.log(`[Vendor Approval Handler] MDG 전송 시작: ${vendor.vendorName} (ID: ${vendor.id})`)
+
+ try {
+ const result = await sendSingleVendorToMDG({
+ vendorId: vendor.id,
+ mode: 'NEW_VENDOR', // 신규 벤더 가입 승인
+ })
+
+ if (result.success) {
+ console.log(`[Vendor Approval Handler] ✅ MDG 전송 성공: ${vendor.vendorName}`)
+ mdgResults.push({
+ vendorId: vendor.id,
+ vendorName: vendor.vendorName,
+ success: true,
+ })
+ } else {
+ console.error(`[Vendor Approval Handler] ❌ MDG 전송 실패: ${vendor.vendorName} - ${result.message}`)
+ mdgResults.push({
+ vendorId: vendor.id,
+ vendorName: vendor.vendorName,
+ success: false,
+ error: result.message,
+ })
+ // MDG 전송 실패 시 전체 프로세스 중단
+ throw new Error(`MDG 전송 실패 (${vendor.vendorName}): ${result.message}`)
+ }
+ } catch (error) {
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error'
+ console.error(`[Vendor Approval Handler] ❌ MDG 전송 예외: ${vendor.vendorName} - ${errorMsg}`)
+ mdgResults.push({
+ vendorId: vendor.id,
+ vendorName: vendor.vendorName,
+ success: false,
+ error: errorMsg,
+ })
+ throw error
+ }
+ }
+
+ console.log(`[Vendor Approval Handler] 모든 벤더 MDG 전송 완료`)
+
+ // 3. 기존 approveVendors 함수 실행
+ // (상태 변경, 유저 활성화, 역할 할당, 이메일 발송, 로그 기록 등)
+ console.log(`[Vendor Approval Handler] 기존 approveVendors 함수 실행`)
+
+ const approveResult = await approveVendors({
+ ids: payload.vendorIds,
+ userId: payload.userId,
+ })
+
+ if (approveResult.error) {
+ console.error(`[Vendor Approval Handler] approveVendors 실패:`, approveResult.error)
+ throw new Error(`벤더 승인 처리 실패: ${approveResult.error}`)
+ }
+
+ console.log(`[Vendor Approval Handler] 벤더 승인 완료: ${vendorRecords.length}개`)
+
+ return {
+ success: true,
+ message: `${vendorRecords.length}개 벤더가 승인되었습니다.`,
+ approvedVendors: vendorRecords,
+ mdgResults,
+ }
+
+ } catch (error) {
+ console.error(`[Vendor Approval Handler] 벤더 승인 실패:`, error)
+ throw error
+ }
+}
+
diff --git a/lib/vendors/table/approve-vendor-dialog.tsx b/lib/vendors/table/approve-vendor-dialog.tsx
index 980953aa..786399a4 100644
--- a/lib/vendors/table/approve-vendor-dialog.tsx
+++ b/lib/vendors/table/approve-vendor-dialog.tsx
@@ -28,7 +28,8 @@ import {
DrawerTrigger,
} from "@/components/ui/drawer"
import { Vendor } from "@/db/schema/vendors"
-import { approveVendors, rejectVendors } from "../service"
+import { rejectVendors } from "../service"
+import { approveVendorsWithApproval } from "../approval-actions"
import { useSession } from "next-auth/react"
interface VendorDecisionDialogProps
@@ -55,25 +56,36 @@ export function VendorDecisionDialog({
return
}
+ if (!session?.user?.epId) {
+ toast.error("Knox EP ID가 없습니다. 시스템 관리자에게 문의하세요.")
+ return
+ }
+
startApproveTransition(async () => {
try {
- console.log("🔍 [DEBUG] 승인 요청 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email })));
- console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, userType: typeof session.user.id });
+ console.log("🔍 [DEBUG] 결재 상신 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email })));
+ console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, epId: session.user.epId });
- const { error } = await approveVendors({
- ids: vendors.map((vendor) => vendor.id),
- userId: Number(session.user.id)
+ const result = await approveVendorsWithApproval({
+ vendorIds: vendors.map((vendor) => vendor.id),
+ currentUser: {
+ id: Number(session.user.id),
+ epId: session.user.epId as string, // 위에서 검증했으므로 타입 단언
+ email: session.user.email || undefined,
+ },
+ // TODO: 필요시 approvers 배열 추가
+ // approvers: ['EP001', 'EP002'],
})
- if (error) {
- console.error("🚨 [DEBUG] 승인 처리 에러:", error);
- toast.error(error)
+ if (!result.success) {
+ console.error("🚨 [DEBUG] 결재 상신 에러:", result.message);
+ toast.error(result.message || "결재 상신에 실패했습니다.")
return
}
- console.log("✅ [DEBUG] 승인 처리 성공");
+ console.log("✅ [DEBUG] 결재 상신 성공:", result);
props.onOpenChange?.(false)
- toast.success("협력업체 등록이 승인되었습니다.")
+ toast.success(`결재가 상신되었습니다. (결재ID: ${result.approvalId})`)
onSuccess?.()
} catch (error) {
console.error("🚨 [DEBUG] 예상치 못한 에러:", error);
diff --git a/public/wsdl/P2MD3007_AO.csv b/public/wsdl/P2MD3007_AO.csv
index ddef7bb6..9e91704c 100644
--- a/public/wsdl/P2MD3007_AO.csv
+++ b/public/wsdl/P2MD3007_AO.csv
@@ -1,6 +1,6 @@
SEQ,Table,Field,M/O,Type,Size,Description
-1,SUPPLIER_MASTER,BP_HEADER,M,CHAR,10,Business Partner Number (벤더코드입력(없는데..어떻게?))
-2,SUPPLIER_MASTER,ZZSRMCD,M,CHAR,20,SRM Vendor Code (SRM코드 없음. 일단 필수값이긴 하니 고정값 보낼 것임.)
+1,SUPPLIER_MASTER,BP_HEADER,M,CHAR,10,Business Partner Number (벤더코드입력(없는데..어떻게?)) >> `evcp${id}` 보낼 것.
+2,SUPPLIER_MASTER,ZZSRMCD,M,CHAR,20,SRM Vendor Code (SRM코드 없음. 일단 필수값이긴 하니 고정값 보낼 것임.) `evcp${id}` 보낼 것.
3,SUPPLIER_MASTER,SORT1,M,NUMC,20,Search Term 1 (벤더명과 동일하게)
4,SUPPLIER_MASTER,NAME1,M,CHAR,40,Name 1 (벤더명과 동일하게)
5,SUPPLIER_MASTER,NAME2,,CHAR,40,Name 2 (무시)
@@ -10,16 +10,16 @@
9,SUPPLIER_MASTER,ZTYPE,,CHAR,2,Account Group type (무시)
10,SUPPLIER_MASTER,VBUND,,CHAR,6,Company ID of Trading Partner (무시)
11,SUPPLIER_MASTER,J_1KFREPRE,,CHAR,10,Name of Representative: 대표자명
-12,SUPPLIER_MASTER,J_1KFTBUS,,CHAR,30,Type of Business: 사업유형
-13,SUPPLIER_MASTER,J_1KFTIND,,CHAR,30,Type of Industry: 산업유형
+12,SUPPLIER_MASTER,J_1KFTBUS,,CHAR,30,Type of Business: 사업유형 (vendor_type_id 컬럼 값으로 vendor_types 테이블 찾아서 name_ko 값으로 보내기)
+13,SUPPLIER_MASTER,J_1KFTIND,,CHAR,30,Type of Industry: 산업유형 (vendor_type_id 컬럼 값으로 vendor_types 테이블 찾아서 name_ko 값으로 보내기)
14,SUPPLIER_MASTER,VQMGRP,,CHAR,20,Vendor QM Group: 무시
15,SUPPLIER_MASTER,VTELNO,,CHAR,30,Vendor QM Group Tel.: 무시
16,SUPPLIER_MASTER,VEMAIL,,CHAR,30,Vendor QM Group E-Mail: 무시
-17,SUPPLIER_MASTER,ZZCNAME1,,CHAR,35,First name: ?
-18,SUPPLIER_MASTER,ZZCNAME2,,CHAR,35,Name 1: ?
-19,SUPPLIER_MASTER,ZZTELF1_C,,CHAR,16,First telephone number: ?
-20,SUPPLIER_MASTER,MASTERFLAG,M,CHAR,1,Master Flag: (항상 V?)
-21,SUPPLIER_MASTER,IBND_TYPE,M,CHAR,1,Inbound Type: (항상 I?)
+17,SUPPLIER_MASTER,ZZCNAME1,,CHAR,35,First name: ? 무시
+18,SUPPLIER_MASTER,ZZCNAME2,,CHAR,35,Name 1: ? 무시
+19,SUPPLIER_MASTER,ZZTELF1_C,,CHAR,16,First telephone number: ? 무시
+20,SUPPLIER_MASTER,MASTERFLAG,M,CHAR,1,Master Flag: (항상 V)
+21,SUPPLIER_MASTER,IBND_TYPE,M,CHAR,1,Inbound Type: (항상 I)
22,SUPPLIER_MASTER,ZZVNDTYP,,CHAR,2,Vendor Type: 무시
23,SUPPLIER_MASTER,ZZREQID,M,VARCHAR2,12,Registered User: (?요청자 녹스ID 대문자?)
24,SUPPLIER_MASTER,ZZIND01,,VARCHAR2,1,Indicator 01: 무시
@@ -27,11 +27,11 @@
26,SUPPLIER_MASTER,NATION,,CHAR,1,International address version ID: (값을 보내면 안된다고 함)
27,SUPPLIER_MASTER,COUNTRY,M,CHAR,3,Country Key: (국가코드 2자리인듯. 내자(국내업체)는 KR)
28,SUPPLIER_MASTER,LANGU,,LANG,1,Language Key: 무시
-29,SUPPLIER_MASTER,POST_CODE1,M,CHAR,10,Postal Code: 우편번호
-30,SUPPLIER_MASTER,CITY1,M,CHAR,40,City: 이게 주소로 쓰이는 걸로 보임. 상세주소 (ex: 한화오션내 H안벽생산지원센터 403)
+29,SUPPLIER_MASTER,POST_CODE1,M,CHAR,10,Postal Code: 우편번호 : postalCode 컬럼 값 보내기. 없으면 00000을 보내기
+30,SUPPLIER_MASTER,CITY1,M,CHAR,40,City: 이게 주소로 쓰이는 걸로 보임. 상세주소 (ex: 한화오션내 H안벽생산지원센터 403): addressDetail 컬럼
31,SUPPLIER_MASTER,CITY2,,CHAR,40,District: 무시
32,SUPPLIER_MASTER,REGION,,CHAR,3,"Region (State, Province, County) : 무시"
-33,SUPPLIER_MASTER,STREET,M,CHAR,60,Street: 이게 주소로 쓰이는 걸로 보임. 기본주소 (ex: 경상남도 거제시 거제대로 3370 (한화오션)
+33,SUPPLIER_MASTER,STREET,M,CHAR,60,Street: 이게 주소로 쓰이는 걸로 보임. 기본주소 (ex: 경상남도 거제시 거제대로 3370 (한화오션): address 컬럼
34,SUPPLIER_MASTER,CONSNUMBER,,NUMC,3,Sequence number: 뭔지 모르겠음. 같은 벤더 여러값 보낼 때 시퀀스인듯. 단건은 항상1로 고정
35,SUPPLIER_MASTER,TEL_NUMBER,,CHAR,30,Telephone no.: dialling code+number (-를 포함한 전화번호)
36,SUPPLIER_MASTER,TEL_EXTENS,,CHAR,10,Telephone no.: Extension (내선번호)
@@ -44,4 +44,4 @@
43,SUPPLIER_MASTER,TAXNUM,,CHAR,20,Business Partner Tax Number (사업자번호 -없이숫자만입력)
44,SUPPLIER_MASTER,BP_TX_TYP,,VARCHAR2,6,Resident Registration Number (YYMMDD0000000)
45,SUPPLIER_MASTER,STCD3,,CHAR,18,법인등록번호: (법인등록번호 -없이 숫자만)
-46,SUPPLIER_MASTER,ZZIND03,,CHAR,1,"기업규모: (A,B,C,D 값을 넣는걸로 아는데 기준을 알려주지 않음)" \ No newline at end of file
+46,SUPPLIER_MASTER,ZZIND03,,CHAR,1,"기업규모: (A,B,C,D 값 - A:대기업, B:중견기업, C:중소기업, D:소기업) : businessSize 컬럼 값 보내기" \ No newline at end of file