@axova/shared 1.0.1 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/lib/db.d.ts +34226 -1
  2. package/dist/lib/db.js +21 -1
  3. package/dist/schemas/admin/admin-schema.d.ts +2 -2
  4. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +8 -8
  5. package/dist/schemas/common/common-schemas.d.ts +71 -71
  6. package/dist/schemas/compliance/compliance-schema.d.ts +20 -20
  7. package/dist/schemas/compliance/kyc-schema.d.ts +6 -6
  8. package/dist/schemas/customer/customer-schema.d.ts +18 -18
  9. package/dist/schemas/index.d.ts +1 -1
  10. package/dist/schemas/index.js +13 -4
  11. package/dist/schemas/inventory/inventory-tables.d.ts +188 -188
  12. package/dist/schemas/inventory/lot-tables.d.ts +104 -104
  13. package/dist/schemas/order/cart-schema.d.ts +2865 -0
  14. package/dist/schemas/order/cart-schema.js +396 -0
  15. package/dist/schemas/order/order-schema.d.ts +19 -19
  16. package/dist/schemas/order/order-schema.js +8 -2
  17. package/dist/schemas/product/discount-schema.d.ts +3 -3
  18. package/dist/schemas/product/product-schema.d.ts +3 -3
  19. package/dist/schemas/store/store-audit-schema.d.ts +22 -22
  20. package/dist/schemas/store/storefront-config-schema.d.ts +434 -823
  21. package/dist/schemas/store/storefront-config-schema.js +35 -62
  22. package/package.json +1 -1
  23. package/src/configs/index.ts +654 -654
  24. package/src/index.ts +26 -26
  25. package/src/interfaces/customer-events.ts +106 -106
  26. package/src/interfaces/inventory-events.ts +545 -545
  27. package/src/interfaces/inventory-types.ts +1004 -1004
  28. package/src/interfaces/order-events.ts +381 -381
  29. package/src/lib/auditLogger.ts +1117 -1117
  30. package/src/lib/authOrganization.ts +153 -153
  31. package/src/lib/db.ts +84 -64
  32. package/src/middleware/serviceAuth.ts +328 -328
  33. package/src/middleware/storeOwnership.ts +199 -199
  34. package/src/middleware/userAuth.ts +248 -248
  35. package/src/schemas/admin/admin-schema.ts +208 -208
  36. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -180
  37. package/src/schemas/common/common-schemas.ts +108 -108
  38. package/src/schemas/compliance/compliance-schema.ts +927 -927
  39. package/src/schemas/customer/customer-schema.ts +576 -576
  40. package/src/schemas/index.ts +204 -194
  41. package/src/schemas/inventory/inventory-tables.ts +1927 -1927
  42. package/src/schemas/order/cart-schema.ts +652 -0
  43. package/src/schemas/order/order-schema.ts +1406 -1400
  44. package/src/schemas/product/product-relations.ts +187 -187
  45. package/src/schemas/product/product-schema.ts +955 -955
  46. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -212
  47. package/src/schemas/store/store-audit-schema.ts +1257 -1257
  48. package/src/schemas/store/store-schema.ts +661 -661
  49. package/src/schemas/store/storefront-config-schema.ts +212 -434
  50. package/src/schemas/types.ts +67 -67
  51. package/src/types/events.ts +646 -646
  52. package/src/utils/errorHandler.ts +44 -44
  53. package/src/utils/subdomain.ts +19 -19
  54. package/tsconfig.json +21 -21
