pelagia-portal/App/app/(portal)/po/new/actions.ts
2026-05-31 01:56:33 +05:30

158 lines
5.6 KiB
TypeScript

"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/po-number";
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"),
companyId: (formData.get("companyId") as string) || undefined,
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: await generatePoNumber(data.vesselId, data.companyId),
title: data.title,
status: intent === "submit" ? "SUBMITTED" : "DRAFT",
totalAmount: total,
currency: data.currency,
vesselId: data.vesselId,
accountId: data.accountId,
companyId: data.companyId ?? null,
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 };
}