diff --git a/beancount_reds_plugins/zerosum/test_zerosum.py b/beancount_reds_plugins/zerosum/test_zerosum.py index fc0b1d5..68aae34 100644 --- a/beancount_reds_plugins/zerosum/test_zerosum.py +++ b/beancount_reds_plugins/zerosum/test_zerosum.py @@ -8,10 +8,11 @@ from beancount import loader config = """{ - 'zerosum_accounts' : { + 'zerosum_accounts' : { 'Assets:Zero-Sum-Accounts:Returns-and-Temporary' : ('', 90), }, - 'account_name_replace' : ('Zero-Sum-Accounts', 'ZSA-Matched') + 'account_name_replace' : ('Zero-Sum-Accounts', 'ZSA-Matched'), + 'tolerance' : 0.0098, }""" def get_entries_with_acc_regexp(entries, regexp): @@ -65,19 +66,19 @@ def test_empty_config(self, entries, _, options_map): @loader.load_doc() def test_single_rename(self, entries, _, options_map): """ - 2015-01-01 open Liabilities:Credit-Cards:Vsia + 2015-01-01 open Liabilities:Credit-Cards:Green 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary 2015-06-15 * "Expensive furniture" - Liabilities:Credit-Cards:Vsia -2526.02 USD + Liabilities:Credit-Cards:Green -2526.02 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 1263.01 USD - + 2015-06-23 * "Expensive furniture Refund" - Liabilities:Credit-Cards:Vsia 1263.01 USD + Liabilities:Credit-Cards:Green 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary - + 2015-06-23 * "Expensive furniture Refund" - Liabilities:Credit-Cards:Vsia 1263.01 USD + Liabilities:Credit-Cards:Green 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary """ new_entries, _ = zerosum.zerosum(entries, options_map, config) @@ -91,16 +92,16 @@ def test_single_rename(self, entries, _, options_map): self.assertEqual('Assets:ZSA-Matched:Returns-and-Temporary', matched[m].postings[p].account) @loader.load_doc() - def test_above_epsilon(self, entries, _, options_map): + def test_above_tolerance(self, entries, _, options_map): """ - 2015-01-01 open Liabilities:Credit-Cards:Vsia + 2015-01-01 open Liabilities:Credit-Cards:Green 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary 2015-06-15 * "Trinket" - Liabilities:Credit-Cards:Vsia -0.014 USD + Liabilities:Credit-Cards:Green -0.014 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary - + 2015-06-23 * "Trinket refund" - Liabilities:Credit-Cards:Vsia 0.014 USD + Liabilities:Credit-Cards:Green 0.014 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary """ new_entries, _ = zerosum.zerosum(entries, options_map, config) @@ -114,16 +115,16 @@ def test_above_epsilon(self, entries, _, options_map): self.assertEqual('Assets:ZSA-Matched:Returns-and-Temporary', matched[m].postings[p].account) @loader.load_doc() - def test_below_epsilon(self, entries, _, options_map): + def test_below_tolerance(self, entries, _, options_map): """ - 2015-01-01 open Liabilities:Credit-Cards:Vsia + 2015-01-01 open Liabilities:Credit-Cards:Green 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary 2015-06-15 * "Trinket" - Liabilities:Credit-Cards:Vsia -0.004 USD + Liabilities:Credit-Cards:Green -0.004 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary - + 2015-06-23 * "Trinket refund" - Liabilities:Credit-Cards:Vsia 0.004 USD + Liabilities:Credit-Cards:Green 0.004 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary """ new_entries, _ = zerosum.zerosum(entries, options_map, config) @@ -139,7 +140,7 @@ def test_below_epsilon(self, entries, _, options_map): @loader.load_doc() def test_lookalike(self, entries, _, options_map): """ - 2015-01-01 open Liabilities:Credit-Cards:Vsia + 2015-01-01 open Liabilities:Credit-Cards:Green 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary 2020-06-01 * "Match two lookalike postings in one txn" ; should not error @@ -159,7 +160,7 @@ def test_lookalike(self, entries, _, options_map): @loader.load_doc() def test_both_postings_in_one_txn(self, entries, _, options_map): """ - 2015-01-01 open Liabilities:Credit-Cards:Vsia + 2015-01-01 open Liabilities:Credit-Cards:Green 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary 2020-01-01 * "Match both postings in one txn" @@ -176,4 +177,38 @@ def test_both_postings_in_one_txn(self, entries, _, options_map): for (m, p) in ref: self.assertEqual('Assets:ZSA-Matched:Returns-and-Temporary', matched[m].postings[p].account) + @loader.load_doc() + def test_two_matched_below_tolerance(self, entries, _, options_map): + """ + 2015-01-01 open Liabilities:Credit-Cards:Green + 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary + + 2021-01-01 * "(two unmatched postings summing under tolerance)" + Assets:Zero-Sum-Accounts:Returns-and-Temporary -0.001 USD + Assets:Zero-Sum-Accounts:Returns-and-Temporary -0.002 USD + Liabilities:Credit-Cards:Green + """ + new_entries, _ = zerosum.zerosum(entries, options_map, config) + + matched_txns = get_entries_with_acc_regexp(new_entries, ':ZSA-Matched') + self.assertEqual(1, len(matched_txns)) + matched_postings = sum(map( + lambda posting: bool(re.search(':ZSA-Matched', posting.account)), + matched_txns[0].postings)) + self.assertEqual(2, matched_postings) + + @loader.load_doc() + def test_two_unmatched_above_tolerance(self, entries, _, options_map): + """ + 2015-01-01 open Liabilities:Credit-Cards:Green + 2015-01-01 open Assets:Zero-Sum-Accounts:Returns-and-Temporary + + 2021-01-01 * "(two unmatched postings summing under tolerance)" + Assets:Zero-Sum-Accounts:Returns-and-Temporary -0.00494 USD + Assets:Zero-Sum-Accounts:Returns-and-Temporary -0.00496 USD + Liabilities:Credit-Cards:Green + """ + new_entries, _ = zerosum.zerosum(entries, options_map, config) + matched_txns = get_entries_with_acc_regexp(new_entries, ':ZSA-Matched') + self.assertEqual(0, len(matched_txns)) diff --git a/beancount_reds_plugins/zerosum/zerosum.py b/beancount_reds_plugins/zerosum/zerosum.py index cf4d842..0ed6ede 100644 --- a/beancount_reds_plugins/zerosum/zerosum.py +++ b/beancount_reds_plugins/zerosum/zerosum.py @@ -173,6 +173,7 @@ from beancount.core import getters DEBUG = 0 +DEFAULT_TOLERANCE = 0.0099 __plugins__ = ('zerosum', 'flag_unmatched',) @@ -202,6 +203,8 @@ def zerosum(entries, options_map, config): - 'account_name_replace': tuple of two entries. See above + - 'tolerance': the maximum cost difference between two matching postings + - 'flag_unmatched': bool to control whether to flag unmatched transactions as warnings (default off) @@ -224,7 +227,7 @@ def find_match(): if p is posting: # Don't match with the same exact posting. continue - if (abs(p.units.number + posting.units.number) < EPSILON_DELTA + if (abs(p.units.number + posting.units.number) < tolerance and p.account == zs_account): return (p, t) return None @@ -237,11 +240,11 @@ def find_match(): config_obj = literal_eval(config) #TODO: error check zs_accounts_list = config_obj.pop('zerosum_accounts', {}) (account_name_from, account_name_to) = config_obj.pop('account_name_replace', ('', '')) + tolerance = config_obj.pop('tolerance', DEFAULT_TOLERANCE) new_accounts = set() zerosum_postings_count = 0 match_count = 0 - EPSILON_DELTA = 0.0099 # Build zerosum_txns_all for all zs_accounts, so we iterate through entries only once (for performance) zerosum_txns_all = defaultdict(list) diff --git a/beancount_reds_plugins/zerosum/zs_test.beancount b/beancount_reds_plugins/zerosum/zs_test.beancount index ee2a245..3122f29 100644 --- a/beancount_reds_plugins/zerosum/zs_test.beancount +++ b/beancount_reds_plugins/zerosum/zs_test.beancount @@ -3,7 +3,7 @@ option "operating_currency" "USD" plugin "beancount.plugins.auto_accounts" plugin "beancount_reds_plugins.zerosum.zerosum" "{ - 'zerosum_accounts' : { + 'zerosum_accounts' : { 'Assets:Zero-Sum-Accounts:Returns-and-Temporary' : ('', 90), }, 'account_name_replace' : ('Zero-Sum-Accounts', 'ZSA-Matched') @@ -11,16 +11,16 @@ plugin "beancount_reds_plugins.zerosum.zerosum" "{ 2015-06-15 * "Expensive furniture" - Liabilities:Credit-Cards:Vsia -2526.02 USD + Liabilities:Credit-Cards:Green -2526.02 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 1263.01 USD 2015-06-23 * "Expensive furniture Refund" - Liabilities:Credit-Cards:Vsia 1263.01 USD + Liabilities:Credit-Cards:Green 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 2015-06-23 * "Expensive furniture Refund" - Liabilities:Credit-Cards:Vsia 1263.01 USD + Liabilities:Credit-Cards:Green 1263.01 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary 2020-01-01 * "Match both postings in one txn" @@ -32,5 +32,5 @@ plugin "beancount_reds_plugins.zerosum.zerosum" "{ Assets:Zero-Sum-Accounts:Returns-and-Temporary 0.00 USD 2021-01-01 * "Unmatched" ; should not error - Liabilities:Credit-Cards:Vsia -0.00495 USD + Liabilities:Credit-Cards:Green -0.00495 USD Assets:Zero-Sum-Accounts:Returns-and-Temporary