/** * Brands (Tenants). Source of truth: `db/migrations/0001_init.sql`. * Multi-brand isolation: every business table has `brand_id` FK → brands.id. */ import { pgTable, uuid, text, integer, boolean, timestamp, index, uniqueIndex, primaryKey, } from "drizzle-orm/pg-core"; export const brands = pgTable( "brands", { id: uuid("id").primaryKey().defaultRandom(), name: text("name").notNull(), slug: text("slug").notNull().unique(), planTier: text("plan_tier", { enum: ["starter", "farm", "enterprise"], }).notNull().default("starter"), maxUsers: integer("max_users").notNull().default(2), maxProducts: integer("max_products").notNull().default(25), maxStopsMonthly: integer("max_stops_monthly").notNull().default(10), stripeCustomerId: text("stripe_customer_id"), stripeSubscriptionId: text("stripe_subscription_id"), stripeSubscriptionStatus: text("stripe_subscription_status", { enum: [ "trialing", "active", "past_due", "canceled", "incomplete", "incomplete_expired", ], }), stripeCurrentPeriodEnd: timestamp("stripe_current_period_end", { withTimezone: true, }), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ slugIdx: uniqueIndex("brands_slug_idx").on(t.slug), }), ); // ── Admin Users ────────────────────────────────────────────────────────────── export const adminUsers = pgTable( "admin_users", { id: uuid("id").primaryKey().defaultRandom(), // FK to neon_auth.user(id) is enforced at DB level by the migration SQL. // Drizzle can't reference tables in other schemas, so no .references() here. userId: uuid("user_id"), email: text("email").notNull().unique(), name: text("name"), role: text("role", { enum: ["platform_admin", "brand_admin", "store_employee"], }).notNull().default("brand_admin"), canManageOrders: boolean("can_manage_orders").notNull().default(true), canManageProducts: boolean("can_manage_products").notNull().default(true), canManageStops: boolean("can_manage_stops").notNull().default(true), canManageCustomers: boolean("can_manage_customers").notNull().default(true), canManageWholesale: boolean("can_manage_wholesale").notNull().default(false), canManageBilling: boolean("can_manage_billing").notNull().default(false), canManageSettings: boolean("can_manage_settings").notNull().default(false), canManageWaterLog: boolean("can_manage_water_log").notNull().default(false), canManageTimeTracking: boolean("can_manage_time_tracking").notNull().default(false), canManageRouteTrace: boolean("can_manage_route_trace").notNull().default(false), canManageReports: boolean("can_manage_reports").notNull().default(true), canManageCommunications: boolean("can_manage_communications").notNull().default(false), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ userIdIdx: uniqueIndex("admin_users_user_id_idx").on(t.userId), emailIdx: uniqueIndex("admin_users_email_idx").on(t.email), }), ); export const adminUserBrands = pgTable( "admin_user_brands", { adminUserId: uuid("admin_user_id") .notNull() .references(() => adminUsers.id, { onDelete: "cascade" }), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), addedAt: timestamp("added_at", { withTimezone: true }) .notNull() .defaultNow(), addedBy: uuid("added_by").references(() => adminUsers.id), }, (t) => ({ pk: primaryKey({ columns: [t.adminUserId, t.brandId] }), }), ); // ── Neon Auth stub (for Drizzle typing only — table is managed by Neon Auth) ── // The actual table is neon_auth.user (singular). We can't FK-reference a table // in another schema via Drizzle, so we store userId as a plain UUID and rely on // the DB-level FK constraint (enforced by the migration SQL). export const authUsers = pgTable("user", { id: uuid("id").primaryKey(), name: text("name"), email: text("email"), emailVerified: boolean("email_verified"), image: text("image"), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), }); export type Brand = typeof brands.$inferSelect; export type NewBrand = typeof brands.$inferInsert; export type AdminUser = typeof adminUsers.$inferSelect; export type NewAdminUser = typeof adminUsers.$inferInsert; export type AdminUserBrand = typeof adminUserBrands.$inferSelect; export type NewAdminUserBrand = typeof adminUserBrands.$inferInsert;