Files
pieced-portal/src/app/api/admin/billing/generate/route.ts
admin c8ed27157f
Some checks failed
Build and Push / build (push) Failing after 28s
Phase2: Invoicecomputation/AdminpricingUI/Ainvoicemgnt
2026-05-24 13:51:38 +02:00

67 lines
2.0 KiB
TypeScript

import { NextResponse } from "next/server";
import { z } from "zod";
import { requirePlatformRole } from "@/lib/session";
import { generateInvoice } from "@/lib/billing";
import { safeError } from "@/lib/errors";
/**
* POST /api/admin/billing/generate
*
* Compute (and optionally commit) an invoice for an (org, year,
* month). Platform-only — this is the testing/admin tool.
*
* Body:
* {
* zitadelOrgId: string,
* year: number (e.g. 2026),
* month: number (1-12),
* locale?: 'de' | 'en' | 'fr' | 'it', // default: from country
* dryRun?: boolean // default: false
* }
*
* Response on success:
* {
* draft: InvoiceDraft, // line breakdown + warnings
* invoice: Invoice | null, // null when dryRun=true
* }
*
* If an invoice for that (org, period) already exists, returns
* 409 with a clear message. Use the delete endpoint first to
* regenerate.
*/
const bodySchema = z.object({
zitadelOrgId: z.string().min(1),
year: z.number().int().min(2020).max(2100),
month: z.number().int().min(1).max(12),
locale: z.enum(["de", "en", "fr", "it"]).optional(),
dryRun: z.boolean().optional().default(false),
});
export async function POST(request: Request) {
try {
await requirePlatformRole();
} catch {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const body = await request.json().catch(() => ({}));
const parsed = bodySchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json(
{ error: "Invalid request", details: parsed.error.flatten() },
{ status: 400 }
);
}
try {
const result = await generateInvoice(parsed.data);
return NextResponse.json(result);
} catch (e: any) {
console.error("Invoice generation failed:", e);
const msg = safeError(e, "Generation failed");
// Specific 409 for the "already exists" case so the UI can
// show a "delete first" link.
const status = /already exists/i.test(msg) ? 409 : 500;
return NextResponse.json({ error: msg }, { status });
}
}