"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { hasPermission } from "@/lib/permissions"; import { createPoSchema } from "@/lib/validations/po"; import { revalidatePath } from "next/cache"; export async function managerEditPo( poId: string, formData: FormData ): Promise<{ ok: true } | { error: string }> { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "approve_po")) { return { error: "Forbidden" }; } const po = await db.purchaseOrder.findUnique({ where: { id: poId }, include: { lineItems: { orderBy: { sortOrder: "asc" } } }, }); if (!po) return { error: "PO not found" }; if (po.status !== "MGR_REVIEW") return { error: "PO can only be edited while under review." }; // Parse line items from FormData const lineItems: Array<{ name: string; description?: string; quantity: number; unit: string; size?: string; unitPrice: number; gstRate: number; }> = []; 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), }); 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; const newTotal = data.lineItems.reduce( (sum, item) => sum + item.quantity * item.unitPrice * (1 + item.gstRate), 0 ); // Snapshot all original values for the audit trail const extPo = po as typeof po & { piQuotationNo?: string | null; piQuotationDate?: Date | null; requisitionNo?: string | null; requisitionDate?: Date | null; placeOfDelivery?: string | null; tcDelivery?: string | null; tcDispatch?: string | null; tcInspection?: string | null; tcTransitInsurance?: string | null; tcPaymentTerms?: string | null; tcOthers?: string | null; }; const original = { title: po.title, vesselId: po.vesselId, accountId: po.accountId, vendorId: po.vendorId, projectCode: po.projectCode, piQuotationNo: extPo.piQuotationNo, requisitionNo: extPo.requisitionNo, placeOfDelivery: extPo.placeOfDelivery, tcDelivery: extPo.tcDelivery, tcDispatch: extPo.tcDispatch, tcInspection: extPo.tcInspection, tcTransitInsurance: extPo.tcTransitInsurance, tcPaymentTerms: extPo.tcPaymentTerms, tcOthers: extPo.tcOthers, totalAmount: Number(po.totalAmount), lineItems: po.lineItems.map((li) => ({ name: (li as typeof li & { name: string }).name, description: li.description ?? undefined, quantity: Number(li.quantity), unit: li.unit, size: li.size ?? undefined, unitPrice: Number(li.unitPrice), gstRate: Number(li.gstRate ?? 0.18), })), }; await db.purchaseOrder.update({ where: { id: poId }, data: { title: data.title, 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, totalAmount: newTotal, lineItems: { deleteMany: {}, 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, })), }, actions: { create: { actionType: "MANAGER_LINE_EDIT", actorId: session.user.id, metadata: { original }, }, }, }, }); revalidatePath(`/approvals/${poId}`); return { ok: true }; }