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 }); } }