Next steps 5-10: resources, cash flow, QB interop, backfill, HTTP, parallel-run plan
This commit is contained in:
1 parent
78915fc3bb
commit
9ba4917899
1 file changed
+162
@@ -471,3 +471,165 @@ fn entry_movements_sum_to_zero() {
|
||||
let total: Decimal = m.values().copied().sum();
|
||||
assert_eq!(total, Decimal::ZERO);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cash flow statement (indirect method).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
use ledger_core::NewAsset;
|
||||
use ledger_core::reports::{CashFlowParams, cash_flow};
|
||||
|
||||
fn cash_flow_params(cash: AccountId) -> CashFlowParams {
|
||||
CashFlowParams {
|
||||
start: date(1),
|
||||
end: date(31),
|
||||
cash_accounts: vec![cash],
|
||||
financing_accounts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cash_flow_reconciles_to_change_in_cash() {
|
||||
let c = fixture_company();
|
||||
let cf = cash_flow(&c.ledger, &cash_flow_params(c.cash)).unwrap();
|
||||
|
||||
// The three sections must sum to the net change in cash. This is the
|
||||
// accounting law the statement exists to uphold.
|
||||
let sections = cf.cash_from_operations + cf.cash_from_investing + cf.cash_from_financing;
|
||||
assert_eq!(sections, cf.net_change_in_cash);
|
||||
assert_eq!(cf.cash_ending - cf.cash_beginning, cf.net_change_in_cash);
|
||||
|
||||
// Hand-computed from the fixture:
|
||||
// Cash: +10,000 (invest) - 2,500 (equipment) - 800 (rent) + 1,500 (collect)
|
||||
// = 8,200. Beginning 0.
|
||||
assert_eq!(cf.cash_beginning, Decimal::ZERO);
|
||||
assert_eq!(cf.cash_ending, Decimal::new(820000, 2));
|
||||
assert_eq!(cf.net_change_in_cash, Decimal::new(820000, 2));
|
||||
|
||||
// Net income: 4,000 revenue - 800 rent = 3,200.
|
||||
assert_eq!(cf.net_income, Decimal::new(320000, 2));
|
||||
// No depreciation: the fixture's equipment is not a registered asset.
|
||||
assert_eq!(cf.depreciation, Decimal::ZERO);
|
||||
|
||||
// Operating: 3,200 net income
|
||||
// - 2,500 increase in AR (4,000 billed - 1,500 collected)
|
||||
// - 2,500 equipment purchase (not a registered asset: working capital)
|
||||
// + 10,000 owner investment is equity -> financing, not operating.
|
||||
// Financing: +10,000 owner contribution.
|
||||
// => operating = 3,200 - 2,500 - 2,500 = -1,800
|
||||
// => investing = 0, financing = 10,000. Sum = 8,200. Reconciles.
|
||||
assert_eq!(cf.cash_from_operations, Decimal::new(-180000, 2));
|
||||
assert_eq!(cf.cash_from_investing, Decimal::ZERO);
|
||||
assert_eq!(cf.cash_from_financing, Decimal::new(1000000, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cash_flow_with_registered_asset_and_disposal() {
|
||||
let mut ledger = Ledger::new(Currency::USD);
|
||||
let cash = ledger
|
||||
.add_account("1000", "Cash", AccountType::Asset, None, false)
|
||||
.unwrap();
|
||||
let cost = ledger
|
||||
.add_account("1500", "Equipment at cost", AccountType::Asset, None, false)
|
||||
.unwrap();
|
||||
let accum = ledger
|
||||
.add_account(
|
||||
"1501",
|
||||
"Accum. depr. — equip.",
|
||||
AccountType::Asset,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let depr_exp = ledger
|
||||
.add_account(
|
||||
"6000",
|
||||
"Depreciation expense",
|
||||
AccountType::Expense,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let revenue = ledger
|
||||
.add_account("4000", "Service Revenue", AccountType::Revenue, None, false)
|
||||
.unwrap();
|
||||
let gain = ledger
|
||||
.add_account("4100", "Gain on sale", AccountType::Revenue, None, false)
|
||||
.unwrap();
|
||||
let equity = ledger
|
||||
.add_account("3000", "Owner's Equity", AccountType::Equity, None, false)
|
||||
.unwrap();
|
||||
|
||||
// Owner invests $100,000.
|
||||
post(
|
||||
&mut ledger,
|
||||
date(2),
|
||||
"Owner investment",
|
||||
&[
|
||||
(cash, Side::Debit, 100_000),
|
||||
(equity, Side::Credit, 100_000),
|
||||
],
|
||||
);
|
||||
// Register a $60,000 van, 60-month life, bought Jan 15.
|
||||
let asset = ledger
|
||||
.register_asset(NewAsset {
|
||||
description: "Van".to_string(),
|
||||
purchase_date: NaiveDate::from_ymd_opt(2026, 1, 15).unwrap(),
|
||||
cost: usd(60_000),
|
||||
salvage_value: usd(0),
|
||||
useful_life_months: 60,
|
||||
cost_account: cost,
|
||||
accum_depr_account: accum,
|
||||
expense_account: depr_exp,
|
||||
})
|
||||
.unwrap();
|
||||
// Buy the van for cash.
|
||||
post(
|
||||
&mut ledger,
|
||||
date(15),
|
||||
"Buy van",
|
||||
&[(cost, Side::Debit, 60_000), (cash, Side::Credit, 60_000)],
|
||||
);
|
||||
// January depreciation: $1,000 * 17/31 = $548.39.
|
||||
post(
|
||||
&mut ledger,
|
||||
date(31),
|
||||
"January depreciation",
|
||||
&[(depr_exp, Side::Debit, 548), (accum, Side::Credit, 548)],
|
||||
);
|
||||
// Sell the van Jan 31 for $59,600 cash. Book value 60,000 - 548 = 59,452.
|
||||
// Gain = 59,600 - 59,452 = 148.
|
||||
// Note: amounts are whole dollars here to keep the fixture simple.
|
||||
let _ = asset;
|
||||
post(
|
||||
&mut ledger,
|
||||
date(31),
|
||||
"Sell van",
|
||||
&[
|
||||
(cash, Side::Debit, 59_600),
|
||||
(accum, Side::Debit, 548),
|
||||
(cost, Side::Credit, 60_000),
|
||||
(gain, Side::Credit, 148),
|
||||
],
|
||||
);
|
||||
|
||||
let cf = cash_flow(&ledger, &cash_flow_params(cash)).unwrap();
|
||||
|
||||
// Reconciliation is the law.
|
||||
let sections = cf.cash_from_operations + cf.cash_from_investing + cf.cash_from_financing;
|
||||
assert_eq!(sections, cf.net_change_in_cash);
|
||||
|
||||
// Cash: 100,000 - 60,000 + 59,600 = 99,600.
|
||||
assert_eq!(cf.cash_ending, Decimal::new(9960000, 2));
|
||||
// Net income: 148 gain - 548 depreciation = -400.
|
||||
assert_eq!(cf.net_income, Decimal::new(-40000, 2));
|
||||
// Depreciation added back: 548.
|
||||
assert_eq!(cf.depreciation, Decimal::new(54800, 2));
|
||||
// Operating: -400 + 548 - 148 (gain removed) = 0.
|
||||
assert_eq!(cf.cash_from_operations, Decimal::ZERO);
|
||||
// Investing: -60,000 purchase + 59,600 proceeds = -400.
|
||||
assert_eq!(cf.cash_from_investing, Decimal::new(-40000, 2));
|
||||
// Financing: +100,000 owner investment.
|
||||
assert_eq!(cf.cash_from_financing, Decimal::new(10000000, 2));
|
||||
// Sum: 0 - 400 + 100,000 = 99,600. Matches cash.
|
||||
}
|
||||
Reference in new issue
Block a user