87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
"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<ActionResult> {
|
|
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 };
|
|
}
|