import { z } from 'zod' /** Coerce empty strings to undefined — solves HTML form inputs sending '' for blank optional fields */ function opt(schema: T) { return z.preprocess((v) => (v === '' ? undefined : v), schema.optional()) } export const CategoryCreateSchema = z.object({ name: z.string().min(1).max(255), description: opt(z.string()), parentId: opt(z.string().uuid()), sortOrder: z.number().int().default(0), }) export type CategoryCreateInput = z.infer export const CategoryUpdateSchema = CategoryCreateSchema.partial() export type CategoryUpdateInput = z.infer export const SupplierCreateSchema = z.object({ name: z.string().min(1).max(255), contactName: opt(z.string().max(255)), email: opt(z.string().email()), phone: opt(z.string().max(50)), website: opt(z.string().max(255)), accountNumber: opt(z.string().max(100)), paymentTerms: opt(z.string().max(100)), notes: opt(z.string()), }) export type SupplierCreateInput = z.infer export const SupplierUpdateSchema = SupplierCreateSchema.partial() export type SupplierUpdateInput = z.infer // System slugs — used for code-level business logic references. // Actual valid values are stored in lookup tables and are company-configurable. export const SystemItemCondition = z.enum(['new', 'excellent', 'good', 'fair', 'poor']) export const SystemUnitStatus = z.enum([ 'available', 'sold', 'rented', 'on_trial', 'in_repair', 'layaway', 'lost', 'retired', ]) // API validation accepts any string slug (validated against lookup table at service layer) export const ItemCondition = z.string().min(1).max(100) export const UnitStatus = z.string().min(1).max(100) export const ProductCreateSchema = z.object({ sku: opt(z.string().max(100)), upc: opt(z.string().max(100)), name: z.string().min(1).max(255), description: opt(z.string()), brand: opt(z.string().max(255)), model: opt(z.string().max(255)), categoryId: opt(z.string().uuid()), locationId: opt(z.string().uuid()), isSerialized: z.boolean().default(false), isRental: z.boolean().default(false), isDualUseRepair: z.boolean().default(false), isConsumable: z.boolean().default(false), price: z.number().min(0).optional(), minPrice: z.number().min(0).optional(), rentalRateMonthly: z.number().min(0).optional(), qtyOnHand: z.number().int().min(0).default(0), qtyReorderPoint: z.number().int().min(0).optional(), }) export type ProductCreateInput = z.infer export const ProductUpdateSchema = ProductCreateSchema.partial() export type ProductUpdateInput = z.infer export const ProductSearchSchema = z.object({ q: z.string().min(1).max(255), }) export const InventoryUnitCreateSchema = z.object({ productId: z.string().uuid(), locationId: opt(z.string().uuid()), serialNumber: opt(z.string().max(255)), condition: ItemCondition.default('new'), status: UnitStatus.default('available'), purchaseDate: opt(z.string().date()), purchaseCost: z.number().min(0).optional(), notes: opt(z.string()), }) export type InventoryUnitCreateInput = z.infer export const InventoryUnitUpdateSchema = InventoryUnitCreateSchema.omit({ productId: true }).partial() export type InventoryUnitUpdateInput = z.infer export const ProductSupplierCreateSchema = z.object({ supplierId: z.string().uuid(), supplierSku: opt(z.string().max(100)), isPreferred: z.boolean().default(false), }) export type ProductSupplierCreateInput = z.infer export const ProductSupplierUpdateSchema = ProductSupplierCreateSchema.omit({ supplierId: true }).partial() export type ProductSupplierUpdateInput = z.infer export const StockReceiptCreateSchema = z.object({ supplierId: opt(z.string().uuid()), inventoryUnitId: opt(z.string().uuid()), qty: z.number().int().min(1).default(1), costPerUnit: z.number().min(0), receivedDate: z.string().date(), invoiceNumber: opt(z.string().max(100)), notes: opt(z.string()), }) export type StockReceiptCreateInput = z.infer