feat(orders): issued-order Zod schemas
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
|||||||
previewIssuedOrderNumber,
|
previewIssuedOrderNumber,
|
||||||
releaseIssuedOrderNumber,
|
releaseIssuedOrderNumber,
|
||||||
} from "../services/numbering.service";
|
} from "../services/numbering.service";
|
||||||
|
import { CreateIssuedOrderSchema } from "../schemas/issued-orders.schema";
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await prisma.number_sequences.deleteMany({ where: { type: "issued_order" } });
|
await prisma.number_sequences.deleteMany({ where: { type: "issued_order" } });
|
||||||
@@ -39,3 +40,18 @@ describe("issued-order numbering", () => {
|
|||||||
expect((await previewIssuedOrderNumber(year)).number).toBe(c.number);
|
expect((await previewIssuedOrderNumber(year)).number).toBe(c.number);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("CreateIssuedOrderSchema", () => {
|
||||||
|
it("coerces string form numbers and rejects an out-of-range VAT", () => {
|
||||||
|
const ok = CreateIssuedOrderSchema.safeParse({
|
||||||
|
customer_id: "5",
|
||||||
|
vat_rate: "21",
|
||||||
|
items: [{ description: "X", quantity: "2", unit_price: "100" }],
|
||||||
|
});
|
||||||
|
expect(ok.success).toBe(true);
|
||||||
|
if (ok.success) expect(ok.data.customer_id).toBe(5);
|
||||||
|
|
||||||
|
const bad = CreateIssuedOrderSchema.safeParse({ vat_rate: "200" });
|
||||||
|
expect(bad.success).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
48
src/schemas/issued-orders.schema.ts
Normal file
48
src/schemas/issued-orders.schema.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
numberInRange,
|
||||||
|
nonNegativeNumberFromForm,
|
||||||
|
positiveNumberFromForm,
|
||||||
|
nullableIntIdFromForm,
|
||||||
|
booleanFromForm,
|
||||||
|
isoDateString,
|
||||||
|
} from "./common";
|
||||||
|
|
||||||
|
export const IssuedOrderItemSchema = z.object({
|
||||||
|
description: z.string().max(500).nullish(),
|
||||||
|
item_description: z.string().max(5000).nullish(),
|
||||||
|
quantity: positiveNumberFromForm.optional(),
|
||||||
|
unit: z.string().max(20).nullish(),
|
||||||
|
unit_price: nonNegativeNumberFromForm.optional(),
|
||||||
|
vat_rate: numberInRange(0, 100).optional(),
|
||||||
|
position: z.number().int().nonnegative().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ISSUED_ORDER_STATUSES = [
|
||||||
|
"draft",
|
||||||
|
"sent",
|
||||||
|
"confirmed",
|
||||||
|
"completed",
|
||||||
|
"cancelled",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const CreateIssuedOrderSchema = z.object({
|
||||||
|
po_number: z.string().max(50).nullish(),
|
||||||
|
customer_id: nullableIntIdFromForm.nullish(),
|
||||||
|
status: z.enum(ISSUED_ORDER_STATUSES).optional(),
|
||||||
|
currency: z.string().max(10).optional(),
|
||||||
|
vat_rate: numberInRange(0, 100).optional(),
|
||||||
|
apply_vat: booleanFromForm.optional(),
|
||||||
|
exchange_rate: nonNegativeNumberFromForm.optional(),
|
||||||
|
order_date: isoDateString.nullish(),
|
||||||
|
delivery_date: isoDateString.nullish(),
|
||||||
|
language: z.string().max(5).optional(),
|
||||||
|
delivery_terms: z.string().max(500).nullish(),
|
||||||
|
payment_terms: z.string().max(500).nullish(),
|
||||||
|
issued_by: z.string().max(255).nullish(),
|
||||||
|
notes: z.string().nullish(),
|
||||||
|
internal_notes: z.string().nullish(),
|
||||||
|
items: z.array(IssuedOrderItemSchema).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateIssuedOrderSchema = CreateIssuedOrderSchema.partial();
|
||||||
Reference in New Issue
Block a user