diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-03 10:15:45 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-03 10:15:45 +0000 |
| commit | f2fafe555b65f9207c2c6e216b7d7b2ff83af866 (patch) | |
| tree | 4a230e4bde10a612150a299922bc04cb15b0930f /lib/vendor-investigation/service.ts | |
| parent | 1e857a0b1443ad2124caf3d180b7195651fe33e4 (diff) | |
(최겸) 구매 PQ/실사 수정
Diffstat (limited to 'lib/vendor-investigation/service.ts')
| -rw-r--r-- | lib/vendor-investigation/service.ts | 198 |
1 files changed, 178 insertions, 20 deletions
diff --git a/lib/vendor-investigation/service.ts b/lib/vendor-investigation/service.ts index 3ccbe880..c365a7ad 100644 --- a/lib/vendor-investigation/service.ts +++ b/lib/vendor-investigation/service.ts @@ -1,6 +1,6 @@ "use server"; // Next.js 서버 액션에서 직접 import하려면 (선택) -import { items, vendorInvestigationAttachments, vendorInvestigations, vendorInvestigationsView, vendorPossibleItems, vendors, siteVisitRequests, vendorPQSubmissions } from "@/db/schema/" +import { items, vendorInvestigationAttachments, vendorInvestigations, vendorInvestigationsView, vendorPossibleItems, vendors, siteVisitRequests, vendorPQSubmissions, users } from "@/db/schema/" import { GetVendorsInvestigationSchema, updateVendorInvestigationSchema, updateVendorInvestigationProgressSchema, updateVendorInvestigationResultSchema } from "./validations" import { asc, desc, ilike, inArray, and, or, gte, lte, eq, isNull, count } from "drizzle-orm"; import { revalidateTag, unstable_noStore, revalidatePath } from "next/cache"; @@ -17,6 +17,7 @@ import { cache } from "react" import { deleteFile } from "../file-stroage"; import { saveDRMFile } from "../file-stroage"; import { decryptWithServerAction } from "@/components/drm/drmUtils"; +import { format, addDays } from "date-fns"; export async function getVendorsInvestigation(input: GetVendorsInvestigationSchema) { return unstable_cache( @@ -1057,27 +1058,102 @@ export async function requestSupplementDocumentAction({ }) .where(eq(vendorInvestigations.id, investigationId)); - // 2. 서류제출 요청을 위한 방문실사 요청 생성 (서류제출용) - const [newSiteVisitRequest] = await db - .insert(siteVisitRequests) - .values({ - investigationId: investigationId, - inspectionDuration: 0, // 서류제출은 방문 시간 0 - shiAttendees: {}, // 서류제출은 참석자 없음 - vendorRequests: { - requiredDocuments: documentRequests.requiredDocuments, - documentSubmissionOnly: true, // 서류제출 전용 플래그 - }, - additionalRequests: documentRequests.additionalRequests, - status: "REQUESTED", - }) - .returning(); + // 2. 실사, 협력업체, 발송자 정보 조회 + const investigationResult = await db + .select() + .from(vendorInvestigations) + .where(eq(vendorInvestigations.id, investigationId)) + .limit(1); + + const investigation = investigationResult[0]; + if (!investigation) { + throw new Error('실사 정보를 찾을 수 없습니다.'); + } + + const vendorResult = await db + .select() + .from(vendors) + .where(eq(vendors.id, investigation.vendorId)) + .limit(1); + + const vendor = vendorResult[0]; + if (!vendor) { + throw new Error('협력업체 정보를 찾을 수 없습니다.'); + } + + const senderResult = await db + .select() + .from(users) + .where(eq(users.id, investigation.requesterId!)) + .limit(1); + + const sender = senderResult[0]; + if (!sender) { + throw new Error('발송자 정보를 찾을 수 없습니다.'); + } + + // 마감일 계산 (발송일 + 7일 또는 실사 예정일 중 먼저 도래하는 날) + const deadlineDate = (() => { + const deadlineFromToday = addDays(new Date(), 7); + if (investigation.forecastedAt) { + const forecastedDate = new Date(investigation.forecastedAt); + return forecastedDate < deadlineFromToday ? forecastedDate : deadlineFromToday; + } + return deadlineFromToday; + })(); + + // 메일 제목 + const subject = `[SHI Audit] 보완 서류제출 요청 _ ${vendor.vendorName}`; + + // 메일 컨텍스트 + const context = { + // 기본 정보 + vendorName: vendor.vendorName, + vendorEmail: vendor.email || '', + requesterName: sender.name, + requesterTitle: 'Procurement Manager', + requesterEmail: sender.email, + + // 보완 요청 서류 + requiredDocuments: documentRequests.requiredDocuments || [], + + // 추가 요청사항 + additionalRequests: documentRequests.additionalRequests || null, + + // 마감일 + deadlineDate: format(deadlineDate, 'yyyy.MM.dd'), + + // 포털 URL + portalUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/ko/partners/site-visit`, + + // 현재 연도 + currentYear: new Date().getFullYear() + }; + + // 메일 발송 (벤더 이메일로 직접 발송) + try { + await sendEmail({ + to: vendor.email || '', + cc: sender.email, + subject, + template: 'supplement-document-request' as string, + context, + }); + + console.log('보완 서류제출 요청 메일 발송 완료:', { + to: vendor.email, + subject, + vendorName: vendor.vendorName + }); + } catch (emailError) { + console.error('보완 서류제출 요청 메일 발송 실패:', emailError); + } // 3. 캐시 무효화 revalidateTag("vendor-investigations"); revalidateTag("site-visit-requests"); - return { success: true, siteVisitRequestId: newSiteVisitRequest.id }; + return { success: true }; } catch (error) { console.error("보완-서류제출 요청 실패:", error); return { @@ -1325,9 +1401,91 @@ export async function submitSupplementDocumentResponseAction({ return { success: true }; } catch (error) { console.error("보완 서류제출 응답 처리 실패:", error); - return { - success: false, - error: error instanceof Error ? error.message : "알 수 없는 오류" + return { + success: false, + error: error instanceof Error ? error.message : "알 수 없는 오류" + }; + } +} + +// QM 담당자 변경 서버 액션 +export async function updateQMManagerAction({ + investigationId, + qmManagerId, +}: { + investigationId: number; + qmManagerId: number; +}) { + try { + // 1. 실사 정보 조회 (상태 확인) + const investigation = await db + .select({ + investigationStatus: vendorInvestigations.investigationStatus, + currentQmManagerId: vendorInvestigations.qmManagerId, + }) + .from(vendorInvestigations) + .where(eq(vendorInvestigations.id, investigationId)) + .limit(1); + + if (!investigation || investigation.length === 0) { + return { + success: false, + error: "실사를 찾을 수 없습니다." + }; + } + + const currentInvestigation = investigation[0]; + + // 2. 상태 검증 (계획 상태만 변경 가능) + if (currentInvestigation.investigationStatus !== "PLANNED") { + return { + success: false, + error: "계획 상태인 실사만 QM 담당자를 변경할 수 있습니다." + }; + } + + // 3. QM 담당자 정보 조회 + const qmManager = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + }) + .from(users) + .where(eq(users.id, qmManagerId)) + .limit(1); + + if (!qmManager || qmManager.length === 0) { + return { + success: false, + error: "존재하지 않는 QM 담당자입니다." + }; + } + + const qmUser = qmManager[0]; + + // 4. QM 담당자 업데이트 + await db + .update(vendorInvestigations) + .set({ + qmManagerId: qmManagerId, + updatedAt: new Date(), + }) + .where(eq(vendorInvestigations.id, investigationId)); + + // 5. 캐시 무효화 + revalidateTag("vendor-investigations"); + + return { + success: true, + message: "QM 담당자가 성공적으로 변경되었습니다." + }; + + } catch (error) { + console.error("QM 담당자 변경 오류:", error); + return { + success: false, + error: error instanceof Error ? error.message : "QM 담당자 변경 중 오류가 발생했습니다." }; } } |
