/** * Wholesale portal. Source: `db/migrations/0001_init.sql`. */ import { pgTable, uuid, text, numeric, integer, boolean, timestamp, date, index, uniqueIndex, jsonb, } from "drizzle-orm/pg-core"; import { brands } from "./brands"; import { authUsers } from "./brands"; export const wholesaleSettings = pgTable( "wholesale_settings", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .unique() .references(() => brands.id, { onDelete: "cascade" }), requireApproval: boolean("require_approval").notNull().default(true), minOrderAmount: numeric("min_order_amount", { precision: 10, scale: 2 }), onlinePaymentEnabled: boolean("online_payment_enabled") .notNull() .default(false), pickupLocation: text("pickup_location"), fobLocation: text("fob_location"), fromEmail: text("from_email"), invoiceBusinessName: text("invoice_business_name"), lastInvoiceNumber: integer("last_invoice_number").notNull().default(0), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, ); export const wholesaleCustomers = pgTable( "wholesale_customers", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id").references(() => authUsers.id, { onDelete: "set null", }), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), companyName: text("company_name"), contactName: text("contact_name"), email: text("email"), phone: text("phone"), billingAddress: text("billing_address"), shippingAddress: text("shipping_address"), accountStatus: text("account_status", { enum: ["active", "suspended", "inactive"], }).notNull().default("active"), creditLimit: numeric("credit_limit", { precision: 10, scale: 2 }) .notNull() .default("0"), depositsEnabled: boolean("deposits_enabled").notNull().default(false), depositThreshold: numeric("deposit_threshold", { precision: 10, scale: 2 }), depositPercentage: integer("deposit_percentage"), orderEmail: text("order_email"), invoiceEmail: text("invoice_email"), adminNotes: text("admin_notes"), role: text("role", { enum: ["buyer", "admin"] }) .notNull() .default("buyer"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("wholesale_customers_brand_idx").on(t.brandId), }), ); export const wholesaleProducts = pgTable( "wholesale_products", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), rcProductId: uuid("rc_product_id"), name: text("name").notNull(), description: text("description"), unitType: text("unit_type").notNull().default("each"), availability: text("availability", { enum: ["available", "unavailable", "limited", "seasonal"], }).notNull().default("unavailable"), qtyAvailable: numeric("qty_available", { precision: 10, scale: 2 }) .default("0"), priceTiers: jsonb("price_tiers").notNull().default([]), hpSku: text("hp_sku"), hpItemId: text("hp_item_id"), internalNotes: text("internal_notes"), handlingInstructions: text("handling_instructions"), transportTemp: text("transport_temp"), storageWarning: text("storage_warning"), loadingNotes: text("loading_notes"), productLabel: text("product_label"), packStyle: text("pack_style"), containerType: text("container_type"), containerSizeCode: text("container_size_code"), unitsPerContainer: integer("units_per_container"), containerNotes: text("container_notes"), defaultPickupLocation: text("default_pickup_location"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("wholesale_products_brand_idx").on(t.brandId), }), ); export const wholesaleOrders = pgTable( "wholesale_orders", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), customerId: uuid("customer_id") .notNull() .references(() => wholesaleCustomers.id), wcOrderId: numeric("wc_order_id"), status: text("status", { enum: [ "pending", "confirmed", "in_production", "ready", "fulfilled", "canceled", ], }).notNull().default("pending"), fulfillmentStatus: text("fulfillment_status", { enum: ["unfulfilled", "partial", "fulfilled"], }).notNull().default("unfulfilled"), paymentStatus: text("payment_status", { enum: ["unpaid", "deposit_paid", "paid", "refunded"], }).notNull().default("unpaid"), anticipatedPickupDate: date("anticipated_pickup_date"), subtotal: numeric("subtotal", { precision: 10, scale: 2 }) .notNull() .default("0"), depositRequired: numeric("deposit_required", { precision: 10, scale: 2 }) .default("0"), depositPaid: numeric("deposit_paid", { precision: 10, scale: 2 }) .notNull() .default("0"), balanceDue: numeric("balance_due", { precision: 10, scale: 2 }) .notNull() .default("0"), assignedEmployeeId: uuid("assigned_employee_id"), fulfillmentNotes: text("fulfillment_notes"), internalNotes: text("internal_notes"), invoiceNumber: text("invoice_number"), invoicePdfPath: text("invoice_pdf_path"), invoiceToken: text("invoice_token"), invoiceType: text("invoice_type"), depositPercentage: integer("deposit_percentage"), fulfilledAt: timestamp("fulfilled_at", { withTimezone: true }), fulfilledBy: uuid("fulfilled_by"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("wholesale_orders_brand_idx").on(t.brandId), customerIdx: index("wholesale_orders_customer_idx").on(t.customerId), statusIdx: index("wholesale_orders_status_idx").on(t.brandId, t.status), }), ); export const wholesaleOrderItems = pgTable( "wholesale_order_items", { id: uuid("id").primaryKey().defaultRandom(), wholesaleOrderId: uuid("wholesale_order_id") .notNull() .references(() => wholesaleOrders.id, { onDelete: "cascade" }), productId: uuid("product_id") .notNull() .references(() => wholesaleProducts.id), quantity: numeric("quantity", { precision: 10, scale: 2 }).notNull(), unitPrice: numeric("unit_price", { precision: 10, scale: 2 }).notNull(), lineTotal: numeric("line_total", { precision: 10, scale: 2 }).notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ orderIdx: index("wholesale_order_items_order_idx").on( t.wholesaleOrderId, ), }), ); export const wholesaleDeposits = pgTable( "wholesale_deposits", { id: uuid("id").primaryKey().defaultRandom(), wholesaleOrderId: uuid("wholesale_order_id") .notNull() .references(() => wholesaleOrders.id, { onDelete: "cascade" }), amount: numeric("amount", { precision: 10, scale: 2 }).notNull(), paymentMethod: text("payment_method"), reference: text("reference"), recordedBy: uuid("recorded_by").notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, ); export const wholesaleNotifications = pgTable( "wholesale_notifications", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), customerId: uuid("customer_id") .notNull() .references(() => wholesaleCustomers.id, { onDelete: "cascade" }), notificationType: text("notification_type", { enum: [ "order_confirmed", "order_ready", "pickup_reminder", "payment_reminder", "invoice", ], }).notNull(), channel: text("channel", { enum: ["email", "sms"] }).notNull(), recipient: text("recipient").notNull(), subject: text("subject"), body: text("body").notNull(), status: text("status", { enum: ["pending", "sent", "failed"], }).notNull().default("pending"), sentAt: timestamp("sent_at", { withTimezone: true }), errorMessage: text("error_message"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("wholesale_notifications_brand_idx").on(t.brandId), }), ); export const wholesaleCustomerProductPricing = pgTable( "wholesale_customer_product_pricing", { id: uuid("id").primaryKey().defaultRandom(), customerId: uuid("customer_id") .notNull() .references(() => wholesaleCustomers.id, { onDelete: "cascade" }), productId: uuid("product_id") .notNull() .references(() => wholesaleProducts.id, { onDelete: "cascade" }), customPriceCents: integer("custom_price_cents").notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ customerProductIdx: uniqueIndex( "wholesale_customer_product_pricing_cust_prod_idx", ).on(t.customerId, t.productId), }), ); export const wholesaleWebhookSettings = pgTable( "wholesale_webhook_settings", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .unique() .references(() => brands.id, { onDelete: "cascade" }), url: text("url").notNull(), secret: text("secret").notNull(), enabled: boolean("enabled").notNull().default(false), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, ); export const wholesaleSyncLog = pgTable( "wholesale_sync_log", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), eventType: text("event_type").notNull(), orderId: uuid("order_id"), payload: jsonb("payload"), status: text("status", { enum: ["pending", "sent", "failed"], }).notNull().default("pending"), response: text("response"), attempts: integer("attempts").notNull().default(0), nextRetry: timestamp("next_retry", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("wholesale_sync_log_brand_idx").on(t.brandId), }), ); export const userCarts = pgTable( "user_carts", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), customerId: uuid("customer_id") .notNull() .references(() => wholesaleCustomers.id, { onDelete: "cascade" }), items: jsonb("items").notNull().default([]), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ customerIdx: uniqueIndex("user_carts_customer_idx").on( t.brandId, t.customerId, ), }), ); export type WholesaleSettings = typeof wholesaleSettings.$inferSelect; export type WholesaleCustomer = typeof wholesaleCustomers.$inferSelect; export type WholesaleProduct = typeof wholesaleProducts.$inferSelect; export type WholesaleOrder = typeof wholesaleOrders.$inferSelect; export type WholesaleOrderItem = typeof wholesaleOrderItems.$inferSelect; export type WholesaleDeposit = typeof wholesaleDeposits.$inferSelect; export type WholesaleNotification = typeof wholesaleNotifications.$inferSelect; export type WholesaleWebhookSettings = typeof wholesaleWebhookSettings.$inferSelect; export type WholesaleSyncLog = typeof wholesaleSyncLog.$inferSelect; export type UserCart = typeof userCarts.$inferSelect;