diff --git a/README.rst b/README.rst index 9c6c7c2192e559e98de57f7d4034789e8cf868d0..e8da0c28a18a892197466c249d1415f7631c12d2 100644 --- a/README.rst +++ b/README.rst @@ -42,7 +42,7 @@ Every setting in your configuration file has to be in a section. ``[DEFAULT]`` The first line of the template is a Ledger tag. The program will leave all kinds of tags and Ledger comments alone, except to indent them nicely. -The next two lines split the money across accounts. They follow almost the same format as they do in Ledger: there's an account named, followed by a tab or two or more spaces, and then an expression. Each time import2ledger generates an entry, it will evaluate this expression using the data it imported to calculate the actual currency amount to write. Your expression can use numbers, basic arithmetic operators (including parentheses for grouping), and imported data referred to as ``{variable_name}``. +The next two lines split the money across accounts. They follow almost the same format as they do in Ledger: there's an account named, followed by a tab or two or more spaces, and then an expression. Each time import2ledger generates an entry, it will evaluate this expression using the data it imported to calculate the actual currency amount to write. Your expression can use numbers, basic arithmetic operators (including parentheses for grouping), conditional expressions in the format ``TRUE_EXPR if CONDITION else FALSE_EXPR``, and imported data referred to as ``{variable_name}``. import2ledger uses decimal math to calculate each amount, and rounds to the number of digits appropriate for that currency. If the amount of currency being imported doesn't split evenly, spare change will be allocated to the last split to keep the entry balanced on both sides. diff --git a/import2ledger/hooks/ledger_entry.py b/import2ledger/hooks/ledger_entry.py index 63984f1fd230d7c708669a18ad30af97c2ccbac4..d5a6d97cdab024cb5c409c05912bd7c676f55d4c 100644 --- a/import2ledger/hooks/ledger_entry.py +++ b/import2ledger/hooks/ledger_entry.py @@ -54,7 +54,21 @@ class TokenTransformer: class AmountTokenTransformer(TokenTransformer): - SUPPORTED_OPS = frozenset('+-*/()') + SUPPORTED_NAMES = frozenset(['if', 'else']) + SUPPORTED_OPS = frozenset([ + '(', + ')', + '+', + '-', + '*', + '/', + '==', + '!=', + '<', + '<=', + '>', + '>=', + ]) def __iter__(self): tokens = super().__iter__() @@ -66,6 +80,12 @@ class AmountTokenTransformer(TokenTransformer): raise ValueError("no amount in expression") yield from tokens + def transform_NAME(self, ttype, tvalue): + if tvalue in self.SUPPORTED_NAMES: + yield from self._noop_transformer(ttype, tvalue) + else: + raise ValueError("unsupported bare word {!r}".format(tvalue)) + def transform_NUMBER(self, ttype, tvalue): yield (tokenize.NAME, 'Decimal') yield (tokenize.OP, '(') diff --git a/tests/data/templates.ini b/tests/data/templates.ini index 640f44c977ff2fec7a66d56016bbb06e5a33469d..cf705446ddc283c644597acdee7108ece12f3c24 100644 --- a/tests/data/templates.ini +++ b/tests/data/templates.ini @@ -47,6 +47,12 @@ template = Income:Sales -{item_sales} ; :Item: +[Conditional] +template = + Assets:Cash {amount} - (6 if {amount} > 50 else 3) + Expenses:Banking Fees (6 if {amount} > 50 else 3) + Income:Sales -{amount} + [Empty] template = diff --git a/tests/test_hook_ledger_entry.py b/tests/test_hook_ledger_entry.py index feb876c05f24892e5b0338ecbd7f9068b5ef5334..fd233929f040bb7dd62b843d2ee68ed4f38b1863 100644 --- a/tests/test_hook_ledger_entry.py +++ b/tests/test_hook_ledger_entry.py @@ -182,6 +182,23 @@ def test_line1_not_custom_payee(): " Income:Donations -15.00 USD", ] +@pytest.mark.parametrize('amount,expect_fee', [ + (40, 3), + (80, 6), +]) +def test_conditional(amount, expect_fee): + expect_cash = amount - expect_fee + amount_s = '{:.02f}'.format(amount) + render_vars = template_vars('Buyer', amount_s) + lines = render_lines(render_vars, 'Conditional') + assert lines == [ + "", + "2015/03/14 Buyer", + " Assets:Cash {:.02f} USD".format(expect_cash), + " Expenses:Banking Fees {:.02f} USD".format(expect_fee), + " Income:Sales -{} USD".format(amount_s), + ] + @pytest.mark.parametrize('amount_expr', [ '', 'name',