RJBULK-APP/app/api/statements/preview/route.ts
SJCoetz 1efa979443 feat: initial setup of rj bulk supplies backoffice app
Adds complete initial codebase for the RJ Bulk Supplies fuel delivery backoffice:
- Authentication pages and protected route layouts
- Customer, supplier, fuel purchase, and delivery note management
- Custom reporting and customer statement sending tools
- Full UI component library and Tailwind styling
- Server actions, API routes, and PocketBase integration
- Development scripts and project configuration updates
- Updated README and package metadata
2026-07-19 20:38:16 +02:00

78 lines
3 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { getAuth } from "@/lib/auth";
import { pbList } from "@/lib/pocketbase";
import { generateCustomerStatement } from "@/lib/pdf/customer-statement";
import type { Customer, DeliveryNote, FuelSettings } from "@/lib/types";
export const dynamic = "force-dynamic";
function rangeDates(range: string, start?: string, end?: string) {
const now = new Date();
if (range === "this-month") {
return {
start: new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10),
end: new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString().slice(0, 10),
};
}
if (range === "last-month") {
return {
start: new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString().slice(0, 10),
end: new Date(now.getFullYear(), now.getMonth(), 0).toISOString().slice(0, 10),
};
}
if (start && end) return { start, end };
return { start: start || "", end: end || "" };
}
export async function GET(req: NextRequest) {
const { token } = await getAuth();
if (!token) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
const { searchParams } = req.nextUrl;
const customerId = searchParams.get("customer");
const range = searchParams.get("range") || "this-month";
const startParam = searchParams.get("start");
const endParam = searchParams.get("end");
const includePricing = searchParams.get("include_pricing") !== "false";
if (!customerId) return NextResponse.json({ error: "customer required" }, { status: 400 });
const { start, end } = rangeDates(range, startParam || undefined, endParam || undefined);
if (!start || !end) return NextResponse.json({ error: "Date range required" }, { status: 400 });
try {
const [customerRes, notesRes, settingsRes] = await Promise.all([
pbList<Customer>("customers", { perPage: 1, filter: `id = "${customerId}"` }, token),
pbList<DeliveryNote>("delivery_notes", {
perPage: 1000,
filter: `customer = "${customerId}" && date >= "${start}" && date <= "${end}"`,
sort: "date",
}, token),
pbList<FuelSettings>("fuel_settings", { perPage: 1 }, token),
]);
if (customerRes.items.length === 0) {
return NextResponse.json({ error: "Customer not found" }, { status: 404 });
}
const pdfBytes = await generateCustomerStatement({
customer: customerRes.items[0],
notes: notesRes.items,
startDate: start,
endDate: end,
includePricing,
companySettings: settingsRes.items[0] || null,
});
const filename = `delivery_statement_${customerRes.items[0].name.replace(/\s+/g, "_")}_${start.replace(/-/g, "")}.pdf`;
return new NextResponse(Buffer.from(pdfBytes), {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `inline; filename="${filename}"`,
"Cache-Control": "no-store",
},
});
} catch (err: any) {
return NextResponse.json({ error: err?.message || "PDF generation failed" }, { status: 500 });
}
}