@axova/shared 1.0.0 → 1.0.2

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 (33) hide show
  1. package/dist/index.d.ts +0 -1
  2. package/dist/index.js +0 -2
  3. package/dist/middleware/storeOwnership.js +3 -22
  4. package/dist/middleware/storeValidationMiddleware.js +39 -16
  5. package/dist/schemas/admin/admin-schema.d.ts +2 -2
  6. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +2 -2
  7. package/dist/schemas/index.d.ts +0 -26
  8. package/dist/schemas/index.js +3 -121
  9. package/dist/schemas/store/storefront-config-schema.d.ts +1320 -0
  10. package/dist/schemas/store/storefront-config-schema.js +109 -0
  11. package/dist/utils/subdomain.d.ts +1 -1
  12. package/dist/utils/subdomain.js +15 -10
  13. package/package.json +1 -1
  14. package/src/index.ts +0 -3
  15. package/src/middleware/storeOwnership.ts +3 -21
  16. package/src/middleware/storeValidationMiddleware.ts +50 -17
  17. package/src/schemas/index.ts +5 -189
  18. package/src/utils/subdomain.ts +15 -11
  19. package/nul +0 -8
  20. package/src/schemas/compliance/compliance-schema.ts +0 -927
  21. package/src/schemas/compliance/kyc-schema.ts +0 -649
  22. package/src/schemas/customer/customer-schema.ts +0 -576
  23. package/src/schemas/inventory/inventory-tables.ts +0 -1927
  24. package/src/schemas/inventory/lot-tables.ts +0 -799
  25. package/src/schemas/order/order-schema.ts +0 -1400
  26. package/src/schemas/product/discount-relations.ts +0 -44
  27. package/src/schemas/product/discount-schema.ts +0 -464
  28. package/src/schemas/product/product-relations.ts +0 -187
  29. package/src/schemas/product/product-schema.ts +0 -955
  30. package/src/schemas/store/ethiopian_business_api.md.resolved +0 -212
  31. package/src/schemas/store/store-audit-schema.ts +0 -1257
  32. package/src/schemas/store/store-schema.ts +0 -661
  33. package/src/schemas/store/store-settings-schema.ts +0 -231
@@ -1,1400 +0,0 @@
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
- }));