summaryrefslogtreecommitdiff
path: root/lib/vendor-investigation/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-investigation/service.ts')
-rw-r--r--lib/vendor-investigation/service.ts139
1 files changed, 134 insertions, 5 deletions
diff --git a/lib/vendor-investigation/service.ts b/lib/vendor-investigation/service.ts
index f81f78f6..3ccbe880 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 } from "@/db/schema/"
+import { items, vendorInvestigationAttachments, vendorInvestigations, vendorInvestigationsView, vendorPossibleItems, vendors, siteVisitRequests, vendorPQSubmissions } 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";
@@ -131,6 +131,46 @@ export async function getExistingInvestigationsForVendors(vendorIds: number[]) {
}
}
+// PQ 제출 타입 조회 (investigation.pqSubmissionId → type)
+export default async function getPQSubmissionTypeAction(pqSubmissionId: number) {
+ try {
+ const row = await db
+ .select({ type: vendorPQSubmissions.type })
+ .from(vendorPQSubmissions)
+ .where(eq(vendorPQSubmissions.id, pqSubmissionId))
+ .limit(1)
+ .then(rows => rows[0]);
+ if (!row) return { success: false, error: "PQ submission not found" };
+ return { success: true, type: row.type as "GENERAL" | "PROJECT" | "NON_INSPECTION" };
+ } catch (e) {
+ return { success: false, error: e instanceof Error ? e.message : "Unknown error" };
+ }
+}
+
+// 실사 계획 취소 액션: 상태를 QM_REVIEW_CONFIRMED로 되돌림
+export async function cancelInvestigationPlanAction(investigationId: number) {
+ try {
+ await db
+ .update(vendorInvestigations)
+ .set({
+ investigationStatus: "QM_REVIEW_CONFIRMED",
+ updatedAt: new Date(),
+ })
+ .where(eq(vendorInvestigations.id, investigationId))
+
+ revalidateTag("vendor-investigations")
+ revalidatePath("/evcp/vendor-investigation")
+
+ return { success: true }
+ } catch (error) {
+ console.error("실사 계획 취소 오류:", error)
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : "알 수 없는 오류",
+ }
+ }
+}
+
interface RequestInvestigateVendorsInput {
ids: number[]
}
@@ -228,7 +268,7 @@ export async function updateVendorInvestigationProgressAction(formData: FormData
processedEntries.confirmedAt = new Date(textEntries.confirmedAt)
}
- // 3) Zod로 파싱/검증
+ // 3) Zod로 파싱/검증 (4개 필수값 규칙 포함)
const parsed = updateVendorInvestigationProgressSchema.parse(processedEntries)
// 4) 업데이트 데이터 준비
@@ -250,7 +290,7 @@ export async function updateVendorInvestigationProgressAction(formData: FormData
updateData.confirmedAt = parsed.confirmedAt
}
- // 실사 방법이 설정되면 PLANNED -> IN_PROGRESS로 상태 변경
+ // 실사 방법이 설정되면 QM_REVIEW_CONFIRMED -> IN_PROGRESS로 상태 변경
if (parsed.investigationMethod) {
updateData.investigationStatus = "IN_PROGRESS"
}
@@ -334,10 +374,12 @@ export async function updateVendorInvestigationResultAction(formData: FormData)
if (parsed.evaluationResult) {
if (parsed.evaluationResult === "REJECTED") {
updateData.investigationStatus = "CANCELED"
- } else if (parsed.evaluationResult === "SUPPLEMENT" ||
- parsed.evaluationResult === "SUPPLEMENT_REINSPECT" ||
+ } else if (parsed.evaluationResult === "SUPPLEMENT" ||
+ parsed.evaluationResult === "SUPPLEMENT_REINSPECT" ||
parsed.evaluationResult === "SUPPLEMENT_DOCUMENT") {
updateData.investigationStatus = "SUPPLEMENT_REQUIRED"
+ // 보완 요청이 있었음을 기록
+ updateData.hasSupplementRequested = true
} else if (parsed.evaluationResult === "APPROVED") {
updateData.investigationStatus = "COMPLETED"
}
@@ -1150,6 +1192,93 @@ export async function completeSupplementReinspectionAction({
}
}
+// 실사 보완요청 메일 발송 액션
+export async function requestInvestigationSupplementAction({
+ investigationId,
+ vendorId,
+ comment,
+}: {
+ investigationId: number;
+ vendorId: number;
+ comment: string;
+}) {
+ unstable_noStore();
+ try {
+ const headersList = await import("next/headers").then(m => m.headers());
+ const host = headersList.get('host') || 'localhost:3000';
+
+ // 실사/벤더 정보 조회
+ const investigation = await db
+ .select({
+ id: vendorInvestigations.id,
+ pqSubmissionId: vendorInvestigations.pqSubmissionId,
+ investigationAddress: vendorInvestigations.investigationAddress,
+ })
+ .from(vendorInvestigations)
+ .where(eq(vendorInvestigations.id, investigationId))
+ .then(rows => rows[0]);
+
+ const vendor = await db
+ .select({ email: vendors.email, vendorName: vendors.vendorName })
+ .from(vendors)
+ .where(eq(vendors.id, vendorId))
+ .then(rows => rows[0]);
+
+ if (!vendor?.email) {
+ return { success: false, error: "벤더 이메일 정보가 없습니다." };
+ }
+
+ // PQ 번호 조회
+ let pqNumber = "N/A";
+ if (investigation?.pqSubmissionId) {
+ const pqRow = await db
+ .select({ pqNumber: vendorPQSubmissions.pqNumber })
+ .from(vendorPQSubmissions)
+ .where(eq(vendorPQSubmissions.id, investigation.pqSubmissionId))
+ .then(rows => rows[0]);
+ if (pqRow) pqNumber = pqRow.pqNumber;
+ }
+
+ // 메일 발송
+ const portalUrl = process.env.NEXTAUTH_URL || `http://${host}`;
+ const reviewUrl = `${portalUrl}/evcp/vendor-investigation`;
+
+ await sendEmail({
+ to: vendor.email,
+ subject: `[eVCP] 실사 보완요청 - ${vendor.vendorName}`,
+ template: "pq-investigation-supplement-request",
+ context: {
+ vendorName: vendor.vendorName,
+ investigationNumber: pqNumber,
+ supplementComment: comment,
+ requestedAt: new Date().toLocaleString('ko-KR'),
+ reviewUrl: reviewUrl,
+ year: new Date().getFullYear(),
+ }
+ });
+
+ // 실사 상태를 SUPPLEMENT_REQUIRED로 변경 (이미 되어있을 수 있음)
+ await db
+ .update(vendorInvestigations)
+ .set({
+ investigationStatus: "SUPPLEMENT_REQUIRED",
+ updatedAt: new Date(),
+ })
+ .where(eq(vendorInvestigations.id, investigationId));
+
+ revalidateTag("vendor-investigations");
+ revalidateTag("pq-submissions");
+
+ return { success: true };
+ } catch (error) {
+ console.error("실사 보완요청 메일 발송 오류:", error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : "알 수 없는 오류",
+ };
+ }
+}
+
// 보완 서류제출 응답 제출 액션
export async function submitSupplementDocumentResponseAction({
investigationId,