90 lines
3.5 KiB
TypeScript
90 lines
3.5 KiB
TypeScript
import { z } from "zod";
|
||
import {
|
||
numberFromForm,
|
||
numberInRange,
|
||
nonNegativeNumberFromForm,
|
||
positiveNumberFromForm,
|
||
nullableIntIdFromForm,
|
||
booleanFromForm,
|
||
nullableIsoDateString,
|
||
DocumentSectionSchema,
|
||
} from "./common";
|
||
|
||
const InvoiceItemSchema = z.object({
|
||
description: z.string().max(500).nullish(),
|
||
item_description: z.string().max(8000).nullish(),
|
||
quantity: positiveNumberFromForm.default(1),
|
||
unit: z.string().max(20).nullish(),
|
||
unit_price: numberFromForm.default(0),
|
||
// Per-item discount as a percentage (0–100); column is Decimal(5,2).
|
||
discount: numberInRange(0, 100).optional().default(0),
|
||
vat_rate: numberInRange(0, 100).optional().default(21.0),
|
||
position: nonNegativeNumberFromForm.optional(),
|
||
});
|
||
|
||
export const CreateInvoiceSchema = z.object({
|
||
invoice_number: z.string().max(50).nullish(),
|
||
order_id: nullableIntIdFromForm.nullish(),
|
||
customer_id: nullableIntIdFromForm.nullish(),
|
||
status: z.string().max(20).optional().default("draft"),
|
||
currency: z.string().max(10).optional().default("CZK"),
|
||
vat_rate: numberInRange(0, 100).optional().default(21.0),
|
||
apply_vat: booleanFromForm.optional().default(true),
|
||
payment_method: z.string().max(50).nullish(),
|
||
constant_symbol: z.string().max(20).nullish(),
|
||
bank_name: z.string().max(255).nullish(),
|
||
bank_swift: z.string().max(20).nullish(),
|
||
bank_iban: z.string().max(50).nullish(),
|
||
bank_account: z.string().max(50).nullish(),
|
||
issue_date: nullableIsoDateString,
|
||
due_date: nullableIsoDateString,
|
||
tax_date: nullableIsoDateString,
|
||
issued_by: z.string().max(255).nullish(),
|
||
billing_text: z.string().max(500).nullish(),
|
||
language: z.string().max(5).optional().default("cs"),
|
||
// `notes` was dropped (printed-notes block removed — free-form PDF content
|
||
// lives in the sections); legacy clients still sending it are silently
|
||
// stripped by z.object. internal_notes is never printed.
|
||
internal_notes: z.string().max(8000).nullish(),
|
||
items: z.array(InvoiceItemSchema).optional(),
|
||
// Rich-text CZ/EN "Obsah" sections rendered after the items on the PDF
|
||
// (mirrors issued orders — shared DocumentSectionSchema, DB-aligned limits).
|
||
sections: z.array(DocumentSectionSchema).optional(),
|
||
// Positional indices of company custom fields to print on this document's PDF.
|
||
selected_custom_fields: z
|
||
.array(z.number().int().nonnegative())
|
||
.max(200)
|
||
.optional(),
|
||
});
|
||
|
||
export const UpdateInvoiceSchema = z.object({
|
||
status: z.string().max(20).optional(),
|
||
currency: z.string().max(10).optional(),
|
||
language: z.string().max(5).optional(),
|
||
payment_method: z.string().max(50).nullish(),
|
||
constant_symbol: z.string().max(20).nullish(),
|
||
bank_name: z.string().max(255).nullish(),
|
||
bank_swift: z.string().max(20).nullish(),
|
||
bank_iban: z.string().max(50).nullish(),
|
||
bank_account: z.string().max(50).nullish(),
|
||
issued_by: z.string().max(255).nullish(),
|
||
billing_text: z.string().max(500).nullish(),
|
||
customer_id: nullableIntIdFromForm.optional(),
|
||
vat_rate: numberInRange(0, 100).optional(),
|
||
apply_vat: booleanFromForm.optional(),
|
||
issue_date: nullableIsoDateString,
|
||
due_date: nullableIsoDateString,
|
||
tax_date: nullableIsoDateString,
|
||
internal_notes: z.string().max(8000).nullish(),
|
||
paid_date: nullableIsoDateString,
|
||
items: z.array(InvoiceItemSchema).optional(),
|
||
sections: z.array(DocumentSectionSchema).optional(),
|
||
selected_custom_fields: z
|
||
.array(z.number().int().nonnegative())
|
||
.max(200)
|
||
.optional(),
|
||
});
|
||
|
||
export type CreateInvoiceInput = z.infer<typeof CreateInvoiceSchema>;
|
||
export type UpdateInvoiceInput = z.infer<typeof UpdateInvoiceSchema>;
|