Phase 3: Bank reconciliation system
Added complete bank statement reconciliation workflow: Database Schema: - BankStatement: metadata for uploaded statements - BankStatementLine: individual transactions from statements - BankReconciliation: links system transactions to bank lines Features: - CSV import with flexible format detection (DD/MM/YYYY, YYYY-MM-DD dates) - Auto-matching algorithm: exact amount+date = 100% confidence, fuzzy matching up to 7 days - Manual matching UI for unmatched transactions - Discrepancy detection (amount mismatches) - Reconciliation dashboard with summary stats - Statement history view with status tracking Pages: - /admin/accounts/reconciliation: Dashboard and statement list - /admin/accounts/reconciliation?id=X: Statement detail with matching UI - /admin/accounts/reconciliation/upload: CSV upload form Server Actions: - uploadBankStatement: CSV parsing and auto-matching - manualMatch: User-initiated transaction linking - unmatchBankLine: Remove incorrect match - archiveStatement: Mark statement as complete - markReconciled: Finalize reconciliation Utils: - parseCSV: Flexible bank statement parser - autoMatchTransactions: Fuzzy matching algorithm - getReconciliationSummary: Stats calculation UI Components: - ReconciliationClient: Interactive matching interface with tabs - Manual match form: User-friendly transaction linking Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
2d55203c1c
commit
7f88379d3b
@@ -417,3 +417,58 @@ model FinancialAuditLog {
|
||||
changedBy String? // "SYSTEM" or IP address if admin
|
||||
reason String? // why was this changed
|
||||
}
|
||||
|
||||
// Bank statement uploaded for reconciliation
|
||||
model BankStatement {
|
||||
id String @id @default(cuid())
|
||||
filename String // original filename
|
||||
bankName String? // e.g., "HSBC", "Barclays"
|
||||
accountNumber String? // last 4 digits for safety
|
||||
statementMonth DateTime // the month this statement covers
|
||||
startDate DateTime // first transaction date in statement
|
||||
endDate DateTime // last transaction date in statement
|
||||
totalTransactions Int // count of lines in statement
|
||||
totalAmount Int? // cents, total of all transactions (for verification)
|
||||
status String @default("PENDING") // PENDING, IN_PROGRESS, RECONCILED, ARCHIVED
|
||||
uploadedAt DateTime @default(now())
|
||||
reconciliedAt DateTime? // when reconciliation was completed
|
||||
lines BankStatementLine[]
|
||||
reconciliations BankReconciliation[]
|
||||
}
|
||||
|
||||
// Individual transaction line from a bank statement
|
||||
model BankStatementLine {
|
||||
id String @id @default(cuid())
|
||||
statementId String
|
||||
statement BankStatement @relation(fields: [statementId], references: [id], onDelete: Cascade)
|
||||
date DateTime // transaction date
|
||||
description String // e.g., "STRIPE DEPOSIT", "TRANSFER TO SAVINGS"
|
||||
amount Int // cents, positive for credit, negative for debit
|
||||
balance Int? // account balance after this transaction (if provided)
|
||||
reference String? // transaction reference from bank
|
||||
matched Boolean @default(false) // whether this line has been reconciled
|
||||
reconciliation BankReconciliation? // the matched transaction, if any
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
// Link between system transaction (order/sale/expense) and bank statement line
|
||||
model BankReconciliation {
|
||||
id String @id @default(cuid())
|
||||
statementId String
|
||||
statement BankStatement @relation(fields: [statementId], references: [id], onDelete: Cascade)
|
||||
bankLineId String @unique
|
||||
bankLine BankStatementLine @relation(fields: [bankLineId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Which system transaction this was matched to
|
||||
transactionType String // "ORDER", "MANUAL_SALE", "REFUND", "EXPENSE", "TRANSFER_OUT", "FEE", "OTHER"
|
||||
transactionId String? // ID of the order/sale/expense if applicable
|
||||
description String // what the match was (e.g., "Order #12345", "Stripe fee")
|
||||
systemAmount Int // cents, amount in our system
|
||||
bankAmount Int // cents, amount on bank statement
|
||||
amountMatches Boolean // whether system and bank amounts are equal
|
||||
|
||||
matchedAt DateTime @default(now())
|
||||
matchedBy String? // IP/admin who manually matched this (null if auto-matched)
|
||||
confidence Int @default(100) // 0-100, how confident the match is (100=exact amount+date, <100=fuzzy match)
|
||||
notes String? // manual notes about the match
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user