beancount icon indicating copy to clipboard operation
beancount copied to clipboard

Support DR and CR abbreviations

Open jcooter opened this issue 4 years ago • 29 comments

I'm used to seeing accounting transactions represented as explicit debits and credits (which, for some strange accounting voodoo reason are abbreviated as DR and CR, respectively), rather than positive and negative numbers. It would be nice if the parser could parse a transaction that looks like this:

2014-05-05 txn "Cafe Mogador" "Lamb tagine with wine"
  Liabilities:CreditCard:CapitalOne         DR 37.45 USD
  Expenses:Restaurant                       CR 37.45 USD

Would be interpreted the same as this:

2014-05-05 txn "Cafe Mogador" "Lamb tagine with wine"
  Liabilities:CreditCard:CapitalOne         -37.45 USD
  Expenses:Restaurant

jcooter avatar Dec 22 '20 10:12 jcooter

Supporting alternative syntax for expressing the same input data complicates Beancount and associated tools for no real benefit.

dnicolodi avatar Dec 22 '20 11:12 dnicolodi

Actually... not so fast, I have disagree with you mildly on this one Daniele. For a long time I've had this idea in the back of my mind: https://github.com/beancount/beancount/blob/master/TODO#debits--credits-normalization

Not exactly with "DR" / "CR" string, but to provide an option allow inverted signs in the input, and also in the output. Internally the signs should remain the same. That should be possible because every amount is associated with an account, and we can use the account type to select the type. It could either be a global option or a per-transaction option (perhaps with a special flag).

Furthermore, I'm reviewing the parser at the moment and creating a clean separated AST for the parser output and this would be a good time to introduce these (in v3).

Just to make sure I understand fully what you'd like, could you provide examples of CR and DR on Assets, Liabilities, Income and Expenses accounts? (8 postings)

blais avatar Dec 22 '20 15:12 blais

I dislike options that change the behavior of the inout syntax non-locally as I believe things are much more clear where minimal information about the context is required to make sense of a construct. However, I haven't read carefully the proposal about sign normalization and there may be some use cases enabled by it that I don't see now.

I still don't see why being able to use DR and CR in place of + and - would bring, other than confusion and extra burden for tools that need minimum knowledge of the beancount syntax (for example editor support). Combine this with multi language support, the capability to rename root accounts, and the proposed sign normalization and you have a rather complex matrix of cases to support.

dnicolodi avatar Dec 22 '20 16:12 dnicolodi

Ideally those tools should be supported by the native parser. Note that in this ticket I'd like to honor reproducing all the input: https://github.com/beancount/beancount/issues/586 This includes:

  • Keep the AST of arithmetic expressions (to rerender them)
  • Keeping other such marks (e.g., in this case, DR/CR would be stored somewhere in the parser output and stripped in the internal structures)
  • Store comments before / trailing directives and postings (to rerender).

blais avatar Dec 22 '20 16:12 blais

This may help alleviate the burden that this proposed syntax brings on external tools, however, it makes the syntax harder to understand for humans, and I haven't yet understood which use case this is solving, other than a personal preference for bike sheds painted in a specific shade of gray.

I think one of the reasons some users prefer Beancount over Ledger is the more regular syntax (for sure it is one of my reasons). In my opinion the syntax should be extended only to solve issues or support new use cases. I don't think this proposal does either.

dnicolodi avatar Dec 22 '20 16:12 dnicolodi

This is slightly more than just a preference.

In an accounting systems, account balances are represented as either credit normal or debit normal. So, it ends up looking like this:

if account.credit_normal:
  balance = sum(credits) - sum(debits)
elif account.debit_normal:
  balance = sum(debits) - sum(credits)

Meaning that a debit against, for example, a credit card, increases its balance. This is because accounting is supposed to be modeling the real world, and in the real world negative money doesn't exist, and account balances are contextual (balances on asset accounts are "money I have" and balances on liability accounts are "money I owe"). I recognize that fully implementing this paradigm represents a huge, likely breaking, change to the core of beancount.

However, it would be possible (and I plan on implementing in my own tools) a function that fakes that output on the display side. But that leads to a followup problem where a transaction that looks like it subtracts from an account results in an increase of the balance. Adding this syntax gives users a choice - if they want to use the normal syntax, nothing changes - but if they're experienced accountants, they can implement their transactions in a more standard notation and make some modifications on the balance display output and have something that works like other accounting systems do.

jcooter avatar Dec 23 '20 00:12 jcooter

Just to make sure I understand fully what you'd like, could you provide examples of CR and DR on Assets, Liabilities, Income and Expenses accounts? (8 postings)

Here's an annotated string of transactions involving all of them in the context of a small business (balance assertions included as currently implemented to provide context):

2020-05-01 open Equity:OwnersEquity
  type: "credit-normal"
2020-05-01 open Assets:BofA:Checking
  type: "debit-normal"
2020-05-01 open Assets:PettyCash
  type: "debit-normal"
