Add accounts, members, and processor-agnostic payment linking
- account table (billing entity, soft-delete, company-scoped) - member table (people on an account, is_minor from DOB) - account_processor_link table (maps accounts to any payment processor — stripe, global_payments — instead of stripe_customer_id directly on account) - Full CRUD routes + search (name, email, phone, account_number) - Member routes nested under accounts with isMinor auto-calculation - Zod validation schemas in @forte/shared - 19 tests passing
This commit is contained in:
83
packages/backend/src/db/schema/accounts.ts
Normal file
83
packages/backend/src/db/schema/accounts.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
varchar,
|
||||
text,
|
||||
jsonb,
|
||||
timestamp,
|
||||
boolean,
|
||||
date,
|
||||
pgEnum,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import { companies } from './stores.js'
|
||||
|
||||
export const billingModeEnum = pgEnum('billing_mode', ['consolidated', 'split'])
|
||||
|
||||
export const accounts = pgTable('account', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id),
|
||||
accountNumber: varchar('account_number', { length: 50 }),
|
||||
name: varchar('name', { length: 255 }).notNull(),
|
||||
email: varchar('email', { length: 255 }),
|
||||
phone: varchar('phone', { length: 50 }),
|
||||
address: jsonb('address').$type<{
|
||||
street?: string
|
||||
city?: string
|
||||
state?: string
|
||||
zip?: string
|
||||
}>(),
|
||||
billingMode: billingModeEnum('billing_mode').notNull().default('consolidated'),
|
||||
notes: text('notes'),
|
||||
isActive: boolean('is_active').notNull().default(true),
|
||||
legacyId: varchar('legacy_id', { length: 255 }),
|
||||
legacySource: varchar('legacy_source', { length: 50 }),
|
||||
migratedAt: timestamp('migrated_at', { withTimezone: true }),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export const members = pgTable('member', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
accountId: uuid('account_id')
|
||||
.notNull()
|
||||
.references(() => accounts.id),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id),
|
||||
firstName: varchar('first_name', { length: 100 }).notNull(),
|
||||
lastName: varchar('last_name', { length: 100 }).notNull(),
|
||||
dateOfBirth: date('date_of_birth'),
|
||||
isMinor: boolean('is_minor').notNull().default(false),
|
||||
email: varchar('email', { length: 255 }),
|
||||
phone: varchar('phone', { length: 50 }),
|
||||
notes: text('notes'),
|
||||
legacyId: varchar('legacy_id', { length: 255 }),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export const processorEnum = pgEnum('payment_processor', ['stripe', 'global_payments'])
|
||||
|
||||
export const accountProcessorLinks = pgTable('account_processor_link', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
accountId: uuid('account_id')
|
||||
.notNull()
|
||||
.references(() => accounts.id),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id),
|
||||
processor: processorEnum('processor').notNull(),
|
||||
processorCustomerId: varchar('processor_customer_id', { length: 255 }).notNull(),
|
||||
isActive: boolean('is_active').notNull().default(true),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export type AccountProcessorLink = typeof accountProcessorLinks.$inferSelect
|
||||
export type AccountProcessorLinkInsert = typeof accountProcessorLinks.$inferInsert
|
||||
|
||||
export type Account = typeof accounts.$inferSelect
|
||||
export type AccountInsert = typeof accounts.$inferInsert
|
||||
export type Member = typeof members.$inferSelect
|
||||
export type MemberInsert = typeof members.$inferInsert
|
||||
Reference in New Issue
Block a user