"use server" import db from "@/db/db" import { and, eq, isNull, desc, sql, ne, or, ilike} from "drizzle-orm"; import { revalidatePath} from "next/cache"; import { format, addDays } from "date-fns" import { vendorInvestigations, vendorPQSubmissions, siteVisitRequests, vendorSiteVisitInfo, siteVisitRequestAttachments } from "@/db/schema/pq" import { sendEmail } from "../mail/sendEmail"; import { decryptWithServerAction } from '@/components/drm/drmUtils' import { vendors } from "@/db/schema/vendors"; import { saveFile, saveDRMFile } from "@/lib/file-stroage"; import { getServerSession } from "next-auth/next" import { authOptions } from "@/app/api/auth/[...nextauth]/route" import { users } from "@/db/schema" // 실사 ID로 모든 siteVisitRequests 조회 (복수 확정정보 지원) // 협력업체 제출 정보(vendorSiteVisitInfo) 포함 export async function getAllSiteVisitRequestsForInvestigationAction(investigationId: number) { try { const siteVisitRequestsList = await db .select({ id: siteVisitRequests.id, status: siteVisitRequests.status, inspectionDuration: siteVisitRequests.inspectionDuration, requestedStartDate: siteVisitRequests.requestedStartDate, requestedEndDate: siteVisitRequests.requestedEndDate, additionalRequests: siteVisitRequests.additionalRequests, createdAt: siteVisitRequests.createdAt, updatedAt: siteVisitRequests.updatedAt, }) .from(siteVisitRequests) .where(eq(siteVisitRequests.investigationId, investigationId)) .orderBy(desc(siteVisitRequests.createdAt)) // 각 siteVisitRequest에 대해 협력업체 제출 정보 조회 const requestsWithVendorInfo = await Promise.all( siteVisitRequestsList.map(async (request) => { const vendorInfoResult = await db .select() .from(vendorSiteVisitInfo) .where(eq(vendorSiteVisitInfo.siteVisitRequestId, request.id)) .limit(1) const vendorInfo = vendorInfoResult.length > 0 ? vendorInfoResult[0] : null // 첨부파일 조회 (vendorSiteVisitInfo가 있는 경우) let attachments: any[] = [] if (vendorInfo) { attachments = await db .select() .from(siteVisitRequestAttachments) .where(eq(siteVisitRequestAttachments.vendorSiteVisitInfoId, vendorInfo.id)) } return { ...request, vendorInfo, attachments, } }) ) return { success: true, requests: requestsWithVendorInfo } } catch (error) { console.error("실사 확정정보 조회 오류:", error) return { success: false, error: "실사 확정정보 조회에 실패했습니다." } } } // 재방문 실사 요청을 위한 방문실사 생성 (동일 investigationId에 대해 여러 개 허용) export async function createReinspectionSiteVisitAction(input: { investigationId: number; inspectionDuration: number; requestedStartDate: Date; requestedEndDate: Date; shiAttendees: Record; // {userId: name} vendorRequests: Record; additionalRequests?: string; investigationAddress: string; }) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("Unauthorized"); } // 실사 정보 확인 const investigation = await db .select() .from(vendorInvestigations) .where(eq(vendorInvestigations.id, input.investigationId)) .limit(1); if (!investigation.length) { return { success: false, error: "실사 정보를 찾을 수 없습니다." }; } // PQ 정보 확인 const pqSubmission = await db .select() .from(vendorPQSubmissions) .where(eq(vendorPQSubmissions.id, investigation[0].pqSubmissionId!)) .limit(1); if (!pqSubmission.length) { return { success: false, error: "PQ 정보를 찾을 수 없습니다." }; } // 방문실사 요청 생성 (재실사이므로 기존 확인 로직 생략) const [newRequest] = await db .insert(siteVisitRequests) .values({ investigationId: input.investigationId, inspectionDuration: input.inspectionDuration, requestedStartDate: input.requestedStartDate, requestedEndDate: input.requestedEndDate, shiAttendees: input.shiAttendees, vendorRequests: input.vendorRequests, additionalRequests: input.additionalRequests, status: "REQUESTED", createdAt: new Date(), updatedAt: new Date(), }) .returning(); // 벤더에게 이메일 발송 const vendor = await db .select({ email: vendors.email, vendorName: vendors.vendorName }) .from(vendors) .where(eq(vendors.id, investigation[0].vendorId)) .limit(1); if (vendor.length && vendor[0].email) { const headersList = await import("next/headers").then(m => m.headers()); const host = headersList.get('host') || 'localhost:3000'; const portalUrl = process.env.NEXTAUTH_URL || `http://${host}`; await sendEmail({ to: vendor[0].email, subject: `[eVCP] 재실사 방문요청 - ${vendor[0].vendorName}`, template: "site-visit-request", context: { vendorName: vendor[0].vendorName, inspectionDuration: input.inspectionDuration, requestedStartDate: format(input.requestedStartDate, "yyyy년 MM월 dd일"), requestedEndDate: format(input.requestedEndDate, "yyyy년 MM월 dd일"), investigationAddress: input.investigationAddress, additionalRequests: input.additionalRequests || "", reviewUrl: `${portalUrl}/evcp/vendor-investigation`, year: new Date().getFullYear(), } }); } revalidatePath("/evcp/vendor-investigation"); return { success: true, siteVisitRequestId: newRequest.id }; } catch (error) { console.error("재실사 방문요청 생성 실패:", error); return { success: false, error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } // 방문실사 요청 서버 액션 export async function createSiteVisitRequestAction(input: { investigationId: number; inspectionDuration: number; requestedStartDate: Date; requestedEndDate: Date; shiAttendees: { [key: string]: { checked: boolean; attendees: Array<{ name: string; department?: string; email?: string; }>; }; }; shiAttendeeDetails?: string; vendorRequests: Record; otherVendorRequests?: string; additionalRequests?: string; attachments?: Array; }) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("Unauthorized"); } const investigationId = Number(input.investigationId) // 기존 방문실사 요청이 있는지 확인 const existingRequest = await db .select() .from(siteVisitRequests) .where(eq(siteVisitRequests.investigationId, investigationId)) .limit(1); // if (existingRequest.length > 0) { // return { // success: false, // error: "이미 방문실사 요청이 존재합니다. 추가 요청은 불가능합니다." // }; // } // 방문실사 요청 생성 const [siteVisitRequest] = await db .insert(siteVisitRequests) .values({ investigationId: investigationId, requesterId: session.user.id, inspectionDuration: input.inspectionDuration, requestedStartDate: input.requestedStartDate, requestedEndDate: input.requestedEndDate, shiAttendees: input.shiAttendees, vendorRequests: input.vendorRequests, additionalRequests: input.additionalRequests, status: "REQUESTED", }) .returning(); // SHI 첨부파일 처리 if (input.attachments && input.attachments.length > 0) { console.log(`📎 첨부파일 처리 시작: ${input.attachments.length}개 파일`); const processedFiles: any[] = []; for (const file of input.attachments) { try { console.log(`📁 파일 처리 중: ${file.name} (${file.size} bytes)`); // saveDRMFile을 사용하여 파일 저장 const saveResult = await saveDRMFile( file, decryptWithServerAction, `site-visit-requests/${siteVisitRequest.id}`, session.user.id.toString() ); if (!saveResult.success) { console.error(`❌ 파일 저장 실패: ${file.name}`, saveResult.error); throw new Error(`파일 저장 실패: ${file.name} - ${saveResult.error}`); } console.log(`✅ 파일 저장 완료: ${file.name} -> ${saveResult.fileName}`); // DB에 첨부파일 레코드 생성 const attachmentValue = { siteVisitRequestId: siteVisitRequest.id, vendorSiteVisitInfoId: null, // SHI 첨부파일은 vendorSiteVisitInfoId가 null fileName: saveResult.fileName!, originalFileName: file.name, filePath: saveResult.publicPath!, fileSize: file.size, mimeType: file.type || 'application/octet-stream', createdAt: new Date(), updatedAt: new Date(), }; processedFiles.push(attachmentValue); } catch (error) { console.error(`❌ 첨부파일 처리 오류: ${file.name}`, error); throw new Error(`첨부파일 처리 중 오류가 발생했습니다: ${file.name}`); } } if (processedFiles.length > 0) { await db.insert(siteVisitRequestAttachments).values(processedFiles); console.log(`✅ 첨부파일 DB 저장 완료: ${processedFiles.length}개`); } } // 이메일 발송 try { // 실사, 협력업체, 발송자 정보 조회 const investigationResult = await db .select() .from(vendorInvestigations) .where(eq(vendorInvestigations.id, siteVisitRequest.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, siteVisitRequest.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 ? format(forecastedDate, 'yyyy.MM.dd') : format(deadlineFromToday, 'yyyy.MM.dd'); } return format(deadlineFromToday, 'yyyy.MM.dd'); })(); // 실사 방법 한글 매핑 const investigationMethodMap: Record = { 'PURCHASE_SELF_EVAL': '구매자체평가', 'DOCUMENT_EVAL': '서류평가', 'PRODUCT_INSPECTION': '제품검사평가', 'SITE_VISIT_EVAL': '방문실사평가' }; const investigationMethodKorean = investigation.investigationMethod ? (investigationMethodMap[investigation.investigationMethod] || investigation.investigationMethod) : null; // SHI 참석자 정보 파싱 (새로운 구조에 맞게) const shiAttendees = input.shiAttendees as any; // 실사 주소 및 기간/일정은 QM이 입력한 값 사용 const investigationAddress = investigation.investigationAddress || ''; const scheduledStartDate = investigation.scheduledStartAt ? format(new Date(investigation.scheduledStartAt), 'yyyy.MM.dd') : format(siteVisitRequest.requestedStartDate!, 'yyyy.MM.dd'); const scheduledEndDate = investigation.scheduledEndAt ? format(new Date(investigation.scheduledEndAt), 'yyyy.MM.dd') : format(siteVisitRequest.requestedEndDate!, 'yyyy.MM.dd'); const scheduledDuration = investigation.scheduledStartAt && investigation.scheduledEndAt ? Math.ceil((new Date(investigation.scheduledEndAt).getTime() - new Date(investigation.scheduledStartAt).getTime()) / (1000 * 60 * 60 * 24)) : siteVisitRequest.inspectionDuration; // SHI 참석자 정보 (새로운 구조: attendees 배열) const shiAttendeesList: string[] = []; const attendeeEmails: string[] = []; Object.entries(shiAttendees).forEach(([key, value]: [string, any]) => { if (value?.checked && value?.attendees && Array.isArray(value.attendees) && value.attendees.length > 0) { const departmentLabels: Record = { technicalSales: "기술영업", design: "설계", procurement: "구매", quality: "품질", production: "생산", commissioning: "시운전", other: "기타" }; const departmentName = departmentLabels[key] || key; // 참석자 목록 생성 const attendeeCount = value.attendees.length; const attendeeDetails = value.attendees .map((attendee: any) => { const parts: string[] = []; if (attendee.name) parts.push(attendee.name); if (attendee.department) parts.push(attendee.department); return parts.join(' / '); }) .filter(Boolean) .join(', '); const details = attendeeDetails ? ` (${attendeeDetails})` : ''; shiAttendeesList.push(`${departmentName} ${attendeeCount}명${details}`); // 이메일 수집 value.attendees.forEach((attendee: any) => { if (attendee?.email && attendee.email.trim() && attendee.email.includes('@')) { attendeeEmails.push(attendee.email.trim()); } }); } }); // 중복 제거 및 유효성 검증 const uniqueAttendeeEmails = Array.from(new Set(attendeeEmails.filter(email => email && email.includes('@')))); // 메일 제목 const subject = `[SHI Audit] 방문실사 시행 안내 및 실사 관련 추가정보 요청 _ ${vendor.vendorName}`; // 메일 컨텍스트 const context = { // 기본 정보 vendorName: vendor.vendorName, vendorEmail: vendor.email || '', requesterName: sender.name, requesterTitle: 'Procurement Manager', requesterEmail: sender.email, // 실사 정보 investigationMethod: investigationMethodKorean, investigationAddress: investigationAddress, requestedStartDate: scheduledStartDate, requestedEndDate: scheduledEndDate, inspectionDuration: scheduledDuration, // 마감일 deadlineDate, // SHI 참석자 정보 (새로운 구조) shiAttendees: shiAttendeesList, shiAttendeeDetails: input.shiAttendeeDetails || null, // 협력업체 요청 정보 (default 값으로 고정) vendorRequests: [ '실사 공장 정보(공장명, 주소, 접근 방법 등)', '실사 일정 확인', '협력업체 실사 참석자 정보', '사전 조치 필요 사항(출입증 등)' ], otherVendorRequests: input.otherVendorRequests, // 추가 요청사항 additionalRequests: siteVisitRequest.additionalRequests, // 포털 URL portalUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/ko/partners/site-visit`, // 현재 연도 currentYear: new Date().getFullYear() }; // 메일 발송 (벤더 이메일로 직접 발송) // cc에는 요청자 및 SHI 참석자 이메일 모두 포함 const ccEmails: string[] = []; if (sender.email) { ccEmails.push(sender.email); } // 참석자 이메일 추가 (요청자 이메일과 중복 제거) uniqueAttendeeEmails.forEach(email => { if (email && email !== sender.email && !ccEmails.includes(email)) { ccEmails.push(email); } }); await sendEmail({ to: vendor.email || '', cc: ccEmails.length > 0 ? ccEmails : undefined, subject, template: 'site-visit-request' as string, context, }); console.log('방문실사 요청 메일 발송 완료:', { to: vendor.email, subject, vendorName: vendor.vendorName }); // 메일 발송 성공 시 상태 업데이트 await db .update(siteVisitRequests) .set({ status: "SENT", sentAt: new Date() }) .where(eq(siteVisitRequests.id, siteVisitRequest.id)); } catch (emailError) { console.error('방문실사 요청 메일 발송 실패:', emailError); } revalidatePath("/evcp/pq_new"); revalidatePath("/partners/site-visit"); return { success: true, data: siteVisitRequest, message: "방문실사 요청이 성공적으로 생성되었습니다." }; } catch (error) { console.error("방문실사 요청 생성 오류:", error); return { success: false, error: "방문실사 요청 생성 중 오류가 발생했습니다." }; } } // 방문실사 요청 조회 서버 액션 export async function getSiteVisitRequestAction(investigationId: number) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("Unauthorized"); } const siteVisitRequest = await db .select() .from(siteVisitRequests) .where(eq(siteVisitRequests.investigationId, investigationId)) .limit(1); if (!siteVisitRequest[0]) { return { success: true, data: null }; } // SHI 첨부파일 조회 (vendorSiteVisitInfoId가 null인 것들) const shiAttachments = await db .select() .from(siteVisitRequestAttachments) .where( and( eq(siteVisitRequestAttachments.siteVisitRequestId, siteVisitRequest[0].id), isNull(siteVisitRequestAttachments.vendorSiteVisitInfoId) ) ); return { success: true, data: { ...siteVisitRequest[0], shiAttachments } }; } catch (error) { console.error("방문실사 요청 조회 오류:", error); return { success: false, error: "방문실사 요청 조회 중 오류가 발생했습니다." }; } } // 협력업체용 방문실사 요청 조회 export async function getSiteVisitRequestsByVendorId(vendorId: number) { try { const result = await db .select({ id: siteVisitRequests.id, investigationId: siteVisitRequests.investigationId, requesterId: siteVisitRequests.requesterId, inspectionDuration: siteVisitRequests.inspectionDuration, requestedStartDate: siteVisitRequests.requestedStartDate, requestedEndDate: siteVisitRequests.requestedEndDate, shiAttendees: siteVisitRequests.shiAttendees, vendorRequests: siteVisitRequests.vendorRequests, additionalRequests: siteVisitRequests.additionalRequests, status: siteVisitRequests.status, sentAt: siteVisitRequests.sentAt, createdAt: siteVisitRequests.createdAt, updatedAt: siteVisitRequests.updatedAt, // 실사 정보 investigationMethod: vendorInvestigations.investigationMethod, investigationAddress: vendorInvestigations.investigationAddress, investigationNotes: vendorInvestigations.investigationNotes, forecastedAt: vendorInvestigations.forecastedAt, actualAt: vendorInvestigations.completedAt, result: vendorInvestigations.evaluationResult, resultNotes: vendorInvestigations.purchaseComment, // PQ 정보 pqItems: vendorPQSubmissions.pqItems, // 협력업체 정보 vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, vendorEmail: vendors.email, }) .from(siteVisitRequests) .leftJoin( vendorInvestigations, eq(siteVisitRequests.investigationId, vendorInvestigations.id) ) .leftJoin( sql`users AS requester`, eq(siteVisitRequests.requesterId, sql`requester.id`) ) .leftJoin( vendors, eq(vendorInvestigations.vendorId, vendors.id) ) .leftJoin( vendorPQSubmissions, eq(vendorInvestigations.pqSubmissionId, vendorPQSubmissions.id) ) .where(eq(vendorInvestigations.vendorId, vendorId)) .orderBy(desc(siteVisitRequests.createdAt)); // 각 방문실사 요청에 대해 협력업체 정보 조회 const resultWithVendorInfo = await Promise.all( result.map(async (item) => { const vendorInfoResult = await db .select({ id: vendorSiteVisitInfo.id, siteVisitRequestId: vendorSiteVisitInfo.siteVisitRequestId, factoryName: vendorSiteVisitInfo.factoryName, factoryLocation: vendorSiteVisitInfo.factoryLocation, factoryAddress: vendorSiteVisitInfo.factoryAddress, factoryPicName: vendorSiteVisitInfo.factoryPicName, factoryPicPhone: vendorSiteVisitInfo.factoryPicPhone, factoryPicEmail: vendorSiteVisitInfo.factoryPicEmail, factoryDirections: vendorSiteVisitInfo.factoryDirections, accessProcedure: vendorSiteVisitInfo.accessProcedure, hasAttachments: vendorSiteVisitInfo.hasAttachments, otherInfo: vendorSiteVisitInfo.otherInfo, submittedAt: vendorSiteVisitInfo.submittedAt, submittedBy: vendorSiteVisitInfo.submittedBy, createdAt: vendorSiteVisitInfo.createdAt, updatedAt: vendorSiteVisitInfo.updatedAt, }) .from(vendorSiteVisitInfo) .where(eq(vendorSiteVisitInfo.siteVisitRequestId, item.id)) .limit(1); const vendorInfo = vendorInfoResult.length > 0 ? vendorInfoResult[0] : null; // SHI 첨부파일 조회 (vendorSiteVisitInfoId가 null인 것들) const shiAttachments = await db .select() .from(siteVisitRequestAttachments) .where( and( eq(siteVisitRequestAttachments.siteVisitRequestId, item.id), isNull(siteVisitRequestAttachments.vendorSiteVisitInfoId) ) ); return { ...item, shiAttendees: item.shiAttendees as Record | null, vendorRequests: item.vendorRequests as Record | null, vendorInfo, shiAttachments, }; }) ); console.log(`📊 방문실사 요청 조회 완료 - 총 ${resultWithVendorInfo.length}개 요청`) console.log(`🔍 실제실사일/실사결과 데이터 확인:`, resultWithVendorInfo.map(item => ({ id: item.id, actualAt: item.actualAt, result: item.result, investigationId: item.investigationId }))) return resultWithVendorInfo; } catch (error) { console.error("방문실사 요청 조회 오류:", error); return []; } } // 협력업체 정보 제출 서버 액션 export async function submitVendorInfoAction(input: { siteVisitRequestId: number; factoryName: string; factoryLocation: string; factoryAddress: string; factoryPicName: string; factoryPicPhone: string; factoryPicEmail: string; factoryDirections: string; accessProcedure: string; hasAttachments: boolean; otherInfo?: string; attachments?: Array; }) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("Unauthorized"); } // 기존 협력업체 정보가 있는지 확인 const existingInfo = await db .select() .from(vendorSiteVisitInfo) .where(eq(vendorSiteVisitInfo.siteVisitRequestId, input.siteVisitRequestId)) .limit(1); if (existingInfo.length > 0) { // 기존 정보 업데이트 await db .update(vendorSiteVisitInfo) .set({ factoryName: input.factoryName, factoryLocation: input.factoryLocation, factoryAddress: input.factoryAddress, factoryPicName: input.factoryPicName, factoryPicPhone: input.factoryPicPhone, factoryPicEmail: input.factoryPicEmail, factoryDirections: input.factoryDirections, accessProcedure: input.accessProcedure, hasAttachments: input.hasAttachments, otherInfo: input.otherInfo, // submittedBy: session.user.id, submittedAt: new Date(), }) .where(eq(vendorSiteVisitInfo.siteVisitRequestId, input.siteVisitRequestId)); } else { // 새로운 정보 삽입 await db .insert(vendorSiteVisitInfo) .values({ siteVisitRequestId: input.siteVisitRequestId, factoryName: input.factoryName, factoryLocation: input.factoryLocation, factoryAddress: input.factoryAddress, factoryPicName: input.factoryPicName, factoryPicPhone: input.factoryPicPhone, factoryPicEmail: input.factoryPicEmail, factoryDirections: input.factoryDirections, accessProcedure: input.accessProcedure, hasAttachments: input.hasAttachments, otherInfo: input.otherInfo, submittedBy: Number(session.user.id), }); } // 첨부파일 처리 if (input.attachments && input.attachments.length > 0) { console.log(`📎 협력업체 첨부파일 처리 시작: ${input.attachments.length}개 파일`); // 기존 첨부파일 삭제 (업데이트 시) if (existingInfo.length > 0) { console.log(`🗑️ 기존 첨부파일 삭제: vendorSiteVisitInfoId ${existingInfo[0].id}`); await db .delete(siteVisitRequestAttachments) .where(eq(siteVisitRequestAttachments.vendorSiteVisitInfoId, existingInfo[0].id)); } let attachmentValues: any[] = []; for (const file of input.attachments) { try { console.log(`📁 협력업체 파일 처리 중: ${file.name} (${file.size} bytes)`); // saveFile을 사용하여 파일 저장 (협력업체 첨부파일은 일반 파일로 처리) const saveResult = await saveFile({ file, directory: `site-visit-vendor-info/${input.siteVisitRequestId}`, originalName: file.name, userId: session.user.id.toString() }); if (!saveResult.success) { console.error(`❌ 협력업체 파일 저장 실패: ${file.name}`, saveResult.error); throw new Error(`파일 저장 실패: ${file.name} - ${saveResult.error}`); } console.log(`✅ 협력업체 파일 저장 완료: ${file.name} -> ${saveResult.fileName}`); // DB에 첨부파일 레코드 생성 const attachmentValue = { siteVisitRequestId: input.siteVisitRequestId, vendorSiteVisitInfoId: existingInfo.length > 0 ? existingInfo[0].id : null, fileName: saveResult.fileName!, originalFileName: file.name, filePath: saveResult.publicPath!, fileSize: file.size, mimeType: file.type || 'application/octet-stream', createdAt: new Date(), updatedAt: new Date(), }; attachmentValues.push(attachmentValue as any); } catch (error) { console.error(`❌ 협력업체 첨부파일 처리 오류: ${file.name}`, error); throw new Error(`첨부파일 처리 중 오류가 발생했습니다: ${file.name}`); } } if (attachmentValues.length > 0) { await db.insert(siteVisitRequestAttachments).values(attachmentValues); console.log(`✅ 협력업체 첨부파일 DB 저장 완료: ${attachmentValues.length}개`); } } // 방문실사 요청 상태 업데이트 await db .update(siteVisitRequests) .set({ status: "VENDOR_SUBMITTED" }) .where(eq(siteVisitRequests.id, input.siteVisitRequestId)); revalidatePath("/evcp/pq_new"); revalidatePath("/partners/site-visit"); return { success: true, message: "협력업체 정보가 성공적으로 제출되었습니다." }; } catch (error) { console.error("협력업체 정보 제출 오류:", error); return { success: false, error: "협력업체 정보 제출 중 오류가 발생했습니다." }; } } // SHI eVCP에서 협력업체 방문실사 정보 조회 export async function getVendorSiteVisitInfoAction(siteVisitRequestId: number) { try { // 새로운 테이블에서 협력업체 정보 조회 const vendorInfoResult = await db .select() .from(vendorSiteVisitInfo) .where(eq(vendorSiteVisitInfo.siteVisitRequestId, siteVisitRequestId)) .limit(1); const vendorInfo = vendorInfoResult.length > 0 ? vendorInfoResult[0] : null; if (!vendorInfo) { return { success: false, error: "해당 방문실사 요청에 대한 협력업체 정보가 없습니다." }; } // 첨부파일 조회 const attachments = await db .select() .from(siteVisitRequestAttachments) .where(eq(siteVisitRequestAttachments.vendorSiteVisitInfoId, vendorInfo.id)); return { success: true, data: { vendorInfo, attachments } }; } catch (error) { console.error("협력업체 방문실사 정보 조회 오류:", error); return { success: false, error: "협력업체 방문실사 정보 조회 중 오류가 발생했습니다." }; } } // domain이 'partners'가 아닌 사용자 목록 가져오기 export async function getUsersForSiteVisitAction() { try { // domain이 'partners'가 아니고, isActive가 true인 사용자만 조회 const userList = await db .select({ id: users.id, name: users.name, email: users.email, deptName: users.deptName, }) .from(users) .where( and( eq(users.isActive, true), ne(users.domain, "partners") ) ) .orderBy(users.name); return { data: userList, success: true } } catch (error) { console.error("사용자 목록 조회 오류:", error) return { data: [], success: false, error: error instanceof Error ? error.message : "사용자 목록을 가져오는 중 오류가 발생했습니다." } } }