P3: the cash flow statement by ASC 230 (defect 1, D-35, D-42)
check / check (push) Canceled after 0s

Direct method with the operating reconciliation and noncash schedules; it
foots by construction (Lean and Kani). Every non-cash account belongs to
one section; reversals net; a member or lender paying for the company is
cash borrowed and spent (D-42, constructive receipt and disbursement);
property a member contributes stays noncash. The bookspec seed: one loader
checks every example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 09:35:32 -06:00
1 parent 2c29d48ab9
commit eb4329bb2a
64 files changed
+12128 -1567

No files matched your search

+262 -122
View File
@@ -1245,6 +1245,46 @@ fn settings_problems(ledger: &Ledger, cfg: &Settings) -> Vec<String> {
} else {
" in the settings"
};
// The cash flow roles: codes the chart lacks here; the rest is the
// engine's own check, the one the statement refuses on (rules.md B-14
// rule 2), so the two never disagree.
let lists = [
(
ledger_core::RoleList::Cash,
label("books.cash_accounts", "[books] cash_accounts"),
&cfg.roles.cash,
),
(
ledger_core::RoleList::Financing,
label("books.financing_accounts", "[books] financing_accounts"),
&cfg.roles.financing,
),
(
ledger_core::RoleList::Investing,
label("books.investing_accounts", "[books] investing_accounts"),
&cfg.roles.investing,
),
];
let mut ids: [Vec<ledger_core::AccountId>; 3] = Default::default();
for ((_, setting, codes), ids) in lists.iter().zip(&mut ids) {
for code in codes.iter() {
match ledger.chart().find_by_code(code) {
Some(a) => ids.push(a.id),
None => out.push(format!(
"{setting} names {code}, which isn't in the chart: add that account, or change {setting}{how}"
)),
}
}
}
let named = |l: ledger_core::RoleList| {
lists
.iter()
.find(|(list, _, _)| *list == l)
.map_or(String::new(), |(_, setting, _)| setting.to_string())
};
for p in ledger_core::role_problems(ledger, &ids[0], &ids[1], &ids[2]) {
out.push(format!("{}{how}", p.message(named)));
}
let mut check = |setting: &str, code: &str, want: &[AccountType]| {
match ledger.chart().find_by_code(code) {
None => out.push(format!(
@@ -1260,27 +1300,7 @@ fn settings_problems(ledger: &Ledger, cfg: &Settings) -> Vec<String> {
}
};
use AccountType::*;
for code in &cfg.roles.cash {
check(
label("books.cash_accounts", "[books] cash_accounts"),
code,
&[Asset],
);
}
for code in &cfg.roles.financing {
check(
label("books.financing_accounts", "[books] financing_accounts"),
code,
&[Liability, Equity],
);
}
for code in &cfg.roles.investing {
check(
label("books.investing_accounts", "[books] investing_accounts"),
code,
&[Asset],
);
}
let c = &cfg.company;
for m in &c.members {
check(
@@ -2798,7 +2818,7 @@ impl BooksServer {
#[tool(
name = "books.cash_flow",
title = "Cash flow statement",
description = "Cash flow statement (indirect method) for a period: net income, depreciation add-back, working-capital changes, investing (asset purchases, disposal proceeds), financing. The three sections reconcile to the net change in the given cash accounts.",
description = "Statement of cash flows for a period, direct method (ASC 230): each entry that moved cash, by section and kind (operating by ASC 230-10-45-25 class; investing and financing by kind, such as each member's contributions), gross; an entry and its reversal in the same period drop out together. What a member or lender paid for the company is borrowed and spent on its date (on_behalf). Also noncash investing and financing activities (property contributed, debt converted), net income reconciled to operating cash, warnings, and reconciles: the sections sum to the change in the cash accounts. Account roles default to the books' settings.",
annotations(read_only_hint = true, open_world_hint = false)
)]
async fn cash_flow(
@@ -2816,25 +2836,7 @@ impl BooksServer {
&p.financing_accounts,
&p.investing_accounts,
)?;
let line = |l: &ledger_core::CashFlowLine| CashFlowLineOut {
label: l.label.clone(),
amount: money(l.amount),
};
Ok(Json(CashFlowOut {
start: stmt.start.to_string(),
end: stmt.end.to_string(),
net_income: money(stmt.net_income),
depreciation: money(stmt.depreciation),
working_capital: stmt.working_capital.iter().map(line).collect(),
cash_from_operations: money(stmt.cash_from_operations),
investing: stmt.investing.iter().map(line).collect(),
cash_from_investing: money(stmt.cash_from_investing),
financing: stmt.financing.iter().map(line).collect(),
cash_from_financing: money(stmt.cash_from_financing),
net_change_in_cash: money(stmt.net_change_in_cash),
cash_beginning: money(stmt.cash_beginning),
cash_ending: money(stmt.cash_ending),
}))
Ok(Json(cash_flow_out(&stmt)))
}
#[tool(
@@ -5330,7 +5332,7 @@ impl BooksServer {
#[tool(
name = "books.export",
title = "Export a report",
description = "Export a report. Reports: profit_and_loss, balance_sheet, cash_flow (needs cash_accounts), trial_balance (as_of), members_equity (multi-member LLC; members from the settings), general_ledger (by account: running balances, splits), general_journal (every entry in date order; alias journal), fixed_asset_register (as_of: every asset with tag, serial, cost, depreciation, book value, reconciled to the books). Formats: pdf and xlsx (base64) for the five statements and fixed_asset_register; xlsx, csv, json for every report; iif for general_journal (QuickBooks Desktop: plain ASCII text, CR LF; every posted entry from the first through as_of, period closes left out, DOCNUM the entry id; names, numbers and memos fit QuickBooks' limits; warnings says what is written differently from the books, memos grouped, to tell the human). Dates: start/end for period reports (start defaults to the first entry, end to today), as_of for balance_sheet, trial_balance, fixed_asset_register, and iif (default today).",
description = "Export a report. Reports: profit_and_loss, balance_sheet, cash_flow (direct method, with its reconciliation and noncash schedules; needs cash_accounts), trial_balance (as_of), members_equity (multi-member LLC; members from the settings), general_ledger (by account: running balances, splits), general_journal (every entry in date order; alias journal), fixed_asset_register (as_of: every asset with tag, serial, cost, depreciation, book value, reconciled to the books). Formats: pdf and xlsx (base64) for the five statements and fixed_asset_register; xlsx, csv, json for every report; iif for general_journal (QuickBooks Desktop: plain ASCII text, CR LF; every posted entry from the first through as_of, period closes left out, DOCNUM the entry id; names, numbers and memos fit QuickBooks' limits; warnings says what is written differently from the books, memos grouped, to tell the human). Dates: start/end for period reports (start defaults to the first entry, end to today), as_of for balance_sheet, trial_balance, fixed_asset_register, and iif (default today).",
annotations(read_only_hint = true, open_world_hint = false)
)]
async fn export(&self, params: Parameters<ExportParams>) -> Result<Json<ExportOut>, String> {
@@ -5431,17 +5433,14 @@ impl BooksServer {
let earnings = self.year_earnings(ledger, bs.as_of)?;
report::balance_sheet(&bs, earnings, &members)
}
"cash_flow" => report::cash_flow(
&self.cash_flow_for(
ledger,
p.start.as_deref(),
p.end.as_deref(),
&p.cash_accounts,
&p.financing_accounts,
&p.investing_accounts,
)?,
&members,
),
"cash_flow" => report::cash_flow(&self.cash_flow_for(
ledger,
p.start.as_deref(),
p.end.as_deref(),
&p.cash_accounts,
&p.financing_accounts,
&p.investing_accounts,
)?),
"trial_balance" => {
let as_of = match &p.as_of {
Some(s) => parse_date(s)?,
@@ -8098,7 +8097,7 @@ impl BooksServer {
#[tool(
name = "books.export_cpa_package",
title = "CPA handoff package",
description = "Build the CPA handoff package as a zip: the profit and loss, balance sheet, cash flow, statement of changes in members' equity (multi-member LLCs), tax summary, and trial balance, each as PDF and XLSX; the fixed asset register as PDF and XLSX when there are fixed assets; the general ledger (by account) and general journal (entry by entry) as XLSX; the journal through as_of as IIF (imports into QuickBooks Desktop; plain ASCII within its limits: warnings says what is written differently from the books, and so does the README); the 1099 worksheet as CSV (payments from January 1 through as_of); with members, the K-1 worksheet (k1-members.csv: per member their party, share, capital account, TIN, and beginning capital, contributions in cash and in property, share of income, distributions, and ending capital for the fiscal year to date); every receipt behind the year's entries, filed on them or linked to them (receipts/, with an index.csv tying each file to its entry) and the company's documents (documents/), unless include_receipts is false; and a README with the journal's verified hash. By default (include_tins) the K-1 and 1099 worksheets carry every member's and payee's full TIN from the TIN vault; include_tins: false, last fours only. The zip is handed over: run its download command to save it in a folder (books served by `wanderbooks mcp`; over stdio it comes back inline). Never file it into the books. A locked vault or a missing TIN doesn't stop the package: tin_gaps lists each one (collect them with books.set_tin or books.set_w9 and export again). cash_accounts (e.g. [\"1000\"]) is required: the cash flow statement reconciles to them. as_of defaults to today; period reports cover the fiscal year to date.",
description = "Build the CPA handoff package as a zip: the profit and loss, balance sheet, cash flow (direct method, with its reconciliation and noncash schedules), statement of changes in members' equity (multi-member LLCs), tax summary, and trial balance, each as PDF and XLSX; the fixed asset register as PDF and XLSX when an asset was bought by as_of; the general ledger (by account) and general journal (entry by entry) as XLSX; the journal through as_of as IIF (imports into QuickBooks Desktop; plain ASCII within its limits: warnings says what is written differently from the books, and so does the README); the 1099 worksheet as CSV (payments from January 1 through as_of); with members, the K-1 worksheet (k1-members.csv: per member their party, share, capital account, TIN, and beginning capital, contributions in cash and in property, share of income, distributions, and ending capital for the fiscal year to date); every receipt behind the year's entries, filed on them or linked to them (receipts/, with an index.csv tying each file to its entry) and the company's documents (documents/), unless include_receipts is false; and a README with the journal's verified hash. By default (include_tins) the K-1 and 1099 worksheets carry every member's and payee's full TIN from the TIN vault; include_tins: false, last fours only. The zip is handed over: run its download command to save it in a folder (books served by `wanderbooks mcp`; over stdio it comes back inline). Never file it into the books. A locked vault or a missing TIN doesn't stop the package: tin_gaps lists each one (collect them with books.set_tin or books.set_w9 and export again). cash_accounts (e.g. [\"1000\"]) is required: the cash flow statement reconciles to them. as_of defaults to today; period reports cover the fiscal year to date.",
annotations(
read_only_hint = false,
destructive_hint = false,
@@ -8602,78 +8601,219 @@ impl BooksServer {
&p.investing_accounts,
)?;
if format == "json" {
return serde_json::to_string_pretty(&serde_json::json!({
"start": stmt.start.to_string(), "end": stmt.end.to_string(),
"net_income": money(stmt.net_income),
"depreciation": money(stmt.depreciation),
"working_capital": stmt.working_capital.iter().map(|l| serde_json::json!({
"label": l.label, "amount": money(l.amount),
})).collect::<Vec<_>>(),
"cash_from_operations": money(stmt.cash_from_operations),
"investing": stmt.investing.iter().map(|l| serde_json::json!({
"label": l.label, "amount": money(l.amount),
})).collect::<Vec<_>>(),
"cash_from_investing": money(stmt.cash_from_investing),
"financing": stmt.financing.iter().map(|l| serde_json::json!({
"label": l.label, "amount": money(l.amount),
})).collect::<Vec<_>>(),
"cash_from_financing": money(stmt.cash_from_financing),
"net_change_in_cash": money(stmt.net_change_in_cash),
"cash_beginning": money(stmt.cash_beginning),
"cash_ending": money(stmt.cash_ending),
}))
.map_err(|e| e.to_string());
// The tool's own answer, from the one function (they can't
// drift apart).
return serde_json::to_string_pretty(&cash_flow_out(&stmt)).map_err(|e| e.to_string());
}
let mut csv = String::from("section,label,amount\n");
csv.push_str(&format!(
"operating,Net income,{}\n",
money(stmt.net_income)
));
csv.push_str(&format!(
"operating,Depreciation,{}\n",
money(stmt.depreciation)
));
for l in &stmt.working_capital {
csv.push_str(&format!(
"operating,{},{}\n",
csv_escape(&l.label),
money(l.amount)
));
}
csv.push_str(&format!(
"operating,Cash from operations,{}\n",
money(stmt.cash_from_operations)
));
for l in &stmt.investing {
csv.push_str(&format!(
"investing,{},{}\n",
csv_escape(&l.label),
money(l.amount)
));
}
csv.push_str(&format!(
"investing,Cash from investing,{}\n",
money(stmt.cash_from_investing)
));
for l in &stmt.financing {
csv.push_str(&format!(
"financing,{},{}\n",
csv_escape(&l.label),
money(l.amount)
));
}
csv.push_str(&format!(
"financing,Cash from financing,{}\n",
money(stmt.cash_from_financing)
));
csv.push_str(&format!(
"reconciliation,Net change in cash,{}\n",
money(stmt.net_change_in_cash)
));
Ok(csv)
Ok(cash_flow_csv(&stmt))
}
}
/// `books.cash_flow`'s answer, and `books.export`'s cash flow JSON.
fn cash_flow_out(stmt: &ledger_core::CashFlowStatement) -> CashFlowOut {
use ledger_core::CashFlowSection as S;
let line = |l: &ledger_core::CashFlowLine| CashFlowLineOut {
entry: l.entry,
date: l.date.to_string(),
label: l.label.clone(),
kind: l.kind.clone(),
amount: money(l.amount),
on_behalf: (!l.on_behalf.is_zero()).then(|| money(l.on_behalf)),
accounts: l.accounts.clone(),
reverses: l.reverses,
class: l.class.map(|c| c.key().to_string()),
};
let kinds = |sec: S| {
stmt.kinds(sec)
.into_iter()
.map(|k| CashFlowKindOut {
kind: k.kind,
class: k.class.map(|c| c.key().to_string()),
amount: money(k.amount),
entries: k.entries,
})
.collect()
};
let r = &stmt.operating_reconciliation;
CashFlowOut {
method: "direct".to_string(),
start: stmt.start.to_string(),
end: stmt.end.to_string(),
operating_by_class: kinds(S::Operating),
operating: stmt.operating.iter().map(line).collect(),
cash_from_operations: money(stmt.cash_from_operations),
investing_by_kind: kinds(S::Investing),
investing: stmt.investing.iter().map(line).collect(),
cash_from_investing: money(stmt.cash_from_investing),
financing_by_kind: kinds(S::Financing),
financing: stmt.financing.iter().map(line).collect(),
cash_from_financing: money(stmt.cash_from_financing),
net_change_in_cash: money(stmt.net_change_in_cash),
cash_beginning: money(stmt.cash_beginning),
cash_ending: money(stmt.cash_ending),
noncash_disclosures: stmt
.noncash_disclosures
.iter()
.map(|n| NoncashDisclosureOut {
entry: n.entry,
date: n.date.to_string(),
label: n.label.clone(),
amount: money(n.amount),
operating: money(n.operating),
investing: money(n.investing),
financing: money(n.financing),
exchanged: money(n.exchanged),
reverses: n.reverses,
})
.collect(),
operating_reconciliation: OperatingReconciliationOut {
net_income: money(r.net_income),
lines: r
.lines
.iter()
.map(|l| ReconcilingLineOut {
kind: l.kind.key().to_string(),
label: l.label.clone(),
amount: money(l.amount),
account: l.account.clone(),
entries: l.entries.clone(),
})
.collect(),
net_cash_from_operating: money(r.net_cash_from_operating),
},
reconciles: CashFlowFootingOut {
sections_sum: money(stmt.reconciles.sections_sum),
net_change: money(stmt.reconciles.net_change),
difference: money(stmt.reconciles.difference),
},
warnings: stmt
.warnings
.iter()
.map(|w| CashFlowWarningOut {
entry: w.entry,
kind: w.kind.key().to_string(),
message: w.message.clone(),
})
.collect(),
}
}
/// `books.export`'s cash flow CSV:
/// `section,kind,class,entry,date,label,amount,on_behalf`, every line of
/// every section (`on_behalf`: of its amount, what a member or lender
/// paid or received for the company, D-42), the reconciliation, the
/// noncash disclosures, the footing and the warnings (`warning`, its
/// kind, its entry and its message; no amount). Only a section's lines
/// have an `on_behalf`.
fn cash_flow_csv(stmt: &ledger_core::CashFlowStatement) -> String {
use ledger_core::CashFlowSection as S;
let mut csv = String::from("section,kind,class,entry,date,label,amount,on_behalf\n");
let mut row = |section: &str,
kind: &str,
class: &str,
entry: Option<u64>,
date: Option<chrono::NaiveDate>,
label: &str,
amount: rust_decimal::Decimal,
on_behalf: Option<rust_decimal::Decimal>| {
csv.push_str(&format!(
"{section},{},{class},{},{},{},{},{}\n",
csv_escape(kind),
entry.map_or(String::new(), |e| e.to_string()),
date.map_or(String::new(), |d| d.to_string()),
csv_escape(label),
money(amount),
on_behalf.map_or(String::new(), money)
));
};
for (sec, total) in [
(S::Operating, "Net cash from operating activities"),
(S::Investing, "Net cash from investing activities"),
(S::Financing, "Net cash from financing activities"),
] {
for l in stmt.lines(sec) {
let class = l.class.map_or("", |c| c.key());
row(
sec.key(),
&l.kind,
class,
Some(l.entry),
Some(l.date),
&l.label,
l.amount,
Some(l.on_behalf),
);
}
row(sec.key(), "", "", None, None, total, stmt.total(sec), None);
}
let r = &stmt.operating_reconciliation;
row(
"reconciliation",
"",
"",
None,
None,
"Net income (loss)",
r.net_income,
None,
);
for l in &r.lines {
row(
"reconciliation",
l.kind.key(),
"",
None,
None,
&l.label,
l.amount,
None,
);
}
row(
"reconciliation",
"",
"",
None,
None,
"Net cash from operating activities",
r.net_cash_from_operating,
None,
);
for n in &stmt.noncash_disclosures {
row(
"noncash",
"",
"",
Some(n.entry),
Some(n.date),
&n.label,
n.amount,
None,
);
}
let f = &stmt.reconciles;
for (label, amount) in [
("Net increase (decrease) in cash", stmt.net_change_in_cash),
("Cash at beginning of period", stmt.cash_beginning),
("Cash at end of period", stmt.cash_ending),
("Sections sum", f.sections_sum),
("Difference", f.difference),
] {
row("footing", "", "", None, None, label, amount, None);
}
// The warnings, as the tool gives them: no amount (figures are
// unaffected).
for w in &stmt.warnings {
csv.push_str(&format!(
"warning,{},,{},,{},,\n",
w.kind.key(),
w.entry,
csv_escape(&w.message)
));
}
csv
}
// ---------------------------------------------------------------------------
// ServerHandler
// ---------------------------------------------------------------------------
+165 -26
View File
@@ -807,61 +807,200 @@ pub struct CashFlowParams {
pub start: Option<String>,
/// Period end, YYYY-MM-DD. Defaults to today.
pub end: Option<String>,
/// Account codes treated as cash (e.g. `["1000", "1010"]`): the
/// statement reconciles to the net change in these accounts. Defaults
/// to `[books] cash_accounts`; one or the other is required.
/// Asset account codes treated as cash (e.g. `["1000", "1010"]`): the
/// statement explains the change in these accounts. Defaults to the
/// books' cash accounts setting (books.settings shows it); one or the
/// other is required.
#[serde(default)]
pub cash_accounts: Vec<String>,
/// Account codes for liabilities treated as financing (loans, and what
/// owners advanced: ASC 230-10-45-14(b)). Defaults to `[books]
/// financing_accounts`, else empty (liabilities are working capital).
/// Liability account codes treated as financing (loans, and what
/// members advanced: ASC 230-10-45-14(b)); equity is always financing.
/// Defaults to the books' financing accounts setting, else empty
/// (other liabilities are operating).
#[serde(default)]
pub financing_accounts: Vec<String>,
/// Asset account codes treated as investing for cash_flow: loans the
/// company makes, investments it buys (ASC 230-10-20). Defaults to
/// `[books] investing_accounts`.
/// Asset account codes treated as investing: loans the company makes,
/// investments it buys (ASC 230-10-20). Registered fixed assets are
/// investing unlisted. Defaults to the books' investing accounts
/// setting.
#[serde(default)]
pub investing_accounts: Vec<String>,
}
/// One cash flow line.
/// One entry's cash in one kind of one section of the cash flow
/// statement.
#[derive(Debug, Serialize, JsonSchema)]
pub struct CashFlowLineOut {
/// Human label.
/// The entry that moved the cash.
pub entry: u64,
/// Its date, YYYY-MM-DD.
pub date: String,
/// "<description> (#N)", "Reversal of #N — <N's description>", or
/// "Reinstatement of #A (reverses #B)".
pub label: String,
/// Signed amount, decimal string. Positive = inflow.
/// The kind it is listed under: an operating class ("Cash paid to
/// suppliers and employees") or an investing or financing kind
/// ("Contributions from members — <member>").
pub kind: String,
/// The cash, decimal string; positive is cash in.
pub amount: String,
/// Of `amount`, what a member or a lender paid or received for the
/// company with no cash account moving (a constructive receipt or
/// payment, D-42: an expense a member paid personally is borrowed from
/// the member and spent), decimal string; absent when none.
#[serde(skip_serializing_if = "Option::is_none")]
pub on_behalf: Option<String>,
/// Codes of this kind's accounts the entry moved.
pub accounts: Vec<String>,
/// The entry this one reverses, if any.
#[serde(skip_serializing_if = "Option::is_none")]
pub reverses: Option<u64>,
/// Operating lines: the ASC 230-10-45-25 class key
/// (`from_customers`, `interest_and_dividends_received`,
/// `other_receipts`, `to_suppliers_and_employees`, `interest_paid`,
/// `income_taxes_paid`, `other_payments`).
#[serde(skip_serializing_if = "Option::is_none")]
pub class: Option<String>,
}
/// Result of `books.cash_flow`.
/// A kind's total: how the printed statement and the viewer show a
/// section.
#[derive(Debug, Serialize, JsonSchema)]
pub struct CashFlowKindOut {
/// The caption.
pub kind: String,
/// The operating class key, for an operating kind.
#[serde(skip_serializing_if = "Option::is_none")]
pub class: Option<String>,
/// The kind's cash, decimal string.
pub amount: String,
/// Its entries.
pub entries: Vec<u64>,
}
/// One reconciling item between net income and operating cash.
#[derive(Debug, Serialize, JsonSchema)]
pub struct ReconcilingLineOut {
/// `depreciation`, `investing_gain_loss`, `noncash_through`,
/// `other_noncash`, or `operating_balance_change`.
pub kind: String,
/// "Depreciation", "Noncash: through 2500 Bank loan", "Increase
/// in 1300 Prepaid Expenses", ...
pub label: String,
/// Added to net income, decimal string.
pub amount: String,
/// The account it is about, if one.
#[serde(skip_serializing_if = "Option::is_none")]
pub account: Option<String>,
/// The entries behind it (an entry and its reversal in the period
/// left out).
pub entries: Vec<u64>,
}
/// Net income reconciled to net cash from operating activities (ASC
/// 230-10-45-30: a separate schedule with the direct method).
#[derive(Debug, Serialize, JsonSchema)]
pub struct OperatingReconciliationOut {
/// The period's net income, decimal string.
pub net_income: String,
/// The reconciling items.
pub lines: Vec<ReconcilingLineOut>,
/// Net income plus the items: equal to cash_from_operations.
pub net_cash_from_operating: String,
}
/// A noncash investing or financing activity (ASC 230-10-50-3).
#[derive(Debug, Serialize, JsonSchema)]
pub struct NoncashDisclosureOut {
/// The entry.
pub entry: u64,
/// Its date, YYYY-MM-DD.
pub date: String,
/// Its label.
pub label: String,
/// The amount disclosed, decimal string.
pub amount: String,
/// The operating part no cash settled, debit-positive.
pub operating: String,
/// The investing part no cash settled, debit-positive.
pub investing: String,
/// The financing part no cash settled, debit-positive.
pub financing: String,
/// What was exchanged inside investing and financing (debt to equity,
/// one asset for another).
pub exchanged: String,
/// The entry it reverses, if any.
#[serde(skip_serializing_if = "Option::is_none")]
pub reverses: Option<u64>,
}
/// The footing check: always a zero difference (a nonzero one is an
/// engine error, never output).
#[derive(Debug, Serialize, JsonSchema)]
pub struct CashFlowFootingOut {
/// Operating + investing + financing.
pub sections_sum: String,
/// The cash accounts' ending balance less their beginning balance.
pub net_change: String,
/// sections_sum − net_change: "0.00".
pub difference: String,
}
/// An entry whose later settlement the books can't classify by
/// themselves, with what to change.
#[derive(Debug, Serialize, JsonSchema)]
pub struct CashFlowWarningOut {
/// The entry.
pub entry: u64,
/// `asset_on_operating_payable` or `asset_sold_on_operating_receivable`.
pub kind: String,
/// What happened and the fix.
pub message: String,
}
/// Result of `books.cash_flow`; `books.export` writes the same JSON.
#[derive(Debug, Serialize, JsonSchema)]
pub struct CashFlowOut {
/// "direct" (ASC 230-10-45-25).
pub method: String,
/// Period start, YYYY-MM-DD.
pub start: String,
/// Period end, YYYY-MM-DD.
pub end: String,
/// Period net income, decimal string.
pub net_income: String,
/// Non-cash depreciation added back, decimal string.
pub depreciation: String,
/// Working-capital adjustments.
pub working_capital: Vec<CashFlowLineOut>,
/// Cash from operations, decimal string.
/// Operating cash by ASC 230-10-45-25 class, (a) to (g).
pub operating_by_class: Vec<CashFlowKindOut>,
/// Every entry's operating cash, by class, then date.
pub operating: Vec<CashFlowLineOut>,
/// Net cash from operating activities, decimal string.
pub cash_from_operations: String,
/// Investing flows (purchases, disposal proceeds).
/// Investing cash by kind (purchases and sales of property and
/// equipment, loans made and collected).
pub investing_by_kind: Vec<CashFlowKindOut>,
/// Every entry's investing cash, by kind, then date.
pub investing: Vec<CashFlowLineOut>,
/// Cash from investing, decimal string.
/// Net cash from investing activities, decimal string.
pub cash_from_investing: String,
/// Financing flows (equity, designated debt).
/// Financing cash by kind (contributions and distributions per member,
/// borrowing and repayments per lender).
pub financing_by_kind: Vec<CashFlowKindOut>,
/// Every entry's financing cash, by kind, then date.
pub financing: Vec<CashFlowLineOut>,
/// Cash from financing, decimal string.
/// Net cash from financing activities, decimal string.
pub cash_from_financing: String,
/// Net change in cash, decimal string.
pub net_change_in_cash: String,
/// Cash balance at period start, decimal string.
/// Cash at the start, decimal string.
pub cash_beginning: String,
/// Cash balance at period end, decimal string.
/// Cash at the end, decimal string.
pub cash_ending: String,
/// Noncash investing and financing activities.
pub noncash_disclosures: Vec<NoncashDisclosureOut>,
/// Net income reconciled to operating cash.
pub operating_reconciliation: OperatingReconciliationOut,
/// The footing check.
pub reconciles: CashFlowFootingOut,
/// Entries whose later settlement the books can't classify.
pub warnings: Vec<CashFlowWarningOut>,
}
/// Parameters for `books.import_bank_csv`.