"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { requirePermission } from "@/lib/permissions"; import { createPoSchema } from "@/lib/validations/po"; import { generatePoNumber } from "@/lib/utils"; import { notify } from "@/lib/notifier"; import { revalidatePath } from "next/cache"; export async function createPo( formData: FormData ): Promise<{ id: string } | { error: string }> { const session = await auth(); if (!session?.user) return { error: "Unauthorized" }; try { requirePermission(session.user.role, "create_po"); } catch { return { error: "You do not have permission to create purchase orders." }; } const intent = formData.get("intent") as "draft" | "submit"; const lineItems: Array<{ name: string; description?: string; quantity: number; unit: string; size?: string; unitPrice: number; gstRate: number; productId?: string; accountId?: string; }> = []; let i = 0; while (formData.has(`lineItems[${i}].name`)) { lineItems.push({ name: formData.get(`lineItems[${i}].name`) as string, description: (formData.get(`lineItems[${i}].description`) as string) || undefined, quantity: Number(formData.get(`lineItems[${i}].quantity`)), unit: formData.get(`lineItems[${i}].unit`) as string, size: (formData.get(`lineItems[${i}].size`) as string) || undefined, unitPrice: Number(formData.get(`lineItems[${i}].unitPrice`)), gstRate: Number(formData.get(`lineItems[${i}].gstRate`) ?? 0.18), productId: (formData.get(`lineItems[${i}].productId`) as string) || undefined, accountId: (formData.get(`lineItems[${i}].accountId`) as string) || undefined, }); i++; } const parsed = createPoSchema.safeParse({ title: formData.get("title"), vesselId: formData.get("vesselId"), accountId: formData.get("accountId"), projectCode: formData.get("projectCode") || undefined, dateRequired: formData.get("dateRequired") || undefined, vendorId: formData.get("vendorId") || undefined, piQuotationNo: formData.get("piQuotationNo") || undefined, piQuotationDate: formData.get("piQuotationDate") || undefined, requisitionNo: formData.get("requisitionNo") || undefined, requisitionDate: formData.get("requisitionDate") || undefined, placeOfDelivery: formData.get("placeOfDelivery") || undefined, tcDelivery: formData.get("tcDelivery") || undefined, tcDispatch: formData.get("tcDispatch") || undefined, tcInspection: formData.get("tcInspection") || undefined, tcTransitInsurance: formData.get("tcTransitInsurance") || undefined, tcPaymentTerms: formData.get("tcPaymentTerms") || undefined, tcOthers: formData.get("tcOthers") || undefined, lineItems, }); if (!parsed.success) { return { error: parsed.error.errors[0]?.message ?? "Validation failed" }; } const data = parsed.data; // totalAmount = grand total including GST const total = data.lineItems.reduce( (sum, item) => sum + item.quantity * item.unitPrice * (1 + item.gstRate), 0 ); const po = await db.purchaseOrder.create({ data: { poNumber: generatePoNumber(), title: data.title, status: intent === "submit" ? "SUBMITTED" : "DRAFT", totalAmount: total, currency: data.currency, vesselId: data.vesselId, accountId: data.accountId, vendorId: data.vendorId ?? null, projectCode: data.projectCode ?? null, dateRequired: data.dateRequired ? new Date(data.dateRequired) : null, piQuotationNo: data.piQuotationNo ?? null, piQuotationDate: data.piQuotationDate ? new Date(data.piQuotationDate) : null, requisitionNo: data.requisitionNo ?? null, requisitionDate: data.requisitionDate ? new Date(data.requisitionDate) : null, placeOfDelivery: data.placeOfDelivery ?? null, tcDelivery: data.tcDelivery ?? null, tcDispatch: data.tcDispatch ?? null, tcInspection: data.tcInspection ?? null, tcTransitInsurance: data.tcTransitInsurance ?? null, tcPaymentTerms: data.tcPaymentTerms ?? null, tcOthers: data.tcOthers ?? null, submitterId: session.user.id, submittedAt: intent === "submit" ? new Date() : null, lineItems: { create: data.lineItems.map((item, idx) => ({ name: item.name, description: item.description ?? null, quantity: item.quantity, unit: item.unit, size: item.size ?? null, unitPrice: item.unitPrice, totalPrice: item.quantity * item.unitPrice, gstRate: item.gstRate, sortOrder: idx, productId: item.productId ?? null, accountId: item.accountId ?? null, })), }, actions: { create: { actionType: "CREATED", actorId: session.user.id, }, }, }, }); if (intent === "submit") { await db.purchaseOrder.update({ where: { id: po.id }, data: { status: "MGR_REVIEW", actions: { create: { actionType: "SUBMITTED", actorId: session.user.id }, }, }, }); const [fullPo, managers] = await Promise.all([ db.purchaseOrder.findUnique({ where: { id: po.id }, include: { submitter: true } }), db.user.findMany({ where: { role: "MANAGER", isActive: true } }), ]); if (fullPo) { await notify({ event: "PO_SUBMITTED", po: fullPo, recipients: managers }); } } revalidatePath("/dashboard"); revalidatePath("/approvals"); return { id: po.id }; }