2020-05-01 open Liabilities:Loans:Ellie
  type: "credit-normal"
2020-05-01 open Expenses:OperatingExpenses:CostOfGoodsSold
  type: "debit-normal"
2020-05-01 open Expenses:Taxes:SalesTax
  type: "debit-normal"
2020-05-01 open Income:LemonadeSales
  type: "credit-normal"

; I start a lemonade stand with $10
2020-06-01 * "Initial start-up capital"
  Equity:OwnersEquity         CR 10.00 USD
  Assets:BofA:Checking        DR 10.00 USD

; I borrow $100 to buy supplies
2020-06-01 * "Note from Grandma Ellie"
  Liabilities:Loans:Ellie     CR 100.00 USD
  Assets:BofA:Checking        DR 100.00 USD

2020-06-02 balance Equity:OwnersEquity      10.00  USD
2020-06-02 balance Liabilities:Loans:Ellie  100.00 USD
2020-06-02 balance Assets:BofA:Checking    -110.00 USD ; It would be awesome if this could be positive
                                                       ; but not pushing my luck
; The next day, I buy lemonade supplies
2020-06-02 * "Safeway, Inc." "Supplies"
  Assets:BofA:Checking                        CR 52.48 USD
  Expenses:OperatingExpenses:CostOfGoodsSold  DR 52.23 USD
  Expenses:Taxes:SalesTax                     DR  0.25 USD

2020-06-03 balance Assets:BofA:Checking                        -57.52 USD ; Again, this should be positive
2020-06-03 balance Expenses:OperatingExpenses:CostOfGoodsSold   52.23 USD
2020-06-03 balance Expenses:Taxes:SalesTax                       0.25 USD

; Getting change from the bank
2020-06-03 * "Cash withdrawl"
  Assets:BofA:Checking CR 20.00 USD
  Assets:PettyCash     DR 20.00 USD

; Let's sell some lemonade
2020-06-03 * "closing deposit"
  Income:LemonadeSales CR 75.00 USD
  Assets:PettyCash     CR 20.00 USD
  Assets:BofA:Checking DR 95.00 USD

2020-06-04 balance Assets:BofA:Checking -132.52 USD ; Ideally should be positive
2020-06-04 balance Assets:PettyCash        0.00 USD
2020-06-04 balance Income:LemonadeSales   75.00 USD

jcooter avatar Dec 23 '20 02:12 jcooter

@jcooter Thank you. I'm going to ask some pointed questions now, please read carefully:

  • Is it always true, that wherever, a CR appears we could replace it with a minus sign, and all DR could be ignored, and we'd have the same result?

  • Is it true that Expenses are almost exclusively DR, and that Income is almost exclusively CR? (Except for contra-accounts and correcting transactions.)

  • Could you provide an example of a CR expense, e.g. for a correction, or a DR on an income account?

If my understanding is correct, I don't even need to consult the account type (the rule with DR/CR is even simpler than I had envisioned doing). The mode I was planning to offer is simpler than what you propose: all amounts are positive by default almost everywhere (basically inverting signs based on the account type for Liabilities, Income and Equity). The only negative numbers would be found on reducing the value of an asset account (e.g. spending) or increasing (from negative toward zero) the value of a liabilities account (e.g. paying back a loan). This would also work for balances (and it should). Perhaps the CR/DR marks increase clarity on this even further (all amounts positive all the time).

Overall, I'm all in with the idea of doing /optional/ automated sign conversions early on to accommodate more familiar inputs. The transactions themselves will be have normalized signs, and output routines could convert back to one of many styles (for Beancount that could be done in Fava, and as a function in bean-query).

blais avatar Dec 23 '20 04:12 blais

  • Is it always true, that wherever, a CR appears we could replace it with a minus sign, and all DR could be ignored, and we'd have the same result?

It's pretty much an arbitrary decision. You can make the choice that credits are positive (thus effectively making the way beancount exists now treat every account as credit normal) or you can make the choice that credits are negative (effectively making all accounts debit normal). I thought about it and I think people normally associate "credit" with a positive, but the only important thing is that it's consistent. Anything that is not a credit is a debit (and vice versa), so if you decide to map CR to -, your assertion is true and always will be true. It's just as valid to map debits to -, which would make your assertion false (and always false).

In proper GAAP, a credit is a minus sign in a debit-normal account and a plus sign in a credit-normal account. The software engineering way of looking at it is that an account's journal has two arrays, one for credits and one for debits. Transactions do not modify the balance directly, the balance is computed based on the sum of the credits and debits array.

For the sake of consistency with the way that current transactions are parsed within beancount, I don't think it'd be an issue to specify CR/DR for one leg of the transaction and infer the opposite for the others. My CPA would probably hate it if he saw it, but from a software perspective it should be completely valid.

  • Is it true that Expenses are almost exclusively DR, and that Income is almost exclusively CR?

