feat: add client_addresses table for multi-address storage

Adds client_addresses table with cascade deletes, port scoping, primary address flag, and indexes. Updates clientsRelations and portsRelations, adds clientAddressesRelations. Generates migration 0000_narrow_longshot.sql.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-14 12:44:11 -04:00
parent f659073b8f
commit 59dd418542
5 changed files with 8105 additions and 7 deletions

View File

@@ -14,7 +14,9 @@ import { ports } from './ports';
export const clients = pgTable(
'clients',
{
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
portId: text('port_id')
.notNull()
.references(() => ports.id),
@@ -52,7 +54,9 @@ export const clients = pgTable(
export const clientContacts = pgTable(
'client_contacts',
{
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'cascade' }),
@@ -66,15 +70,21 @@ export const clientContacts = pgTable(
},
(table) => [
index('idx_cc_client').on(table.clientId),
index('idx_cc_email').on(table.channel, table.value).where(sql`${table.channel} = 'email'`),
index('idx_cc_phone').on(table.channel, table.value).where(sql`${table.channel} = 'phone'`),
index('idx_cc_email')
.on(table.channel, table.value)
.where(sql`${table.channel} = 'email'`),
index('idx_cc_phone')
.on(table.channel, table.value)
.where(sql`${table.channel} = 'phone'`),
],
);
export const clientRelationships = pgTable(
'client_relationships',
{
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
portId: text('port_id')
.notNull()
.references(() => ports.id),
@@ -94,7 +104,9 @@ export const clientRelationships = pgTable(
export const clientNotes = pgTable(
'client_notes',
{
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'cascade' }),
@@ -122,7 +134,9 @@ export const clientTags = pgTable(
export const clientMergeLog = pgTable(
'client_merge_log',
{
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
portId: text('port_id')
.notNull()
.references(() => ports.id),
@@ -137,6 +151,31 @@ export const clientMergeLog = pgTable(
(table) => [index('idx_cml_port').on(table.portId)],
);
export const clientAddresses = pgTable(
'client_addresses',
{
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'cascade' }),
portId: text('port_id')
.notNull()
.references(() => ports.id, { onDelete: 'cascade' }),
label: text('label').notNull().default('Primary'),
streetAddress: text('street_address'),
city: text('city'),
stateProvince: text('state_province'),
postalCode: text('postal_code'),
country: text('country'),
isPrimary: boolean('is_primary').notNull().default(true),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [index('idx_ca_client').on(table.clientId), index('idx_ca_port').on(table.portId)],
);
export type Client = typeof clients.$inferSelect;
export type NewClient = typeof clients.$inferInsert;
export type ClientContact = typeof clientContacts.$inferSelect;
@@ -147,3 +186,5 @@ export type ClientNote = typeof clientNotes.$inferSelect;
export type NewClientNote = typeof clientNotes.$inferInsert;
export type ClientMergeLog = typeof clientMergeLog.$inferSelect;
export type NewClientMergeLog = typeof clientMergeLog.$inferInsert;
export type ClientAddress = typeof clientAddresses.$inferSelect;
export type NewClientAddress = typeof clientAddresses.$inferInsert;