Phase2: Invoicecomputation/AdminpricingUI/Ainvoicemgnt
Some checks failed
Build and Push / build (push) Failing after 28s

This commit is contained in:
2026-05-24 13:51:38 +02:00
parent 6baca1a459
commit c8ed27157f
29 changed files with 4465 additions and 11 deletions

View File

@@ -520,3 +520,130 @@ export interface OrgBillingConfig {
createdAt: string;
updatedAt: string;
}
// ---------------------------------------------------------------------------
// Billing — Phase 2: invoices and lines
// ---------------------------------------------------------------------------
export type InvoiceStatus =
| "draft"
| "open"
| "paid"
| "overdue"
| "void"
| "uncollectible";
export type InvoicePaymentMethod = "invoice" | "card";
export type InvoiceLineKind =
| "tenant_monthly"
| "tenant_setup"
| "ai_usage"
| "threema_messages"
| "skill_usage"
| "adjustment";
/**
* Snapshot of the customer's billing details captured at invoice
* issue time. Subsequent edits to org_billing do not mutate
* historical invoices.
*
* Field names mirror OrgBilling (minus the timestamps) so the
* snapshot is a straightforward copy at issue time.
*/
export interface InvoiceBillingSnapshot {
companyName: string;
streetAddress: string;
postalCode: string;
city: string;
country: string;
vatNumber: string | null;
billingEmail: string;
notes: string | null;
}
/**
* One line on an invoice. The `metadata` shape varies by `kind`:
* tenant_monthly: { proration_days, days_in_month, billable_days, suspended_days }
* tenant_setup: {}
* ai_usage: { litellm_key_alias, spend_chf, requests }
* threema_messages: { in_count, out_count, total_count }
* skill_usage: { skill_id, billable_days, event_count }
* adjustment: { reason, admin_user_id }
*/
export interface InvoiceLine {
id: string;
invoiceId: string;
tenantName: string | null;
kind: InvoiceLineKind;
description: string;
quantity: number;
unitLabel: string | null;
unitPriceChf: number;
amountChf: number;
metadata: Record<string, unknown> | null;
displayOrder: number;
}
/**
* Immutable invoice record. The PDF blob is fetched separately via
* the download endpoint to avoid loading bytea on every list query.
*/
export interface Invoice {
id: string;
invoiceNumber: string;
zitadelOrgId: string;
periodStart: string; // ISO date (YYYY-MM-DD)
periodEnd: string;
issuedAt: string;
dueAt: string;
subtotalChf: number;
vatRate: number;
vatAmountChf: number;
totalChf: number;
status: InvoiceStatus;
locale: string;
paymentMethod: InvoicePaymentMethod;
billingSnapshot: InvoiceBillingSnapshot;
stripePaymentIntentId: string | null;
pdfFilename: string | null;
hasPdf: boolean; // computed: pdf_data IS NOT NULL
adminNotes: string | null;
paidAt: string | null;
paidBy: string | null;
paidMethodDetail: string | null;
createdAt: string;
}
/** Invoice with its line items, used by detail views. */
export interface InvoiceDetail {
invoice: Invoice;
lines: InvoiceLine[];
}
/**
* In-memory draft produced by the computation pipeline before the
* invoice is allocated a number and persisted. Used by both the
* preview endpoint (return without persisting) and the commit
* endpoint (compute → persist atomically).
*/
export interface InvoiceDraft {
zitadelOrgId: string;
periodStart: string;
periodEnd: string;
dueAt: string;
locale: string;
paymentMethod: InvoicePaymentMethod;
billingSnapshot: InvoiceBillingSnapshot;
lines: Omit<InvoiceLine, "id" | "invoiceId">[];
subtotalChf: number;
vatRate: number;
vatAmountChf: number;
totalChf: number;
/**
* Non-blocking warnings the compute pipeline surfaced — e.g.
* "tenant X has no LiteLLM team, AI usage skipped". Rendered in
* the admin UI to help the operator decide whether to commit.
*/
warnings: string[];
}