Here's the GAAP rules for accounts: Assets: Debit-normal Liabilities: Credit-normal Capital/Equity: Credit-normal Income: Credit-normal Expenses: Debit-normal

That means that increasing the balance of an expense account is a debit, and decreasing it is a credit. So an "expense" transaction would be a debit against an expense account and a credit against an asset or liability account

(Except for contra-accounts and correcting transactions.)

Contra accounts are just the account type with a reversed rule, so a contra-asset account is effectively an asset account that is credit-normal

  • Could you provide an example of a CR expense, e.g. for a correction, or a DR on an income account?

Sure, it would just be the exact opposite of a normal transaction:

2020-06-02 * "Safeway, Inc." "Supplies"
  Assets:BofA:Checking                        CR 52.48 USD
  Expenses:OperatingExpenses:CostOfGoodsSold  DR 52.23 USD
  Expenses:Taxes:SalesTax                     DR  0.25 USD

2020-06-02 * "Safeway, Inc." "Chargeback"
  Assets:BofA:Checking                        DR 52.48 USD
  Expenses:OperatingExpenses:CostOfGoodsSold  CR 52.23 USD
  Expenses:Taxes:SalesTax                     CR  0.25 USD

If my understanding is correct, I don't even need to consult the account type (the rule with DR/CR is even simpler than I had envisioned doing). The mode I was planning to offer is simpler than what you propose: all amounts are positive by default almost everywhere (basically inverting signs based on the account type for Liabilities, Income and Equity). The only negative numbers would be found on reducing the value of an asset account (e.g. spending) or increasing (from negative toward zero) the value of a liabilities account (e.g. paying back a loan). This would also work for balances (and it should). Perhaps the CR/DR marks increase clarity on this even further (all amounts positive all the time).

It's important to note that you CAN have a negative balance in an account under GAAP, for example if you overdraft your bank account your balance is legitimately negative. But negative balances in this context are always exceptions that need to be resolved (you overdrafted your bank account and your bank is not going to be happy, or you overpaid your credit card bill and they owe you money)

What I'm planning on implementing is to use account metadata to define whether an account is credit-normal or debit-normal. Then, I decide how credits and debits map to + or - in transactions. Then, if an account is anti-pattern (e.g. it's debit-normal where my pattern is credit-normal), invert the sign on the balance that beancount provides prior to display. It's definitely a hack, and it won't work for balance assertions (though you could implement the same logic on the parser for balance assertions - I'm just not planning on making use of them), but it will produce the correct output in every instance.

jcooter avatar Dec 23 '20 04:12 jcooter

  • Is it always true, that wherever, a CR appears we could replace it with a minus sign, and all DR could be ignored, and we'd have the same result?

It's pretty much an arbitrary decision. You can make the choice that credits are positive (thus effectively making the way beancount exists now treat every account as credit normal) or you can make the choice that credits are negative (effectively making all accounts debit normal). I thought about it and I think people normally associate "credit" with a positive, but the only important thing is that it's consistent. Anything that is not a credit is a debit (and vice versa), so if you decide to map CR to -, your assertion is true and always will be true. It's just as valid to map debits to -, which would make your assertion false (and always false).

I'm losing you here. I understand some of your terminology (credit-normal, debit-normal, that's just a way to state what the positive value of the balance ought to be under normal situations), but I don't understand how this can be arbitrary. Isn't there a single meaning for the words credit and debit, or are they simply two relatives?

In proper GAAP, a credit is a minus sign in a debit-normal account and a plus sign in a credit-normal account. The software engineering way of looking at it is that an account's journal has two arrays, one for credits and one for debits. Transactions do not modify the balance directly, the balance is computed based on the sum of the credits and debits array.

Yes

For the sake of consistency with the way that current transactions are parsed within beancount, I don't think it'd be an issue to specify CR/DR for one leg of the transaction and infer the opposite for the others. My CPA would probably hate it if he saw it, but from a software perspective it should be completely valid.

I can require that if it appears once, it must appear on all postings (except ones with missing numbers).

  • Is it true that Expenses are almost exclusively DR, and that Income is almost exclusively CR?

Here's the GAAP rules for accounts: Assets: Debit-normal Liabilities: Credit-normal Capital/Equity: Credit-normal Income: Credit-normal Expenses: Debit-normal

That means that increasing the balance of an expense account is a debit, and decreasing it is a credit. So an "expense" transaction would be a debit against an expense account and a credit against an asset or liability account

(Except for contra-accounts and correcting transactions.)

Contra accounts are just the account type with a reversed rule, so a contra-asset account is effectively an asset account that is credit-normal

Yes. Right now there is no way (and no need) in Beancount to specify that an account is a contra-account. We can do with negative signs for those, but it could be an additional option that could apply on rendering out, and it would be needed to ensure that all numbers are positive.

  • Could you provide an example of a CR expense, e.g. for a correction, or a DR on an income account?

Sure, it would just be the exact opposite of a normal transaction:

