This commit is contained in:
@@ -240,6 +240,87 @@ export const ReorderOperationsSchema = z.object({
|
||||
|
||||
// A scan-page "Pause" — stops the clock but does not complete the step. The
|
||||
// operator can enter a partial unit count before dropping the claim.
|
||||
// ---- fasteners ----------------------------------------------------------
|
||||
|
||||
export const CreateFastenerSchema = z.object({
|
||||
partNumber: z.string().trim().min(1).max(120),
|
||||
description: NonEmpty,
|
||||
qty: z.coerce.number().int().positive().max(1_000_000),
|
||||
supplier: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(200)
|
||||
.transform((v) => (v.length === 0 ? null : v))
|
||||
.nullable()
|
||||
.optional(),
|
||||
unitCost: z.coerce.number().min(0).max(1_000_000).nullable().optional(),
|
||||
notes: OptionalText,
|
||||
});
|
||||
|
||||
export const UpdateFastenerSchema = z
|
||||
.object({
|
||||
partNumber: z.string().trim().min(1).max(120).optional(),
|
||||
description: NonEmpty.optional(),
|
||||
qty: z.coerce.number().int().positive().max(1_000_000).optional(),
|
||||
supplier: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(200)
|
||||
.transform((v) => (v.length === 0 ? null : v))
|
||||
.nullable()
|
||||
.optional(),
|
||||
unitCost: z.coerce.number().min(0).max(1_000_000).nullable().optional(),
|
||||
notes: OptionalText,
|
||||
})
|
||||
.strict();
|
||||
|
||||
// ---- purchase orders ----------------------------------------------------
|
||||
|
||||
export const PoStatuses = ["draft", "sent", "partial", "received", "cancelled"] as const;
|
||||
|
||||
// A single line on a draft PO. Fastener must belong to the same project (the
|
||||
// route handler verifies that; we keep the schema pure).
|
||||
const PoLineInput = z.object({
|
||||
fastenerId: z.string().min(1),
|
||||
qty: z.coerce.number().int().positive().max(1_000_000),
|
||||
unitCost: z.coerce.number().min(0).max(1_000_000).nullable().optional(),
|
||||
});
|
||||
|
||||
export const CreatePOSchema = z.object({
|
||||
vendor: NonEmpty,
|
||||
notes: OptionalText,
|
||||
lines: z.array(PoLineInput).min(1, "Add at least one line"),
|
||||
});
|
||||
|
||||
// PATCH: vendor + notes are free to change any time. Lines can only be
|
||||
// rewritten while the PO is in draft — the route enforces that.
|
||||
export const UpdatePOSchema = z
|
||||
.object({
|
||||
vendor: NonEmpty.optional(),
|
||||
notes: OptionalText,
|
||||
lines: z.array(PoLineInput).min(1).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
// Status transitions are validated in the route (see PO_TRANSITIONS).
|
||||
export const UpdatePOStatusSchema = z.object({
|
||||
status: z.enum(PoStatuses),
|
||||
});
|
||||
|
||||
// Receipt: bump receivedQty by `qty` for each listed line. If every line on
|
||||
// the PO is fully received the route auto-moves status to "received";
|
||||
// otherwise it moves to "partial".
|
||||
export const ReceivePOSchema = z.object({
|
||||
receipts: z
|
||||
.array(
|
||||
z.object({
|
||||
lineId: z.string().min(1),
|
||||
qty: z.coerce.number().int().positive().max(1_000_000),
|
||||
}),
|
||||
)
|
||||
.min(1),
|
||||
});
|
||||
|
||||
export const ReleaseOperationSchema = z.object({
|
||||
unitsProcessed: z.coerce.number().int().min(0).max(1_000_000).nullable().optional(),
|
||||
note: OptionalText,
|
||||
|
||||
Reference in New Issue
Block a user