Add company profile schema (bank accounts, cards, addresses)
Three new per-company tables backing the upcoming Profile page: - company_bank_accounts: bank/account name, account number, type, branch, SWIFT/BIC, IBAN, currency (default THB), isPrimary, isActive, notes - company_cards: brand (visa/mastercard/amex/jcb/unionpay/discover/ other), last4 (varchar(4)), cardholder, expiry month/year, nickname, optional FK to a bank account. Stores ONLY last 4 digits — never the full PAN, to avoid PCI-DSS scope. - company_addresses: type enum (legal/shipping/billing/other), label, recipient, full Thai address fields (subdistrict/district/ province/postal code), country defaulting to Thailand, contact person + phone, isDefault, notes Eight new audit events in companyLogEventEnum cover add/update/ remove operations on each. Page UI and export integration follow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+106
-2
@@ -10,7 +10,9 @@ import {
|
||||
numeric,
|
||||
date,
|
||||
index,
|
||||
primaryKey
|
||||
primaryKey,
|
||||
integer,
|
||||
varchar
|
||||
} from 'drizzle-orm/pg-core';
|
||||
|
||||
// ── Enums ──────────────────────────────────────────────
|
||||
@@ -671,6 +673,100 @@ export const featureRequestVotes = pgTable(
|
||||
(table) => [uniqueIndex('feature_request_votes_request_user_idx').on(table.requestId, table.userId)]
|
||||
);
|
||||
|
||||
// ── Company Profile (bank accounts, cards, addresses) ──
|
||||
|
||||
export const companyAddressTypeEnum = pgEnum('company_address_type', [
|
||||
'legal',
|
||||
'shipping',
|
||||
'billing',
|
||||
'other'
|
||||
]);
|
||||
|
||||
export const cardBrandEnum = pgEnum('card_brand', [
|
||||
'visa',
|
||||
'mastercard',
|
||||
'amex',
|
||||
'jcb',
|
||||
'unionpay',
|
||||
'discover',
|
||||
'other'
|
||||
]);
|
||||
|
||||
export const companyBankAccounts = pgTable(
|
||||
'company_bank_accounts',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id, { onDelete: 'cascade' }),
|
||||
bankName: text('bank_name').notNull(),
|
||||
accountName: text('account_name').notNull(),
|
||||
accountNumber: text('account_number').notNull(),
|
||||
accountType: text('account_type'),
|
||||
branch: text('branch'),
|
||||
swiftBic: text('swift_bic'),
|
||||
iban: text('iban'),
|
||||
currency: text('currency').notNull().default('THB'),
|
||||
isPrimary: boolean('is_primary').notNull().default(false),
|
||||
isActive: boolean('is_active').notNull().default(true),
|
||||
notes: text('notes'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
|
||||
},
|
||||
(table) => [index('company_bank_accounts_company_idx').on(table.companyId)]
|
||||
);
|
||||
|
||||
export const companyCards = pgTable(
|
||||
'company_cards',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id, { onDelete: 'cascade' }),
|
||||
brand: cardBrandEnum('brand').notNull(),
|
||||
last4: varchar('last4', { length: 4 }).notNull(),
|
||||
cardholderName: text('cardholder_name').notNull(),
|
||||
expiryMonth: integer('expiry_month'),
|
||||
expiryYear: integer('expiry_year'),
|
||||
nickname: text('nickname'),
|
||||
bankAccountId: uuid('bank_account_id').references(() => companyBankAccounts.id, {
|
||||
onDelete: 'set null'
|
||||
}),
|
||||
isActive: boolean('is_active').notNull().default(true),
|
||||
notes: text('notes'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
|
||||
},
|
||||
(table) => [index('company_cards_company_idx').on(table.companyId)]
|
||||
);
|
||||
|
||||
export const companyAddresses = pgTable(
|
||||
'company_addresses',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id, { onDelete: 'cascade' }),
|
||||
type: companyAddressTypeEnum('type').notNull(),
|
||||
label: text('label'),
|
||||
recipient: text('recipient'),
|
||||
addressLine1: text('address_line_1'),
|
||||
addressLine2: text('address_line_2'),
|
||||
subdistrict: text('subdistrict'),
|
||||
district: text('district'),
|
||||
province: text('province'),
|
||||
postalCode: text('postal_code'),
|
||||
country: text('country').notNull().default('Thailand'),
|
||||
contactPerson: text('contact_person'),
|
||||
contactPhone: text('contact_phone'),
|
||||
isDefault: boolean('is_default').notNull().default(false),
|
||||
notes: text('notes'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
|
||||
},
|
||||
(table) => [index('company_addresses_company_type_idx').on(table.companyId, table.type)]
|
||||
);
|
||||
|
||||
// ── Company Log (Audit Trail) ──────────────────────────
|
||||
|
||||
export const companyLogEventEnum = pgEnum('company_log_event', [
|
||||
@@ -715,7 +811,15 @@ export const companyLogEventEnum = pgEnum('company_log_event', [
|
||||
'package_status_refreshed',
|
||||
'shipping_account_added',
|
||||
'shipping_account_removed',
|
||||
'financial_exported'
|
||||
'financial_exported',
|
||||
'bank_account_added',
|
||||
'bank_account_updated',
|
||||
'bank_account_removed',
|
||||
'card_added',
|
||||
'card_removed',
|
||||
'address_added',
|
||||
'address_updated',
|
||||
'address_removed'
|
||||
]);
|
||||
|
||||
export const companyLog = pgTable(
|
||||
|
||||
Reference in New Issue
Block a user