@@ -1,1400 +1,1406 @@
1
- import { createId } from "@paralleldrive/cuid2";
2
- import { relations } from "drizzle-orm";
3
- import {
4
- boolean,
5
- decimal,
6
- index,
7
- integer,
8
- jsonb,
9
- pgEnum,
10
- pgTable,
11
- text,
12
- timestamp,
13
- unique,
14
- varchar,
15
- } from "drizzle-orm/pg-core";
16
-
17
- // =====================================================
18
- // ENUMS
19
- // =====================================================
20
-
21
- export const orderTypeEnum = pgEnum("order_type", [
22
- "ONLINE",
23
- "POS",
24
- "PICKUP",
25
- "DELIVERY",
26
- "MARKETPLACE",
27
- "PHONE",
28
- "WHOLESALE",
29
- "SUBSCRIPTION",
30
- "SUBSCRIPTION_RENEWAL",
31
- ]);
32
-
33
- export const orderStatusEnum = pgEnum("order_status", [
34
- "DRAFT",
35
- "PENDING",
36
- "CONFIRMED",
37
- "PROCESSING",
38
- "PICKING",
39
- "PACKED",
40
- "SHIPPED",
41
- "OUT_FOR_DELIVERY",
42
- "DELIVERED",
43
- "COMPLETED",
44
- "CANCELLED",
45
- "REFUNDED",
46
- "PARTIALLY_REFUNDED",
47
- "RETURNED",
48
- "PARTIALLY_RETURNED",
49
- "DISPUTED",
50
- "ON_HOLD",
51
- "FAILED",
52
- ]);
53
-
54
- export const paymentStatusEnum = pgEnum("payment_status", [
55
- "PENDING",
56
- "AUTHORIZED",
57
- "PAID",
58
- "PARTIALLY_PAID",
59
- "FAILED",
60
- "CANCELLED",
61
- "REFUNDED",
62
- "PARTIALLY_REFUNDED",
63
- "VOIDED",
64
- "CHARGEBACK",
65
- "DISPUTED",
66
- ]);
67
-
68
- export const fulfillmentStatusEnum = pgEnum("fulfillment_status", [
69
- "PENDING",
70
- "ALLOCATED",
71
- "PICKING",
72
- "PICKED",
73
- "PACKED",
74
- "READY_FOR_PICKUP",
75
- "SHIPPED",
76
- "IN_TRANSIT",
77
- "OUT_FOR_DELIVERY",
78
- "DELIVERED",
79
- "ATTEMPTED_DELIVERY",
80
- "DELIVERY_FAILED",
81
- "RETURNED_TO_SENDER",
82
- "CANCELLED",
83
- "LOST",
84
- ]);
85
-
86
- export const orderPriorityEnum = pgEnum("order_priority", [
87
- "LOW",
88
- "NORMAL",
89
- "HIGH",
90
- "URGENT",
91
- "CRITICAL",
92
- ]);
93
-
94
- export const discountTypeEnum = pgEnum("discount_type", [
95
- "PERCENTAGE",
96
- "FIXED_AMOUNT",
97
- "FREE_SHIPPING",
98
- "BOGO",
99
- "LOYALTY_POINTS",
100
- "COUPON",
101
- "PROMOTIONAL",
102
- "BULK",
103
- "EARLY_BIRD",
104
- ]);
105
-
106
- export const addressTypeEnum = pgEnum("address_type", [
107
- "BILLING",
108
- "SHIPPING",
109
- "PICKUP",
110
- "DELIVERY",
111
- ]);
112
-
113
- export const paymentMethodEnum = pgEnum("payment_method", [
114
- "CREDIT_CARD",
115
- "DEBIT_CARD",
116
- "PAYPAL",
117
- "APPLE_PAY",
118
- "GOOGLE_PAY",
119
- "BANK_TRANSFER",
120
- "CASH",
121
- "CHECK",
122
- "STORE_CREDIT",
123
- "GIFT_CARD",
124
- "CRYPTOCURRENCY",
125
- "BUY_NOW_PAY_LATER",
126
- "INVOICE",
127
- "COD", // Cash on Delivery
128
- ]);
129
-
130
- export const refundReasonEnum = pgEnum("refund_reason", [
131
- "CUSTOMER_REQUEST",
132
- "DEFECTIVE_PRODUCT",
133
- "WRONG_ITEM",
134
- "NOT_AS_DESCRIBED",
135
- "DAMAGED_IN_SHIPPING",
136
- "QUALITY_ISSUE",
137
- "SIZE_ISSUE",
138
- "CHANGE_OF_MIND",
139
- "DUPLICATE_ORDER",
140
- "FRAUD_PREVENTION",
141
- "BUSINESS_POLICY",
142
- "GOODWILL",
143
- ]);
144
-
145
- // =====================================================
146
- // CORE ORDERS TABLE
147
- // =====================================================
148
-
149
- export const orders = pgTable(
150
- "orders",
151
- {
152
- id: text("id")
153
- .primaryKey()
154
- .$defaultFn(() => createId()),
155
-
156
- // External References
157
- storeId: text("store_id").notNull(),
158
- customerId: text("customer_id"), // Nullable for guest orders
159
- userId: text("user_id"), // Account holder
160
- sessionId: text("session_id"), // Browser/device session
161
-
162
- // Order Identification
163
- orderNumber: varchar("order_number", { length: 50 }).notNull(),
164
- displayOrderNumber: varchar("display_order_number", { length: 50 }), // Customer-friendly number
165
- externalOrderId: text("external_order_id"), // For marketplace orders
166
- parentOrderId: text("parent_order_id"), // For split orders
167
-
168
- // Order Classification
169
- orderType: orderTypeEnum("order_type").notNull(),
170
- orderSource: varchar("order_source", { length: 50 })
171
- .notNull()
172
- .$type<
173
- | "WEB"
174
- | "MOBILE_APP"
175
- | "POS"
176
- | "PHONE"
177
- | "EMAIL"
178
- | "SOCIAL_MEDIA"
179
- | "MARKETPLACE"
180
- | "API"
181
- | "ADMIN"
182
- | "IMPORT"
183
- | "SUBSCRIPTION_RENEWAL"
184
- >(),
185
- marketplaceInfo: jsonb("marketplace_info").$type<{
186
- platform?: string;
187
- orderId?: string;
188
- accountId?: string;
189
- fees?: number;
190
- commission?: number;
191
- }>(),
192
-
193
- // Status Management
194
- status: orderStatusEnum("status").notNull().default("DRAFT"),
195
- paymentStatus: paymentStatusEnum("payment_status")
196
- .notNull()
197
- .default("PENDING"),
198
- fulfillmentStatus: fulfillmentStatusEnum("fulfillment_status")
199
- .notNull()
200
- .default("PENDING"),
201
-
202
- // Priority and Flags
203
- priority: orderPriorityEnum("priority").notNull().default("NORMAL"),
204
- isGuestOrder: boolean("is_guest_order").notNull().default(false),
205
- isTestOrder: boolean("is_test_order").notNull().default(false),
206
- isGift: boolean("is_gift").notNull().default(false),
207
- isPreorder: boolean("is_preorder").notNull().default(false),
208
- isSubscription: boolean("is_subscription").notNull().default(false),
209
- requiresShipping: boolean("requires_shipping").notNull().default(true),
210
- requiresPickup: boolean("requires_pickup").notNull().default(false),
211
-
212
- // Financial Information
213
- currency: varchar("currency", { length: 3 }).notNull().default("USD"),
214
- exchangeRate: decimal("exchange_rate", { precision: 10, scale: 6 }).default(
215
- "1.000000",
216
- ),
217
-
218
- // Amounts (in minor currency units - cents)
219
- subtotalAmount: decimal("subtotal_amount", { precision: 12, scale: 2 })
220
- .notNull()
221
- .default("0.00"),
222
- taxAmount: decimal("tax_amount", { precision: 12, scale: 2 })
223
- .notNull()
224
- .default("0.00"),
225
- shippingAmount: decimal("shipping_amount", { precision: 12, scale: 2 })
226
- .notNull()
227
- .default("0.00"),
228
- discountAmount: decimal("discount_amount", { precision: 12, scale: 2 })
229
- .notNull()
230
- .default("0.00"),
231
- tipAmount: decimal("tip_amount", { precision: 12, scale: 2 })
232
- .notNull()
233
- .default("0.00"),
234
- feeAmount: decimal("fee_amount", { precision: 12, scale: 2 })
235
- .notNull()
236
- .default("0.00"),
237
- totalAmount: decimal("total_amount", { precision: 12, scale: 2 }).notNull(),
238
- paidAmount: decimal("paid_amount", { precision: 12, scale: 2 })
239
- .notNull()
240
- .default("0.00"),
241
- refundedAmount: decimal("refunded_amount", { precision: 12, scale: 2 })
242
- .notNull()
243
- .default("0.00"),
244
- outstandingAmount: decimal("outstanding_amount", {
245
- precision: 12,
246
- scale: 2,
247
- })
248
- .notNull()
249
- .default("0.00"),
250
-
251
- // Tax Information
252
- taxRate: decimal("tax_rate", { precision: 5, scale: 4 }),
253
- taxIncluded: boolean("tax_included").notNull().default(false),
254
- taxExempt: boolean("tax_exempt").notNull().default(false),
255
- taxExemptReason: text("tax_exempt_reason"),
256
-
257
- // Customer Information (for guest orders)
258
- guestEmail: varchar("guest_email", { length: 255 }),
259
- guestPhone: varchar("guest_phone", { length: 20 }),
260
- guestName: varchar("guest_name", { length: 100 }),
261
-
262
- // Location Information
263
- storeLocationId: text("store_location_id"), // Physical store/warehouse
264
- posTerminalId: text("pos_terminal_id"), // POS terminal for in-store orders
265
- salesAssociateId: text("sales_associate_id"), // Staff who processed the order
266
-
267
- // Timing Information
268
- requestedDeliveryDate: timestamp("requested_delivery_date", {
269
- withTimezone: true,
270
- }),
271
- promisedDeliveryDate: timestamp("promised_delivery_date", {
272
- withTimezone: true,
273
- }),
274
- actualDeliveryDate: timestamp("actual_delivery_date", {
275
- withTimezone: true,
276
- }),
277
- orderPlacedAt: timestamp("order_placed_at", { withTimezone: true }),
278
- orderConfirmedAt: timestamp("order_confirmed_at", { withTimezone: true }),
279
- orderShippedAt: timestamp("order_shipped_at", { withTimezone: true }),
280
- orderDeliveredAt: timestamp("order_delivered_at", { withTimezone: true }),
281
- orderCompletedAt: timestamp("order_completed_at", { withTimezone: true }),
282
-
283
- // Special Instructions and Notes
284
- customerNotes: text("customer_notes"),
285
- internalNotes: text("internal_notes"),
286
- giftMessage: text("gift_message"),
287
- specialInstructions: text("special_instructions"),
288
- deliveryInstructions: text("delivery_instructions"),
289
-
290
- // Marketing and Attribution
291
- referrerUrl: text("referrer_url"),
292
- utmSource: varchar("utm_source", { length: 100 }),
293
- utmMedium: varchar("utm_medium", { length: 100 }),
294
- utmCampaign: varchar("utm_campaign", { length: 100 }),
295
- affiliateId: text("affiliate_id"),
296
- promoCode: varchar("promo_code", { length: 50 }),
297
-
298
- // Technical Information
299
- userAgent: text("user_agent"),
300
- ipAddress: varchar("ip_address", { length: 45 }),
301
- deviceInfo: jsonb("device_info").$type<{
302
- type?: string;
303
- brand?: string;
304
- model?: string;
305
- os?: string;
306
- browser?: string;
307
- }>(),
308
-
309
- // Risk and Fraud
310
- riskScore: decimal("risk_score", { precision: 5, scale: 2 }),
311
- fraudScore: decimal("fraud_score", { precision: 5, scale: 2 }),
312
- isHighRisk: boolean("is_high_risk").notNull().default(false),
313
- requiresVerification: boolean("requires_verification")
314
- .notNull()
315
- .default(false),
316
- verifiedAt: timestamp("verified_at", { withTimezone: true }),
317
-
318
- // Subscription Information
319
- subscriptionId: text("subscription_id"),
320
- subscriptionCycle: integer("subscription_cycle"),
321
- isSubscriptionRenewal: boolean("is_subscription_renewal")
322
- .notNull()
323
- .default(false),
324
-
325
- // Advanced Features
326
- tags: jsonb("tags").$type<string[]>().default([]),
327
- customFields: jsonb("custom_fields")
328
- .$type<Record<string, unknown>>()
329
- .default({}),
330
- metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
331
-
332
- // Analytics
333
- conversionValue: decimal("conversion_value", { precision: 12, scale: 2 }),
334
- customerLifetimeValue: decimal("customer_lifetime_value", {
335
- precision: 12,
336
- scale: 2,
337
- }),
338
- profitMargin: decimal("profit_margin", { precision: 5, scale: 2 }),
339
-
340
- // Timestamps
341
- createdAt: timestamp("created_at", { withTimezone: true })
342
- .defaultNow()
343
- .notNull(),
344
- updatedAt: timestamp("updated_at", { withTimezone: true })
345
- .defaultNow()
346
- .notNull(),
347
- deletedAt: timestamp("deleted_at", { withTimezone: true }),
348
- },
349
- (table) => ({
350
- // Primary Indexes
351
- orderNumberIndex: unique("idx_orders_order_number").on(
352
- table.storeId,
353
- table.orderNumber,
354
- ),
355
- externalOrderIndex: index("idx_orders_external_id").on(
356
- table.externalOrderId,
357
- ),
358
-
359
- // Customer and User Indexes
360
- customerIndex: index("idx_orders_customer").on(table.customerId),
361
- userIndex: index("idx_orders_user").on(table.userId),
362
- guestEmailIndex: index("idx_orders_guest_email").on(table.guestEmail),
363
-
364
- // Status Indexes
365
- statusIndex: index("idx_orders_status").on(table.status),
366
- paymentStatusIndex: index("idx_orders_payment_status").on(
367
- table.paymentStatus,
368
- ),
369
- fulfillmentStatusIndex: index("idx_orders_fulfillment_status").on(
370
- table.fulfillmentStatus,
371
- ),
372
-
373
- // Type and Source Indexes
374
- orderTypeIndex: index("idx_orders_type").on(table.orderType),
375
- orderSourceIndex: index("idx_orders_source").on(table.orderSource),
376
-
377
- // Location Indexes
378
- storeLocationIndex: index("idx_orders_store_location").on(
379
- table.storeLocationId,
380
- ),
381
- posTerminalIndex: index("idx_orders_pos_terminal").on(table.posTerminalId),
382
-
383
- // Timing Indexes
384
- placedAtIndex: index("idx_orders_placed_at").on(table.orderPlacedAt),
385
- deliveryDateIndex: index("idx_orders_delivery_date").on(
386
- table.promisedDeliveryDate,
387
- ),
388
- createdAtIndex: index("idx_orders_created_at").on(table.createdAt),
389
-
390
- // Financial Indexes
391
- totalAmountIndex: index("idx_orders_total_amount").on(table.totalAmount),
392
- currencyIndex: index("idx_orders_currency").on(table.currency),
393
-
394
- // Special Flags Indexes
395
- priorityIndex: index("idx_orders_priority").on(table.priority),
396
- testOrderIndex: index("idx_orders_test").on(table.isTestOrder),
397
- guestOrderIndex: index("idx_orders_guest").on(table.isGuestOrder),
398
-
399
- // Composite Indexes
400
- storeStatusIndex: index("idx_orders_store_status").on(
401
- table.storeId,
402
- table.status,
403
- ),
404
- customerStatusIndex: index("idx_orders_customer_status").on(
405
- table.customerId,
406
- table.status,
407
- ),
408
- typeStatusIndex: index("idx_orders_type_status").on(
409
- table.orderType,
410
- table.status,
411
- ),
412
- dateRangeIndex: index("idx_orders_date_range").on(
413
- table.storeId,
414
- table.createdAt,
415
- ),
416
- }),
417
- );
418
-
419
- // =====================================================
420
- // ORDER ITEMS TABLE
421
- // =====================================================
422
-
423
- export const orderItems = pgTable(
424
- "order_items",
425
- {
426
- id: text("id")
427
- .primaryKey()
428
- .$defaultFn(() => createId()),
429
-
430
- orderId: text("order_id")
431
- .notNull()
432
- .references(() => orders.id, { onDelete: "cascade" }),
433
-
434
- // Product Information
435
- productId: text("product_id").notNull(),
436
- variantId: text("variant_id"),
437
- sku: varchar("sku", { length: 100 }).notNull(),
438
- productHandle: varchar("product_handle", { length: 255 }),
439
-
440
- // Product Details (snapshot at time of order)
441
- productTitle: varchar("product_title", { length: 255 }).notNull(),
442
- variantTitle: varchar("variant_title", { length: 255 }),
443
- productType: varchar("product_type", { length: 50 }),
444
- vendor: varchar("vendor", { length: 100 }),
445
-
446
- // Quantity and Pricing
447
- quantity: integer("quantity").notNull(),
448
- quantityShipped: integer("quantity_shipped").notNull().default(0),
449
- quantityReturned: integer("quantity_returned").notNull().default(0),
450
- quantityRefunded: integer("quantity_refunded").notNull().default(0),
451
-
452
- unitPrice: decimal("unit_price", { precision: 12, scale: 2 }).notNull(),
453
- compareAtPrice: decimal("compare_at_price", { precision: 12, scale: 2 }),
454
- costPrice: decimal("cost_price", { precision: 12, scale: 2 }),
455
- totalPrice: decimal("total_price", { precision: 12, scale: 2 }).notNull(),
456
-
457
- // Discounts and Taxes
458
- discountAmount: decimal("discount_amount", { precision: 12, scale: 2 })
459
- .notNull()
460
- .default("0.00"),
461
- taxAmount: decimal("tax_amount", { precision: 12, scale: 2 })
462
- .notNull()
463
- .default("0.00"),
464
-
465
- // Physical Properties
466
- weight: decimal("weight", { precision: 8, scale: 3 }),
467
- requiresShipping: boolean("requires_shipping").notNull().default(true),
468
- isGiftWrap: boolean("is_gift_wrap").notNull().default(false),
469
- giftWrapMessage: text("gift_wrap_message"),
470
-
471
- // Customization
472
- customization: jsonb("customization").$type<{
473
- options?: Record<string, string>;
474
- personalization?: string;
475
- engraving?: string;
476
- specialRequests?: string;
477
- }>(),
478
-
479
- // Inventory and Fulfillment
480
- inventoryLocationId: text("inventory_location_id"),
481
- fulfillmentService: varchar("fulfillment_service", { length: 50 }).default(
482
- "MANUAL",
483
- ),
484
- trackingRequired: boolean("tracking_required").notNull().default(true),
485
-
486
- // Subscription Information
487
- isSubscriptionItem: boolean("is_subscription_item")
488
- .notNull()
489
- .default(false),
490
- subscriptionFrequency: varchar("subscription_frequency", { length: 20 }),
491
- subscriptionEndDate: timestamp("subscription_end_date", {
492
- withTimezone: true,
493
- }),
494
-
495
- // Product Snapshot (preserve product data at time of order)
496
- productSnapshot: jsonb("product_snapshot").$type<{
497
- images?: string[];
498
- description?: string;
499
- attributes?: Record<string, unknown>;
500
- categories?: string[];
501
- }>(),
502
-
503
- // Line Item Flags
504
- isDigital: boolean("is_digital").notNull().default(false),
505
- isBackorder: boolean("is_backorder").notNull().default(false),
506
- isPreorder: boolean("is_preorder").notNull().default(false),
507
-
508
- // Notes and Instructions
509
- lineItemNotes: text("line_item_notes"),
510
- internalNotes: text("internal_notes"),
511
-
512
- // Analytics
513
- profitMargin: decimal("profit_margin", { precision: 5, scale: 2 }),
514
- marginAmount: decimal("margin_amount", { precision: 12, scale: 2 }),
515
-
516
- // Timestamps
517
- createdAt: timestamp("created_at", { withTimezone: true })
518
- .defaultNow()
519
- .notNull(),
520
- updatedAt: timestamp("updated_at", { withTimezone: true })
521
- .defaultNow()
522
- .notNull(),
523
- },
524
- (table) => ({
525
- // Foreign Key Indexes
526
- orderIdIndex: index("idx_order_items_order_id").on(table.orderId),
527
- productIdIndex: index("idx_order_items_product_id").on(table.productId),
528
- variantIdIndex: index("idx_order_items_variant_id").on(table.variantId),
529
- skuIndex: index("idx_order_items_sku").on(table.sku),
530
-
531
- // Inventory and Fulfillment
532
- inventoryLocationIndex: index("idx_order_items_inventory_location").on(
533
- table.inventoryLocationId,
534
- ),
535
- fulfillmentServiceIndex: index("idx_order_items_fulfillment_service").on(
536
- table.fulfillmentService,
537
- ),
538
-
539
- // Product Information
540
- vendorIndex: index("idx_order_items_vendor").on(table.vendor),
541
- productTypeIndex: index("idx_order_items_product_type").on(
542
- table.productType,
543
- ),
544
-
545
- // Flags
546
- digitalIndex: index("idx_order_items_digital").on(table.isDigital),
547
- backorderIndex: index("idx_order_items_backorder").on(table.isBackorder),
548
- subscriptionIndex: index("idx_order_items_subscription").on(
549
- table.isSubscriptionItem,
550
- ),
551
-
552
- // Composite Indexes
553
- orderProductIndex: index("idx_order_items_order_product").on(
554
- table.orderId,
555
- table.productId,
556
- ),
557
- orderSkuIndex: index("idx_order_items_order_sku").on(
558
- table.orderId,
559
- table.sku,
560
- ),
561
- }),
562
- );
563
-
564
- // =====================================================
565
- // ORDER ADDRESSES TABLE
566
- // =====================================================
567
-
568
- export const orderAddresses = pgTable(
569
- "order_addresses",
570
- {
571
- id: text("id")
572
- .primaryKey()
573
- .$defaultFn(() => createId()),
574
-
575
- orderId: text("order_id")
576
- .notNull()
577
- .references(() => orders.id, { onDelete: "cascade" }),
578
-
579
- addressType: addressTypeEnum("address_type").notNull(),
580
-
581
- // Contact Information
582
- firstName: varchar("first_name", { length: 100 }),
583
- lastName: varchar("last_name", { length: 100 }),
584
- company: varchar("company", { length: 100 }),
585
- email: varchar("email", { length: 255 }),
586
- phone: varchar("phone", { length: 20 }),
587
-
588
- // Address Information
589
- address1: varchar("address1", { length: 255 }).notNull(),
590
- address2: varchar("address2", { length: 255 }),
591
- city: varchar("city", { length: 100 }).notNull(),
592
- province: varchar("province", { length: 100 }),
593
- provinceCode: varchar("province_code", { length: 10 }),
594
- country: varchar("country", { length: 100 }).notNull(),
595
- countryCode: varchar("country_code", { length: 2 }).notNull(),
596
- postalCode: varchar("postal_code", { length: 20 }),
597
-
598
- // Geolocation
599
- latitude: decimal("latitude", { precision: 10, scale: 8 }),
600
- longitude: decimal("longitude", { precision: 11, scale: 8 }),
601
- timezone: varchar("timezone", { length: 50 }),
602
-
603
- // Delivery Instructions
604
- deliveryInstructions: text("delivery_instructions"),
605
- accessCode: varchar("access_code", { length: 20 }),
606
- deliveryPreference: varchar("delivery_preference", { length: 50 }).$type<
607
- | "FRONT_DOOR"
608
- | "BACK_DOOR"
609
- | "SIDE_DOOR"
610
- | "MAILBOX"
611
- | "SAFE_PLACE"
612
- | "NEIGHBOR"
613
- | "PICKUP_POINT"
614
- >(),
615
-
616
- // Validation Flags
617
- isValidated: boolean("is_validated").notNull().default(false),
618
- isResidential: boolean("is_residential").notNull().default(true),
619
- isPOBox: boolean("is_po_box").notNull().default(false),
620
- isMilitary: boolean("is_military").notNull().default(false),
621
-
622
- // Timestamps
623
- createdAt: timestamp("created_at", { withTimezone: true })
624
- .defaultNow()
625
- .notNull(),
626
- updatedAt: timestamp("updated_at", { withTimezone: true })
627
- .defaultNow()
628
- .notNull(),
629
- },
630
- (table) => ({
631
- orderIdIndex: index("idx_order_addresses_order_id").on(table.orderId),
632
- addressTypeIndex: index("idx_order_addresses_type").on(table.addressType),
633
- countryIndex: index("idx_order_addresses_country").on(table.countryCode),
634
- postalCodeIndex: index("idx_order_addresses_postal_code").on(
635
- table.postalCode,
636
- ),
637
- geoLocationIndex: index("idx_order_addresses_geo").on(
638
- table.latitude,
639
- table.longitude,
640
- ),
641
-
642
- // Ensure one address per type per order
643
- uniqueOrderAddressType: unique("unq_order_address_type").on(
644
- table.orderId,
645
- table.addressType,
646
- ),
647
- }),
648
- );
649
-
650
- // =====================================================
651
- // ORDER PAYMENTS TABLE
652
- // =====================================================
653
-
654
- export const orderPayments = pgTable(
655
- "order_payments",
656
- {
657
- id: text("id")
658
- .primaryKey()
659
- .$defaultFn(() => createId()),
660
-
661
- orderId: text("order_id")
662
- .notNull()
663
- .references(() => orders.id, { onDelete: "cascade" }),
664
-
665
- // Payment Identification
666
- paymentMethod: paymentMethodEnum("payment_method").notNull(),
667
- paymentProvider: varchar("payment_provider", { length: 50 }),
668
- transactionId: varchar("transaction_id", { length: 100 }),
669
- externalTransactionId: varchar("external_transaction_id", { length: 100 }),
670
- authorizationCode: varchar("authorization_code", { length: 50 }),
671
-
672
- // Payment Status and Type
673
- status: paymentStatusEnum("status").notNull().default("PENDING"),
674
- paymentType: varchar("payment_type", { length: 20 })
675
- .notNull()
676
- .default("PAYMENT")
677
- .$type<
678
- "PAYMENT" | "REFUND" | "PARTIAL_REFUND" | "CHARGEBACK" | "ADJUSTMENT"
679
- >(),
680
-
681
- // Financial Information
682
- currency: varchar("currency", { length: 3 }).notNull(),
683
- amount: decimal("amount", { precision: 12, scale: 2 }).notNull(),
684
- feeAmount: decimal("fee_amount", { precision: 12, scale: 2 })
685
- .notNull()
686
- .default("0.00"),
687
- netAmount: decimal("net_amount", { precision: 12, scale: 2 }).notNull(),
688
- exchangeRate: decimal("exchange_rate", { precision: 10, scale: 6 }).default(
689
- "1.000000",
690
- ),
691
-
692
- // Card Information (for card payments)
693
- cardLast4: varchar("card_last4", { length: 4 }),
694
- cardBrand: varchar("card_brand", { length: 20 }),
695
- cardType: varchar("card_type", { length: 20 }),
696
- cardCountry: varchar("card_country", { length: 2 }),
697
- cardFingerprint: varchar("card_fingerprint", { length: 100 }),
698
-
699
- // Processing Information
700
- processorResponse:
701
- jsonb("processor_response").$type<Record<string, unknown>>(),
702
- gatewayResponse: jsonb("gateway_response").$type<Record<string, unknown>>(),
703
- riskAssessment: jsonb("risk_assessment").$type<{
704
- score?: number;
705
- level?: string;
706
- factors?: string[];
707
- recommendation?: string;
708
- }>(),
709
-
710
- // Timing Information
711
- authorizedAt: timestamp("authorized_at", { withTimezone: true }),
712
- capturedAt: timestamp("captured_at", { withTimezone: true }),
713
- settledAt: timestamp("settled_at", { withTimezone: true }),
714
- refundedAt: timestamp("refunded_at", { withTimezone: true }),
715
- voidedAt: timestamp("voided_at", { withTimezone: true }),
716
-
717
- // Customer Information
718
- customerIp: varchar("customer_ip", { length: 45 }),
719
- billingAddressId: text("billing_address_id"),
720
-
721
- // Reference Information
722
- parentPaymentId: text("parent_payment_id"), // For refunds
723
- originalTransactionId: varchar("original_transaction_id", { length: 100 }), // For refunds/chargebacks
724
-
725
- // Failure Information
726
- failureCode: varchar("failure_code", { length: 50 }),
727
- failureMessage: text("failure_message"),
728
- declineCode: varchar("decline_code", { length: 50 }),
729
-
730
- // Metadata
731
- metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
732
- processorData: jsonb("processor_data")
733
- .$type<Record<string, unknown>>()
734
- .default({}),
735
-
736
- // Timestamps
737
- createdAt: timestamp("created_at", { withTimezone: true })
738
- .defaultNow()
739
- .notNull(),
740
- updatedAt: timestamp("updated_at", { withTimezone: true })
741
- .defaultNow()
742
- .notNull(),
743
- },
744
- (table) => ({
745
- orderIdIndex: index("idx_order_payments_order_id").on(table.orderId),
746
- statusIndex: index("idx_order_payments_status").on(table.status),
747
- paymentMethodIndex: index("idx_order_payments_method").on(
748
- table.paymentMethod,
749
- ),
750
- transactionIdIndex: index("idx_order_payments_transaction_id").on(
751
- table.transactionId,
752
- ),
753
- externalTransactionIdIndex: index(
754
- "idx_order_payments_external_transaction_id",
755
- ).on(table.externalTransactionId),
756
- paymentProviderIndex: index("idx_order_payments_provider").on(
757
- table.paymentProvider,
758
- ),
759
- parentPaymentIndex: index("idx_order_payments_parent").on(
760
- table.parentPaymentId,
761
- ),
762
- createdAtIndex: index("idx_order_payments_created_at").on(table.createdAt),
763
-
764
- // Financial Indexes
765
- amountIndex: index("idx_order_payments_amount").on(table.amount),
766
- currencyIndex: index("idx_order_payments_currency").on(table.currency),
767
-
768
- // Card Information Indexes
769
- cardFingerprintIndex: index("idx_order_payments_card_fingerprint").on(
770
- table.cardFingerprint,
771
- ),
772
- cardBrandIndex: index("idx_order_payments_card_brand").on(table.cardBrand),
773
-
774
- // Composite Indexes
775
- orderStatusIndex: index("idx_order_payments_order_status").on(
776
- table.orderId,
777
- table.status,
778
- ),
779
- methodStatusIndex: index("idx_order_payments_method_status").on(
780
- table.paymentMethod,
781
- table.status,
782
- ),
783
- }),
784
- );
785
-
786
- // =====================================================
787
- // ORDER FULFILLMENTS TABLE
788
- // =====================================================
789
-
790
- export const orderFulfillments = pgTable(
791
- "order_fulfillments",
792
- {
793
- id: text("id")
794
- .primaryKey()
795
- .$defaultFn(() => createId()),
796
-
797
- orderId: text("order_id")
798
- .notNull()
799
- .references(() => orders.id, { onDelete: "cascade" }),
800
-
801
- // Fulfillment Identification
802
- fulfillmentNumber: varchar("fulfillment_number", { length: 50 }),
803
- trackingNumber: varchar("tracking_number", { length: 100 }),
804
- trackingUrl: text("tracking_url"),
805
-
806
- // Status and Type
807
- status: fulfillmentStatusEnum("status").notNull().default("PENDING"),
808
- fulfillmentType: varchar("fulfillment_type", { length: 20 })
809
- .notNull()
810
- .$type<"SHIPPING" | "PICKUP" | "DELIVERY" | "DIGITAL" | "SERVICE">(),
811
-
812
- // Carrier and Service Information
813
- shippingCarrier: varchar("shipping_carrier", { length: 50 }),
814
- shippingService: varchar("shipping_service", { length: 100 }),
815
- shippingServiceCode: varchar("shipping_service_code", { length: 50 }),
816
- estimatedDeliveryDate: timestamp("estimated_delivery_date", {
817
- withTimezone: true,
818
- }),
819
- guaranteedDeliveryDate: timestamp("guaranteed_delivery_date", {
820
- withTimezone: true,
821
- }),
822
-
823
- // Location Information
824
- originLocationId: text("origin_location_id"),
825
- destinationAddressId: text("destination_address_id"),
826
-
827
- // Package Information
828
- packageCount: integer("package_count").notNull().default(1),
829
- totalWeight: decimal("total_weight", { precision: 8, scale: 3 }),
830
- totalVolume: decimal("total_volume", { precision: 8, scale: 3 }),
831
- packageDimensions: jsonb("package_dimensions").$type<{
832
- length?: number;
833
- width?: number;
834
- height?: number;
835
- unit?: "cm" | "in";
836
- }>(),
837
-
838
- // Shipping Costs
839
- shippingCost: decimal("shipping_cost", { precision: 12, scale: 2 })
840
- .notNull()
841
- .default("0.00"),
842
- insuranceCost: decimal("insurance_cost", { precision: 12, scale: 2 })
843
- .notNull()
844
- .default("0.00"),
845
- handlingCost: decimal("handling_cost", { precision: 12, scale: 2 })
846
- .notNull()
847
- .default("0.00"),
848
-
849
- // Special Services
850
- signatureRequired: boolean("signature_required").notNull().default(false),
851
- adultSignatureRequired: boolean("adult_signature_required")
852
- .notNull()
853
- .default(false),
854
- insuranceRequired: boolean("insurance_required").notNull().default(false),
855
- saturdayDelivery: boolean("saturday_delivery").notNull().default(false),
856
-
857
- // Delivery Information
858
- deliveryAttempts: integer("delivery_attempts").notNull().default(0),
859
- lastDeliveryAttempt: timestamp("last_delivery_attempt", {
860
- withTimezone: true,
861
- }),
862
- deliveryNotes: text("delivery_notes"),
863
- proofOfDelivery: jsonb("proof_of_delivery").$type<{
864
- signedBy?: string;
865
- image?: string;
866
- location?: { lat: number; lng: number };
867
- timestamp?: string;
868
- }>(),
869
-
870
- // Timing Information
871
- labelCreatedAt: timestamp("label_created_at", { withTimezone: true }),
872
- pickedUpAt: timestamp("picked_up_at", { withTimezone: true }),
873
- inTransitAt: timestamp("in_transit_at", { withTimezone: true }),
874
- outForDeliveryAt: timestamp("out_for_delivery_at", { withTimezone: true }),
875
- deliveredAt: timestamp("delivered_at", { withTimezone: true }),
876
- returnedAt: timestamp("returned_at", { withTimezone: true }),
877
-
878
- // Staff Information
879
- packedBy: text("packed_by"),
880
- shippedBy: text("shipped_by"),
881
- deliveredBy: text("delivered_by"),
882
-
883
- // Failure and Return Information
884
- failureReason: text("failure_reason"),
885
- returnReason: text("return_reason"),
886
- returnTrackingNumber: varchar("return_tracking_number", { length: 100 }),
887
-
888
- // Metadata
889
- carrierData: jsonb("carrier_data")
890
- .$type<Record<string, unknown>>()
891
- .default({}),
892
- trackingEvents: jsonb("tracking_events")
893
- .$type<
894
- Array<{
895
- timestamp: string;
896
- status: string;
897
- location?: string;
898
- description?: string;
899
- }>
900
- >()
901
- .default([]),
902
-
903
- // Timestamps
904
- createdAt: timestamp("created_at", { withTimezone: true })
905
- .defaultNow()
906
- .notNull(),
907
- updatedAt: timestamp("updated_at", { withTimezone: true })
908
- .defaultNow()
909
- .notNull(),
910
- },
911
- (table) => ({
912
- orderIdIndex: index("idx_order_fulfillments_order_id").on(table.orderId),
913
- statusIndex: index("idx_order_fulfillments_status").on(table.status),
914
- trackingNumberIndex: index("idx_order_fulfillments_tracking_number").on(
915
- table.trackingNumber,
916
- ),
917
- carrierIndex: index("idx_order_fulfillments_carrier").on(
918
- table.shippingCarrier,
919
- ),
920
- fulfillmentTypeIndex: index("idx_order_fulfillments_type").on(
921
- table.fulfillmentType,
922
- ),
923
- originLocationIndex: index("idx_order_fulfillments_origin").on(
924
- table.originLocationId,
925
- ),
926
- destinationAddressIndex: index("idx_order_fulfillments_destination").on(
927
- table.destinationAddressId,
928
- ),
929
- estimatedDeliveryIndex: index(
930
- "idx_order_fulfillments_estimated_delivery",
931
- ).on(table.estimatedDeliveryDate),
932
- deliveredAtIndex: index("idx_order_fulfillments_delivered_at").on(
933
- table.deliveredAt,
934
- ),
935
- createdAtIndex: index("idx_order_fulfillments_created_at").on(
936
- table.createdAt,
937
- ),
938
-
939
- // Staff Indexes
940
- packedByIndex: index("idx_order_fulfillments_packed_by").on(table.packedBy),
941
- shippedByIndex: index("idx_order_fulfillments_shipped_by").on(
942
- table.shippedBy,
943
- ),
944
-
945
- // Composite Indexes
946
- orderStatusIndex: index("idx_order_fulfillments_order_status").on(
947
- table.orderId,
948
- table.status,
949
- ),
950
- carrierStatusIndex: index("idx_order_fulfillments_carrier_status").on(
951
- table.shippingCarrier,
952
- table.status,
953
- ),
954
- }),
955
- );
956
-
957
- // =====================================================
958
- // ORDER DISCOUNTS TABLE
959
- // =====================================================
960
-
961
- export const orderDiscounts = pgTable(
962
- "order_discounts",
963
- {
964
- id: text("id")
965
- .primaryKey()
966
- .$defaultFn(() => createId()),
967
-
968
- orderId: text("order_id")
969
- .notNull()
970
- .references(() => orders.id, { onDelete: "cascade" }),
971
-
972
- // Discount Identification
973
- discountCode: varchar("discount_code", { length: 50 }),
974
- discountType: discountTypeEnum("discount_type").notNull(),
975
- discountSource: varchar("discount_source", { length: 50 })
976
- .notNull()
977
- .$type<
978
- | "COUPON"
979
- | "PROMOTION"
980
- | "LOYALTY"
981
- | "MANUAL"
982
- | "AUTOMATIC"
983
- | "BULK"
984
- | "REFERRAL"
985
- >(),
986
-
987
- // Discount Details
988
- title: varchar("title", { length: 255 }).notNull(),
989
- description: text("description"),
990
-
991
- // Discount Values
992
- discountAmount: decimal("discount_amount", {
993
- precision: 12,
994
- scale: 2,
995
- }).notNull(),
996
- discountPercentage: decimal("discount_percentage", {
997
- precision: 5,
998
- scale: 2,
999
- }),
1000
- maximumDiscount: decimal("maximum_discount", { precision: 12, scale: 2 }),
1001
-
1002
- // Application Rules
1003
- appliedToOrderTotal: boolean("applied_to_order_total")
1004
- .notNull()
1005
- .default(true),
1006
- appliedToShipping: boolean("applied_to_shipping").notNull().default(false),
1007
- appliedToTax: boolean("applied_to_tax").notNull().default(false),
1008
-
1009
- // Scope and Conditions
1010
- minimumOrderAmount: decimal("minimum_order_amount", {
1011
- precision: 12,
1012
- scale: 2,
1013
- }),
1014
- maximumOrderAmount: decimal("maximum_order_amount", {
1015
- precision: 12,
1016
- scale: 2,
1017
- }),
1018
- applicableProductIds: jsonb("applicable_product_ids")
1019
- .$type<string[]>()
1020
- .default([]),
1021
- applicableCategories: jsonb("applicable_categories")
1022
- .$type<string[]>()
1023
- .default([]),
1024
- excludedProductIds: jsonb("excluded_product_ids")
1025
- .$type<string[]>()
1026
- .default([]),
1027
-
1028
- // Customer Eligibility
1029
- eligibleCustomerTypes: jsonb("eligible_customer_types")
1030
- .$type<string[]>()
1031
- .default([]),
1032
- firstTimeCustomerOnly: boolean("first_time_customer_only")
1033
- .notNull()
1034
- .default(false),
1035
- loyaltyPointsUsed: integer("loyalty_points_used").default(0),
1036
-
1037
- // Validation and Usage
1038
- isValid: boolean("is_valid").notNull().default(true),
1039
- validationErrors: jsonb("validation_errors").$type<string[]>().default([]),
1040
- usageCount: integer("usage_count").notNull().default(1),
1041
-
1042
- // Campaign Information
1043
- campaignId: text("campaign_id"),
1044
- campaignName: varchar("campaign_name", { length: 100 }),
1045
- affiliateId: text("affiliate_id"),
1046
-
1047
- // Metadata
1048
- metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1049
- promotionRules: jsonb("promotion_rules")
1050
- .$type<Record<string, unknown>>()
1051
- .default({}),
1052
-
1053
- // Timestamps
1054
- appliedAt: timestamp("applied_at", { withTimezone: true })
1055
- .defaultNow()
1056
- .notNull(),
1057
- createdAt: timestamp("created_at", { withTimezone: true })
1058
- .defaultNow()
1059
- .notNull(),
1060
- },
1061
- (table) => ({
1062
- orderIdIndex: index("idx_order_discounts_order_id").on(table.orderId),
1063
- discountCodeIndex: index("idx_order_discounts_code").on(table.discountCode),
1064
- discountTypeIndex: index("idx_order_discounts_type").on(table.discountType),
1065
- discountSourceIndex: index("idx_order_discounts_source").on(
1066
- table.discountSource,
1067
- ),
1068
- campaignIdIndex: index("idx_order_discounts_campaign").on(table.campaignId),
1069
- affiliateIdIndex: index("idx_order_discounts_affiliate").on(
1070
- table.affiliateId,
1071
- ),
1072
- appliedAtIndex: index("idx_order_discounts_applied_at").on(table.appliedAt),
1073
-
1074
- // Composite Indexes
1075
- orderDiscountTypeIndex: index("idx_order_discounts_order_type").on(
1076
- table.orderId,
1077
- table.discountType,
1078
- ),
1079
- codeValidIndex: index("idx_order_discounts_code_valid").on(
1080
- table.discountCode,
1081
- table.isValid,
1082
- ),
1083
- }),
1084
- );
1085
-
1086
- // =====================================================
1087
- // ORDER HISTORY TABLE
1088
- // =====================================================
1089
-
1090
- export const orderHistory = pgTable(
1091
- "order_history",
1092
- {
1093
- id: text("id")
1094
- .primaryKey()
1095
- .$defaultFn(() => createId()),
1096
-
1097
- orderId: text("order_id")
1098
- .notNull()
1099
- .references(() => orders.id, { onDelete: "cascade" }),
1100
-
1101
- // Status Change Information
1102
- eventType: varchar("event_type", { length: 50 })
1103
- .notNull()
1104
- .$type<
1105
- | "STATUS_CHANGE"
1106
- | "PAYMENT_UPDATE"
1107
- | "FULFILLMENT_UPDATE"
1108
- | "ADDRESS_CHANGE"
1109
- | "ITEM_ADDED"
1110
- | "ITEM_REMOVED"
1111
- | "ITEM_UPDATED"
1112
- | "NOTE_ADDED"
1113
- | "DISCOUNT_APPLIED"
1114
- | "REFUND_PROCESSED"
1115
- | "CANCELLATION"
1116
- | "MODIFICATION"
1117
- | "ESCALATION"
1118
- | "RETURN_REQUEST"
1119
- >(),
1120
-
1121
- // Change Details
1122
- fromValue: text("from_value"),
1123
- toValue: text("to_value"),
1124
- fieldChanged: varchar("field_changed", { length: 100 }),
1125
- changeReason: text("change_reason"),
1126
-
1127
- // Actor Information
1128
- actorType: varchar("actor_type", { length: 20 })
1129
- .notNull()
1130
- .$type<"CUSTOMER" | "STAFF" | "SYSTEM" | "ADMIN" | "AUTOMATION">(),
1131
- actorId: text("actor_id"),
1132
- actorName: varchar("actor_name", { length: 100 }),
1133
- actorRole: varchar("actor_role", { length: 50 }),
1134
-
1135
- // Context Information
1136
- source: varchar("source", { length: 50 })
1137
- .notNull()
1138
- .$type<
1139
- | "WEB"
1140
- | "MOBILE"
1141
- | "POS"
1142
- | "ADMIN"
1143
- | "API"
1144
- | "PHONE"
1145
- | "EMAIL"
1146
- | "CHAT"
1147
- | "SYSTEM"
1148
- >(),
1149
- sessionId: text("session_id"),
1150
- ipAddress: varchar("ip_address", { length: 45 }),
1151
- userAgent: text("user_agent"),
1152
-
1153
- // Detailed Information
1154
- description: text("description"),
1155
- internalNotes: text("internal_notes"),
1156
- customerVisible: boolean("customer_visible").notNull().default(true),
1157
-
1158
- // Related Data
1159
- relatedEntityType: varchar("related_entity_type", { length: 50 }),
1160
- relatedEntityId: text("related_entity_id"),
1161
- changeData: jsonb("change_data")
1162
- .$type<Record<string, unknown>>()
1163
- .default({}),
1164
-
1165
- // Impact Assessment
1166
- businessImpact: varchar("business_impact", { length: 20 }).$type<
1167
- "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
1168
- >(),
1169
- customerImpact: varchar("customer_impact", { length: 20 }).$type<
1170
- "POSITIVE" | "NEUTRAL" | "NEGATIVE"
1171
- >(),
1172
-
1173
- // Metadata
1174
- metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1175
-
1176
- // Timestamps
1177
- occurredAt: timestamp("occurred_at", { withTimezone: true })
1178
- .defaultNow()
1179
- .notNull(),
1180
- createdAt: timestamp("created_at", { withTimezone: true })
1181
- .defaultNow()
1182
- .notNull(),
1183
- },
1184
- (table) => ({
1185
- orderIdIndex: index("idx_order_history_order_id").on(table.orderId),
1186
- eventTypeIndex: index("idx_order_history_event_type").on(table.eventType),
1187
- actorTypeIndex: index("idx_order_history_actor_type").on(table.actorType),
1188
- actorIdIndex: index("idx_order_history_actor_id").on(table.actorId),
1189
- sourceIndex: index("idx_order_history_source").on(table.source),
1190
- occurredAtIndex: index("idx_order_history_occurred_at").on(
1191
- table.occurredAt,
1192
- ),
1193
- customerVisibleIndex: index("idx_order_history_customer_visible").on(
1194
- table.customerVisible,
1195
- ),
1196
-
1197
- // Composite Indexes
1198
- orderEventIndex: index("idx_order_history_order_event").on(
1199
- table.orderId,
1200
- table.eventType,
1201
- ),
1202
- orderDateIndex: index("idx_order_history_order_date").on(
1203
- table.orderId,
1204
- table.occurredAt,
1205
- ),
1206
- actorEventIndex: index("idx_order_history_actor_event").on(
1207
- table.actorId,
1208
- table.eventType,
1209
- ),
1210
- }),
1211
- );
1212
-
1213
- // =====================================================
1214
- // ORDER NOTES TABLE
1215
- // =====================================================
1216
-
1217
- export const orderNotes = pgTable(
1218
- "order_notes",
1219
- {
1220
- id: text("id")
1221
- .primaryKey()
1222
- .$defaultFn(() => createId()),
1223
-
1224
- orderId: text("order_id")
1225
- .notNull()
1226
- .references(() => orders.id, { onDelete: "cascade" }),
1227
-
1228
- // Note Classification
1229
- noteType: varchar("note_type", { length: 30 })
1230
- .notNull()
1231
- .$type<
1232
- | "GENERAL"
1233
- | "CUSTOMER_SERVICE"
1234
- | "FULFILLMENT"
1235
- | "PAYMENT"
1236
- | "FRAUD"
1237
- | "QUALITY"
1238
- | "SHIPPING"
1239
- | "RETURNS"
1240
- | "ESCALATION"
1241
- | "FOLLOW_UP"
1242
- >(),
1243
-
1244
- // Note Content
1245
- title: varchar("title", { length: 255 }),
1246
- content: text("content").notNull(),
1247
-
1248
- // Visibility and Priority
1249
- isCustomerVisible: boolean("is_customer_visible").notNull().default(false),
1250
- isInternal: boolean("is_internal").notNull().default(true),
1251
- priority: varchar("priority", { length: 20 })
1252
- .notNull()
1253
- .default("NORMAL")
1254
- .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT">(),
1255
-
1256
- // Actor Information
1257
- createdBy: text("created_by").notNull(),
1258
- createdByName: varchar("created_by_name", { length: 100 }).notNull(),
1259
- createdByRole: varchar("created_by_role", { length: 50 }),
1260
- department: varchar("department", { length: 50 }),
1261
-
1262
- // Follow-up and Actions
1263
- requiresFollowUp: boolean("requires_follow_up").notNull().default(false),
1264
- followUpDate: timestamp("follow_up_date", { withTimezone: true }),
1265
- followUpAssignedTo: text("follow_up_assigned_to"),
1266
- isFollowUpCompleted: boolean("is_follow_up_completed")
1267
- .notNull()
1268
- .default(false),
1269
-
1270
- // Context and References
1271
- relatedEntityType: varchar("related_entity_type", { length: 50 }),
1272
- relatedEntityId: text("related_entity_id"),
1273
- parentNoteId: text("parent_note_id"),
1274
-
1275
- // Tagging and Categorization
1276
- tags: jsonb("tags").$type<string[]>().default([]),
1277
- category: varchar("category", { length: 50 }),
1278
- sentiment: varchar("sentiment", { length: 20 }).$type<
1279
- "POSITIVE" | "NEUTRAL" | "NEGATIVE"
1280
- >(),
1281
-
1282
- // Metadata
1283
- metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1284
-
1285
- // Timestamps
1286
- createdAt: timestamp("created_at", { withTimezone: true })
1287
- .defaultNow()
1288
- .notNull(),
1289
- updatedAt: timestamp("updated_at", { withTimezone: true })
1290
- .defaultNow()
1291
- .notNull(),
1292
- },
1293
- (table) => ({
1294
- orderIdIndex: index("idx_order_notes_order_id").on(table.orderId),
1295
- noteTypeIndex: index("idx_order_notes_type").on(table.noteType),
1296
- createdByIndex: index("idx_order_notes_created_by").on(table.createdBy),
1297
- priorityIndex: index("idx_order_notes_priority").on(table.priority),
1298
- customerVisibleIndex: index("idx_order_notes_customer_visible").on(
1299
- table.isCustomerVisible,
1300
- ),
1301
- followUpIndex: index("idx_order_notes_follow_up").on(
1302
- table.requiresFollowUp,
1303
- table.followUpDate,
1304
- ),
1305
- parentNoteIndex: index("idx_order_notes_parent").on(table.parentNoteId),
1306
- createdAtIndex: index("idx_order_notes_created_at").on(table.createdAt),
1307
-
1308
- // Composite Indexes
1309
- orderNoteTypeIndex: index("idx_order_notes_order_type").on(
1310
- table.orderId,
1311
- table.noteType,
1312
- ),
1313
- orderCreatedIndex: index("idx_order_notes_order_created").on(
1314
- table.orderId,
1315
- table.createdAt,
1316
- ),
1317
- }),
1318
- );
1319
-
1320
- // =====================================================
1321
- // RELATIONS
1322
- // =====================================================
1323
-
1324
- export const ordersRelations = relations(orders, ({ many, one }) => ({
1325
- orderItems: many(orderItems),
1326
- orderAddresses: many(orderAddresses),
1327
- orderPayments: many(orderPayments),
1328
- orderFulfillments: many(orderFulfillments),
1329
- orderDiscounts: many(orderDiscounts),
1330
- orderHistory: many(orderHistory),
1331
- orderNotes: many(orderNotes),
1332
-
1333
- // Parent order relation for split orders
1334
- parentOrder: one(orders, {
1335
- fields: [orders.parentOrderId],
1336
- references: [orders.id],
1337
- }),
1338
- childOrders: many(orders),
1339
- }));
1340
-
1341
- export const orderItemsRelations = relations(orderItems, ({ one }) => ({
1342
- order: one(orders, {
1343
- fields: [orderItems.orderId],
1344
- references: [orders.id],
1345
- }),
1346
- }));
1347
-
1348
- export const orderAddressesRelations = relations(orderAddresses, ({ one }) => ({
1349
- order: one(orders, {
1350
- fields: [orderAddresses.orderId],
1351
- references: [orders.id],
1352
- }),
1353
- }));
1354
-
1355
- export const orderPaymentsRelations = relations(orderPayments, ({ one }) => ({
1356
- order: one(orders, {
1357
- fields: [orderPayments.orderId],
1358
- references: [orders.id],
1359
- }),
1360
- parentPayment: one(orderPayments, {
1361
- fields: [orderPayments.parentPaymentId],
1362
- references: [orderPayments.id],
1363
- }),
1364
- }));
1365
-
1366
- export const orderFulfillmentsRelations = relations(
1367
- orderFulfillments,
1368
- ({ one }) => ({
1369
- order: one(orders, {
1370
- fields: [orderFulfillments.orderId],
1371
- references: [orders.id],
1372
- }),
1373
- }),
1374
- );
1375
-
1376
- export const orderDiscountsRelations = relations(orderDiscounts, ({ one }) => ({
1377
- order: one(orders, {
1378
- fields: [orderDiscounts.orderId],
1379
- references: [orders.id],
1380
- }),
1381
- }));
1382
-
1383
- export const orderHistoryRelations = relations(orderHistory, ({ one }) => ({
1384
- order: one(orders, {
1385
- fields: [orderHistory.orderId],
1386
- references: [orders.id],
1387
- }),
1388
- }));
1389
-
1390
- export const orderNotesRelations = relations(orderNotes, ({ one, many }) => ({
1391
- order: one(orders, {
1392
- fields: [orderNotes.orderId],
1393
- references: [orders.id],
1394
- }),
1395
- parentNote: one(orderNotes, {
1396
- fields: [orderNotes.parentNoteId],
1397
- references: [orderNotes.id],
1398
- }),
1399
- childNotes: many(orderNotes),
1400
- }));
1
+ import { createId } from "@paralleldrive/cuid2";
2
+ import { relations } from "drizzle-orm";
3
+ import {
4
+ boolean,
5
+ decimal,
6
+ index,
7
+ integer,
8
+ jsonb,
9
+ pgEnum,
10
+ pgTable,
11
+ text,
12
+ timestamp,
13
+ unique,
14
+ varchar,
15
+ } from "drizzle-orm/pg-core";
16
+
17
+ // =====================================================
18
+ // ENUMS
19
+ // =====================================================
20
+
21
+ export const orderTypeEnum = pgEnum("order_type", [
22
+ "ONLINE",
23
+ "POS",
24
+ "PICKUP",
25
+ "DELIVERY",
26
+ "MARKETPLACE",
27
+ "PHONE",
28
+ "WHOLESALE",
29
+ "SUBSCRIPTION",
30
+ "SUBSCRIPTION_RENEWAL",
31
+ ]);
32
+
33
+ export const orderStatusEnum = pgEnum("order_status", [
34
+ "DRAFT",
35
+ "PENDING",
36
+ "CONFIRMED",
37
+ "PROCESSING",
38
+ "PICKING",
39
+ "PACKED",
40
+ "SHIPPED",
41
+ "OUT_FOR_DELIVERY",
42
+ "DELIVERED",
43
+ "COMPLETED",
44
+ "CANCELLED",
45
+ "REFUNDED",
46
+ "PARTIALLY_REFUNDED",
47
+ "RETURNED",
48
+ "PARTIALLY_RETURNED",
49
+ "DISPUTED",
50
+ "ON_HOLD",
51
+ "FAILED",
52
+ ]);
53
+
54
+ export const paymentStatusEnum = pgEnum("payment_status", [
55
+ "PENDING",
56
+ "AUTHORIZED",
57
+ "PAID",
58
+ "PARTIALLY_PAID",
59
+ "FAILED",
60
+ "CANCELLED",
61
+ "REFUNDED",
62
+ "PARTIALLY_REFUNDED",
63
+ "VOIDED",
64
+ "CHARGEBACK",
65
+ "DISPUTED",
66
+ ]);
67
+
68
+ export const fulfillmentStatusEnum = pgEnum("fulfillment_status", [
69
+ "PENDING",
70
+ "ALLOCATED",
71
+ "PICKING",
72
+ "PICKED",
73
+ "PACKED",
74
+ "READY_FOR_PICKUP",
75
+ "SHIPPED",
76
+ "IN_TRANSIT",
77
+ "OUT_FOR_DELIVERY",
78
+ "DELIVERED",
79
+ "ATTEMPTED_DELIVERY",
80
+ "DELIVERY_FAILED",
81
+ "RETURNED_TO_SENDER",
82
+ "CANCELLED",
83
+ "LOST",
84
+ ]);
85
+
86
+ export const orderPriorityEnum = pgEnum("order_priority", [
87
+ "LOW",
88
+ "NORMAL",
89
+ "HIGH",
90
+ "URGENT",
91
+ "CRITICAL",
92
+ ]);
93
+
94
+ export const discountTypeEnum = pgEnum("discount_type", [
95
+ "PERCENTAGE",
96
+ "FIXED_AMOUNT",
97
+ "FREE_SHIPPING",
98
+ "BOGO",
99
+ "LOYALTY_POINTS",
100
+ "COUPON",
101
+ "PROMOTIONAL",
102
+ "BULK",
103
+ "EARLY_BIRD",
104
+ ]);
105
+
106
+ export const addressTypeEnum = pgEnum("address_type", [
107
+ "BILLING",
108
+ "SHIPPING",
109
+ "PICKUP",
110
+ "DELIVERY",
111
+ ]);
112
+
113
+ export const paymentMethodEnum = pgEnum("payment_method", [
114
+ "CREDIT_CARD",
115
+ "DEBIT_CARD",
116
+ "PAYPAL",
117
+ "APPLE_PAY",
118
+ "GOOGLE_PAY",
119
+ "BANK_TRANSFER",
120
+ "CASH",
121
+ "CHECK",
122
+ "STORE_CREDIT",
123
+ "GIFT_CARD",
124
+ "CRYPTOCURRENCY",
125
+ "BUY_NOW_PAY_LATER",
126
+ "INVOICE",
127
+ "COD", // Cash on Delivery
128
+ ]);
129
+
130
+ export const refundReasonEnum = pgEnum("refund_reason", [
131
+ "CUSTOMER_REQUEST",
132
+ "DEFECTIVE_PRODUCT",
133
+ "WRONG_ITEM",
134
+ "NOT_AS_DESCRIBED",
135
+ "DAMAGED_IN_SHIPPING",
136
+ "QUALITY_ISSUE",
137
+ "SIZE_ISSUE",
138
+ "CHANGE_OF_MIND",
139
+ "DUPLICATE_ORDER",
140
+ "FRAUD_PREVENTION",
141
+ "BUSINESS_POLICY",
142
+ "GOODWILL",
143
+ ]);
144
+
145
+ // =====================================================
146
+ // CORE ORDERS TABLE
147
+ // =====================================================
148
+
149
+ export const orders = pgTable(
150
+ "orders",
151
+ {
152
+ id: text("id")
153
+ .primaryKey()
154
+ .$defaultFn(() => createId()),
155
+
156
+ // External References
157
+ storeId: text("store_id").notNull(),
158
+ customerId: text("customer_id"), // Nullable for guest orders
159
+ userId: text("user_id"), // Account holder
160
+ sessionId: text("session_id"), // Browser/device session
161
+
162
+ // Order Identification
163
+ orderNumber: varchar("order_number", { length: 50 }).notNull(),
164
+ displayOrderNumber: varchar("display_order_number", { length: 50 }), // Customer-friendly number
165
+ externalOrderId: text("external_order_id"), // For marketplace orders
166
+ parentOrderId: text("parent_order_id"), // For split orders
167
+
168
+ // Order Classification
169
+ orderType: orderTypeEnum("order_type").notNull(),
170
+ orderSource: varchar("order_source", { length: 50 })
171
+ .notNull()
172
+ .$type<
173
+ | "WEB"
174
+ | "MOBILE_APP"
175
+ | "POS"
176
+ | "PHONE"
177
+ | "EMAIL"
178
+ | "SOCIAL_MEDIA"
179
+ | "MARKETPLACE"
180
+ | "API"
181
+ | "ADMIN"
182
+ | "IMPORT"
183
+ | "SUBSCRIPTION_RENEWAL"
184
+ >(),
185
+ marketplaceInfo: jsonb("marketplace_info").$type<{
186
+ platform?: string;
187
+ orderId?: string;
188
+ accountId?: string;
189
+ fees?: number;
190
+ commission?: number;
191
+ }>(),
192
+
193
+ // Status Management
194
+ status: orderStatusEnum("status").notNull().default("DRAFT"),
195
+ paymentStatus: paymentStatusEnum("payment_status")
196
+ .notNull()
197
+ .default("PENDING"),
198
+ fulfillmentStatus: fulfillmentStatusEnum("fulfillment_status")
199
+ .notNull()
200
+ .default("PENDING"),
201
+
202
+ // Priority and Flags
203
+ priority: orderPriorityEnum("priority").notNull().default("NORMAL"),
204
+ isGuestOrder: boolean("is_guest_order").notNull().default(false),
205
+ isTestOrder: boolean("is_test_order").notNull().default(false),
206
+ isGift: boolean("is_gift").notNull().default(false),
207
+ isPreorder: boolean("is_preorder").notNull().default(false),
208
+ isSubscription: boolean("is_subscription").notNull().default(false),
209
+ requiresShipping: boolean("requires_shipping").notNull().default(true),
210
+ requiresPickup: boolean("requires_pickup").notNull().default(false),
211
+
212
+ // Financial Information
213
+ currency: varchar("currency", { length: 3 }).notNull().default("USD"),
214
+ exchangeRate: decimal("exchange_rate", { precision: 10, scale: 6 }).default(
215
+ "1.000000",
216
+ ),
217
+
218
+ // Amounts (in minor currency units - cents)
219
+ subtotalAmount: decimal("subtotal_amount", { precision: 12, scale: 2 })
220
+ .notNull()
221
+ .default("0.00"),
222
+ taxAmount: decimal("tax_amount", { precision: 12, scale: 2 })
223
+ .notNull()
224
+ .default("0.00"),
225
+ shippingAmount: decimal("shipping_amount", { precision: 12, scale: 2 })
226
+ .notNull()
227
+ .default("0.00"),
228
+ discountAmount: decimal("discount_amount", { precision: 12, scale: 2 })
229
+ .notNull()
230
+ .default("0.00"),
231
+ tipAmount: decimal("tip_amount", { precision: 12, scale: 2 })
232
+ .notNull()
233
+ .default("0.00"),
234
+ feeAmount: decimal("fee_amount", { precision: 12, scale: 2 })
235
+ .notNull()
236
+ .default("0.00"),
237
+ totalAmount: decimal("total_amount", { precision: 12, scale: 2 }).notNull(),
238
+ paidAmount: decimal("paid_amount", { precision: 12, scale: 2 })
239
+ .notNull()
240
+ .default("0.00"),
241
+ refundedAmount: decimal("refunded_amount", { precision: 12, scale: 2 })
242
+ .notNull()
243
+ .default("0.00"),
244
+ outstandingAmount: decimal("outstanding_amount", {
245
+ precision: 12,
246
+ scale: 2,
247
+ })
248
+ .notNull()
249
+ .default("0.00"),
250
+
251
+ // Tax Information
252
+ taxRate: decimal("tax_rate", { precision: 5, scale: 4 }),
253
+ taxIncluded: boolean("tax_included").notNull().default(false),
254
+ taxExempt: boolean("tax_exempt").notNull().default(false),
255
+ taxExemptReason: text("tax_exempt_reason"),
256
+
257
+ // Customer Information (for guest orders)
258
+ guestEmail: varchar("guest_email", { length: 255 }),
259
+ guestPhone: varchar("guest_phone", { length: 20 }),
260
+ guestName: varchar("guest_name", { length: 100 }),
261
+
262
+ // Location Information
263
+ storeLocationId: text("store_location_id"), // Physical store/warehouse
264
+ posTerminalId: text("pos_terminal_id"), // POS terminal for in-store orders
265
+ salesAssociateId: text("sales_associate_id"), // Staff who processed the order
266
+
267
+ // Timing Information
268
+ requestedDeliveryDate: timestamp("requested_delivery_date", {
269
+ withTimezone: true,
270
+ }),
271
+ promisedDeliveryDate: timestamp("promised_delivery_date", {
272
+ withTimezone: true,
273
+ }),
274
+ actualDeliveryDate: timestamp("actual_delivery_date", {
275
+ withTimezone: true,
276
+ }),
277
+ orderPlacedAt: timestamp("order_placed_at", { withTimezone: true }),
278
+ orderConfirmedAt: timestamp("order_confirmed_at", { withTimezone: true }),
279
+ orderShippedAt: timestamp("order_shipped_at", { withTimezone: true }),
280
+ orderDeliveredAt: timestamp("order_delivered_at", { withTimezone: true }),
281
+ orderCompletedAt: timestamp("order_completed_at", { withTimezone: true }),
282
+
283
+ // Special Instructions and Notes
284
+ customerNotes: text("customer_notes"),
285
+ internalNotes: text("internal_notes"),
286
+ giftMessage: text("gift_message"),
287
+ specialInstructions: text("special_instructions"),
288
+ deliveryInstructions: text("delivery_instructions"),
289
+
290
+ // Marketing and Attribution
291
+ referrerUrl: text("referrer_url"),
292
+ utmSource: varchar("utm_source", { length: 100 }),
293
+ utmMedium: varchar("utm_medium", { length: 100 }),
294
+ utmCampaign: varchar("utm_campaign", { length: 100 }),
295
+ affiliateId: text("affiliate_id"),
296
+ promoCode: varchar("promo_code", { length: 50 }),
297
+
298
+ // Technical Information
299
+ userAgent: text("user_agent"),
300
+ ipAddress: varchar("ip_address", { length: 45 }),
301
+ deviceInfo: jsonb("device_info").$type<{
302
+ type?: string;
303
+ brand?: string;
304
+ model?: string;
305
+ os?: string;
306
+ browser?: string;
307
+ }>(),
308
+
309
+ // Risk and Fraud
310
+ riskScore: decimal("risk_score", { precision: 5, scale: 2 }),
311
+ fraudScore: decimal("fraud_score", { precision: 5, scale: 2 }),
312
+ isHighRisk: boolean("is_high_risk").notNull().default(false),
313
+ requiresVerification: boolean("requires_verification")
314
+ .notNull()
315
+ .default(false),
316
+ verifiedAt: timestamp("verified_at", { withTimezone: true }),
317
+
318
+ // Subscription Information
319
+ subscriptionId: text("subscription_id"),
320
+ subscriptionCycle: integer("subscription_cycle"),
321
+ isSubscriptionRenewal: boolean("is_subscription_renewal")
322
+ .notNull()
323
+ .default(false),
324
+
325
+ // Advanced Features
326
+ tags: jsonb("tags").$type<string[]>().default([]),
327
+ customFields: jsonb("custom_fields")
328
+ .$type<Record<string, unknown>>()
329
+ .default({}),
330
+ metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
331
+
332
+ // Analytics
333
+ conversionValue: decimal("conversion_value", { precision: 12, scale: 2 }),
334
+ customerLifetimeValue: decimal("customer_lifetime_value", {
335
+ precision: 12,
336
+ scale: 2,
337
+ }),
338
+ profitMargin: decimal("profit_margin", { precision: 5, scale: 2 }),
339
+
340
+ // Timestamps
341
+ createdAt: timestamp("created_at", { withTimezone: true })
342
+ .defaultNow()
343
+ .notNull(),
344
+ updatedAt: timestamp("updated_at", { withTimezone: true })
345
+ .defaultNow()
346
+ .notNull(),
347
+ deletedAt: timestamp("deleted_at", { withTimezone: true }),
348
+ },
349
+ (table) => ({
350
+ // Primary Indexes
351
+ orderNumberIndex: unique("idx_orders_order_number").on(
352
+ table.storeId,
353
+ table.orderNumber,
354
+ ),
355
+ externalOrderIndex: index("idx_orders_external_id").on(
356
+ table.externalOrderId,
357
+ ),
358
+
359
+ // Customer and User Indexes
360
+ customerIndex: index("idx_orders_customer").on(table.customerId),
361
+ userIndex: index("idx_orders_user").on(table.userId),
362
+ guestEmailIndex: index("idx_orders_guest_email").on(table.guestEmail),
363
+
364
+ // Status Indexes
365
+ statusIndex: index("idx_orders_status").on(table.status),
366
+ paymentStatusIndex: index("idx_orders_payment_status").on(
367
+ table.paymentStatus,
368
+ ),
369
+ fulfillmentStatusIndex: index("idx_orders_fulfillment_status").on(
370
+ table.fulfillmentStatus,
371
+ ),
372
+
373
+ // Type and Source Indexes
374
+ orderTypeIndex: index("idx_orders_type").on(table.orderType),
375
+ orderSourceIndex: index("idx_orders_source").on(table.orderSource),
376
+
377
+ // Location Indexes
378
+ storeLocationIndex: index("idx_orders_store_location").on(
379
+ table.storeLocationId,
380
+ ),
381
+ posTerminalIndex: index("idx_orders_pos_terminal").on(table.posTerminalId),
382
+
383
+ // Timing Indexes
384
+ placedAtIndex: index("idx_orders_placed_at").on(table.orderPlacedAt),
385
+ deliveryDateIndex: index("idx_orders_delivery_date").on(
386
+ table.promisedDeliveryDate,
387
+ ),
388
+ createdAtIndex: index("idx_orders_created_at").on(table.createdAt),
389
+
390
+ // Financial Indexes
391
+ totalAmountIndex: index("idx_orders_total_amount").on(table.totalAmount),
392
+ currencyIndex: index("idx_orders_currency").on(table.currency),
393
+
394
+ // Special Flags Indexes
395
+ priorityIndex: index("idx_orders_priority").on(table.priority),
396
+ testOrderIndex: index("idx_orders_test").on(table.isTestOrder),
397
+ guestOrderIndex: index("idx_orders_guest").on(table.isGuestOrder),
398
+
399
+ // Composite Indexes
400
+ storeStatusIndex: index("idx_orders_store_status").on(
401
+ table.storeId,
402
+ table.status,
403
+ ),
404
+ customerStatusIndex: index("idx_orders_customer_status").on(
405
+ table.customerId,
406
+ table.status,
407
+ ),
408
+ typeStatusIndex: index("idx_orders_type_status").on(
409
+ table.orderType,
410
+ table.status,
411
+ ),
412
+ dateRangeIndex: index("idx_orders_date_range").on(
413
+ table.storeId,
414
+ table.createdAt,
415
+ ),
416
+ }),
417
+ );
418
+
419
+ // =====================================================
420
+ // ORDER ITEMS TABLE
421
+ // =====================================================
422
+
423
+ export const orderItems = pgTable(
424
+ "order_items",
425
+ {
426
+ id: text("id")
427
+ .primaryKey()
428
+ .$defaultFn(() => createId()),
429
+
430
+ orderId: text("order_id")
431
+ .notNull()
432
+ .references(() => orders.id, { onDelete: "cascade" }),
433
+
434
+ // Product Information
435
+ productId: text("product_id").notNull(),
436
+ variantId: text("variant_id"),
437
+ sku: varchar("sku", { length: 100 }).notNull(),
438
+ productHandle: varchar("product_handle", { length: 255 }),
439
+
440
+ // Product Details (snapshot at time of order)
441
+ productTitle: varchar("product_title", { length: 255 }).notNull(),
442
+ variantTitle: varchar("variant_title", { length: 255 }),
443
+ productType: varchar("product_type", { length: 50 }),
444
+ vendor: varchar("vendor", { length: 100 }),
445
+
446
+ // Quantity and Pricing
447
+ quantity: integer("quantity").notNull(),
448
+ quantityShipped: integer("quantity_shipped").notNull().default(0),
449
+ quantityReturned: integer("quantity_returned").notNull().default(0),
450
+ quantityRefunded: integer("quantity_refunded").notNull().default(0),
451
+
452
+ unitPrice: decimal("unit_price", { precision: 12, scale: 2 }).notNull(),
453
+ compareAtPrice: decimal("compare_at_price", { precision: 12, scale: 2 }),
454
+ costPrice: decimal("cost_price", { precision: 12, scale: 2 }),
455
+ totalPrice: decimal("total_price", { precision: 12, scale: 2 }).notNull(),
456
+
457
+ // Discounts and Taxes
458
+ discountAmount: decimal("discount_amount", { precision: 12, scale: 2 })
459
+ .notNull()
460
+ .default("0.00"),
461
+ taxAmount: decimal("tax_amount", { precision: 12, scale: 2 })
462
+ .notNull()
463
+ .default("0.00"),
464
+
465
+ // Physical Properties
466
+ weight: decimal("weight", { precision: 8, scale: 3 }),
467
+ requiresShipping: boolean("requires_shipping").notNull().default(true),
468
+ isGiftWrap: boolean("is_gift_wrap").notNull().default(false),
469
+ giftWrapMessage: text("gift_wrap_message"),
470
+
471
+ // Customization
472
+ customization: jsonb("customization").$type<{
473
+ options?: Record<string, string>;
474
+ personalization?: string;
475
+ engraving?: string;
476
+ specialRequests?: string;
477
+ }>(),
478
+
479
+ // Inventory and Fulfillment
480
+ inventoryLocationId: text("inventory_location_id"),
481
+ fulfillmentService: varchar("fulfillment_service", { length: 50 }).default(
482
+ "MANUAL",
483
+ ),
484
+ trackingRequired: boolean("tracking_required").notNull().default(true),
485
+
486
+ // Subscription Information
487
+ isSubscriptionItem: boolean("is_subscription_item")
488
+ .notNull()
489
+ .default(false),
490
+ subscriptionFrequency: varchar("subscription_frequency", { length: 20 }),
491
+ subscriptionEndDate: timestamp("subscription_end_date", {
492
+ withTimezone: true,
493
+ }),
494
+
495
+ // Product Snapshot (preserve product data at time of order)
496
+ productSnapshot: jsonb("product_snapshot").$type<{
497
+ images?: string[];
498
+ description?: string;
499
+ attributes?: Record<string, unknown>;
500
+ categories?: string[];
501
+ }>(),
502
+
503
+ // Line Item Flags
504
+ isDigital: boolean("is_digital").notNull().default(false),
505
+ isBackorder: boolean("is_backorder").notNull().default(false),
506
+ isPreorder: boolean("is_preorder").notNull().default(false),
507
+
508
+ // Notes and Instructions
509
+ lineItemNotes: text("line_item_notes"),
510
+ internalNotes: text("internal_notes"),
511
+
512
+ // Analytics
513
+ profitMargin: decimal("profit_margin", { precision: 5, scale: 2 }),
514
+ marginAmount: decimal("margin_amount", { precision: 12, scale: 2 }),
515
+
516
+ // Timestamps
517
+ createdAt: timestamp("created_at", { withTimezone: true })
518
+ .defaultNow()
519
+ .notNull(),
520
+ updatedAt: timestamp("updated_at", { withTimezone: true })
521
+ .defaultNow()
522
+ .notNull(),
523
+ },
524
+ (table) => ({
525
+ // Foreign Key Indexes
526
+ orderIdIndex: index("idx_order_items_order_id").on(table.orderId),
527
+ productIdIndex: index("idx_order_items_product_id").on(table.productId),
528
+ variantIdIndex: index("idx_order_items_variant_id").on(table.variantId),
529
+ skuIndex: index("idx_order_items_sku").on(table.sku),
530
+
531
+ // Inventory and Fulfillment
532
+ inventoryLocationIndex: index("idx_order_items_inventory_location").on(
533
+ table.inventoryLocationId,
534
+ ),
535
+ fulfillmentServiceIndex: index("idx_order_items_fulfillment_service").on(
536
+ table.fulfillmentService,
537
+ ),
538
+
539
+ // Product Information
540
+ vendorIndex: index("idx_order_items_vendor").on(table.vendor),
541
+ productTypeIndex: index("idx_order_items_product_type").on(
542
+ table.productType,
543
+ ),
544
+
545
+ // Flags
546
+ digitalIndex: index("idx_order_items_digital").on(table.isDigital),
547
+ backorderIndex: index("idx_order_items_backorder").on(table.isBackorder),
548
+ subscriptionIndex: index("idx_order_items_subscription").on(
549
+ table.isSubscriptionItem,
550
+ ),
551
+
552
+ // Composite Indexes
553
+ orderProductIndex: index("idx_order_items_order_product").on(
554
+ table.orderId,
555
+ table.productId,
556
+ ),
557
+ orderSkuIndex: index("idx_order_items_order_sku").on(
558
+ table.orderId,
559
+ table.sku,
560
+ ),
561
+ }),
562
+ );
563
+
564
+ // =====================================================
565
+ // ORDER ADDRESSES TABLE
566
+ // =====================================================
567
+
568
+ export const orderAddresses = pgTable(
569
+ "order_addresses",
570
+ {
571
+ id: text("id")
572
+ .primaryKey()
573
+ .$defaultFn(() => createId()),
574
+
575
+ orderId: text("order_id")
576
+ .notNull()
577
+ .references(() => orders.id, { onDelete: "cascade" }),
578
+
579
+ addressType: addressTypeEnum("address_type").notNull(),
580
+
581
+ // Contact Information
582
+ firstName: varchar("first_name", { length: 100 }),
583
+ lastName: varchar("last_name", { length: 100 }),
584
+ company: varchar("company", { length: 100 }),
585
+ email: varchar("email", { length: 255 }),
586
+ phone: varchar("phone", { length: 20 }),
587
+
588
+ // Address Information
589
+ address1: varchar("address1", { length: 255 }).notNull(),
590
+ address2: varchar("address2", { length: 255 }),
591
+ city: varchar("city", { length: 100 }).notNull(),
592
+ province: varchar("province", { length: 100 }),
593
+ provinceCode: varchar("province_code", { length: 10 }),
594
+ country: varchar("country", { length: 100 }).notNull(),
595
+ countryCode: varchar("country_code", { length: 2 }).notNull(),
596
+ postalCode: varchar("postal_code", { length: 20 }),
597
+
598
+ // Geolocation
599
+ latitude: decimal("latitude", { precision: 10, scale: 8 }),
600
+ longitude: decimal("longitude", { precision: 11, scale: 8 }),
601
+ timezone: varchar("timezone", { length: 50 }),
602
+
603
+ // Delivery Instructions
604
+ deliveryInstructions: text("delivery_instructions"),
605
+ accessCode: varchar("access_code", { length: 20 }),
606
+ deliveryPreference: varchar("delivery_preference", { length: 50 }).$type<
607
+ | "FRONT_DOOR"
608
+ | "BACK_DOOR"
609
+ | "SIDE_DOOR"
610
+ | "MAILBOX"
611
+ | "SAFE_PLACE"
612
+ | "NEIGHBOR"
613
+ | "PICKUP_POINT"
614
+ >(),
615
+
616
+ // Validation Flags
617
+ isValidated: boolean("is_validated").notNull().default(false),
618
+ isResidential: boolean("is_residential").notNull().default(true),
619
+ isPOBox: boolean("is_po_box").notNull().default(false),
620
+ isMilitary: boolean("is_military").notNull().default(false),
621
+
622
+ // Timestamps
623
+ createdAt: timestamp("created_at", { withTimezone: true })
624
+ .defaultNow()
625
+ .notNull(),
626
+ updatedAt: timestamp("updated_at", { withTimezone: true })
627
+ .defaultNow()
628
+ .notNull(),
629
+ },
630
+ (table) => ({
631
+ orderIdIndex: index("idx_order_addresses_order_id").on(table.orderId),
632
+ addressTypeIndex: index("idx_order_addresses_type").on(table.addressType),
633
+ countryIndex: index("idx_order_addresses_country").on(table.countryCode),
634
+ postalCodeIndex: index("idx_order_addresses_postal_code").on(
635
+ table.postalCode,
636
+ ),
637
+ geoLocationIndex: index("idx_order_addresses_geo").on(
638
+ table.latitude,
639
+ table.longitude,
640
+ ),
641
+
642
+ // Ensure one address per type per order
643
+ uniqueOrderAddressType: unique("unq_order_address_type").on(
644
+ table.orderId,
645
+ table.addressType,
646
+ ),
647
+ }),
648
+ );
649
+
650
+ // =====================================================
651
+ // ORDER PAYMENTS TABLE
652
+ // =====================================================
653
+
654
+ export const orderPayments = pgTable(
655
+ "order_payments",
656
+ {
657
+ id: text("id")
658
+ .primaryKey()
659
+ .$defaultFn(() => createId()),
660
+
661
+ orderId: text("order_id")
662
+ .notNull()
663
+ .references(() => orders.id, { onDelete: "cascade" }),
664
+
665
+ // Payment Identification
666
+ paymentMethod: paymentMethodEnum("payment_method").notNull(),
667
+ paymentProvider: varchar("payment_provider", { length: 50 }),
668
+ transactionId: varchar("transaction_id", { length: 100 }),
669
+ externalTransactionId: varchar("external_transaction_id", { length: 100 }),
670
+ authorizationCode: varchar("authorization_code", { length: 50 }),
671
+
672
+ // Payment Status and Type
673
+ status: paymentStatusEnum("status").notNull().default("PENDING"),
674
+ paymentType: varchar("payment_type", { length: 20 })
675
+ .notNull()
676
+ .default("PAYMENT")
677
+ .$type<
678
+ "PAYMENT" | "REFUND" | "PARTIAL_REFUND" | "CHARGEBACK" | "ADJUSTMENT"
679
+ >(),
680
+
681
+ // Financial Information
682
+ currency: varchar("currency", { length: 3 }).notNull(),
683
+ amount: decimal("amount", { precision: 12, scale: 2 }).notNull(),
684
+ feeAmount: decimal("fee_amount", { precision: 12, scale: 2 })
685
+ .notNull()
686
+ .default("0.00"),
687
+ netAmount: decimal("net_amount", { precision: 12, scale: 2 }).notNull(),
688
+ exchangeRate: decimal("exchange_rate", { precision: 10, scale: 6 }).default(
689
+ "1.000000",
690
+ ),
691
+
692
+ // Card Information (for card payments)
693
+ cardLast4: varchar("card_last4", { length: 4 }),
694
+ cardBrand: varchar("card_brand", { length: 20 }),
695
+ cardType: varchar("card_type", { length: 20 }),
696
+ cardCountry: varchar("card_country", { length: 2 }),
697
+ cardFingerprint: varchar("card_fingerprint", { length: 100 }),
698
+
699
+ // Processing Information
700
+ processorResponse:
701
+ jsonb("processor_response").$type<Record<string, unknown>>(),
702
+ gatewayResponse: jsonb("gateway_response").$type<Record<string, unknown>>(),
703
+ riskAssessment: jsonb("risk_assessment").$type<{
704
+ score?: number;
705
+ level?: string;
706
+ factors?: string[];
707
+ recommendation?: string;
708
+ }>(),
709
+
710
+ // Timing Information
711
+ authorizedAt: timestamp("authorized_at", { withTimezone: true }),
712
+ capturedAt: timestamp("captured_at", { withTimezone: true }),
713
+ settledAt: timestamp("settled_at", { withTimezone: true }),
714
+ refundedAt: timestamp("refunded_at", { withTimezone: true }),
715
+ voidedAt: timestamp("voided_at", { withTimezone: true }),
716
+
717
+ // Customer Information
718
+ customerIp: varchar("customer_ip", { length: 45 }),
719
+ billingAddressId: text("billing_address_id"),
720
+
721
+ // Reference Information
722
+ parentPaymentId: text("parent_payment_id"), // For refunds
723
+ originalTransactionId: varchar("original_transaction_id", { length: 100 }), // For refunds/chargebacks
724
+
725
+ // Failure Information
726
+ failureCode: varchar("failure_code", { length: 50 }),
727
+ failureMessage: text("failure_message"),
728
+ declineCode: varchar("decline_code", { length: 50 }),
729
+
730
+ // Metadata
731
+ metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
732
+ processorData: jsonb("processor_data")
733
+ .$type<Record<string, unknown>>()
734
+ .default({}),
735
+
736
+ // Timestamps
737
+ createdAt: timestamp("created_at", { withTimezone: true })
738
+ .defaultNow()
739
+ .notNull(),
740
+ updatedAt: timestamp("updated_at", { withTimezone: true })
741
+ .defaultNow()
742
+ .notNull(),
743
+ },
744
+ (table) => ({
745
+ orderIdIndex: index("idx_order_payments_order_id").on(table.orderId),
746
+ statusIndex: index("idx_order_payments_status").on(table.status),
747
+ paymentMethodIndex: index("idx_order_payments_method").on(
748
+ table.paymentMethod,
749
+ ),
750
+ transactionIdIndex: index("idx_order_payments_transaction_id").on(
751
+ table.transactionId,
752
+ ),
753
+ externalTransactionIdIndex: index(
754
+ "idx_order_payments_external_transaction_id",
755
+ ).on(table.externalTransactionId),
756
+ paymentProviderIndex: index("idx_order_payments_provider").on(
757
+ table.paymentProvider,
758
+ ),
759
+ parentPaymentIndex: index("idx_order_payments_parent").on(
760
+ table.parentPaymentId,
761
+ ),
762
+ createdAtIndex: index("idx_order_payments_created_at").on(table.createdAt),
763
+
764
+ // Financial Indexes
765
+ amountIndex: index("idx_order_payments_amount").on(table.amount),
766
+ currencyIndex: index("idx_order_payments_currency").on(table.currency),
767
+
768
+ // Card Information Indexes
769
+ cardFingerprintIndex: index("idx_order_payments_card_fingerprint").on(
770
+ table.cardFingerprint,
771
+ ),
772
+ cardBrandIndex: index("idx_order_payments_card_brand").on(table.cardBrand),
773
+
774
+ // Composite Indexes
775
+ orderStatusIndex: index("idx_order_payments_order_status").on(
776
+ table.orderId,
777
+ table.status,
778
+ ),
779
+ methodStatusIndex: index("idx_order_payments_method_status").on(
780
+ table.paymentMethod,
781
+ table.status,
782
+ ),
783
+ }),
784
+ );
785
+
786
+ // =====================================================
787
+ // ORDER FULFILLMENTS TABLE
788
+ // =====================================================
789
+
790
+ export const orderFulfillments = pgTable(
791
+ "order_fulfillments",
792
+ {
793
+ id: text("id")
794
+ .primaryKey()
795
+ .$defaultFn(() => createId()),
796
+
797
+ orderId: text("order_id")
798
+ .notNull()
799
+ .references(() => orders.id, { onDelete: "cascade" }),
800
+
801
+ // Fulfillment Identification
802
+ fulfillmentNumber: varchar("fulfillment_number", { length: 50 }),
803
+ trackingNumber: varchar("tracking_number", { length: 100 }),
804
+ trackingUrl: text("tracking_url"),
805
+
806
+ // Status and Type
807
+ status: fulfillmentStatusEnum("status").notNull().default("PENDING"),
808
+ fulfillmentType: varchar("fulfillment_type", { length: 20 })
809
+ .notNull()
810
+ .$type<"SHIPPING" | "PICKUP" | "DELIVERY" | "DIGITAL" | "SERVICE">(),
811
+
812
+ // Carrier and Service Information
813
+ shippingCarrier: varchar("shipping_carrier", { length: 50 }),
814
+ shippingService: varchar("shipping_service", { length: 100 }),
815
+ shippingServiceCode: varchar("shipping_service_code", { length: 50 }),
816
+ estimatedDeliveryDate: timestamp("estimated_delivery_date", {
817
+ withTimezone: true,
818
+ }),
819
+ guaranteedDeliveryDate: timestamp("guaranteed_delivery_date", {
820
+ withTimezone: true,
821
+ }),
822
+
823
+ // Location Information
824
+ originLocationId: text("origin_location_id"),
825
+ destinationAddressId: text("destination_address_id"),
826
+
827
+ // Package Information
828
+ packageCount: integer("package_count").notNull().default(1),
829
+ totalWeight: decimal("total_weight", { precision: 8, scale: 3 }),
830
+ totalVolume: decimal("total_volume", { precision: 8, scale: 3 }),
831
+ packageDimensions: jsonb("package_dimensions").$type<{
832
+ length?: number;
833
+ width?: number;
834
+ height?: number;
835
+ unit?: "cm" | "in";
836
+ }>(),
837
+
838
+ // Shipping Costs
839
+ shippingCost: decimal("shipping_cost", { precision: 12, scale: 2 })
840
+ .notNull()
841
+ .default("0.00"),
842
+ insuranceCost: decimal("insurance_cost", { precision: 12, scale: 2 })
843
+ .notNull()
844
+ .default("0.00"),
845
+ handlingCost: decimal("handling_cost", { precision: 12, scale: 2 })
846
+ .notNull()
847
+ .default("0.00"),
848
+
849
+ // Special Services
850
+ signatureRequired: boolean("signature_required").notNull().default(false),
851
+ adultSignatureRequired: boolean("adult_signature_required")
852
+ .notNull()
853
+ .default(false),
854
+ insuranceRequired: boolean("insurance_required").notNull().default(false),
855
+ saturdayDelivery: boolean("saturday_delivery").notNull().default(false),
856
+
857
+ // Delivery Information
858
+ deliveryAttempts: integer("delivery_attempts").notNull().default(0),
859
+ lastDeliveryAttempt: timestamp("last_delivery_attempt", {
860
+ withTimezone: true,
861
+ }),
862
+ deliveryNotes: text("delivery_notes"),
863
+ proofOfDelivery: jsonb("proof_of_delivery").$type<{
864
+ signedBy?: string;
865
+ image?: string;
866
+ location?: { lat: number; lng: number };
867
+ timestamp?: string;
868
+ }>(),
869
+
870
+ // Timing Information
871
+ labelCreatedAt: timestamp("label_created_at", { withTimezone: true }),
872
+ pickedUpAt: timestamp("picked_up_at", { withTimezone: true }),
873
+ inTransitAt: timestamp("in_transit_at", { withTimezone: true }),
874
+ outForDeliveryAt: timestamp("out_for_delivery_at", { withTimezone: true }),
875
+ deliveredAt: timestamp("delivered_at", { withTimezone: true }),
876
+ returnedAt: timestamp("returned_at", { withTimezone: true }),
877
+
878
+ // Staff Information
879
+ packedBy: text("packed_by"),
880
+ shippedBy: text("shipped_by"),
881
+ deliveredBy: text("delivered_by"),
882
+
883
+ // Failure and Return Information
884
+ failureReason: text("failure_reason"),
885
+ returnReason: text("return_reason"),
886
+ returnTrackingNumber: varchar("return_tracking_number", { length: 100 }),
887
+
888
+ // Metadata
889
+ carrierData: jsonb("carrier_data")
890
+ .$type<Record<string, unknown>>()
891
+ .default({}),
892
+ trackingEvents: jsonb("tracking_events")
893
+ .$type<
894
+ Array<{
895
+ timestamp: string;
896
+ status: string;
897
+ location?: string;
898
+ description?: string;
899
+ }>
900
+ >()
901
+ .default([]),
902
+
903
+ // Timestamps
904
+ createdAt: timestamp("created_at", { withTimezone: true })
905
+ .defaultNow()
906
+ .notNull(),
907
+ updatedAt: timestamp("updated_at", { withTimezone: true })
908
+ .defaultNow()
909
+ .notNull(),
910
+ },
911
+ (table) => ({
912
+ orderIdIndex: index("idx_order_fulfillments_order_id").on(table.orderId),
913
+ statusIndex: index("idx_order_fulfillments_status").on(table.status),
914
+ trackingNumberIndex: index("idx_order_fulfillments_tracking_number").on(
915
+ table.trackingNumber,
916
+ ),
917
+ carrierIndex: index("idx_order_fulfillments_carrier").on(
918
+ table.shippingCarrier,
919
+ ),
920
+ fulfillmentTypeIndex: index("idx_order_fulfillments_type").on(
921
+ table.fulfillmentType,
922
+ ),
923
+ originLocationIndex: index("idx_order_fulfillments_origin").on(
924
+ table.originLocationId,
925
+ ),
926
+ destinationAddressIndex: index("idx_order_fulfillments_destination").on(
927
+ table.destinationAddressId,
928
+ ),
929
+ estimatedDeliveryIndex: index(
930
+ "idx_order_fulfillments_estimated_delivery",
931
+ ).on(table.estimatedDeliveryDate),
932
+ deliveredAtIndex: index("idx_order_fulfillments_delivered_at").on(
933
+ table.deliveredAt,
934
+ ),
935
+ createdAtIndex: index("idx_order_fulfillments_created_at").on(
936
+ table.createdAt,
937
+ ),
938
+
939
+ // Staff Indexes
940
+ packedByIndex: index("idx_order_fulfillments_packed_by").on(table.packedBy),
941
+ shippedByIndex: index("idx_order_fulfillments_shipped_by").on(
942
+ table.shippedBy,
943
+ ),
944
+
945
+ // Composite Indexes
946
+ orderStatusIndex: index("idx_order_fulfillments_order_status").on(
947
+ table.orderId,
948
+ table.status,
949
+ ),
950
+ carrierStatusIndex: index("idx_order_fulfillments_carrier_status").on(
951
+ table.shippingCarrier,
952
+ table.status,
953
+ ),
954
+ }),
955
+ );
956
+
957
+ // =====================================================
958
+ // ORDER DISCOUNTS TABLE
959
+ // =====================================================
960
+
961
+ export const orderDiscounts = pgTable(
962
+ "order_discounts",
963
+ {
964
+ id: text("id")
965
+ .primaryKey()
966
+ .$defaultFn(() => createId()),
967
+
968
+ orderId: text("order_id")
969
+ .notNull()
970
+ .references(() => orders.id, { onDelete: "cascade" }),
971
+
972
+ // Discount Identification
973
+ discountCode: varchar("discount_code", { length: 50 }),
974
+ discountType: discountTypeEnum("discount_type").notNull(),
975
+ discountSource: varchar("discount_source", { length: 50 })
976
+ .notNull()
977
+ .$type<
978
+ | "COUPON"
979
+ | "PROMOTION"
980
+ | "LOYALTY"
981
+ | "MANUAL"
982
+ | "AUTOMATIC"
983
+ | "BULK"
984
+ | "REFERRAL"
985
+ >(),
986
+
987
+ // Discount Details
988
+ title: varchar("title", { length: 255 }).notNull(),
989
+ description: text("description"),
990
+
991
+ // Discount Values
992
+ discountAmount: decimal("discount_amount", {
993
+ precision: 12,
994
+ scale: 2,
995
+ }).notNull(),
996
+ discountPercentage: decimal("discount_percentage", {
997
+ precision: 5,
998
+ scale: 2,
999
+ }),
1000
+ maximumDiscount: decimal("maximum_discount", { precision: 12, scale: 2 }),
1001
+
1002
+ // Application Rules
1003
+ appliedToOrderTotal: boolean("applied_to_order_total")
1004
+ .notNull()
1005
+ .default(true),
1006
+ appliedToShipping: boolean("applied_to_shipping").notNull().default(false),
1007
+ appliedToTax: boolean("applied_to_tax").notNull().default(false),
1008
+
1009
+ // Scope and Conditions
1010
+ minimumOrderAmount: decimal("minimum_order_amount", {
1011
+ precision: 12,
1012
+ scale: 2,
1013
+ }),
1014
+ maximumOrderAmount: decimal("maximum_order_amount", {
1015
+ precision: 12,
1016
+ scale: 2,
1017
+ }),
1018
+ applicableProductIds: jsonb("applicable_product_ids")
1019
+ .$type<string[]>()
1020
+ .default([]),
1021
+ applicableCategories: jsonb("applicable_categories")
1022
+ .$type<string[]>()
1023
+ .default([]),
1024
+ excludedProductIds: jsonb("excluded_product_ids")
1025
+ .$type<string[]>()
1026
+ .default([]),
1027
+
1028
+ // Customer Eligibility
1029
+ eligibleCustomerTypes: jsonb("eligible_customer_types")
1030
+ .$type<string[]>()
1031
+ .default([]),
1032
+ firstTimeCustomerOnly: boolean("first_time_customer_only")
1033
+ .notNull()
1034
+ .default(false),
1035
+ loyaltyPointsUsed: integer("loyalty_points_used").default(0),
1036
+
1037
+ // Validation and Usage
1038
+ isValid: boolean("is_valid").notNull().default(true),
1039
+ validationErrors: jsonb("validation_errors").$type<string[]>().default([]),
1040
+ usageCount: integer("usage_count").notNull().default(1),
1041
+
1042
+ // Campaign Information
1043
+ campaignId: text("campaign_id"),
1044
+ campaignName: varchar("campaign_name", { length: 100 }),
1045
+ affiliateId: text("affiliate_id"),
1046
+
1047
+ // Metadata
1048
+ metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1049
+ promotionRules: jsonb("promotion_rules")
1050
+ .$type<Record<string, unknown>>()
1051
+ .default({}),
1052
+
1053
+ // Timestamps
1054
+ appliedAt: timestamp("applied_at", { withTimezone: true })
1055
+ .defaultNow()
1056
+ .notNull(),
1057
+ createdAt: timestamp("created_at", { withTimezone: true })
1058
+ .defaultNow()
1059
+ .notNull(),
1060
+ },
1061
+ (table) => ({
1062
+ orderIdIndex: index("idx_order_discounts_order_id").on(table.orderId),
1063
+ discountCodeIndex: index("idx_order_discounts_code").on(table.discountCode),
1064
+ discountTypeIndex: index("idx_order_discounts_type").on(table.discountType),
1065
+ discountSourceIndex: index("idx_order_discounts_source").on(
1066
+ table.discountSource,
1067
+ ),
1068
+ campaignIdIndex: index("idx_order_discounts_campaign").on(table.campaignId),
1069
+ affiliateIdIndex: index("idx_order_discounts_affiliate").on(
1070
+ table.affiliateId,
1071
+ ),
1072
+ appliedAtIndex: index("idx_order_discounts_applied_at").on(table.appliedAt),
1073
+
1074
+ // Composite Indexes
1075
+ orderDiscountTypeIndex: index("idx_order_discounts_order_type").on(
1076
+ table.orderId,
1077
+ table.discountType,
1078
+ ),
1079
+ codeValidIndex: index("idx_order_discounts_code_valid").on(
1080
+ table.discountCode,
1081
+ table.isValid,
1082
+ ),
1083
+ }),
1084
+ );
1085
+
1086
+ // =====================================================
1087
+ // ORDER HISTORY TABLE
1088
+ // =====================================================
1089
+
1090
+ export const orderHistory = pgTable(
1091
+ "order_history",
1092
+ {
1093
+ id: text("id")
1094
+ .primaryKey()
1095
+ .$defaultFn(() => createId()),
1096
+
1097
+ orderId: text("order_id")
1098
+ .notNull()
1099
+ .references(() => orders.id, { onDelete: "cascade" }),
1100
+
1101
+ // Status Change Information
1102
+ eventType: varchar("event_type", { length: 50 })
1103
+ .notNull()
1104
+ .$type<
1105
+ | "STATUS_CHANGE"
1106
+ | "PAYMENT_UPDATE"
1107
+ | "FULFILLMENT_UPDATE"
1108
+ | "ADDRESS_CHANGE"
1109
+ | "ITEM_ADDED"
1110
+ | "ITEM_REMOVED"
1111
+ | "ITEM_UPDATED"
1112
+ | "NOTE_ADDED"
1113
+ | "DISCOUNT_APPLIED"
1114
+ | "REFUND_PROCESSED"
1115
+ | "CANCELLATION"
1116
+ | "MODIFICATION"
1117
+ | "ESCALATION"
1118
+ | "RETURN_REQUEST"
1119
+ >(),
1120
+
1121
+ // Change Details
1122
+ fromValue: text("from_value"),
1123
+ toValue: text("to_value"),
1124
+ fieldChanged: varchar("field_changed", { length: 100 }),
1125
+ changeReason: text("change_reason"),
1126
+
1127
+ // Actor Information
1128
+ actorType: varchar("actor_type", { length: 20 })
1129
+ .notNull()
1130
+ .$type<"CUSTOMER" | "STAFF" | "SYSTEM" | "ADMIN" | "AUTOMATION">(),
1131
+ actorId: text("actor_id"),
1132
+ actorName: varchar("actor_name", { length: 100 }),
1133
+ actorRole: varchar("actor_role", { length: 50 }),
1134
+
1135
+ // Context Information
1136
+ source: varchar("source", { length: 50 })
1137
+ .notNull()
1138
+ .$type<
1139
+ | "WEB"
1140
+ | "MOBILE"
1141
+ | "POS"
1142
+ | "ADMIN"
1143
+ | "API"
1144
+ | "PHONE"
1145
+ | "EMAIL"
1146
+ | "CHAT"
1147
+ | "SYSTEM"
1148
+ >(),
1149
+ sessionId: text("session_id"),
1150
+ ipAddress: varchar("ip_address", { length: 45 }),
1151
+ userAgent: text("user_agent"),
1152
+
1153
+ // Detailed Information
1154
+ description: text("description"),
1155
+ internalNotes: text("internal_notes"),
1156
+ customerVisible: boolean("customer_visible").notNull().default(true),
1157
+
1158
+ // Related Data
1159
+ relatedEntityType: varchar("related_entity_type", { length: 50 }),
1160
+ relatedEntityId: text("related_entity_id"),
1161
+ changeData: jsonb("change_data")
1162
+ .$type<Record<string, unknown>>()
1163
+ .default({}),
1164
+
1165
+ // Impact Assessment
1166
+ businessImpact: varchar("business_impact", { length: 20 }).$type<
1167
+ "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
1168
+ >(),
1169
+ customerImpact: varchar("customer_impact", { length: 20 }).$type<
1170
+ "POSITIVE" | "NEUTRAL" | "NEGATIVE"
1171
+ >(),
1172
+
1173
+ // Metadata
1174
+ metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1175
+
1176
+ // Timestamps
1177
+ occurredAt: timestamp("occurred_at", { withTimezone: true })
1178
+ .defaultNow()
1179
+ .notNull(),
1180
+ createdAt: timestamp("created_at", { withTimezone: true })
1181
+ .defaultNow()
1182
+ .notNull(),
1183
+ },
1184
+ (table) => ({
1185
+ orderIdIndex: index("idx_order_history_order_id").on(table.orderId),
1186
+ eventTypeIndex: index("idx_order_history_event_type").on(table.eventType),
1187
+ actorTypeIndex: index("idx_order_history_actor_type").on(table.actorType),
1188
+ actorIdIndex: index("idx_order_history_actor_id").on(table.actorId),
1189
+ sourceIndex: index("idx_order_history_source").on(table.source),
1190
+ occurredAtIndex: index("idx_order_history_occurred_at").on(
1191
+ table.occurredAt,
1192
+ ),
1193
+ customerVisibleIndex: index("idx_order_history_customer_visible").on(
1194
+ table.customerVisible,
1195
+ ),
1196
+
1197
+ // Composite Indexes
1198
+ orderEventIndex: index("idx_order_history_order_event").on(
1199
+ table.orderId,
1200
+ table.eventType,
1201
+ ),
1202
+ orderDateIndex: index("idx_order_history_order_date").on(
1203
+ table.orderId,
1204
+ table.occurredAt,
1205
+ ),
1206
+ actorEventIndex: index("idx_order_history_actor_event").on(
1207
+ table.actorId,
1208
+ table.eventType,
1209
+ ),
1210
+ }),
1211
+ );
1212
+
1213
+ // =====================================================
1214
+ // ORDER NOTES TABLE
1215
+ // =====================================================
1216
+
1217
+ export const orderNotes = pgTable(
1218
+ "order_notes",
1219
+ {
1220
+ id: text("id")
1221
+ .primaryKey()
1222
+ .$defaultFn(() => createId()),
1223
+
1224
+ orderId: text("order_id")
1225
+ .notNull()
1226
+ .references(() => orders.id, { onDelete: "cascade" }),
1227
+
1228
+ // Note Classification
1229
+ noteType: varchar("note_type", { length: 30 })
1230
+ .notNull()
1231
+ .$type<
1232
+ | "GENERAL"
1233
+ | "CUSTOMER_SERVICE"
1234
+ | "FULFILLMENT"
1235
+ | "PAYMENT"
1236
+ | "FRAUD"
1237
+ | "QUALITY"
1238
+ | "SHIPPING"
1239
+ | "RETURNS"
1240
+ | "ESCALATION"
1241
+ | "FOLLOW_UP"
1242
+ >(),
1243
+
1244
+ // Note Content
1245
+ title: varchar("title", { length: 255 }),
1246
+ content: text("content").notNull(),
1247
+
1248
+ // Visibility and Priority
1249
+ isCustomerVisible: boolean("is_customer_visible").notNull().default(false),
1250
+ isInternal: boolean("is_internal").notNull().default(true),
1251
+ priority: varchar("priority", { length: 20 })
1252
+ .notNull()
1253
+ .default("NORMAL")
1254
+ .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT">(),
1255
+
1256
+ // Actor Information
1257
+ createdBy: text("created_by").notNull(),
1258
+ createdByName: varchar("created_by_name", { length: 100 }).notNull(),
1259
+ createdByRole: varchar("created_by_role", { length: 50 }),
1260
+ department: varchar("department", { length: 50 }),
1261
+
1262
+ // Follow-up and Actions
1263
+ requiresFollowUp: boolean("requires_follow_up").notNull().default(false),
1264
+ followUpDate: timestamp("follow_up_date", { withTimezone: true }),
1265
+ followUpAssignedTo: text("follow_up_assigned_to"),
1266
+ isFollowUpCompleted: boolean("is_follow_up_completed")
1267
+ .notNull()
1268
+ .default(false),
1269
+
1270
+ // Context and References
1271
+ relatedEntityType: varchar("related_entity_type", { length: 50 }),
1272
+ relatedEntityId: text("related_entity_id"),
1273
+ parentNoteId: text("parent_note_id"),
1274
+
1275
+ // Tagging and Categorization
1276
+ tags: jsonb("tags").$type<string[]>().default([]),
1277
+ category: varchar("category", { length: 50 }),
1278
+ sentiment: varchar("sentiment", { length: 20 }).$type<
1279
+ "POSITIVE" | "NEUTRAL" | "NEGATIVE"
1280
+ >(),
1281
+
1282
+ // Metadata
1283
+ metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
1284
+
1285
+ // Timestamps
1286
+ createdAt: timestamp("created_at", { withTimezone: true })
1287
+ .defaultNow()
1288
+ .notNull(),
1289
+ updatedAt: timestamp("updated_at", { withTimezone: true })
1290
+ .defaultNow()
1291
+ .notNull(),
1292
+ },
1293
+ (table) => ({
1294
+ orderIdIndex: index("idx_order_notes_order_id").on(table.orderId),
1295
+ noteTypeIndex: index("idx_order_notes_type").on(table.noteType),
1296
+ createdByIndex: index("idx_order_notes_created_by").on(table.createdBy),
1297
+ priorityIndex: index("idx_order_notes_priority").on(table.priority),
1298
+ customerVisibleIndex: index("idx_order_notes_customer_visible").on(
1299
+ table.isCustomerVisible,
1300
+ ),
1301
+ followUpIndex: index("idx_order_notes_follow_up").on(
1302
+ table.requiresFollowUp,
1303
+ table.followUpDate,
1304
+ ),
1305
+ parentNoteIndex: index("idx_order_notes_parent").on(table.parentNoteId),
1306
+ createdAtIndex: index("idx_order_notes_created_at").on(table.createdAt),
1307
+
1308
+ // Composite Indexes
1309
+ orderNoteTypeIndex: index("idx_order_notes_order_type").on(
1310
+ table.orderId,
1311
+ table.noteType,
1312
+ ),
1313
+ orderCreatedIndex: index("idx_order_notes_order_created").on(
1314
+ table.orderId,
1315
+ table.createdAt,
1316
+ ),
1317
+ }),
1318
+ );
1319
+
1320
+ // =====================================================
1321
+ // RELATIONS
1322
+ // =====================================================
1323
+
1324
+ export const ordersRelations = relations(orders, ({ many, one }) => ({
1325
+ orderItems: many(orderItems),
1326
+ orderAddresses: many(orderAddresses),
1327
+ orderPayments: many(orderPayments),
1328
+ orderFulfillments: many(orderFulfillments),
1329
+ orderDiscounts: many(orderDiscounts),
1330
+ orderHistory: many(orderHistory),
1331
+ orderNotes: many(orderNotes),
1332
+
1333
+ // Parent order relation for split orders
1334
+ parentOrder: one(orders, {
1335
+ fields: [orders.parentOrderId],
1336
+ references: [orders.id],
1337
+ relationName: "orderSplits",
1338
+ }),
1339
+ childOrders: many(orders, {
1340
+ relationName: "orderSplits",
1341
+ }),
1342
+ }));
1343
+
1344
+ export const orderItemsRelations = relations(orderItems, ({ one }) => ({
1345
+ order: one(orders, {
1346
+ fields: [orderItems.orderId],
1347
+ references: [orders.id],
1348
+ }),
1349
+ }));
1350
+
1351
+ export const orderAddressesRelations = relations(orderAddresses, ({ one }) => ({
1352
+ order: one(orders, {
1353
+ fields: [orderAddresses.orderId],
1354
+ references: [orders.id],
1355
+ }),
1356
+ }));
1357
+
1358
+ export const orderPaymentsRelations = relations(orderPayments, ({ one }) => ({
1359
+ order: one(orders, {
1360
+ fields: [orderPayments.orderId],
1361
+ references: [orders.id],
1362
+ }),
1363
+ parentPayment: one(orderPayments, {
1364
+ fields: [orderPayments.parentPaymentId],
1365
+ references: [orderPayments.id],
1366
+ }),
1367
+ }));
1368
+
1369
+ export const orderFulfillmentsRelations = relations(
1370
+ orderFulfillments,
1371
+ ({ one }) => ({
1372
+ order: one(orders, {
1373
+ fields: [orderFulfillments.orderId],
1374
+ references: [orders.id],
1375
+ }),
1376
+ }),
1377
+ );
1378
+
1379
+ export const orderDiscountsRelations = relations(orderDiscounts, ({ one }) => ({
1380
+ order: one(orders, {
1381
+ fields: [orderDiscounts.orderId],
1382
+ references: [orders.id],
1383
+ }),
1384
+ }));
1385
+
1386
+ export const orderHistoryRelations = relations(orderHistory, ({ one }) => ({
1387
+ order: one(orders, {
1388
+ fields: [orderHistory.orderId],
1389
+ references: [orders.id],
1390
+ }),
1391
+ }));
1392
+
1393
+ export const orderNotesRelations = relations(orderNotes, ({ one, many }) => ({
1394
+ order: one(orders, {
1395
+ fields: [orderNotes.orderId],
1396
+ references: [orders.id],
1397
+ }),
1398
+ parentNote: one(orderNotes, {
1399
+ fields: [orderNotes.parentNoteId],
1400
+ references: [orderNotes.id],
1401
+ relationName: "noteReplies",
1402
+ }),
1403
+ childNotes: many(orderNotes, {
1404
+ relationName: "noteReplies",
1405
+ }),
1406
+ }));