"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { hasPermission } from "@/lib/permissions"; import { revalidatePath } from "next/cache"; import { z } from "zod"; type ActionResult = { ok: true } | { error: string }; const lineItemSchema = z.object({ name: z.string().min(1), description: z.string().optional(), quantity: z.coerce.number().positive(), unit: z.string().min(1), size: z.string().optional(), unitPrice: z.coerce.number().nonnegative(), gstRate: z.coerce.number().min(0).max(1).default(0.18), }); export async function managerEditLineItems({ poId, lineItems, }: { poId: string; lineItems: Array<{ name: string; description?: string; quantity: number; unit: string; size?: string; unitPrice: number }>; }): Promise { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "approve_po")) { return { error: "Forbidden" }; } const parsed = z.array(lineItemSchema).min(1).safeParse(lineItems); if (!parsed.success) return { error: parsed.error.errors[0].message }; 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: "Line items can only be edited while the PO is under review." }; const originalSnapshot = 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), })); const newTotal = parsed.data.reduce( (sum, item) => sum + item.quantity * item.unitPrice * (1 + item.gstRate), 0 ); await db.purchaseOrder.update({ where: { id: poId }, data: { totalAmount: newTotal, lineItems: { deleteMany: {}, create: parsed.data.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: originalSnapshot }, }, }, }, }); revalidatePath(`/approvals/${poId}`); return { ok: true }; }