2020-06-02 * "Safeway, Inc." "Supplies"
  Assets:BofA:Checking                        CR 52.48 USD
  Expenses:OperatingExpenses:CostOfGoodsSold  DR 52.23 USD
  Expenses:Taxes:SalesTax                     DR  0.25 USD

2020-06-02 * "Safeway, Inc." "Chargeback"
  Assets:BofA:Checking                        DR 52.48 USD
  Expenses:OperatingExpenses:CostOfGoodsSold  CR 52.23 USD
  Expenses:Taxes:SalesTax                     CR  0.25 USD

If my understanding is correct, I don't even need to consult the account type (the rule with DR/CR is even simpler than I had envisioned doing). The mode I was planning to offer is simpler than what you propose: all amounts are positive by default almost everywhere (basically inverting signs based on the account type for Liabilities, Income and Equity). The only negative numbers would be found on reducing the value of an asset account (e.g. spending) or increasing (from negative toward zero) the value of a liabilities account (e.g. paying back a loan). This would also work for balances (and it should). Perhaps the CR/DR marks increase clarity on this even further (all amounts positive all the time).

It's important to note that you CAN have a negative balance in an account under GAAP, for example if you overdraft your bank account your balance is legitimately negative. But negative balances in this context are always exceptions that need to be resolved (you overdrafted your bank account and your bank is not going to be happy, or you overpaid your credit card bill and they owe you money)

Right. (But not on contra-accounts.)

What I'm planning on implementing is to use account metadata to define whether an account is credit-normal or debit-normal. Then, I decide how credits and debits map to + or - in transactions. Then, if an account is anti-pattern (e.g. it's debit-normal where my pattern is credit-normal), invert the sign on the balance that beancount provides prior to display. It's definitely a hack, and it won't work for balance assertions (though you could implement the same logic on the parser for balance assertions - I'm just not planning on making use of them), but it will produce the correct output in every instance.

blais avatar Dec 23 '20 06:12 blais

I'm losing you here. I understand some of your terminology (credit-normal, debit-normal, that's just a way to state what the positive value of the balance ought to be under normal situations), but I don't understand how this can be arbitrary. Isn't there a single meaning for the words credit and debit, or are they simply two relatives?

Credit and Debit aren't operators, they're functions. If an account is debit-normal, then the debit function is an + operator (and the credit function is a - operator) and vice versa. The way that beancount is currently parsing transactions (as I understand it) is that legs of a transaction are mapping to operators. So, if you're keeping the paradigm and not changing how beancount is calculating balances to take that into account properly and you're only changing the parser, you're mapping credits and debits to a static operator, which means that for some accounts beancount will be calculating the balance as expected, and for other accounts you're calculating the inverse of the balance. In this context, which operator you map to which function is arbitrary, because in each case you get some balances that are correct and some that are the inverse of the correct balance.

I can require that if it appears once, it must appear on all postings (except ones with missing numbers).

That should be fine.

Yes. Right now there is no way (and no need) in Beancount to specify that an account is a contra-account. We can do with negative signs for those, but it could be an additional option that could apply on rendering out, and it would be needed to ensure that all numbers are positive.

Contra accounts aren't really negative balance, but in practice are used to offset some other account and are often represented in reports as a negative balance, so I think it probably would work as-is. I can't think of a reason you'd need to treat them differently.

jcooter avatar Dec 23 '20 07:12 jcooter

I'm losing you here. I understand some of your terminology (credit-normal, debit-normal, that's just a way to state what the positive value of the balance ought to be under normal situations), but I don't understand how this can be arbitrary. Isn't there a single meaning for the words credit and debit, or are they simply two relatives?

Credit and Debit aren't operators, they're functions. If an account is debit-normal, then the debit function is an + operator (and the credit function is a - operator) and vice versa.

