Files
app/src/schemas/orders.schema.ts
BOHA 7398cad466 feat(vat)!: remove VAT entirely from offers, orders and issued orders
Accounting rule: nabidky, objednavky (incl. confirmation PDF) and objednavky
vydane are NOT tax documents - VAT belongs only on invoices. Per user
decision this is a FULL removal including DB columns.

- migration: DROP orders.vat_rate/apply_vat, issued_orders.vat_rate/apply_vat,
  issued_order_items.vat_rate, quotations.vat_rate/apply_vat (applied to
  dev + test DBs)
- services: net-only totals everywhere (computeIssuedOrderTotals(items) ->
  {total}; enrichOrder/enrichQuotation net; per-currency list totals net)
- PDFs (offers, order confirmation, issued order): no VAT columns/summary,
  single 'Celkem bez DPH' / 'Total excl. VAT' total, note under totals:
  'Ceny jsou uvedeny bez DPH. DPH bude uctovano dle platnych predpisu.';
  duplicate Cena/Celkem column merged (desc width 56%); orders-pdf render
  extracted as exported renderOrderConfirmationHtml for testability
- frontend: Uplatnit DPH checkboxes, VAT selects and per-item VAT columns
  removed from OfferDetail/OrderDetail/IssuedOrderDetail/
  OrderConfirmationModal/ReceivedOrders manual-create; list footers read
  'Celkem bez DPH'; invoice-from-order prefill now takes the company default
  VAT (invoice decides its own VAT)
- tests: suites reworked to net math; new pdf-vat-note.test.ts pins the
  exact cs+en note text and VAT-free layout on all three PDFs

Invoices and received invoices keep their VAT handling unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:13:16 +02:00

71 lines
2.5 KiB
TypeScript

import { z } from "zod";
import {
numberFromForm,
nonNegativeNumberFromForm,
positiveNumberFromForm,
intIdFromForm,
nullableIntIdFromForm,
booleanFromForm,
} from "./common";
const OrderItemSchema = z.object({
description: z.string().max(8000).nullish(),
item_description: z.string().max(8000).nullish(),
quantity: positiveNumberFromForm.default(1),
unit: z.string().max(255).nullish(),
unit_price: numberFromForm.default(0),
is_included_in_total: booleanFromForm.optional().default(true),
position: nonNegativeNumberFromForm.optional(),
});
const OrderSectionSchema = z.object({
title: z.string().max(255).nullish(),
title_cz: z.string().max(255).nullish(),
content: z.string().max(8000).nullish(),
position: nonNegativeNumberFromForm.optional(),
});
export const CreateOrderFromQuotationSchema = z.object({
quotationId: intIdFromForm,
customerOrderNumber: z.string().max(255).optional().default(""),
});
export const CreateOrderSchema = z.object({
order_number: z.string().max(255).nullish(),
customer_order_number: z.string().max(255).nullish(),
quotation_id: nullableIntIdFromForm.nullish(),
customer_id: nullableIntIdFromForm.nullish(),
status: z
.enum(["prijata", "v_realizaci", "dokoncena", "zrusena"])
.optional()
.default("prijata"),
currency: z.string().max(20).optional().default("CZK"),
language: z.string().max(20).optional().default("cs"),
exchange_rate: positiveNumberFromForm.optional().default(1.0),
scope_title: z.string().max(255).nullish(),
scope_description: z.string().max(8000).nullish(),
notes: z.string().max(8000).nullish(),
items: z.array(OrderItemSchema).optional(),
sections: z.array(OrderSectionSchema).optional(),
create_project: booleanFromForm.optional().default(true),
});
export const UpdateOrderSchema = z.object({
customer_order_number: z.string().max(255).nullish(),
status: z.enum(["prijata", "v_realizaci", "dokoncena", "zrusena"]).optional(),
currency: z.string().max(20).optional(),
language: z.string().max(20).optional(),
scope_title: z.string().max(255).nullish(),
scope_description: z.string().max(8000).nullish(),
notes: z.string().max(8000).nullish(),
customer_id: nullableIntIdFromForm.optional(),
items: z.array(OrderItemSchema).optional(),
sections: z.array(OrderSectionSchema).optional(),
});
export type CreateOrderFromQuotationInput = z.infer<
typeof CreateOrderFromQuotationSchema
>;
export type CreateOrderInput = z.infer<typeof CreateOrderSchema>;
export type UpdateOrderInput = z.infer<typeof UpdateOrderSchema>;