/** * Orders Domain - Schemas * * Zod schemas for runtime validation of order data. */ import { z } from "zod"; // ============================================================================ // Order Item Summary Schema // ============================================================================ export const orderItemSummarySchema = z.object({ productName: z.string().optional(), name: z.string().optional(), sku: z.string().optional(), status: z.string().optional(), billingCycle: z.string().optional(), itemClass: z.string().optional(), quantity: z.number().int().min(0).optional(), unitPrice: z.number().optional(), totalPrice: z.number().optional(), }); // ============================================================================ // Order Item Details Schema // ============================================================================ export const orderItemDetailsSchema = z.object({ id: z.string(), orderId: z.string(), quantity: z.number().int().min(1), unitPrice: z.number().optional(), totalPrice: z.number().optional(), billingCycle: z.string().optional(), product: z.object({ id: z.string().optional(), name: z.string().optional(), sku: z.string().optional(), itemClass: z.string().optional(), whmcsProductId: z.string().optional(), internetOfferingType: z.string().optional(), internetPlanTier: z.string().optional(), vpnRegion: z.string().optional(), }).optional(), }); // ============================================================================ // Order Summary Schema // ============================================================================ export const orderSummarySchema = z.object({ id: z.string(), orderNumber: z.string(), status: z.string(), orderType: z.string().optional(), effectiveDate: z.string(), // IsoDateTimeString totalAmount: z.number().optional(), createdDate: z.string(), // IsoDateTimeString lastModifiedDate: z.string(), // IsoDateTimeString whmcsOrderId: z.string().optional(), activationStatus: z.string().optional(), itemsSummary: z.array(orderItemSummarySchema), }); // ============================================================================ // Order Details Schema // ============================================================================ export const orderDetailsSchema = orderSummarySchema.extend({ accountId: z.string().optional(), accountName: z.string().optional(), pricebook2Id: z.string().optional(), activationType: z.string().optional(), activationStatus: z.string().optional(), activationScheduledAt: z.string().optional(), // IsoDateTimeString activationErrorCode: z.string().optional(), activationErrorMessage: z.string().optional(), activatedDate: z.string().optional(), // IsoDateTimeString items: z.array(orderItemDetailsSchema), }); // ============================================================================ // Query Parameter Schemas // ============================================================================ /** * Schema for order query parameters */ export const orderQueryParamsSchema = z.object({ page: z.coerce.number().int().positive().optional(), limit: z.coerce.number().int().positive().max(100).optional(), status: z.string().optional(), orderType: z.string().optional(), }); // Duplicate - remove this line // export type OrderQueryParams = z.infer; // ============================================================================ // Order Creation Schemas // ============================================================================ const orderConfigurationsAddressSchema = z.object({ street: z.string().nullable().optional(), streetLine2: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), postalCode: z.string().nullable().optional(), country: z.string().nullable().optional(), }); export const orderConfigurationsSchema = z.object({ activationType: z.enum(["Immediate", "Scheduled"]).optional(), scheduledAt: z.string().optional(), accessMode: z.enum(["IPoE-BYOR", "IPoE-HGW", "PPPoE"]).optional(), simType: z.enum(["eSIM", "Physical SIM"]).optional(), eid: z.string().optional(), isMnp: z.string().optional(), mnpNumber: z.string().optional(), mnpExpiry: z.string().optional(), mnpPhone: z.string().optional(), mvnoAccountNumber: z.string().optional(), portingLastName: z.string().optional(), portingFirstName: z.string().optional(), portingLastNameKatakana: z.string().optional(), portingFirstNameKatakana: z.string().optional(), portingGender: z.enum(["Male", "Female", "Corporate/Other"]).optional(), portingDateOfBirth: z.string().optional(), address: orderConfigurationsAddressSchema.optional(), }); const baseCreateOrderSchema = z.object({ orderType: z.enum(["Internet", "SIM", "VPN", "Other"]), skus: z.array(z.string()), configurations: orderConfigurationsSchema.optional(), }); export const createOrderRequestSchema = baseCreateOrderSchema; export const orderBusinessValidationSchema = baseCreateOrderSchema .extend({ userId: z.string().uuid(), opportunityId: z.string().optional(), }) .refine( (data) => { if (data.orderType === "Internet") { const mainServiceSkus = data.skus.filter(sku => { const upperSku = sku.toUpperCase(); return ( !upperSku.includes("INSTALL") && !upperSku.includes("ADDON") && !upperSku.includes("ACTIVATION") && !upperSku.includes("FEE") ); }); return mainServiceSkus.length >= 1; } return true; }, { message: "Internet orders must have at least one main service SKU (non-installation, non-addon)", path: ["skus"], } ) .refine( (data) => { if (data.orderType === "SIM" && data.configurations) { return data.configurations.simType !== undefined; } return true; }, { message: "SIM orders must specify SIM type", path: ["configurations", "simType"], } ) .refine( (data) => { if (data.configurations?.simType === "eSIM") { return data.configurations.eid !== undefined && data.configurations.eid.length > 0; } return true; }, { message: "eSIM orders must provide EID", path: ["configurations", "eid"], } ) .refine( (data) => { if (data.configurations?.isMnp === "true") { const required = [ "mnpNumber", "portingLastName", "portingFirstName", ] as const; return required.every(field => data.configurations?.[field] !== undefined); } return true; }, { message: "MNP orders must provide porting information", path: ["configurations"], } ); export const sfOrderIdParamSchema = z.object({ sfOrderId: z .string() .length(18, "Salesforce order ID must be 18 characters") .regex(/^[A-Za-z0-9]+$/, "Salesforce order ID must be alphanumeric"), }); export type SfOrderIdParam = z.infer; // ============================================================================ // Inferred Types from Schemas (Schema-First Approach) // ============================================================================ // Order item types export type OrderItemSummary = z.infer; export type OrderItemDetails = z.infer; // Order types export type OrderSummary = z.infer; export type OrderDetails = z.infer; // Query and creation types export type OrderQueryParams = z.infer; export type OrderConfigurationsAddress = z.infer; export type OrderConfigurations = z.infer; export type CreateOrderRequest = z.infer; export type OrderBusinessValidation = z.infer;