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);
|
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 = [
|
const ACCOUNT_TYPE_OPTIONS = [
|
||||||
{ value: 'asset', label: 'Asset' },
|
{ value: 'asset', label: 'Asset (Bank Account)' },
|
||||||
{ value: 'liability', label: 'Liability' },
|
{ value: 'liability', label: 'Liability' },
|
||||||
{ value: 'equity', label: 'Equity' },
|
|
||||||
{ value: 'income', label: 'Income' },
|
|
||||||
{ value: 'expense', label: 'Expense' },
|
|
||||||
{ value: '__divider', label: '── Investment Accounts ──', disabled: true },
|
{ value: '__divider', label: '── Investment Accounts ──', disabled: true },
|
||||||
{ value: 'inv_cd', label: 'Investment — CD' },
|
{ value: 'inv_cd', label: 'Investment — CD' },
|
||||||
{ value: 'inv_money_market', label: 'Investment — Money Market' },
|
{ value: 'inv_money_market', label: 'Investment — Money Market' },
|
||||||
@@ -161,7 +161,7 @@ export function AccountsPage() {
|
|||||||
accountNumber: '',
|
accountNumber: '',
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
accountType: 'expense',
|
accountType: 'asset',
|
||||||
fundType: 'operating',
|
fundType: 'operating',
|
||||||
is1099Reportable: false,
|
is1099Reportable: false,
|
||||||
initialBalance: 0,
|
initialBalance: 0,
|
||||||
@@ -390,9 +390,13 @@ export function AccountsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ── Filtering ──
|
// ── 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) => {
|
const filtered = accounts.filter((a) => {
|
||||||
if (a.is_system) return false;
|
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 (search && !a.name.toLowerCase().includes(search.toLowerCase()) && !String(a.account_number).includes(search)) return false;
|
||||||
if (filterType && a.account_type !== filterType) return false;
|
if (filterType && a.account_type !== filterType) return false;
|
||||||
if (filterFund && a.fund_type !== filterFund) 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);
|
const reserveInvestments = investments.filter((i) => i.fund_type === 'reserve' && i.is_active);
|
||||||
|
|
||||||
// ── Summary cards ──
|
// ── 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(
|
const totalsByType = accounts.reduce(
|
||||||
(acc, a) => {
|
(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');
|
acc[a.account_type] = (acc[a.account_type] || 0) + parseFloat(a.balance || '0');
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
@@ -418,14 +423,17 @@ export function AccountsPage() {
|
|||||||
{} as Record<string, number>,
|
{} as Record<string, number>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Include investment principal in the asset totals
|
// Include investment current value in a separate summary card
|
||||||
const investmentTotal = investments
|
const investmentTotal = investments
|
||||||
.filter((i) => i.is_active)
|
.filter((i) => i.is_active)
|
||||||
.reduce((s, i) => s + parseFloat(i.current_value || i.principal || '0'), 0);
|
.reduce((s, i) => s + parseFloat(i.current_value || i.principal || '0'), 0);
|
||||||
if (investmentTotal > 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 ──
|
// ── Adjust modal: current balance from trial balance ──
|
||||||
const adjustCurrentBalance = adjustingAccount
|
const adjustCurrentBalance = adjustingAccount
|
||||||
? parseFloat(
|
? parseFloat(
|
||||||
@@ -461,17 +469,27 @@ export function AccountsPage() {
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<SimpleGrid cols={{ base: 2, sm: 5 }}>
|
<SimpleGrid cols={{ base: 2, sm: 4 }}>
|
||||||
{Object.entries(totalsByType).map(([type, total]) => (
|
<Card withBorder p="xs">
|
||||||
<Card withBorder p="xs" key={type}>
|
<Text size="xs" c="dimmed">Cash on Hand</Text>
|
||||||
<Text size="xs" c="dimmed" tt="capitalize">
|
<Text fw={700} size="sm" c="green">{fmt(totalsByType['asset'] || 0)}</Text>
|
||||||
{type}
|
</Card>
|
||||||
</Text>
|
{investmentTotal > 0 && (
|
||||||
<Text fw={700} size="sm" c={accountTypeColors[type]}>
|
<Card withBorder p="xs">
|
||||||
{fmt(total)}
|
<Text size="xs" c="dimmed">Investments</Text>
|
||||||
</Text>
|
<Text fw={700} size="sm" c="teal">{fmt(investmentTotal)}</Text>
|
||||||
</Card>
|
</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>
|
</SimpleGrid>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
@@ -485,7 +503,10 @@ export function AccountsPage() {
|
|||||||
<Select
|
<Select
|
||||||
placeholder="Type"
|
placeholder="Type"
|
||||||
clearable
|
clearable
|
||||||
data={['asset', 'liability', 'equity', 'income', 'expense']}
|
data={[
|
||||||
|
{ value: 'asset', label: 'Asset' },
|
||||||
|
{ value: 'liability', label: 'Liability' },
|
||||||
|
]}
|
||||||
value={filterType}
|
value={filterType}
|
||||||
onChange={setFilterType}
|
onChange={setFilterType}
|
||||||
w={150}
|
w={150}
|
||||||
@@ -616,11 +637,8 @@ export function AccountsPage() {
|
|||||||
label="Account Type"
|
label="Account Type"
|
||||||
required
|
required
|
||||||
data={[
|
data={[
|
||||||
{ value: 'asset', label: 'Asset' },
|
{ value: 'asset', label: 'Asset (Bank Account)' },
|
||||||
{ value: 'liability', label: 'Liability' },
|
{ value: 'liability', label: 'Liability' },
|
||||||
{ value: 'equity', label: 'Equity' },
|
|
||||||
{ value: 'income', label: 'Income' },
|
|
||||||
{ value: 'expense', label: 'Expense' },
|
|
||||||
]}
|
]}
|
||||||
{...form.getInputProps('accountType')}
|
{...form.getInputProps('accountType')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user