Phase2: Invoicecomputation/AdminpricingUI/Ainvoicemgnt
Some checks failed
Build and Push / build (push) Failing after 28s
Some checks failed
Build and Push / build (push) Failing after 28s
This commit is contained in:
66
src/app/api/admin/billing/generate/route.ts
Normal file
66
src/app/api/admin/billing/generate/route.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user