since3-portal/modules/shared/schemas/invoice.ts
2026-09-29 13:05:38 +02:00

68 lines
2.4 KiB
TypeScript

import * as z from "zod";
export const INVOICE_STATUSES = ["pending", "paid", "cancelled"] as const;
export type InvoiceStatus = (typeof INVOICE_STATUSES)[number];
const isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, { error: "Ungültiges Datum." });
export const InvoiceItemSchema = z.object({
productTemplateId: z.string().uuid().nullable(),
description: z.string().trim().min(1, { error: "Jede Position braucht eine Bezeichnung." }),
quantity: z.number().positive({ error: "Menge muss größer als 0 sein." }),
unitPrice: z.number().min(0, { error: "Preis darf nicht negativ sein." }),
taxRate: z.number().min(0).max(100),
});
export const InvoiceDraftSchema = z
.object({
profileId: z.string().uuid(),
companyName: z.string().trim().min(1, { error: "Firmenname ist erforderlich." }),
billingStreet: z.string().trim().min(1, { error: "Straße ist erforderlich." }),
billingPostalCode: z.string().trim().min(1, { error: "PLZ ist erforderlich." }),
billingCity: z.string().trim().min(1, { error: "Stadt ist erforderlich." }),
billingCountry: z.string().trim().length(2, { error: "Land muss ein 2-stelliger Code sein." }),
invoiceDate: isoDate,
dueDate: isoDate,
items: z.array(InvoiceItemSchema).min(1, { error: "Mindestens eine Position hinzufügen." }),
})
.refine((data) => data.dueDate >= data.invoiceDate, {
path: ["dueDate"],
error: "Fälligkeit darf nicht vor dem Rechnungsdatum liegen.",
});
export type InvoiceItemDraft = z.infer<typeof InvoiceItemSchema>;
export type InvoiceDraft = z.infer<typeof InvoiceDraftSchema>;
export type InvoicePdfData = {
invoiceNumber: string;
invoiceDate: string;
dueDate: string;
companyName: string;
billingStreet: string;
billingPostalCode: string;
billingCity: string;
billingCountry: string;
items: { description: string; quantity: number; unitPrice: number; taxRate: number }[];
};
export type InvoiceListItem = {
id: string;
invoiceNumber: string;
status: InvoiceStatus;
displayStatus: InvoiceStatus | "overdue";
grossTotal: number;
dueDate: string;
invoiceDate: string;
};
export type ProductTemplate = {
id: string;
name: string;
description: string | null;
unitPrice: number;
taxRate: number;
};
export function firstIssueMessage(error: z.ZodError) {
return error.issues[0]?.message ?? "Bitte alle Pflichtfelder ausfüllen.";
}