That's only true if the amount you're applying the operator to is the positive amount. Beancount represents everything as signed. (Having threads with accountants always trips me up a bit because I think of the absolute amount first, e.g., it's natural for me to think of a loan as a negative number.)

What you're describing is precisely why we don't bother with this gobbledigook in plain text accounting: it's more complicated than just doing the mathematically simplest thing, which is to treat all accounts the same, and to just know that liabilities, income and equity are negative values. All the operations become unified. The CR/DR thing ends up being a diversion that doesn't help with anything, but to produce nice reports with all positive numbers.

In any case, in terms of implementing it, it looks like translating "CR" to a minus sign would always work, on any account type, and I like the idea of converting for reporting and at input, for those used to this.

The way that beancount is currently parsing transactions (as I understand it) is that legs of a transaction are mapping to operators. So, if you're keeping the paradigm and not changing how beancount is calculating balances to take that into account properly and you're only changing the parser, you're mapping credits and debits to a static operator, which means that for some accounts beancount will be calculating the balance as expected, and for other accounts you're calculating the inverse of the balance. In this context, which operator you map to which function is arbitrary, because in each case you get some balances that are correct and some that are the inverse of the correct balance.

I can require that if it appears once, it must appear on all postings (except ones with missing numbers).

That should be fine.

Yes. Right now there is no way (and no need) in Beancount to specify that an account is a contra-account. We can do with negative signs for those, but it could be an additional option that could apply on rendering out, and it would be needed to ensure that all numbers are positive.

Contra accounts aren't really negative balance, but in practice are used to offset some other account and are often represented in reports as a negative balance, so I think it probably would work as-is. I can't think of a reason you'd need to treat them differently.

If you don't treat them differently - the simplest thing to do - their rendered balance would be negative. The only reason to mark them as special is so that their rendered balance also view as positive.

blais avatar Dec 23 '20 16:12 blais

The root of this is a difference in philosophy of what books actually represent. Both philosophies are attempts to track money.

In the beancount philosophy of accounting, books are a method to faithfully record every transaction. In this philosophy, you account for every movement of money within the system, and once all that balances out, any negative or positive result is money that beancount is no longer tracking transactions for.

In the GAAP philosophy of accounting, books are the specific slice of all the money that exists in the world that the person keeping track of them cares about for some reason. It's a system that is focused on answering the question "how much money do I have", so it's focused on balances first, and implements transactions as a way of manipulating the balance to answer the question.

The easiest way to contrast the philosophies is by way of example. You have $5000 in your checking account and you owe $1200 in rent. Can you afford a $2,000 laptop. The intuitive way of answering this question is 5000 - 1200 = 3800 = yes

The way beancount answers this question is 5000 + -1200 = 3800 = yes The way GAAP answers this question is 5000 - 1200 = 3800 = yes

beancount prioritizes having transactions make sense, GAAP prioritizes having balances make sense.

jcooter avatar Dec 23 '20 17:12 jcooter

I don't see it that way. To me it's the same thing.

blais avatar Dec 23 '20 22:12 blais

IANAA and I'm coming to this a bit late, but I've been loving starting to use beancount for my personal finances and am also trying it out as the treasurer of a local club.

I have to disagree with jcooter about a few statements:

; I start a lemonade stand with $10
2020-06-01 * "Initial start-up capital"
  Equity:OwnersEquity         CR 10.00 USD
  Assets:BofA:Checking        DR 10.00 USD

; I borrow $100 to buy supplies
2020-06-01 * "Note from Grandma Ellie"
  Liabilities:Loans:Ellie     CR 100.00 USD
  Assets:BofA:Checking        DR 100.00 USD
 
2020-06-02 balance Equity:OwnersEquity      10.00  USD
2020-06-02 balance Liabilities:Loans:Ellie  100.00 USD
2020-06-02 balance Assets:BofA:Checking    -110.00 USD ; It would be awesome if this could be positive
                                                       ; but not pushing my luck

That is not correct. The balance for Checking should be positive (as in beancount). Using your pseudocode:

if account.credit_normal:
  balance = sum(credits) - sum(debits)
elif account.debit_normal:
  balance = sum(debits) - sum(credits)

the balance for Checking is 110 = (10 + 100) - 0.

The balance for Loan:Ellie is correct given the equation but, assuming we wanted to differentiate, it would be more correct to write something along the lines of

2020-06-02 balance Liabilities:Loans:Ellie 100.00 USD (CR)

and to be consistent, the line for Checking should probably read:

2020-06-02 balance Assets:BofA:Checking 110.00 USD (DR)

Also:

It's pretty much an arbitrary decision. You can make the choice that credits are positive (thus effectively making the way beancount exists now treat every account as credit normal) or you can make the choice that credits are negative (effectively making all accounts debit normal). I thought about it and I think people normally associate "credit" with a positive, but the only important thing is that it's consistent. Anything that is not a credit is a debit (and vice versa), so if you decide to map CR to -, your assertion is true and always will be true. It's just as valid to map debits to -, which would make your assertion false (and always false).

The first sentence is incorrect: Beancount is exclusively debit-normal. Recalling the fundamental equation of accounting, beancount essentially changes A=E+L to A+E+L=0, where E and L are normally negative numbers. In both cases, assets are positive.

And I completely agree with:

I don't see it that way. To me it's the same thing.

Notwithstanding the mathematical equivalence, I think it would be great to be able to enter Dr and Cr instead of signs and to see balances more intuitively signed (positive asset means I have, positive liability means I owe, positive income means I took in, positive expenses means I spent). Using the fundamental equation with example numbers, that would then look like this:

A(250) + Ex(400) = Eq(300) + L(200) + I(150)

which is exactly what you'd get by reversing the signs of the balances for equity, liability and income accounts or by adding the debits and the credits separately and using the aforementioned pseudocode.

hitit-web avatar May 04 '21 14:05 hitit-web

And just to further add a bit to my "notwithstanding" comment: From a visual standpoint, separating the two sides of the accounting equation and making the "normal" state of all accounts be a positive balance would allow the graph of a liability balance to be more intuitive, in my opinion.

Currently, because of the negative numbers, a liability viewed in isolation has an upward curve: Screenshot

Thus, at first glance, it looks like the loan is increasing and one has to actively keep in mind, that "up" is good for loans instead of the more intuitive "down". Therefore, I think this change in v3 would increase the intuitiveness of the resulting dataset and the visuals.

I appreciate everything you've done so far!

hitit-web avatar May 12 '21 20:05 hitit-web

I apologize for multiple comments but I think it's also relevant to point out what I only partially mentioned in my first comment: Jcooter's initial comment is signed incorrectly according to standard accounting rules.

> 2014-05-05 txn "Cafe Mogador" "Lamb tagine with wine"
>   Liabilities:CreditCard:CapitalOne         DR 37.45 USD
>   Expenses:Restaurant                       CR 37.45 USD

should read

> 2014-05-05 txn "Cafe Mogador" "Lamb tagine with wine"
>   Expenses:Restaurant                       DR 37.45 USD
>   Liabilities:CreditCard:CapitalOne         CR 37.45 USD

This correction further emphasizes the mathematical correctness of negative numbers always being equivalent to CR entries. I'm not trying to harp on any errors by jcooter or to be pedantic for pedantry's sake.

hitit-web avatar May 12 '21 20:05 hitit-web

How would one attempt to include DR CR notation in beancount?

I tried using a plugin to modify (pre-process) the DR/CR syntax but got syntax errors. My plugin did not have an opportunity to run in order to modify the syntax to something beancount understands. It seems that plugins are run after beancount parses the input (I tried changing the order of plugin execution with "Raw Mode" but was unable to get my plugin to run before the beancount parser). Code is included below

# drcr.beancount

option "insert_pythonpath" "True"
; option "plugin_processing_mode" "raw"

plugin "drcr"
; plugin "beancount.ops.pad"
; plugin "beancount.ops.documents"
; plugin "beancount.ops.balance"

2014-05-05 open Expenses:Restaurant USD
2014-05-05 open Liabilities:CreditCard:CapitalOne USD

2014-05-05 txn "Regular Syntax"
    Expenses:Restaurant                       37.45 USD
    Liabilities:CreditCard:CapitalOne         -37.45 USD

2014-05-05 txn "New Syntax 1"
    Expenses:Restaurant                       DR 37.45 USD
    Liabilities:CreditCard:CapitalOne         CR 37.45 USD

2014-05-05 txn "New Syntax 2"
    DR Expenses:Restaurant                       37.45 USD
    CR Liabilities:CreditCard:CapitalOne         37.45 USD
# drcr.py
__plugins__ = ['replace_drcr_with_plus_minus']

from beancount.core.data import Transaction

def replace_drcr_with_plus_minus(entries, options_map):
    errors = []
    for entry in entries:
        print(entry)
    return entries, errors
# bean-check -v ./drcr.beancount 
INFO    : Operation: 'beancount.parser.parser.parse_file'             Time:                 13 ms
INFO    : Operation: 'beancount.parser.parser'                        Time:           13 ms
INFO    : Operation: 'parse'                                          Time:           13 ms
INFO    : Operation: 'booking'                                        Time:            0 ms
INFO    : Operation: 'beancount.ops.pad'                              Time:                  0 ms
INFO    : Operation: 'beancount.ops.documents'                        Time:                  0 ms
Open(meta={'filename': '/Users/karan/Documents/pfin/books/drcr.beancount', 'lineno': 9}, date=datetime.date(2014, 5, 5), account='Expenses:Restaurant', currencies=['USD'], booking=None)
Open(meta={'filename': '/Users/karan/Documents/pfin/books/drcr.beancount', 'lineno': 10}, date=datetime.date(2014, 5, 5), account='Liabilities:CreditCard:CapitalOne', currencies=['USD'], booking=None)
Transaction(meta={'filename': '/Users/karan/Documents/pfin/books/drcr.beancount', 'lineno': 12, '__tolerances__': {'USD': Decimal('0.005')}}, date=datetime.date(2014, 5, 5), flag='*', payee=None, narration='Regular Syntax', tags=frozenset(), links=frozenset(), postings=[Posting(account='Expenses:Restaurant', units=37.45 USD, cost=None, price=None, flag=None, meta={'filename': '/Users/karan/Documents/pfin/books/drcr.beancount', 'lineno': 13}), Posting(account='Liabilities:CreditCard:CapitalOne', units=-37.45 USD, cost=None, price=None, flag=None, meta={'filename': '/Users/karan/Documents/pfin/books/drcr.beancount', 'lineno': 14})])
INFO    : Operation: 'drcr'                                           Time:                  0 ms
INFO    : Operation: 'beancount.ops.balance'                          Time:                  0 ms
INFO    : Operation: 'run_transformations'                            Time:            4 ms
INFO    : Operation: 'function: validate_open_close'                  Time:                  0 ms
INFO    : Operation: 'function: validate_active_accounts'             Time:                  0 ms
INFO    : Operation: 'function: validate_currency_constraints'        Time:                  0 ms
INFO    : Operation: 'function: validate_duplicate_balances'          Time:                  0 ms
INFO    : Operation: 'function: validate_duplicate_commodities'       Time:                  0 ms
INFO    : Operation: 'function: validate_documents_paths'             Time:                  0 ms
INFO    : Operation: 'function: validate_check_transaction_balances'  Time:                  0 ms
INFO    : Operation: 'function: validate_data_types'                  Time:                  0 ms
INFO    : Operation: 'beancount.ops.validate'                         Time:            1 ms
/Users/karan/Documents/pfin/books/drcr.beancount:17:      syntax error, unexpected NUMBER, expecting end of file or EOL or ATAT or AT

/Users/karan/Documents/pfin/books/drcr.beancount:21:      syntax error, unexpected CURRENCY, expecting end of file or EOL or TAG or LINK

INFO    : Operation: 'beancount.loader (total)'                       Time:     19 ms

Is there anything I can do to make the plugin run right after lexing? If not, Is there any other approach to include DR CR syntax into beancount?

karan718 avatar Jun 22 '21 08:06 karan718

How would one attempt to include DR CR notation in beancount?

There is no way other than modifying the lexer and the parser. I still don't understand what this alternative notation enables, thus I am not convinced that it is desirable to include support for it upstream.

dnicolodi avatar Jun 22 '21 11:06 dnicolodi

because of the negative numbers, a liability viewed in isolation has an upward curve

What do you mean by "upward"? Of course negative y axis values on a graph approach zero from below. I don't think there is anything to be done about it. If anything, it is something that should be changed at the presentation level. Also, please note that what is discussed there is an alternative syntax for a ledger, not a change of how data is represented in Beancount, thus the change would not have any effect on how that graph would look like.

dnicolodi avatar Jun 22 '21 11:06 dnicolodi

There is no way other than modifying the lexer and the parser. I still don't understand what this alternative notation enables, thus I am not convinced that it is desirable to include support for it upstream.

I will try and explain why this notation is important. A short two transaction journal follows along with a discussion comparing beancount's notation (+ve and -ve) with DR CR notation for each transaction.

Context:

A person wants to start a business and get's a friend to invest money in the business. Transaction is done via wire transfer. At the end of this transaction the business owes money to the investor friend.

Transaction 1:

NB (New Business) Inc setup. InvestorFriend deposits 100 USD into companies bank account. bank

2020-01-01 txn "NB Inc setup. Equity 100 USD into Bank A" #regular-beancount
    Assets:BankA      +100 USD
    Equity:InvestorFriend -100USD

2020-01-01 txn "NB Inc setup. Equity 100 USD" #drcr-beancount
    Assets:BankA      DR 100 USD
    Equity:InvestorFriend CR 100USD

At the end of first transaction

  • This transaction shows us that NB inc owes the proprietor 100 USD and BankA owes NB Inc 100 USD.
  • Position of BankA:
    • it is holding 100 USD for NB Inc => BankA owes NB Inc => BankA is in debt of NB Inc => BankA is a debtor for NB Inc.
    • This owing of debt by BankA to NB Inc is more clearly reflected with Debit. Note: BankA is an Asset for NB Inc because it owes a debt to NB Inc (not because we have classified it as an Asset. Explained with example later).
    • Beancount's treatment of this leg of the transaction is easy to understand. Assets are increasing => hence use the positive sign. No problem.
  • Position of Investor Friend:
    • To someone not familiar with beancount (or any of the other text based ledgers) a negative balance fo equity makes little sense. How, can NB Inc owe InvestorFriend -100USD?
    • InvestorFriend has given NB Inc money and hence is a creditor, this fact is very clear with the credit balance and the CR notation.

Transaction 2:

One year later, NB Inc has become a very important company. It has won contracts to repair all bridges. No one else has the knowledge to repair the bridges. Simultaneously the InvestorFriend is short of funds and needs a loan of 105 USD.

2021-01-01 txn "105 USD loan to Investor Friend. Using bank overdraft facility" #regular-beancount
    Assets:BankA      -105 USD
    Equity:InvestorFriend +105USD

2021-01-01 txn "105 USD loan to Investor Friend. Using bank overdraft facility" #drcr-beancount
    Assets:BankA      CR 105 USD
    Equity:InvestorFriend DR 105USD

At the end of second transaction

  • Position of BankA:
    • BankA has extended a credit line to NB Inc due to NB Inc's importance
    • NB Inc has a negative balance with BankA and now owes BankA 5 USD.
    • Even though BankA is classified as an Asset in NB Inc's accounting books, it is not an asset. BankA is a creditor and a liability for NB Inc
    • A balance of -5 USD in beancount's regular is easy to understand. However, a CR balance is easier to understand.
  • Position of InvestorFriend:
    • Even though InvestorFriend is classified as Equity and the account itself is called InvestorFriend, instead of NB Inc owing InvestorFriend the opposite is true. InvestorFriend owes NB Inc.
    • InvestorFriend is an asset for NB Inc and will be correctly reflected with CR and DR notation
    • In case of beancount's regular syntax, InevstorFriend will have a balance of +5 USD. Interpreting this +5 USD correctly is almost impossible. At first glance, +5 USD for InvestorFriend sure looks like NB Inc owes InvestorFriend 5 USD.

@dnicolodi I hope this convinces you that using DR CR has advantages and makes things clearer.

If you are convinced, can you please guide me as to how I should go about implemented this. What code should I change? Maybe I can implement it just for myself in my fork and not upstream the changes.

karan718 avatar Jun 22 '21 14:06 karan718

Do you want to change or extend the file syntax or do you want to change how amounts held in accounts are represented within Beancount? Your explanation seem to imply that you want to do both things normalizing amounts to always be positive. If this is the case, I don't think there is a simple solution other than rewriting most of Beancount with this completely different assumption.

I don't see CR and DR any more understandable than + and -, quite the opposite, actually. The fact that one of the proponents of this syntax apparently got it wrong in one of his examples strikes me as a proof that the CR DR syntax is indeed not as clear as it may seem. I don't understand what is so complicated about interpreting negative numbers.

I may be too slow to understand it, but IIUC, the syntax you are proposing maps CR to + and DR to -. If this is the case, you can simply pre-process the Beancount input file and do a trivial text replacement.

dnicolodi avatar Jun 22 '21 14:06 dnicolodi

I only wish to extend the beancount file syntax. I don't wish to change how data is stored within beancount. I don't wish to change any code deep within the internals of beancount. I simply want to add a wrapper.

  • On the input side of things, I would like to add a step (maybe by writing a plugin) that changes the DR/CR syntax to regular beancount syntax. One can call it a pre processor for existing beancount code. Ideally, I want to run this step after the lexer and before the parser so that the parser get to process regular beancount syntax. This can be achieved by using regular expressions and substitutions but that seems like a brittle solution.
  • On the reporting/output side of beancount, I would like to append a processing layer that changes the reports to DR/CR syntax. One can call it a post processor.

Imagine that the parser stage in the above image were split into lexer and parser. I want to add some code after the lexer and before the parser.

the syntax you are proposing maps CR to + and DR to -.

This is incorrect. DR maps to + for expenses and Assets. CR maps to + for liabilities, equities and income.

karan718 avatar Jun 23 '21 02:06 karan718

Sorry guys I'm swamped with work and unable to get into the weeds here now, but

  1. not in v2, v2 syntax is frozen
  2. we change change the syntax in v3, but v3 parser not hooked up yet (unit tests remain to be completed on the new parser + the builder has to be finished) If you'd like to contribute to v3 let me know, there are some well-defined next steps. (I'm looking fwd to taking several weeks off to work on this myself, hopefully within a few weeks)

