Filter Accounts page to show only cash positions (asset, liability, investment)
- Hide income, expense, and equity accounts from Accounts view — they are internal bookkeeping managed via budget import and system operations - Restrict Add Account form to asset and liability types plus investments - Update summary cards to show Cash on Hand, Investments, Liabilities, Net Position - Reports (Income Statement, Balance Sheet, Budget vs Actual, etc.) are unchanged and continue to use all account types from their own API endpoints Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,12 +45,12 @@ const INVESTMENT_TYPES = ['inv_cd', 'inv_money_market', 'inv_treasury', 'inv_sav
|
||||
|
||||
const isInvestmentType = (t: string) => INVESTMENT_TYPES.includes(t);
|
||||
|
||||
// Only show account types that represent real cash / financial positions.
|
||||
// Income, expense, and equity accounts are internal bookkeeping — managed via
|
||||
// budget import and system operations, not manually on this page.
|
||||
const ACCOUNT_TYPE_OPTIONS = [
|
||||
{ value: 'asset', label: 'Asset' },
|
||||
{ value: 'asset', label: 'Asset (Bank Account)' },
|
||||
{ value: 'liability', label: 'Liability' },
|
||||
{ value: 'equity', label: 'Equity' },
|
||||
{ value: 'income', label: 'Income' },
|
||||
{ value: 'expense', label: 'Expense' },
|
||||
{ value: '__divider', label: '── Investment Accounts ──', disabled: true },
|
||||
{ value: 'inv_cd', label: 'Investment — CD' },
|
||||
{ value: 'inv_money_market', label: 'Investment — Money Market' },
|
||||
@@ -161,7 +161,7 @@ export function AccountsPage() {
|
||||
accountNumber: '',
|
||||
name: '',
|
||||
description: '',
|
||||
accountType: 'expense',
|
||||
accountType: 'asset',
|
||||
fundType: 'operating',
|
||||
is1099Reportable: false,
|
||||
initialBalance: 0,
|
||||
@@ -390,9 +390,13 @@ export function AccountsPage() {
|
||||
};
|
||||
|
||||
// ── Filtering ──
|
||||
// Hide system accounts (auto-created equity offsets) from the accounts view
|
||||
// Only show asset and liability accounts — these represent real cash positions.
|
||||
// Income, expense, and equity accounts are internal bookkeeping managed via
|
||||
// budget import, transactions, and system operations.
|
||||
const VISIBLE_ACCOUNT_TYPES = ['asset', 'liability'];
|
||||
const filtered = accounts.filter((a) => {
|
||||
if (a.is_system) return false;
|
||||
if (!VISIBLE_ACCOUNT_TYPES.includes(a.account_type)) return false;
|
||||
if (search && !a.name.toLowerCase().includes(search.toLowerCase()) && !String(a.account_number).includes(search)) return false;
|
||||
if (filterType && a.account_type !== filterType) return false;
|
||||
if (filterFund && a.fund_type !== filterFund) return false;
|
||||
@@ -407,10 +411,11 @@ export function AccountsPage() {
|
||||
const reserveInvestments = investments.filter((i) => i.fund_type === 'reserve' && i.is_active);
|
||||
|
||||
// ── Summary cards ──
|
||||
// Exclude system accounts from summaries to avoid double-counting
|
||||
// Only show asset and liability totals — these are real cash positions.
|
||||
// Income/expense/equity are internal bookkeeping and don't belong here.
|
||||
const totalsByType = accounts.reduce(
|
||||
(acc, a) => {
|
||||
if (a.is_active && !a.is_system) {
|
||||
if (a.is_active && !a.is_system && VISIBLE_ACCOUNT_TYPES.includes(a.account_type)) {
|
||||
acc[a.account_type] = (acc[a.account_type] || 0) + parseFloat(a.balance || '0');
|
||||
}
|
||||
return acc;
|
||||
@@ -418,14 +423,17 @@ export function AccountsPage() {
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
// Include investment principal in the asset totals
|
||||
// Include investment current value in a separate summary card
|
||||
const investmentTotal = investments
|
||||
.filter((i) => i.is_active)
|
||||
.reduce((s, i) => s + parseFloat(i.current_value || i.principal || '0'), 0);
|
||||
if (investmentTotal > 0) {
|
||||
totalsByType['asset (investments)'] = investmentTotal;
|
||||
totalsByType['investments'] = investmentTotal;
|
||||
}
|
||||
|
||||
// Net position = assets + investments - liabilities
|
||||
const netPosition = (totalsByType['asset'] || 0) + investmentTotal - (totalsByType['liability'] || 0);
|
||||
|
||||
// ── Adjust modal: current balance from trial balance ──
|
||||
const adjustCurrentBalance = adjustingAccount
|
||||
? parseFloat(
|
||||
@@ -461,17 +469,27 @@ export function AccountsPage() {
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<SimpleGrid cols={{ base: 2, sm: 5 }}>
|
||||
{Object.entries(totalsByType).map(([type, total]) => (
|
||||
<Card withBorder p="xs" key={type}>
|
||||
<Text size="xs" c="dimmed" tt="capitalize">
|
||||
{type}
|
||||
</Text>
|
||||
<Text fw={700} size="sm" c={accountTypeColors[type]}>
|
||||
{fmt(total)}
|
||||
</Text>
|
||||
<SimpleGrid cols={{ base: 2, sm: 4 }}>
|
||||
<Card withBorder p="xs">
|
||||
<Text size="xs" c="dimmed">Cash on Hand</Text>
|
||||
<Text fw={700} size="sm" c="green">{fmt(totalsByType['asset'] || 0)}</Text>
|
||||
</Card>
|
||||
{investmentTotal > 0 && (
|
||||
<Card withBorder p="xs">
|
||||
<Text size="xs" c="dimmed">Investments</Text>
|
||||
<Text fw={700} size="sm" c="teal">{fmt(investmentTotal)}</Text>
|
||||
</Card>
|
||||
)}
|
||||
{(totalsByType['liability'] || 0) > 0 && (
|
||||
<Card withBorder p="xs">
|
||||
<Text size="xs" c="dimmed">Liabilities</Text>
|
||||
<Text fw={700} size="sm" c="red">{fmt(totalsByType['liability'] || 0)}</Text>
|
||||
</Card>
|
||||
)}
|
||||
<Card withBorder p="xs">
|
||||
<Text size="xs" c="dimmed">Net Position</Text>
|
||||
<Text fw={700} size="sm" c={netPosition >= 0 ? 'green' : 'red'}>{fmt(netPosition)}</Text>
|
||||
</Card>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
<Group>
|
||||
@@ -485,7 +503,10 @@ export function AccountsPage() {
|
||||
<Select
|
||||
placeholder="Type"
|
||||
clearable
|
||||
data={['asset', 'liability', 'equity', 'income', 'expense']}
|
||||
data={[
|
||||
{ value: 'asset', label: 'Asset' },
|
||||
{ value: 'liability', label: 'Liability' },
|
||||
]}
|
||||
value={filterType}
|
||||
onChange={setFilterType}
|
||||
w={150}
|
||||
@@ -616,11 +637,8 @@ export function AccountsPage() {
|
||||
label="Account Type"
|
||||
required
|
||||
data={[
|
||||
{ value: 'asset', label: 'Asset' },
|
||||
{ value: 'asset', label: 'Asset (Bank Account)' },
|
||||
{ value: 'liability', label: 'Liability' },
|
||||
{ value: 'equity', label: 'Equity' },
|
||||
{ value: 'income', label: 'Income' },
|
||||
{ value: 'expense', label: 'Expense' },
|
||||
]}
|
||||
{...form.getInputProps('accountType')}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user