Files @ b41a7a99af17
Branch filter:

Location: NPO-Accounting/import2ledger/tests/test_templates.py

Brett Smith
hooks: Filter entries by setting entry_data['_hook_cancel'] to True.

The current method only works for plain dicts and other simple mappings.
Mapping that may still contain items after .clear(), like ChainMap, can't
rely on the old method.
import collections
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 template_vars(payee, amount, currency='USD', date=DATE, other_vars=None):
    call_vars = {
        'amount': decimal.Decimal(amount),
        'currency': currency,
        'date': date,
        'payee': payee,
    }
    if other_vars is None:
        return call_vars
    else:
        return collections.ChainMap(call_vars, other_vars)

def assert_easy_render(tmpl, entity, amount, currency, expect_date, expect_amt):
    rendered = tmpl.render(template_vars(entity, amount, currency))
    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(template_vars('BB', '8.99')) == ''
    assert tmpl.is_empty()

def test_complex_template():
    render_vars = template_vars('TT', '125.50', other_vars={
        'entity': 'T-T',
        'program': 'Spectrum Defense',
        'txid': 'ABCDEF',
    })
    tmpl = template_from('Complex', date_fmt='%Y-%m-%d', signed_currencies=['USD'])
    rendered = tmpl.render(render_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(template_vars('FF', '1.01'))
    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():
    render_vars = template_vars('DD', '150.00', other_vars={
        'tax': decimal.Decimal('12.50'),
    })
    tmpl = template_from('Multivalue')
    rendered = tmpl.render(render_vars)
    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():
    render_vars = template_vars('GG', '110.00', other_vars={
        'tax': decimal.Decimal(0),
    })
    tmpl = template_from('Multivalue')
    rendered = tmpl.render(render_vars)
    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():
    render_vars = template_vars('ZZ', '10.00', other_vars={
        'custom_date': datetime.date(2014, 2, 13),
    })
    tmpl = template_from('Custom Payee')
    rendered = tmpl.render(render_vars)
    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():
    render_vars = template_vars('VV', '15.00', other_vars={
        'custom_date': datetime.date(2014, 2, 12),
    })
    tmpl = template_from('Simplest')
    rendered = tmpl.render(render_vars)
    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)