Reconciliation link, account CSVs in export, drop legacy bank/card tables
Validate / validate (push) Successful in 31s
Validate / validate (push) Successful in 31s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,9 +19,9 @@ import {
|
||||
externalAccounts,
|
||||
externalTransactions,
|
||||
users,
|
||||
companyBankAccounts,
|
||||
companyCards,
|
||||
companyAddresses,
|
||||
companyAccounts,
|
||||
companyAccountTransactions,
|
||||
companyDocuments,
|
||||
companyDocumentVersions
|
||||
} from '../db/schema.js';
|
||||
@@ -73,8 +73,8 @@ export async function buildFinancialExport(
|
||||
``,
|
||||
`Files:`,
|
||||
` company.csv — company record`,
|
||||
` company_bank_accounts.csv — company bank accounts`,
|
||||
` company_cards.csv — company credit/debit cards (last 4 only)`,
|
||||
` company_accounts.csv — unified ledger accounts (bank, card, cash, etc.)`,
|
||||
` company_account_transactions.csv — ledger transactions in the selected year`,
|
||||
` company_addresses.csv — legal/shipping/billing/other addresses`,
|
||||
` company_documents.csv — uploaded document metadata (files not bundled)`,
|
||||
` projects.csv — all projects (active + inactive)`,
|
||||
@@ -119,70 +119,93 @@ export async function buildFinancialExport(
|
||||
)
|
||||
);
|
||||
|
||||
// ── company_bank_accounts.csv ──────────────────────
|
||||
// ── company_accounts.csv ───────────────────────────
|
||||
{
|
||||
const bankRows = await db
|
||||
const acctRows = await db
|
||||
.select()
|
||||
.from(companyBankAccounts)
|
||||
.where(eq(companyBankAccounts.companyId, companyId))
|
||||
.orderBy(asc(companyBankAccounts.bankName));
|
||||
.from(companyAccounts)
|
||||
.where(eq(companyAccounts.companyId, companyId))
|
||||
.orderBy(asc(companyAccounts.accountType), asc(companyAccounts.name));
|
||||
const rows: unknown[][] = [
|
||||
[
|
||||
'id', 'bankName', 'accountName', 'accountNumber', 'accountType', 'branch',
|
||||
'swiftBic', 'iban', 'currency', 'isPrimary', 'isActive', 'notes',
|
||||
'createdAt', 'updatedAt'
|
||||
'id', 'accountType', 'name', 'currency', 'isActive', 'isArchived',
|
||||
'bankName', 'accountNumber', 'branch', 'swiftBic', 'iban', 'accountHolderName',
|
||||
'cardBrand', 'last4', 'cardholderName', 'expiryMonth', 'expiryYear',
|
||||
'creditLimit', 'statementCloseDay', 'paymentDueDay',
|
||||
'externalAccountId', 'notes', 'deletedAt', 'createdAt', 'updatedAt'
|
||||
]
|
||||
];
|
||||
for (const b of bankRows) {
|
||||
for (const a of acctRows) {
|
||||
rows.push([
|
||||
b.id, b.bankName, b.accountName, b.accountNumber, b.accountType ?? '',
|
||||
b.branch ?? '', b.swiftBic ?? '', b.iban ?? '', b.currency,
|
||||
b.isPrimary, b.isActive, b.notes ?? '',
|
||||
b.createdAt.toISOString(), b.updatedAt.toISOString()
|
||||
a.id, a.accountType, a.name, a.currency, a.isActive, a.isArchived,
|
||||
a.bankName ?? '', a.accountNumber ?? '', a.branch ?? '', a.swiftBic ?? '',
|
||||
a.iban ?? '', a.accountHolderName ?? '',
|
||||
a.cardBrand ?? '', a.last4 ?? '', a.cardholderName ?? '',
|
||||
a.expiryMonth ?? '', a.expiryYear ?? '',
|
||||
a.creditLimit ?? '', a.statementCloseDay ?? '', a.paymentDueDay ?? '',
|
||||
a.externalAccountId ?? '', a.notes ?? '',
|
||||
a.deletedAt ? a.deletedAt.toISOString() : '',
|
||||
a.createdAt.toISOString(), a.updatedAt.toISOString()
|
||||
]);
|
||||
}
|
||||
zip.file('company_bank_accounts.csv', withBom(csvBuild(rows)));
|
||||
zip.file('company_accounts.csv', withBom(csvBuild(rows)));
|
||||
}
|
||||
|
||||
// ── company_cards.csv ──────────────────────────────
|
||||
// ── company_account_transactions.csv ───────────────
|
||||
{
|
||||
const cardRows = await db
|
||||
const yearStartDate = new Date(`${year}-01-01T00:00:00Z`);
|
||||
const yearEndDate = new Date(`${year}-12-31T23:59:59.999Z`);
|
||||
const txRows = await db
|
||||
.select({
|
||||
id: companyCards.id,
|
||||
brand: companyCards.brand,
|
||||
last4: companyCards.last4,
|
||||
cardholderName: companyCards.cardholderName,
|
||||
expiryMonth: companyCards.expiryMonth,
|
||||
expiryYear: companyCards.expiryYear,
|
||||
nickname: companyCards.nickname,
|
||||
bankAccountId: companyCards.bankAccountId,
|
||||
bankAccountName: companyBankAccounts.bankName,
|
||||
isActive: companyCards.isActive,
|
||||
notes: companyCards.notes,
|
||||
createdAt: companyCards.createdAt,
|
||||
updatedAt: companyCards.updatedAt
|
||||
id: companyAccountTransactions.id,
|
||||
accountId: companyAccountTransactions.accountId,
|
||||
accountName: companyAccounts.name,
|
||||
type: companyAccountTransactions.type,
|
||||
amount: companyAccountTransactions.amount,
|
||||
currency: companyAccountTransactions.currency,
|
||||
occurredAt: companyAccountTransactions.occurredAt,
|
||||
description: companyAccountTransactions.description,
|
||||
reference: companyAccountTransactions.reference,
|
||||
counterpartyAccountId: companyAccountTransactions.counterpartyAccountId,
|
||||
sourceExpenseId: companyAccountTransactions.sourceExpenseId,
|
||||
sourceInvoiceId: companyAccountTransactions.sourceInvoiceId,
|
||||
sourceExternalTransactionId: companyAccountTransactions.sourceExternalTransactionId,
|
||||
fxRate: companyAccountTransactions.fxRate,
|
||||
fxAmount: companyAccountTransactions.fxAmount,
|
||||
createdAt: companyAccountTransactions.createdAt
|
||||
})
|
||||
.from(companyCards)
|
||||
.leftJoin(companyBankAccounts, eq(companyCards.bankAccountId, companyBankAccounts.id))
|
||||
.where(eq(companyCards.companyId, companyId))
|
||||
.orderBy(asc(companyCards.brand));
|
||||
.from(companyAccountTransactions)
|
||||
.innerJoin(companyAccounts, eq(companyAccountTransactions.accountId, companyAccounts.id))
|
||||
.where(
|
||||
and(
|
||||
eq(companyAccountTransactions.companyId, companyId),
|
||||
sql`${companyAccountTransactions.occurredAt} >= ${yearStartDate}`,
|
||||
sql`${companyAccountTransactions.occurredAt} <= ${yearEndDate}`
|
||||
)
|
||||
)
|
||||
.orderBy(
|
||||
asc(companyAccountTransactions.occurredAt),
|
||||
asc(companyAccountTransactions.createdAt)
|
||||
);
|
||||
const rows: unknown[][] = [
|
||||
[
|
||||
'id', 'brand', 'last4', 'cardholderName', 'expiryMonth', 'expiryYear',
|
||||
'nickname', 'bankAccountId', 'bankAccountName', 'isActive', 'notes',
|
||||
'createdAt', 'updatedAt'
|
||||
'id', 'accountId', 'accountName', 'type', 'amount', 'currency',
|
||||
'occurredAt', 'description', 'reference',
|
||||
'counterpartyAccountId', 'sourceExpenseId', 'sourceInvoiceId',
|
||||
'sourceExternalTransactionId', 'fxRate', 'fxAmount', 'createdAt'
|
||||
]
|
||||
];
|
||||
for (const c of cardRows) {
|
||||
for (const t of txRows) {
|
||||
rows.push([
|
||||
c.id, c.brand, c.last4, c.cardholderName,
|
||||
c.expiryMonth ?? '', c.expiryYear ?? '',
|
||||
c.nickname ?? '', c.bankAccountId ?? '', c.bankAccountName ?? '',
|
||||
c.isActive, c.notes ?? '',
|
||||
c.createdAt.toISOString(), c.updatedAt.toISOString()
|
||||
t.id, t.accountId, t.accountName, t.type, t.amount, t.currency,
|
||||
t.occurredAt.toISOString(), t.description ?? '', t.reference ?? '',
|
||||
t.counterpartyAccountId ?? '', t.sourceExpenseId ?? '',
|
||||
t.sourceInvoiceId ?? '', t.sourceExternalTransactionId ?? '',
|
||||
t.fxRate ?? '', t.fxAmount ?? '',
|
||||
t.createdAt.toISOString()
|
||||
]);
|
||||
}
|
||||
zip.file('company_cards.csv', withBom(csvBuild(rows)));
|
||||
zip.file('company_account_transactions.csv', withBom(csvBuild(rows)));
|
||||
}
|
||||
|
||||
// ── company_addresses.csv ──────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user