"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { pbCreate, pbDelete, pbList, pbUpdate } from "@/lib/pocketbase"; import { getAuth } from "@/lib/auth"; import { pbId } from "@/lib/pb"; export type ActionResult = { ok: true } | { ok: false; error: string }; const ruleSchema = z.object({ customer: z.string().min(1, "Customer is required"), year: z.coerce.number().int().min(2000).max(2100), month: z.coerce.number().int().min(1).max(12), price_per_litre: z.coerce.number().positive("Price must be > 0"), special_price: z.coerce.number().optional(), }); export async function createPricingRule(_: unknown, formData: FormData): Promise { const { token, user } = await getAuth(); if (!token) return { ok: false, error: "Not signed in" }; const parsed = ruleSchema.safeParse({ customer: formData.get("customer"), year: formData.get("year"), month: formData.get("month"), price_per_litre: formData.get("price_per_litre"), special_price: formData.get("special_price") || undefined, }); if (!parsed.success) return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid input" }; try { await pbCreate( "customer_pricing", { id: pbId(), ...parsed.data, ...(user ? { created_by: user.id } : {}), }, token, ); } catch (err: any) { return { ok: false, error: err?.message || "Failed to create rule" }; } revalidatePath("/pricing"); return { ok: true }; } export async function updatePricingRule(id: string, _: unknown, formData: FormData): Promise { const { token } = await getAuth(); if (!token) return { ok: false, error: "Not signed in" }; const parsed = ruleSchema.safeParse({ customer: formData.get("customer"), year: formData.get("year"), month: formData.get("month"), price_per_litre: formData.get("price_per_litre"), special_price: formData.get("special_price") || undefined, }); if (!parsed.success) return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid input" }; try { await pbUpdate("customer_pricing", id, parsed.data, token); } catch (err: any) { return { ok: false, error: err?.message || "Failed to update rule" }; } revalidatePath("/pricing"); return { ok: true }; } export async function deletePricingRule(id: string): Promise { const { token } = await getAuth(); if (!token) return { ok: false, error: "Not signed in" }; try { await pbDelete("customer_pricing", id, token); } catch (err: any) { return { ok: false, error: err?.message || "Failed to delete" }; } revalidatePath("/pricing"); return { ok: true }; } export async function updateDefaultPrice(price: number): Promise { const { token } = await getAuth(); if (!token) return { ok: false, error: "Not signed in" }; if (isNaN(price) || price <= 0) return { ok: false, error: "Price must be > 0" }; try { const settings = await pbList("fuel_settings", { perPage: 1 }, token); if (settings.items.length === 0) { return { ok: false, error: "Settings not configured" }; } await pbUpdate("fuel_settings", settings.items[0].id, { default_price_per_litre: price }, token); } catch (err: any) { return { ok: false, error: err?.message || "Failed to update" }; } revalidatePath("/pricing"); revalidatePath("/settings"); return { ok: true }; } export async function applyMonthlyIncrease(year: number, month: number, deltaCents: number): Promise { const { token, user } = await getAuth(); if (!token) return { ok: false, error: "Not signed in" }; if (isNaN(deltaCents) || deltaCents === 0) return { ok: false, error: "Delta must be non-zero" }; try { // 1. Log the increase const existing = await pbList("monthly_price_increases", { perPage: 1, filter: `year = ${year} && month = ${month}` }, token); if (existing.items.length > 0) { await pbUpdate("monthly_price_increases", existing.items[0].id, { percentage_increase: deltaCents, applied_by: user?.id }, token); } else { await pbCreate("monthly_price_increases", { id: pbId(), year, month, percentage_increase: deltaCents, applied_by: user?.id }, token); } // 2. Get all active customers const customers = (await pbList("customers", { perPage: 500 }, token)).items; // 3. For each customer, create a new pricing rule with the increased price (if no special price exists) for (const c of customers) { // Check existing rule for this month const filter = `customer = "${c.id}" && year = ${year} && month = ${month}`; const existingRule = await pbList("customer_pricing", { perPage: 1, filter }, token); // Get the previous month's price const prev = new Date(year, month - 2, 1); const prevFilter = `customer = "${c.id}" && year = ${prev.getFullYear()} && month = ${prev.getMonth() + 1}`; const prevRule = await pbList("customer_pricing", { perPage: 1, filter: prevFilter, sort: "-id" }, token); let basePrice: number; if (prevRule.items.length > 0) { basePrice = Number(prevRule.items[0].special_price ?? prevRule.items[0].price_per_litre); } else { // Use default price const settings = await pbList("fuel_settings", { perPage: 1 }, token); basePrice = Number(settings.items[0]?.default_price_per_litre || 0); } // Apply cents increase (not percentage — field is misnamed) const newPrice = Number((basePrice + deltaCents / 100).toFixed(2)); if (existingRule.items.length > 0) { // Update the rule (keep special_price if it existed) const row = existingRule.items[0]; await pbUpdate("customer_pricing", row.id, { price_per_litre: newPrice }, token); } else { // Create new rule await pbCreate("customer_pricing", { id: pbId(), customer: c.id, year, month, price_per_litre: newPrice, created_by: user?.id, }, token); } } } catch (err: any) { return { ok: false, error: err?.message || "Failed to apply increase" }; } revalidatePath("/pricing"); return { ok: true }; }