import configparser import datetime import decimal import pathlib import pytest from import2ledger import errors, template from . import DATA_DIR, normalize_whitespace DATE = datetime.date(2015, 3, 14) config = configparser.ConfigParser(comment_prefixes='#') with pathlib.Path(DATA_DIR, 'templates.ini').open() as conffile: config.read_file(conffile) def template_from(section_name, *args, **kwargs): return template.Template(config[section_name]['template'], *args, **kwargs) def assert_easy_render(tmpl, entity, amount, currency, expect_date, expect_amt): rendered = tmpl.render(entity, decimal.Decimal(amount), currency, DATE) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "{} {}".format(expect_date, entity), " Accrued:Accounts Receivable " + expect_amt, " Income:Donations " + expect_amt.replace(amount, "-" + amount), ] assert not tmpl.is_empty() def test_easy_template(): tmpl = template_from('Simplest') assert_easy_render(tmpl, 'JJ', '5.99', 'CAD', '2015/03/14', '5.99 CAD') def test_date_formatting(): tmpl = template_from('Simplest', date_fmt='%Y-%m-%d') assert_easy_render(tmpl, 'KK', '6.99', 'CAD', '2015-03-14', '6.99 CAD') def test_currency_formatting(): tmpl = template_from('Simplest', signed_currencies=['USD']) assert_easy_render(tmpl, 'CC', '7.99', 'USD', '2015/03/14', '$7.99') def test_empty_template(): tmpl = template.Template("\n \n") assert tmpl.render('BB', decimal.Decimal('8.99'), 'USD', DATE) == '' assert tmpl.is_empty() def test_complex_template(): template_vars = { 'entity': 'T-T', 'program': 'Spectrum Defense', 'txid': 'ABCDEF', } tmpl = template_from('Complex', date_fmt='%Y-%m-%d', signed_currencies=['USD']) rendered = tmpl.render('TT', decimal.Decimal('125.50'), 'USD', DATE, **template_vars) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2015-03-14 TT", " ;Tag: Value", " ;TransactionID: ABCDEF", " Accrued:Accounts Receivable $125.50", " ;Entity: Supplier", " Income:Donations:Spectrum Defense $-119.85", " ;Program: Spectrum Defense", " ;Entity: T-T", " Income:Donations:General $-5.65", " ;Entity: T-T", ] def test_balancing(): tmpl = template_from('FiftyFifty') rendered = tmpl.render('FF', decimal.Decimal('1.01'), 'USD', DATE) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2015/03/14 FF", " Accrued:Accounts Receivable 1.01 USD", " Income:Donations -0.50 USD", " Income:Sales -0.51 USD", ] def test_multivalue(): tmpl = template_from('Multivalue') rendered = tmpl.render('DD', decimal.Decimal('150.00'), 'USD', DATE, tax=decimal.Decimal('12.50')) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2015/03/14 DD", " Expenses:Taxes 12.50 USD", " ;TaxAuthority: IRS", " Accrued:Accounts Receivable 137.50 USD", " Income:RBI -15.00 USD", " Income:Donations -135.00 USD", ] def test_zeroed_account_skipped(): tmpl = template_from('Multivalue') rendered = tmpl.render('GG', decimal.Decimal('110.00'), 'USD', DATE, tax=decimal.Decimal(0)) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2015/03/14 GG", " Accrued:Accounts Receivable 110.00 USD", " Income:RBI -11.00 USD", " Income:Donations -99.00 USD", ] def test_custom_payee_line(): tmpl = template_from('Custom Payee') rendered = tmpl.render('ZZ', decimal.Decimal('10.00'), 'USD', DATE, custom_date=datetime.date(2014, 2, 13)) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2014/02/13 ZZ - Custom", " Accrued:Accounts Receivable 10.00 USD", " Income:Donations -10.00 USD", ] def test_line1_not_custom_payee(): tmpl = template_from('Simplest') rendered = tmpl.render('VV', decimal.Decimal('15.00'), 'USD', DATE, custom_date=datetime.date(2014, 2, 12)) lines = [normalize_whitespace(s) for s in rendered.splitlines()] assert lines == [ "", "2015/03/14 VV", " Accrued:Accounts Receivable 15.00 USD", " Income:Donations -15.00 USD", ] @pytest.mark.parametrize('amount_expr', [ '', 'name', '-', '()', '+()', '{}', '{{}}', '{()}', '{name', 'name}', '{42}', '(5).real', '{amount.real}', '{amount.is_nan()}', '{Decimal}', '{FOO}', ]) def test_bad_amount_expression(amount_expr): with pytest.raises(errors.UserInputError): template.Template(" Income " + amount_expr)