summaryrefslogtreecommitdiff
path: root/lib/vendor-regular-registrations
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-26 01:17:56 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-26 01:17:56 +0000
commit12e936c0b45ffa1c8f3c02ff77961212767be9a7 (patch)
tree34f31b9a64c6d30e187c1114530c4d47b95d30a9 /lib/vendor-regular-registrations
parent83f67ed333f0237b434a41d1eceef417c0d48313 (diff)
(대표님) 가입, 기본계약, 벤더
(최겸) 기술영업 아이템 관련
Diffstat (limited to 'lib/vendor-regular-registrations')
-rw-r--r--lib/vendor-regular-registrations/repository.ts69
-rw-r--r--lib/vendor-regular-registrations/service.ts215
-rw-r--r--lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx24
3 files changed, 206 insertions, 102 deletions
diff --git a/lib/vendor-regular-registrations/repository.ts b/lib/vendor-regular-registrations/repository.ts
index aec3d275..6f73b98f 100644
--- a/lib/vendor-regular-registrations/repository.ts
+++ b/lib/vendor-regular-registrations/repository.ts
@@ -125,19 +125,6 @@ export async function getVendorRegularRegistrations(
return acc;
}, [] as typeof filteredContracts);
- // 디버깅을 위한 로그
- console.log(`📋 벤더 ID ${registration.vendorId} (${registration.companyName}) 현황:`, {
- vendorFiles: vendorFiles.map(f => ({ type: f.attachmentType, fileName: f.fileName })),
- investigationFiles: investigationFiles.map(f => ({ type: f.attachmentType, fileName: f.fileName })),
- allContracts: allVendorContracts.length,
- uniqueContracts: vendorContracts.map(c => ({
- templateName: c.templateName,
- status: c.status,
- createdAt: c.createdAt?.toISOString()
- })),
- contactTypes: vendorContacts.map(c => c.contactType)
- });
-
// 문서 제출 현황 - 국가별 요구사항 적용
const isForeign = registration.country !== 'KR';
const documentSubmissionsStatus = {
@@ -155,47 +142,6 @@ export async function getVendorRegularRegistrations(
auditResult: investigationFiles,
};
- // 디버깅용 로그 추가
- console.log(`🔍 벤더 ID ${registration.vendorId} documentFiles 구조:`, {
- businessRegistration: documentFiles.businessRegistration.map(f => ({
- fileName: f.fileName,
- filePath: f.filePath,
- attachmentType: f.attachmentType,
- allKeys: Object.keys(f)
- })),
- creditEvaluation: documentFiles.creditEvaluation.map(f => ({
- fileName: f.fileName,
- filePath: f.filePath,
- attachmentType: f.attachmentType,
- allKeys: Object.keys(f)
- })),
- bankCopy: documentFiles.bankCopy.map(f => ({
- fileName: f.fileName,
- filePath: f.filePath,
- attachmentType: f.attachmentType,
- allKeys: Object.keys(f)
- })),
- auditResult: documentFiles.auditResult.map(f => ({
- fileName: f.fileName,
- attachmentType: f.attachmentType,
- allKeys: Object.keys(f)
- })),
- totalVendorFiles: vendorFiles.length,
- totalInvestigationFiles: investigationFiles.length
- });
-
- // 문서 제출 현황 로그
- console.log(`📊 벤더 ID ${registration.vendorId} 문서 제출 현황:`, {
- documentSubmissionsStatus,
- isForeign,
- vendorFiles: vendorFiles.map(f => ({
- type: f.attachmentType,
- fileName: f.fileName
- })),
- investigationFilesCount: investigationFiles.length,
- country: registration.country
- });
-
// 계약 동의 현황 - 실제 기본 계약 데이터 기반으로 단순화
const contractAgreementsStatus = {
cp: vendorContracts.some(c => c.status === "COMPLETED") ? "completed" : "not_submitted",
@@ -214,16 +160,6 @@ export async function getVendorRegularRegistrations(
const additionalInfoTableCompleted = vendorAdditionalInfoData.length > 0;
const additionalInfoCompleted = contactsCompleted && additionalInfoTableCompleted;
- // 추가정보 디버깅 로그
- console.log(`🔍 벤더 ID ${registration.vendorId} 추가정보 상세:`, {
- requiredContactTypes,
- vendorContactTypes: vendorContacts.map(c => c.contactType),
- contactsCompleted,
- additionalInfoTableCompleted,
- additionalInfoData: vendorAdditionalInfoData,
- finalAdditionalInfoCompleted: additionalInfoCompleted
- });
-
// 모든 조건 충족 여부 확인
const allDocumentsSubmitted = Object.values(documentSubmissionsStatus).every(status => status === true);
const allContractsCompleted = vendorContracts.length > 0 && vendorContracts.every(c => c.status === "COMPLETED");
@@ -233,7 +169,8 @@ export async function getVendorRegularRegistrations(
const shouldUpdateStatus = allDocumentsSubmitted && allContractsCompleted && safetyQualificationCompleted && additionalInfoCompleted;
// 현재 상태가 조건충족이 아닌데 모든 조건이 충족되면 상태 업데이트
- if (shouldUpdateStatus && registration.status !== "approval_ready") {
+ // 단, 이미 registration_requested 상태라면 자동 업데이트하지 않음
+ if (shouldUpdateStatus && registration.status !== "approval_ready" && registration.status !== "registration_requested") {
// 비동기 업데이트 (백그라운드에서 실행)
updateVendorRegularRegistration(registration.id, {
status: "approval_ready"
@@ -245,7 +182,7 @@ export async function getVendorRegularRegistrations(
return {
id: registration.id,
vendorId: registration.vendorId,
- status: shouldUpdateStatus ? "approval_ready" : (registration.status || "audit_pass"),
+ status: registration.status || "audit_pass",
potentialCode: registration.potentialCode,
businessNumber: registration.businessNumber || "",
companyName: registration.companyName || "",
diff --git a/lib/vendor-regular-registrations/service.ts b/lib/vendor-regular-registrations/service.ts
index 7ec433b4..c4f1a2a8 100644
--- a/lib/vendor-regular-registrations/service.ts
+++ b/lib/vendor-regular-registrations/service.ts
@@ -26,6 +26,7 @@ import {
} from "@/db/schema";
import db from "@/db/db";
import { inArray, eq, desc, and, lt } from "drizzle-orm";
+import { sendTestVendorDataToMDG } from "@/lib/soap/mdg/send/vendor-master/action";
// 3개월 이상 정규등록검토 상태인 등록을 장기미등록으로 변경
async function updatePendingApprovals() {
@@ -125,7 +126,7 @@ export async function fetchVendorRegularRegistrations(input?: {
},
[JSON.stringify(input || {})],
{
- revalidate: 300, // 5분 캐시
+ revalidate: 60, // 1분 캐시로 단축
tags: ["vendor-regular-registrations"],
}
)();
@@ -1184,6 +1185,13 @@ export async function submitRegistrationRequest(
}
// 조건충족 상태인지 확인
+ console.log("📋 업데이트 전 현재 데이터:", {
+ registrationId,
+ currentStatus: registration[0].status,
+ currentRemarks: registration[0].remarks,
+ currentUpdatedAt: registration[0].updatedAt
+ });
+
if (registration[0].status !== "approval_ready") {
return { success: false, error: "조건충족 상태가 아닙니다." };
}
@@ -1197,18 +1205,35 @@ export async function submitRegistrationRequest(
status: "requested" // 요청됨
};
- // 상태를 '등록요청됨'으로 변경하고 요청 데이터 저장
- await db
- .update(vendorRegularRegistrations)
- .set({
- status: "registration_requested",
- remarks: `정규업체 등록 요청됨 - ${new Date().toISOString()}\n요청자: ${session.user.name}`,
- updatedAt: new Date(),
- })
- .where(eq(vendorRegularRegistrations.id, registrationId));
+ // 트랜잭션으로 상태 변경
+ const updateResult = await db.transaction(async (tx) => {
+ return await tx
+ .update(vendorRegularRegistrations)
+ .set({
+ status: "registration_requested",
+ remarks: `정규업체 등록 요청됨 - ${new Date().toISOString()}\n요청자: ${session.user.name}`,
+ updatedAt: new Date(),
+ })
+ .where(eq(vendorRegularRegistrations.id, registrationId));
+ });
+
+ console.log("🔄 업데이트 결과:", {
+ registrationId,
+ updateResult,
+ statusToSet: "registration_requested"
+ });
+
- // TODO: MDG 인터페이스 연동
- // await sendToMDG(registrationRequestData);
+
+ // MDG 인터페이스 연동
+ const mdgResult = await sendRegistrationRequestToMDG(registrationId, requestData);
+
+ if (!mdgResult.success) {
+ console.error('❌ MDG 송신 실패:', mdgResult.error);
+ // MDG 송신 실패해도 등록 요청은 성공으로 처리 (재시도 가능하도록)
+ } else {
+ console.log('✅ MDG 송신 성공:', mdgResult.message);
+ }
// TODO: Knox 결재 연동
// - 사업자등록증, 신용평가보고서, 개인정보동의서, 통장사본
@@ -1225,13 +1250,14 @@ export async function submitRegistrationRequest(
requestDate: new Date().toISOString()
});
- // 캐시 무효화
+ // 캐시 무효화 - 더 강력한 무효화
revalidateTag("vendor-regular-registrations");
revalidateTag(`vendor-regular-registration-${registrationId}`);
+ revalidateTag("vendor-registration-status");
return {
success: true,
- message: "정규업체 등록 요청이 성공적으로 제출되었습니다.\nKnox 결재 시스템과 MDG 인터페이스 연동은 추후 구현 예정입니다."
+ message: `정규업체 등록 요청이 성공적으로 제출되었습니다.\n${mdgResult.success ? 'MDG 인터페이스 연동이 완료되었습니다.' : 'MDG 인터페이스 연동에 실패했습니다. (재시도 가능)'}\nKnox 결재 시스템 연동은 추후 구현 예정입니다.`
};
} catch (error) {
@@ -1241,4 +1267,165 @@ export async function submitRegistrationRequest(
error: error instanceof Error ? error.message : "정규업체 등록 요청 중 오류가 발생했습니다."
};
}
+}
+
+// MDG로 정규업체 등록 요청 데이터를 보내는 함수
+export async function sendRegistrationRequestToMDG(
+ registrationId: number,
+ requestData: RegistrationRequestData
+) {
+ try {
+ console.log('🚀 MDG로 정규업체 등록 요청 데이터 송신 시작');
+
+ // 세션 사용자 정보 가져오기
+ const session = await getServerSession(authOptions);
+ const userId = session?.user?.id || 'EVCP_USER';
+ const userName = session?.user?.name || 'EVCP_USER';
+ // 등록 정보 조회
+ const registration = await db
+ .select()
+ .from(vendorRegularRegistrations)
+ .where(eq(vendorRegularRegistrations.id, registrationId))
+ .limit(1);
+
+ if (!registration[0]) {
+ return { success: false, error: "등록 정보를 찾을 수 없습니다." };
+ }
+
+ // registration[0].vendorId를 이용해 벤더 정보 조회
+ const vendor = await db
+ .select()
+ .from(vendors)
+ .where(eq(vendors.id, registration[0].vendorId))
+ .limit(1);
+
+ if (!vendor[0]) {
+ return { success: false, error: "벤더 정보를 찾을 수 없습니다." };
+ }
+
+ // MDG 필수 필드 매핑 (이메일 내용 기반)
+ const mdgData: Record<string, string> = {
+ // 1. BP_HEADER: 벤더코드가 있으면 벤더코드를, 없으면 eVCP에서 관리번호를 보내드리겠습니다. (필수)
+ BP_HEADER: vendor[0].vendorCode || vendor[0].id.toString(),
+
+ // 2. ZZSRMCD: eVCP에서 내부관리번호를 보내드리겠습니다. (필수)
+ ZZSRMCD: vendor[0].id.toString(),
+
+ // 3. SORT1: 벤더명 보내드립니다. (필수)
+ SORT1: requestData.companyNameKor,
+
+ // 4. NAME1: 벤더명 보내드립니다. (필수)
+ NAME1: requestData.companyNameKor,
+
+ // 5. NAME2: 벤더 영문명 (있는 경우) (선택)
+ NAME2: requestData.companyNameEng || '',
+
+ // 6. KTOKK: 셈플로 받은자료에는 "LIEF"로 되어 있습니다. 고정값 (필수)
+ KTOKK: 'LIEF',
+
+ // 7. J_1KFREPRE: 대표자명 (필수)
+ J_1KFREPRE: requestData.representativeNameKor,
+
+ // 8. MASTERFLAG: 지시자 같은데, 어떤값을 보내면 되나요? -> V (필수)
+ MASTERFLAG: 'V',
+
+ // 9. IBND_TYPE: 입력가능한 값을 알려주시기 바랍니다. -> 생성 : I, 변경: U (필수)
+ IBND_TYPE: 'I',
+
+ // 10. ZZREQID: SAP의 USER ID를 보내드리겠습니다. (필수)
+ ZZREQID: userName,
+
+ // 11. ADDRNO: I/F정의서에는 필수입력으로 되어 있습니다. -> 빈값으로 처리 (필수)
+ ADDRNO: '',
+
+ // 12. COUNTRY: ISO 3166-1의 규약에 따른 국가코드를 보내드릴 예정입니다. (필수)
+ COUNTRY: vendor[0].country || 'KR',
+
+ // 13. POST_CODE1: 우편번호를 송부하겠습니다. (필수)
+ POST_CODE1: vendor[0].postalCode || '',
+
+ // 14. CITY1: 상세 주소를 송부하겠습니다. (필수) - 샘플에서는 상세주소가 들어감
+ CITY1: vendor[0].addressDetail || '',
+
+ // 15. STREET: 주소를 송부하겠습니다. (필수) - 샘플에서는 기본주소가 들어감
+ STREET: vendor[0].address || '',
+
+ // 16. TEL_NUMBER: 전화번호 (필수)
+ TEL_NUMBER: vendor[0].phone || '',
+
+ // 17. R3_USER: 전화/휴대폰 구분자로 해석됩니다. 0이면 전화, 1이면 휴대폰 (필수)
+ R3_USER: '0', // 일반 전화번호로 가정
+
+ // 18. TAXTYPE: 국가 코드에 맞게 하면 됩니다. 국가 KR -> TAXTYPE KR2 (필수)
+ TAXTYPE: (vendor[0].country || 'KR') + '2',
+
+ // 19. TAXNUM: 사업자번호 (필수)
+ TAXNUM: vendor[0].taxId || '',
+
+ // 20. BP_TX_TYP: 대표자 주민번호 YYMMDD + 0000000 (YYMMDD0000000) - 대표자 생년월일 기준으로 생성
+ BP_TX_TYP: requestData.representativeBirthDate ?
+ requestData.representativeBirthDate.replace(/-/g, '') + '0000000' : '',
+
+ // 21. STCD3: 법인등록번호 (선택)
+ STCD3: requestData.corporateNumber || '',
+
+ // 22. CONSNUMBER: 순번 (샘플에서는 1, 2로 설정됨)
+ CONSNUMBER: '1',
+
+ // 23. ZZIND03: 기업규모 (A,B,C,D 값을 넣는 것으로 알고 있습니다.) (선택)
+ ZZIND03: 'B', // 기본값으로 B 설정
+
+ // 24. J_1KFTBUS: 사업유형 (샘플에서는 "건설업외")
+ J_1KFTBUS: '',
+
+ // 25. J_1KFTIND: 산업유형 (샘플에서는 "제조업")
+ J_1KFTIND: '',
+
+ // 26. SMTP_ADDR: 대표 이메일 주소 (필수)
+ SMTP_ADDR: requestData.representativeEmail || vendor[0].email || '',
+
+ // 27. URI_ADDR: 웹사이트 주소 (선택)
+ URI_ADDR: vendor[0].website || '',
+
+ // 28. ZZCNAME1: 해당 벤더의 첫번째 유저의 이름 (영문) (선택)
+ ZZCNAME1: requestData.representativeNameEng || '',
+
+ // 29. ZZCNAME2: 해당 벤더의 첫번째 유저의 이름 (한글) (선택)
+ ZZCNAME2: requestData.representativeNameKor || '',
+
+ // 30. ZZTELF1_C: 해당 벤더의 첫번째 유저의 전화번호 (선택)
+ ZZTELF1_C: requestData.representativeContact || '',
+ };
+
+ // MDG로 데이터 전송
+ const result = await sendTestVendorDataToMDG(mdgData);
+
+ console.log('📤 MDG 송신 결과:', result);
+
+ if (!result.success) {
+ // 필수 필드 누락 에러인 경우 더 자세한 메시지 제공
+ if (result.message.includes('필수 필드가 누락되었습니다')) {
+ return {
+ success: false,
+ error: `MDG 송신 실패: ${result.message}\n\n누락된 필수 필드들을 확인하고 다시 시도해주세요.`
+ };
+ }
+ }
+
+ return {
+ success: result.success,
+ message: result.success ?
+ 'MDG로 정규업체 등록 요청이 성공적으로 전송되었습니다.' :
+ `MDG 송신 실패: ${result.message}`,
+ responseData: result.responseData,
+ generatedXML: result.generatedXML
+ };
+
+ } catch (error) {
+ console.error('❌ MDG 송신 실패:', error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'MDG 송신 중 오류가 발생했습니다.'
+ };
+ }
} \ No newline at end of file
diff --git a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
index 3a1216f2..df2ab53a 100644
--- a/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
+++ b/lib/vendor-regular-registrations/table/vendor-regular-registrations-table-toolbar-actions.tsx
@@ -2,7 +2,6 @@
import { type Table } from "@tanstack/react-table"
import { toast } from "sonner"
-import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Mail, FileWarning, Scale, FileText } from "lucide-react"
@@ -16,6 +15,7 @@ import {
import { useState } from "react"
import { SkipReasonDialog } from "@/components/vendor-regular-registrations/skip-reason-dialog"
import { RegistrationRequestDialog } from "@/components/vendor-regular-registrations/registration-request-dialog"
+import { useRouter } from "next/navigation"
interface VendorRegularRegistrationsTableToolbarActionsProps {
table: Table<VendorRegularRegistration>
@@ -158,7 +158,7 @@ export function VendorRegularRegistrationsTableToolbarActions({
if (result.success) {
toast.success(result.message);
setRegistrationRequestDialog({ open: false, registration: null });
- window.location.reload(); // 데이터 새로고침
+ router.refresh();
} else {
toast.error(result.error);
}
@@ -178,26 +178,6 @@ export function VendorRegularRegistrationsTableToolbarActions({
return (
<div className="flex items-center gap-2">
- {/* <Button
- variant="outline"
- size="sm"
- onClick={handleSyncDocuments}
- disabled={syncLoading.documents || selectedRows.length === 0}
- >
- <FileText className="mr-2 h-4 w-4" />
- {syncLoading.documents ? "동기화 중..." : "문서 동기화"}
- </Button>
-
- <Button
- variant="outline"
- size="sm"
- onClick={handleSyncAgreements}
- disabled={syncLoading.agreements || selectedRows.length === 0}
- >
- <RefreshCw className="mr-2 h-4 w-4" />
- {syncLoading.agreements ? "동기화 중..." : "계약 동기화"}
- </Button> */}
-
<Button
variant="outline"
size="sm"