blais avatar Jun 23 '21 03:06 blais

I have no experience with C/C++ or grammars. However, I can have a crack at it. Let me know what the well defined next step are and will try.

karan718 avatar Jun 23 '21 04:06 karan718

Hmm, if you don't have C++ experience then it's best to wait for us to get it done. There's a fair amount of C++ arcana involved in getting this done and this is not even just C++, it's using the conservative C++ Google style the contours of which won't be obvious unless you've done a fair amount of C++.

blais avatar Jun 23 '21 05:06 blais

On the input side of things, I would like to add a step (maybe by writing a plugin) that changes the DR/CR syntax to regular beancount syntax. One can call it a pre processor for existing beancount code. Ideally, I want to run this step after the lexer and before the parser so that the parser get to process regular beancount syntax.

The lexer used by Beancount is a (mostly) context-free lexer, thus, if the meaning of CR and DR changes based on whether they apply to an account class or another, this cannot work (at least without significant changes to the Beancount parser architecture).

dnicolodi avatar Jun 23 '21 11:06 dnicolodi

I still don't understand what this alternative notation enables, thus I am not convinced that it is desirable to include support for it upstream.

Apart from any philosophical debate, this practice is mandated by GAAP. That means that, as a business owner who has already demonstrated an incomplete knowledge of accounting, my bookkeeper and accountant is going to be trained to work this way. It also means that any banks that I interact with will expect my books to work this way, and will refuse to loan me money if they don't. If I ever take my company public, GAAP becomes a legal requirement and the SEC will issue me a fine if my books don't work this way.

jcooter avatar Jul 01 '21 20:07 jcooter

LOL. This issue is about the Benacount input format and I don't see how that has any relevance to GAAP which describes book keeping and reporting: I would be very surprised if the CR and DR substitution is the only obstacle for Beancount ledgers to be recognized as official company books. Anyhow, I had a quick look at the GAAP and I don't see anything there regarding CR or DR annotations. Can you point me in the right direction?

dnicolodi avatar Jul 02 '21 08:07 dnicolodi