Flexible budget import with auto-account creation and text-based account numbers

Change account_number from INTEGER to VARCHAR(50) to support segmented codes
like 30-3001-0000 used by real HOA accounting systems. Budget CSV import now:
- Auto-creates income/expense accounts from CSV when they don't exist
- Infers account_type and fund_type from account number prefix conventions
- Parses currency-formatted values ($48,065.21, $(13,000.00), $-, etc.)
- Reports created accounts back to the user

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 14:24:00 -05:00
parent 1e40848222
commit 61e43212b9
14 changed files with 188 additions and 63 deletions

View File

@@ -61,7 +61,7 @@ const ACCOUNT_TYPE_OPTIONS = [
interface Account {
id: string;
account_number: number;
account_number: string;
name: string;
description: string;
account_type: string;
@@ -93,7 +93,7 @@ interface Investment {
interface TrialBalanceEntry {
id: string;
account_number: number;
account_number: string;
name: string;
account_type: string;
fund_type: string;
@@ -158,7 +158,7 @@ export function AccountsPage() {
// ── Create / Edit form ──
const form = useForm({
initialValues: {
accountNumber: 0,
accountNumber: '',
name: '',
description: '',
accountType: 'expense',
@@ -176,7 +176,7 @@ export function AccountsPage() {
withdrawFromPrimary: true,
},
validate: {
accountNumber: (v, values) => isInvestmentType(values.accountType) ? null : (v > 0 ? null : 'Required'),
accountNumber: (v, values) => isInvestmentType(values.accountType) ? null : (v.trim().length > 0 ? null : 'Required'),
name: (v) => (v.length > 0 ? null : 'Required'),
principal: (v, values) => isInvestmentType(values.accountType) ? (v > 0 ? null : 'Required') : null,
},
@@ -609,7 +609,7 @@ export function AccountsPage() {
{!isInvestmentType(form.values.accountType) && (
<>
<NumberInput label="Account Number" required {...form.getInputProps('accountNumber')} />
<TextInput label="Account Number" placeholder="e.g. 4000 or 40-4007-0000" required {...form.getInputProps('accountNumber')} />
<TextInput label="Description" {...form.getInputProps('description')} />
{editing && (
<Select