prune and update balance report docs
This commit is contained in:
parent
aaf8a0caf6
commit
7fba880fef
@ -2,14 +2,9 @@
|
||||
|
||||
A ledger-compatible @balance@ command.
|
||||
|
||||
ledger's balance command is easy to use but hard to describe precisely.
|
||||
Here are some attempts.
|
||||
|
||||
|
||||
I. high level description with examples
|
||||
---------------------------------------
|
||||
|
||||
We'll use sample.ledger, which has the following account tree:
|
||||
ledger's balance command is easy to use but not easy to describe
|
||||
precisely. In the examples below we'll use sample.ledger, which has the
|
||||
following account tree:
|
||||
|
||||
@
|
||||
assets
|
||||
@ -28,8 +23,9 @@ We'll use sample.ledger, which has the following account tree:
|
||||
@
|
||||
|
||||
The balance command shows accounts with their aggregate balances.
|
||||
Subaccounts are displayed with more indentation. Each balance is the sum
|
||||
of any transactions in that account plus any balances from subaccounts:
|
||||
Subaccounts are displayed indented below their parent. Each balance is the
|
||||
sum of any transactions in that account plus any balances from
|
||||
subaccounts:
|
||||
|
||||
@
|
||||
$ hledger -f sample.ledger balance
|
||||
@ -45,15 +41,25 @@ of any transactions in that account plus any balances from subaccounts:
|
||||
$1 liabilities:debts
|
||||
@
|
||||
|
||||
Usually, the non-interesting accounts are elided or omitted. To be
|
||||
precise, an interesting account is one with: a non-zero balance, or a
|
||||
balance different from its single subaccount, or two or more interesting
|
||||
subaccounts. (More subtleties to be filled in here.)
|
||||
Usually, the non-interesting accounts are elided or omitted. Above,
|
||||
@checking@ is omitted because it has no subaccounts and a zero balance.
|
||||
@bank@ is elided because it has only a single displayed subaccount
|
||||
(@saving@) and it would be showing the same balance as that ($1). Ditto
|
||||
for @liabilities@. We will return to this in a moment.
|
||||
|
||||
So, above, @checking@ is omitted because it has no interesting subaccounts
|
||||
and a zero balance. @bank@ is elided because it has only a single
|
||||
interesting subaccount (saving) and it would be showing the same balance
|
||||
($1). Ditto for @liabilities@.
|
||||
The --depth argument can be used to limit the depth of the balance report.
|
||||
So, to see just the top level accounts:
|
||||
|
||||
@
|
||||
$ hledger -f sample.ledger balance --depth 1
|
||||
$-1 assets
|
||||
$2 expenses
|
||||
$-2 income
|
||||
$1 liabilities
|
||||
@
|
||||
|
||||
This time liabilities has no displayed subaccounts (due to --depth) and
|
||||
is not elided.
|
||||
|
||||
With one or more account pattern arguments, the balance command shows
|
||||
accounts whose name matches one of the patterns, plus their parents
|
||||
@ -76,237 +82,15 @@ Also, the balance report shows the total of all displayed accounts, when
|
||||
that is non-zero. Here, it is displayed because the accounts shown add up
|
||||
to $-1.
|
||||
|
||||
Here is a more precise definition of \"interesting\" accounts in ledger's
|
||||
balance report:
|
||||
|
||||
II. Some notes for the implementation
|
||||
-------------------------------------
|
||||
|
||||
- a simple balance report shows top-level accounts
|
||||
|
||||
- with an account pattern, it shows accounts whose leafname matches, plus their parents
|
||||
|
||||
- with the subtotal option, it also shows all subaccounts of the above
|
||||
|
||||
- zero-balance leaf accounts are removed
|
||||
|
||||
- the resulting account tree is displayed with each account's aggregated
|
||||
balance, with boring parents prefixed to the next line
|
||||
|
||||
- a boring parent has the same balance as its child and is not explicitly
|
||||
matched by the display options.
|
||||
|
||||
- the sum of the balances shown is displayed at the end, if it is non-zero
|
||||
|
||||
|
||||
III. John's description 2009/02
|
||||
-------------------------------
|
||||
|
||||
johnw: \"Since you've been re-implementing the balance report in Haskell, I thought
|
||||
I'd share with you in pseudocode how I rewrote it for Ledger 3.0, since
|
||||
the old method never stopped with the bugs. The new scheme uses a 5 stage
|
||||
algorithm, with each stage gathering information for the next:
|
||||
|
||||
STEP 1
|
||||
|
||||
Based on the user's query, walk through all the transactions in their
|
||||
journal, finding which ones to include in the account subtotals. For each
|
||||
transaction that matches, mark the account as VISITED.
|
||||
|
||||
STEP 2
|
||||
|
||||
Recursively walk the accounts tree, depth-first, computing aggregate
|
||||
totals and aggregate \"counts\" (number of transactions contributing to the
|
||||
aggregate total).
|
||||
|
||||
STEP 3
|
||||
|
||||
Walk the account tree again, breadth-first, and for every VISITED account,
|
||||
check whether it matches the user's \"display predicate\". If so, mark the
|
||||
account as MATCHING.
|
||||
|
||||
STEP 4
|
||||
|
||||
Do an in-order traversal of the account tree. Except for the top-most
|
||||
account (which serves strictly as a container for the other accounts):
|
||||
|
||||
a. If the account was MATCHING, or two or more of its children are
|
||||
MATCHING or had descendents who were MATCHING, display the account.
|
||||
|
||||
b. Otherwise, if the account had *any* children or descendants who
|
||||
were VISITED and *no* children or descendants who were MATCHING,
|
||||
then apply the display predicate from STEP 3 to the account. If
|
||||
it matches, also print this account. (This step allows -E to
|
||||
report empty accounts which otherwise did match the original
|
||||
query).
|
||||
|
||||
STEP 5
|
||||
|
||||
When printing an account, display a \"depth spacer\" followed by the \"partial name\".
|
||||
tal
|
||||
The partial name is found by taking the base account's name, then
|
||||
prepending to it every non-MATCHING parent until a MATCHING parent is
|
||||
found.
|
||||
|
||||
The depth spacer is found by outputting two spaces for every MATCHING parent.
|
||||
|
||||
This way, \"Assets:Bank:Checking\" might be reported as:
|
||||
|
||||
Assets
|
||||
Bank
|
||||
Checking
|
||||
|
||||
or
|
||||
|
||||
Assets
|
||||
Bank:Checking
|
||||
|
||||
or
|
||||
|
||||
Assets:Bank:Checking
|
||||
|
||||
Depending on whether the parents were or were not reported for their own reasons.
|
||||
\"
|
||||
|
||||
\"I just had to add one more set of tree traversals, to correctly determine
|
||||
whether a final balance should be displayed
|
||||
|
||||
without --flat, sort at each level in the hierarchy
|
||||
with --flat, sort across all accounts\"
|
||||
|
||||
IV. A functional description
|
||||
-----------------------------
|
||||
|
||||
1. filter the transactions, keeping only those included in the calculation.
|
||||
Remember the subset of accounts involved. (VISITED)
|
||||
|
||||
2. generate a full account & balance tree from all transactions
|
||||
|
||||
3. Remember the subset of VISITED accounts which are matched for display.
|
||||
(MATCHING)
|
||||
|
||||
4. walk through the account tree:
|
||||
|
||||
a. If the account is in MATCHING, or two or more of its children are or
|
||||
have descendants who are, display it.
|
||||
|
||||
b. Otherwise, if the account has any children or descendants in VISITED
|
||||
but none in MATCHING, and it is matched for display, display it.
|
||||
(allows -E to report empty accounts which otherwise did match the
|
||||
original query).
|
||||
|
||||
5. when printing an account, display a \"depth spacer\" followed by the
|
||||
\"partial name\". The partial name is found by taking the base account's
|
||||
name, then prepending to it every non-MATCHING parent until a MATCHING
|
||||
parent is found. The depth spacer is two spaces per MATCHING parent.
|
||||
|
||||
6. I just had to add one more set of tree traversals, to correctly
|
||||
determine whether a final balance should be displayed
|
||||
|
||||
7. without --flat, sort at each level in the hierarchy
|
||||
with --flat, sort across all accounts
|
||||
|
||||
V. Another functional description with new terminology
|
||||
------------------------------------------------------
|
||||
|
||||
- included transactions are those included in the calculation, specified
|
||||
by -b, -e, -p, -C, -R, account patterns and description patterns.
|
||||
|
||||
- included accounts are the accounts referenced by included transactions.
|
||||
|
||||
- matched transactions are the included transactions which match the
|
||||
display predicate, specified by -d.
|
||||
|
||||
- matched accounts are the included accounts which match the display
|
||||
predicate, specified by -d, --depth, -E, -s
|
||||
|
||||
- an account name tree is the full hierarchy of account names implied by a
|
||||
set of transactions
|
||||
|
||||
- an account tree is an account name tree augmented with the aggregate
|
||||
balances and transaction counts for each named account
|
||||
|
||||
- the included account tree is the account tree for the included transactions
|
||||
|
||||
- a matched account tree contains one or more matched accounts
|
||||
|
||||
- to generate the balance report, walk through the included account tree
|
||||
and display each account if
|
||||
|
||||
- it is matching
|
||||
|
||||
- or it has two more more matching subtrees
|
||||
|
||||
- or it has included offspring but no matching offspring
|
||||
|
||||
- to display an account, display an indent then the \"partial name\". The
|
||||
partial name is the account's name, prefixed by each unmatched parent
|
||||
until a matched parent is found. The indent is two spaces per matched
|
||||
parent.
|
||||
|
||||
|
||||
VI. John's description 2009/03/11
|
||||
---------------------------------
|
||||
|
||||
johnw: \"Well, I had to rewrite the balance reporting code yet again,
|
||||
because it wouldn't work with --depth correctly. Here's the new algorithm.
|
||||
|
||||
STEP 1: Walk all postings, looking for those that match the user's query.
|
||||
As a match is found, mark its account VISITED.
|
||||
|
||||
STEP 2: Do a traversal of all accounts, sorting as need be, and collect
|
||||
them all into an ordered list.
|
||||
|
||||
STEP 3: Keeping that list on the side, do a *depth-first* traversal of
|
||||
the account tree.
|
||||
|
||||
visited = 0
|
||||
to_display = 0
|
||||
|
||||
(visited, to_display) += <recurse for all immediate children>
|
||||
|
||||
if account is VISITED or (no --flat and visited > 0):
|
||||
if account matches display predicate and
|
||||
(--flat or to_display != 1):
|
||||
mark account as TO_DISPLAY
|
||||
to_display = 1
|
||||
visited = 1
|
||||
|
||||
return (visited, to_display)
|
||||
|
||||
STEP 4: top_displayed = 0
|
||||
|
||||
for every account in the ordered list:
|
||||
if account has been marked TO_DISPLAY:
|
||||
mark account as DISPLAYED
|
||||
format the account and print
|
||||
|
||||
if --flat and account is DISPLAYED:
|
||||
top_displayed += 1
|
||||
|
||||
if no --flat:
|
||||
for every top-most account:
|
||||
if account is DISPLAYED or any children or DISPLAYED:
|
||||
top_displayed += 1
|
||||
|
||||
if no --no-total and top_displayed > 1 and
|
||||
top-most accounts sum to a non-zero balance:
|
||||
output separator
|
||||
output balance sum account as DISPLAYED
|
||||
format the account and print
|
||||
|
||||
if --flat and account is DISPLAYED:
|
||||
top_displayed += 1
|
||||
|
||||
if no --flat:
|
||||
for every top-most account:
|
||||
if account is DISPLAYED or any children or DISPLAYED:
|
||||
top_displayed += 1
|
||||
|
||||
if no --no-total and top_displayed > 1 and
|
||||
top-most accounts sum to a non-zero balance:
|
||||
output separator
|
||||
output balance sum
|
||||
\"
|
||||
- an account which has just one interesting subaccount branch, and which
|
||||
is not at the report's maximum depth, is interesting if the balance is
|
||||
different from the subaccount's, and otherwise boring.
|
||||
|
||||
- any other account is interesting if it has a non-zero balance, or the -E
|
||||
flag is used.
|
||||
|
||||
-}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user