simonmichael / hledger

Robust, fast, intuitive plain text accounting tool with CLI, TUI and web interfaces.
https://hledger.org
GNU General Public License v3.0
3.05k stars 321 forks source link

Support assertions which include subaccounts #290

Closed mathstuf closed 5 years ago

mathstuf commented 9 years ago

I have the current accounts for each credit card:

credit:bank:card
credit:bank:card:bill

I would like to assert on the outstanding balance on each card, but there is no way to take the amount paid in bills to the card when asserting on its balance.

Is there a better way to structure the accounts? I'd really like a separate one for the bills to go to so I can see how much I've paid over time, but if there's some other way to do so with a merged account, that'd be fine too.

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/27361636-support-assertions-which-include-subaccounts?utm_campaign=plugin&utm_content=tracker%2F536505&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F536505&utm_medium=issues&utm_source=github).
simonmichael commented 9 years ago

I agree we should have some way to assert on the subaccount-inclusive balance. (#195 is another balance assertions wish.)

I'm not familiar with how you're using the subaccount here. Can't you easily see what you've paid over time with a report ?

simonmichael commented 9 years ago

PS for example, I'm thinking of something like:

hledger register credit:bank:card amt:'<0'
hledger register credit:bank:card amt:'<0' -Q
hledger balance  credit:bank:card amt:'<0' -MA
mathstuf commented 9 years ago

On Sun, Oct 18, 2015 at 16:53:14 -0700, Simon Michael wrote:

I agree we should have some way to assert on the subaccount-inclusive balance. (#195 is another balance assertions wish.)

I'm not familiar with how you're using the subaccount here. Can't you easily see what you've paid over time with a report ?

As for how the accounts are used:

credit line -> card
card -> purchase
card -> purchase
card -> purchase
card -> purchase
credit line increase -> card

which all works fine. I also need an account to pay off bills. My thought was to use a subaccount so that I can see it individually, but I guess I could just use queries to get this information instead. Playing around with it, it seems to be sufficient.

Setting the card == bill account and instead splitting the credit line into a subaccount lets me assert on values matching the bills.

mathstuf commented 9 years ago

On Sun, Oct 18, 2015 at 20:56:43 -0400, Ben Boeckel wrote:

Setting the card == bill account and instead splitting the credit line into a subaccount lets me assert on values matching the bills.

Maybe adding such a layout should be put into some kind of documentation? I'd also be interested in recommended layouts for things like refunds (where I use a :refund subaccount), 401k tracking with shares, and other common patterns. For example, I'm now thinking that refunds shouls use the same account, but managed using queries.

For example, here is how I handle increases in pledge values on Kickstarter:

04-23=03-22 * Item - Kickstarter
    account for item        $99.00
    source account

04-23=04-04 * Item boost - Kickstarter
    account for item       $199.00
    account for item       $-99.00
    source account

Where the transaction date is that of the pledge or increase and the clear date is when the kickstarter ended.

And then if the Kickstarter fails:

04-23 * Item failure - Kickstarter
    account for item      $-199.00
    source account

Having all the clear dates be the same ensures that no assertion is problematic, but I can also accurately budget before it ends.

simonmichael commented 9 years ago

Maybe adding such a layout should be put into some kind of documentation?

Yes please! (But where ? Too big a topic to discuss here, but a good h/ledger cookbook is needed).

mathstuf commented 9 years ago

Well, some place on the website would be nice, but I think soliciting advice from the h/ledger lists on how others approach it would be a first step.

simonmichael commented 9 years ago

Back to the topic at hand - if you come up with a design for this, let me know. I assume I can't just make it work that way by default, because it would cause unexpected breakage and confusion when switching between Ledger and hledger.

mathstuf commented 9 years ago

Maybe use == or some other (currently invalid) token? Maybe =~ would work too, but maybe that is too perl/shell-ish.

simonmichael commented 9 years ago

I was going to use == for "all commodities, not just one" - how could we cover all four of these cases (or should we ?)

simonmichael commented 9 years ago

Well, answering myself, you could do the obvious:

=  - one commodity, exclude subaccounts (Ledger default)
==  - all commodities, exclude subaccounts
===  - one commodity, include subaccounts
====  - all commodities, include subaccounts

I think not, too hard to remember.

mathstuf commented 9 years ago

I don't think multiple commodities is needed; can't you just have multiple assertion transactions?

simonmichael commented 9 years ago

We need a way to say that no other commodities are present, too.

ony commented 7 years ago

@simonmichael, I'd agree with @mathstuf that attempt to assert total amount of multi-currency account is nearly impossible without specifying rates of conversion in the same posting. Though scenario when you put all prices right before a transaction and never split this pair is still valid. Thus I would expect that most popular usages would be = and ===. Re-ordering this scheme according to an estimation of usage might be useful. I.e. consider using == for single currency including subaccounts.

simonmichael commented 7 years ago

That makes sense, thanks for the comment. Asserting a multicommodity zero (account is completely empty) might still be a reason to support ====.

Would a * suffix for "include subaccounts" be more mnemonic ? = = == ==

ony commented 6 years ago

@simonmichael, agree *= for sub-accounts, =* for multi-currency, and *=* for both might be more mnemonic.

Note that I'm not sure how to represent right side of multi-currency assertion. I guess it is safe to have zero assertions, but whenever we refer to other value we have to deal with commodities exchange rates. As well multi-currency assertions cannot produce implicit flow.

2017/12/01 selling some commodities
    assets:bank:investment  -1 TSLA
    assets:bank:investment  -2 GOOG
    assets:bank:current  $203 @ *

2017/12/31 closing portfolio
    assets:bank:investment  =* 0
    assets:bank:current  $203 @ *

I just came back to issue with sub-accounts again when I was touching one of my accounts balances:

2017/01/01
    assets:bank:compte vert  = €1000
    assets:bank:current

2017/01/01 rent deposit
    assets:bank:compte vert  €-600
    assets:bank:compte vert:hold

2017/12/31 interest accrual report
    assets:bank:compte vert  *= €1003
    expenses:taxes:income  €1
    income:interest

Worth to mention that with such syntax there is no way to specify different sub-account as a recipient of balance difference. Though I have no such cases I can imagine them:

2018/01/02 balance report
    {assets:bank}:current  = €4004
    expenses:taxes:income  ; tax return

which is effectively

2018/01/02 balance report
    assets:bank:current  €1
    assets:bank   €0 = €4004
    expenses:taxes:income  €-1  ; tax return

Though I'd expect that initial implementation should only allow assertion without implicit flow generation.

P.S. Actually other way to represent "hold" on account which I use right now is to add sub-account with negative balance and in tree-view of balance see it as available funds:

2017/01/01 rent deposit
    assets:bank:compte vert:hold  €-600
    liabilities  ; payee: landlord

2017/12/31 interest accrual report
    assets:bank:compte vert  = €1003
    expenses:taxes:income  €1
    income:interest

Updated: I actually assert with positive balance

mathstuf commented 6 years ago

P.S. Actually other way to represent "hold" on account which I use right now is to add sub-account with negative balance and in tree-view of balance see it as available funds:

This is similar to the way I've done credit lines (my original use case for multi-account assertions):

01-01 Credit card issued
    acct:for:credit:card:credit   $1000.00
    null:credit

01-02 Use credit card
    personal:gifts       $100.00
    acct:for:credit:card

02-01 Statement for credit card
    [acct:for:credit:card]    = $-100.00
mildred commented 6 years ago

Note that ledger-cli supports them using the global assert directive:

assert account("assets").total == 100

except that they don't work well (https://github.com/ledger/ledger/issues/1679). If correct syntax is difficult to come by, especially if we want to be compatible with ledger-cli, perhaps an assert directive could be used.

simonmichael commented 6 years ago

That's a dateless assertion, entirely dependent on parse order; I don't think we want those.

mildred commented 6 years ago

here: https://github.com/simonmichael/hledger/blob/2960c9209f2ae920e12d01696e6efb01760f88ab/hledger-lib/Hledger/Data/Journal.hs#L537

mildred commented 6 years ago

parsing here: https://github.com/simonmichael/hledger/blob/3de8c11de1b358ed5ea86745d081d1e21ee6be6c/hledger-lib/Hledger/Read/JournalReader.hs#L559

mildred commented 6 years ago

About the syntax, could it be possible to represent multi currencies assertions by specifying the different currencies on the right hand side? Something like that:

2018-10-01 Bankins assertion
  banking  = $100.00 + 10.00 EUR + 0

Here we want to assert that the banking account has $100, 100€, and nothing more. A sign like / could be used too to indicate the end of amounts and that we want a full assertion over all currencies.

Then, to assert over sub-accounts as well, we could use another operator like *= or == as it has been suggested, to signify we want to recurse to sub-accounts.

simonmichael commented 6 years ago

@mildred yes, https://github.com/simonmichael/hledger/pull/871 is exploring this.

mildred commented 5 years ago

Something like that for the parser...

diff hledger-lib/Hledger/Read/Common.hs hledger-lib/Hledger/Read/Common.hs
 balanceassertionp :: JournalParser m BalanceAssertion
 balanceassertionp = do
   sourcepos <- genericSourcePos <$> lift getSourcePos
   char '='
   exact <- optional $ try $ char '='
+  recurse <- optional $ try $ char '*'
   lift (skipMany spacenonewline)
   a <- amountp <?> "amount (for a balance assertion or assignment)" -- XXX should restrict to a simple amount
   return BalanceAssertion
     { baamount = a
+    , barecurse = isJust recurse
     , baexact = isJust exact
     , baposition = sourcepos
     }
simonmichael commented 5 years ago

934 is the latest discussion on that.

simonmichael commented 5 years ago

@mildred I mean, #934 is a proposal to allow writing multi-commodity amounts in assertions and elsewhere. (If you have a real need for this feature, do write about it there.)

Regarding this issue #290, I have lost the context. Maybe someone can review it and make a recommendation on whether we need subaccount-inclusive assertions and how they should look and work.

simonmichael commented 5 years ago

Reminding myself.. here are some distinct, interrelated balance assertion issues under discussion:

[for completeness: we also have]

simonmichael commented 5 years ago

Inviting list discussion

mildred commented 5 years ago

Sorry, my comments might seem a little difficult to understand. I'm tracking this issue because I have a real need for this. Unfortunately, Iḿ not well versed into Haskell. if I was, I would have made a pull request long ago. So until someone works on this, I'm looking at bits and pieces to decipher the code to eventually make a contribution.

I need this feature to ensure that my ledger file is consistent with the amount shown on my bank account. Until then, I make manual checks, when I find the time to, and when I do, I often find surprises... Basically, I have my accounts organized like this:

But in reality, I have only one bank account. I'd like to only have a single place where I store cash too, but there I can arrange for different physical envelopes. I need to make sure that each month, the amount shown on my bank account matches all the ledger accounts under bank:. I'd like to be able to write:

2018-02-01 February balance assertion
    bank  0 =* xxx EUR
    cash  0 =* xxx EUR

In the future, I'd also like to reorganize accounts to have a single provisions hierarchy, and different commodities for cash and virtual money on the bank account, so I also need recursive multi commodity assertions. Write something like:

2018-02-01 February balance assertion
    provisions  0 =* xxx BankEUR
    provisions  0 =* xxx CashEUR
simonmichael commented 5 years ago

So until someone works on this, I'm looking at bits and pieces to decipher the code to eventually make a contribution.

That's appreciated! Feel free to ask for more help on #hledger. Also thanks for the extra details, which make sense.

I wonder why I haven't felt this need myself ? I have ~300 assertions in 2018, excluding opening/closing transactions. It seems my assertions tend to be on accounts which don't have subaccounts. And when reconciling, I tend to just do a one time check of the inclusive account balances in hledger-ui. I don't usually record that as an assertion.

simonmichael commented 5 years ago

Making some progress on this: #974

simonmichael commented 5 years ago

Now in master. https://groups.google.com/d/msg/hledger/47F-ZiajbMU/9edMGm2OBAAJ, http://hledger.org/journal.html#assertions-and-subaccounts

=*  AMT   ; partial balance assertion including subaccounts
==* AMT   ; total balance assertion including subaccounts
mildred commented 5 years ago

Thank you a lot !

ony commented 5 years ago

Ehh... I hoped * to be associated with what part is aggregated. I.e. *= (closer to account) - all sub-accounts =* (closer to amount) - all currencies *=* (on both sides) - all sub-accounts, all currencies.

Anyway thank you. I also wanted this feature. I think I still can associate difference between = and == to be similar to == and === in JavaScript (type aware equality).

simonmichael commented 5 years ago

Hmm. I don't love what we have, but everything else so far, including this, seems a bit harder to remember or intuit.