Files @ d2f8772e08fb
Branch filter:

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

Brett Smith
config: Add open_output_file method.

Make this functionality accessible to hooks.
34b71baaf7af
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
a1f815c60b03
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
34b71baaf7af
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
5c73c40bccfe
34b71baaf7af
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
0b665d388e7c
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
0b665d388e7c
0b665d388e7c
34b71baaf7af
0b665d388e7c
0b665d388e7c
5c73c40bccfe
34b71baaf7af
5c73c40bccfe
093834dd15db
5c73c40bccfe
34b71baaf7af
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
52f2bdcd0eb3
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
093834dd15db
093834dd15db
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
52f2bdcd0eb3
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
5c73c40bccfe
3b821cbbee95
3b821cbbee95
34b71baaf7af
34b71baaf7af
34b71baaf7af
52f2bdcd0eb3
3b821cbbee95
3b821cbbee95
3b821cbbee95
3b821cbbee95
e3ec03cf1933
3b821cbbee95
3b821cbbee95
3b821cbbee95
3b821cbbee95
a1f815c60b03
e3ec03cf1933
34b71baaf7af
34b71baaf7af
34b71baaf7af
52f2bdcd0eb3
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
e3ec03cf1933
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
f6599a3debc2
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
d9f68e981798
474a0390e366
34b71baaf7af
34b71baaf7af
34b71baaf7af
52f2bdcd0eb3
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
34b71baaf7af
34b71baaf7af
34b71baaf7af
52f2bdcd0eb3
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
474a0390e366
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
a1f815c60b03
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 render_lines(render_vars, section_name, *args, **kwargs):
    tmpl = template_from(section_name, *args, **kwargs)
    rendered = tmpl.render(render_vars)
    return [normalize_whitespace(s) for s in rendered.splitlines()]

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',
    })
    lines = render_lines(
        render_vars, 'Complex',
        date_fmt='%Y-%m-%d',
        signed_currencies=['USD'],
    )
    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():
    lines = render_lines(template_vars('FF', '1.01'), 'FiftyFifty')
    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'),
    })
    lines = render_lines(render_vars, 'Multivalue')
    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),
    })
    lines = render_lines(render_vars, 'Multivalue')
    assert lines == [
        "",
        "2015/03/14 GG",
        "  Accrued:Accounts Receivable  110.00 USD",
        "  Income:RBI  -11.00 USD",
        "  Income:Donations  -99.00 USD",
    ]

def test_zeroed_account_last():
    render_vars = template_vars('JJ', '90.00', other_vars={
        'item_sales': decimal.Decimal(0),
    })
    lines = render_lines(render_vars, 'Multisplit')
    assert lines == [
        "",
        "2015/03/14 JJ",
        "  Assets:Cash  90.00 USD",
        "  Income:Sales  -90.00 USD",
        "  ; :NonItem:",
    ]

def test_multiple_postings_same_account():
    render_vars = template_vars('LL', '80.00', other_vars={
        'item_sales': decimal.Decimal(30),
    })
    lines = render_lines(render_vars, 'Multisplit')
    assert lines == [
        "",
        "2015/03/14 LL",
        "  Assets:Cash  80.00 USD",
        "  Income:Sales  -50.00 USD",
        "  ; :NonItem:",
        "  Income:Sales  -30.00 USD",
        "  ; :Item:",
    ]

def test_custom_payee_line():
    render_vars = template_vars('ZZ', '10.00', other_vars={
        'custom_date': datetime.date(2014, 2, 13),
    })
    lines = render_lines(render_vars, 'Custom Payee')
    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),
    })
    lines = render_lines(render_vars, 'Simplest')
    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)