Ciao Martin,
I'm building some reporting/form generation on top of Beancount, and have a question about how best to handle rounding/tolerance in the code.
The problem I'm trying to solve is to safely assert invariants which will detect accounting errors.
Let me give an example:
from beancount.query.query import run_query
from beancount.query.query_render import render_text
from beancount.core.amount import Amount, mul as amount_mul
from beancount.core.number import Decimal
def total_query_(entries, options, currency, query_string):
'''Runs a query, then returns the last value (or 0 if no result) as an Amount
'''
qy_types, qy_rows = run_query(entries, options, query_string)
if len(qy_rows):
return qy_rows[-1].total.get_units(currency)
else:
return Amount(Decimal('0.00'), currency)
taxable_earnings = total_query_(entries, options, currency,
'''SELECT sum(position) as total
WHERE account ~ "EarningsBlahBlah"
'''.format(fiscal_quarter.query())
)
tax_owed = total_query_(entries, options, currency,
'''SELECT sum(position) as total
WHERE account ~ "SpecificTaxAccount"
'''.format(fiscal_quarter.query())
)
tax_rate = Decimal('0.07')
currency = 'WHATEVER'
# invariant: bookings to SpecificTaxAccount must equal EarningsBlahBlah times the tax rate
assert amount_mul(taxable_earnings, tax_rate) == tax_owed, (
'taxable earnings {0} * {1} = {2} != tax_owed {3}'.format(
taxable_earnings, tax_rate,
amount_mul(taxable_earnings, tax_rate)
,
tax_owed)
)
This might fail because 1.4188 != 1.42
However, accounting errors might create a (perfectly valid, error-free) ledger where there is much more discrepancy between the figures - and I need to catch that.
As a note, I'm running with:
option "inferred_tolerance_default" "*:0.005"
It's not immediately obvious to me how to best plug into Beancount's "standard" rounding approach (and TBH I don't fully understand that approach yet :P)
I could of course just hack something like this:
assert round(amount_mul(taxable_earnings, tax_rate).number, 2) == round(tax_owed.number, 2)
... but that's so ugly it hurts and I'm also hard-coding a rounding value disregarding beancount's tolerance figures.
Any advice is appreciated.
Thank you and all the best,
Sirio