mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 21:41:16 +00:00
78 lines
2.9 KiB
TypeScript
78 lines
2.9 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." });
|
|
|
|
// Grenzen und Nachkommastellen passend zu invoice_items: quantity numeric(10,2), unit_price numeric(12,2), tax_rate numeric(5,2)
|
|
const twoDecimals = (label: string) =>
|
|
z
|
|
.number({ error: `${label}: bitte eine gültige Zahl eingeben (z. B. 1.250,00).` })
|
|
.multipleOf(0.01, { error: `${label}: maximal 2 Nachkommastellen.` });
|
|
|
|
export const InvoiceItemSchema = z.object({
|
|
productTemplateId: z.string().uuid().nullable(),
|
|
description: z.string().trim().min(1, { error: "Jede Position braucht eine Bezeichnung." }),
|
|
quantity: twoDecimals("Menge")
|
|
.positive({ error: "Menge muss größer als 0 sein." })
|
|
.lt(1e8, { error: "Menge ist zu groß." }),
|
|
unitPrice: twoDecimals("Preis")
|
|
.min(0, { error: "Preis darf nicht negativ sein." })
|
|
.lt(1e10, { error: "Preis ist zu groß." }),
|
|
taxRate: twoDecimals("MwSt").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.";
|
|
}
|