From 8867cf47ae61596f5cc7e69e771ede278bef5676 Mon Sep 17 00:00:00 2001 From: Ray <12386291+librarianmage@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:36:02 -0500 Subject: [PATCH] fix: ensure independence of metadata / linking config (#27) * fix: ensure independence of metadata / linking config * rfct: Address linting issues --- .../zerosum/test_zerosum.py | 83 +++++++++++++++++++ beancount_reds_plugins/zerosum/zerosum.py | 29 ++++--- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/beancount_reds_plugins/zerosum/test_zerosum.py b/beancount_reds_plugins/zerosum/test_zerosum.py index 8b11750..b06a3eb 100644 --- a/beancount_reds_plugins/zerosum/test_zerosum.py +++ b/beancount_reds_plugins/zerosum/test_zerosum.py @@ -395,3 +395,86 @@ def test_link_prefix_successfully_changed(self, entries, _, options_map): any(link.startswith("ZSM") for link in (matched["Pay stub"].links & matched["401k statement"].links))) self.assertFalse( any(link.startswith("ZSM") for link in (matched["Bank account"].links & matched["401k statement"].links))) + + @loader.load_doc() + def test_metadata_independent_from_linking(self, entries, _, options_map): + """ + 2023-01-01 open Income:Salary + 2023-01-01 open Assets:Bank:Checkings + 2023-01-01 open Assets:Zero-Sum-Accounts:Checkings + 2023-01-01 open Assets:Brokerage:401k + 2023-01-01 open Assets:Zero-Sum-Accounts:401k + + 2024-02-15 * "Pay stub" + Income:Salary -1100.06 USD + Assets:Zero-Sum-Accounts:Checkings 999.47 USD + Assets:Zero-Sum-Accounts:401k 100.59 USD + + 2024-02-16 * "Bank account" + Assets:Bank:Checkings 999.47 USD + Assets:Zero-Sum-Accounts:Checkings + + 2024-02-16 * "401k statement" + Assets:Brokerage:401k 100.59 USD + Assets:Zero-Sum-Accounts:401k + """ + new_entries, _ = zerosum.zerosum( + entries, options_map, + config[:-2] + """'match_metadata': True,\n'match_metadata_name': 'MATCH',\n + 'link_transactions': False,\n'link_prefix': 'ZSM'\n}""") + + matched = dict( + [(m.narration, m) for m in + get_entries_with_acc_regexp(new_entries, ':ZSA-Matched')]) + + self.assertEqual(3, len(matched)) + self.assertEqual(matched["Pay stub"].postings[1].meta['MATCH'], + matched["Bank account"].postings[1].meta['MATCH']) + self.assertEqual(matched["Pay stub"].postings[2].meta['MATCH'], + matched["401k statement"].postings[1].meta['MATCH']) + + for _, m in matched.items(): + self.assertFalse(any(link.startswith("ZSM") for link in m.links)) + + @loader.load_doc() + def test_linking_independent_from_metadata(self, entries, _, options_map): + """ + 2023-01-01 open Income:Salary + 2023-01-01 open Assets:Bank:Checkings + 2023-01-01 open Assets:Zero-Sum-Accounts:Checkings + 2023-01-01 open Assets:Brokerage:401k + 2023-01-01 open Assets:Zero-Sum-Accounts:401k + + 2024-02-15 * "Pay stub" + Income:Salary -1100.06 USD + Assets:Zero-Sum-Accounts:Checkings 999.47 USD + Assets:Zero-Sum-Accounts:401k 100.59 USD + + 2024-02-16 * "Bank account" + Assets:Bank:Checkings 999.47 USD + Assets:Zero-Sum-Accounts:Checkings + + 2024-02-16 * "401k statement" + Assets:Brokerage:401k 100.59 USD + Assets:Zero-Sum-Accounts:401k + """ + new_entries, _ = zerosum.zerosum( + entries, options_map, + config[:-2] + """'match_metadata': False,\n'match_metadata_name': 'MATCH',\n + 'link_transactions': True,\n'link_prefix': 'ZSM'\n}""") + + matched = dict( + [(m.narration, m) for m in + get_entries_with_acc_regexp(new_entries, ':ZSA-Matched')]) + + self.assertEqual(3, len(matched)) + for _, m in matched.items(): + for p in m.postings: + self.assertTrue('MATCH' not in p.meta) + + self.assertTrue( + any(link.startswith("ZSM") for link in (matched["Pay stub"].links & matched["Bank account"].links))) + self.assertTrue( + any(link.startswith("ZSM") for link in (matched["Pay stub"].links & matched["401k statement"].links))) + self.assertFalse( + any(link.startswith("ZSM") for link in (matched["Bank account"].links & matched["401k statement"].links))) diff --git a/beancount_reds_plugins/zerosum/zerosum.py b/beancount_reds_plugins/zerosum/zerosum.py index b96f3d0..edd1874 100644 --- a/beancount_reds_plugins/zerosum/zerosum.py +++ b/beancount_reds_plugins/zerosum/zerosum.py @@ -183,18 +183,21 @@ # replace the account on a given posting with a new account -def account_replace(txn, posting, new_account, match_id, matching_id_string): +def account_replace(txn, posting, new_account): # create a new posting with the new account, then remove old and add new # from parent transaction - if match_id: + new_posting = posting._replace(account=new_account) + txn.postings.remove(posting) + txn.postings.append(new_posting) + + +def metadata_update(txn, posting, match_id, matching_id_string): + if match_id and matching_id_string: if posting.meta: # Will overwrite an existing match (shouldn't exist) posting.meta.update({matching_id_string: match_id}) else: posting.meta = {matching_id_string: match_id} - new_posting = posting._replace(account=new_account) - txn.postings.remove(posting) - txn.postings.append(new_posting) def transaction_update(txn, match_id, link_prefix): @@ -274,9 +277,9 @@ def generate_match_id(): (account_name_from, account_name_to) = config_obj.pop('account_name_replace', ('', '')) tolerance = config_obj.pop('tolerance', DEFAULT_TOLERANCE) match_metadata = config_obj.pop('match_metadata', False) - match_metadata_name = config_obj.pop('match_metadata_name', MATCHING_ID_STRING if match_metadata else "") + match_metadata_name = config_obj.pop('match_metadata_name', MATCHING_ID_STRING) link_transactions = config_obj.pop('link_transactions', False) - link_prefix = config_obj.pop('link_prefix', LINK_PREFIX if link_transactions else "") + link_prefix = config_obj.pop('link_prefix', LINK_PREFIX) new_accounts = set() zerosum_postings_count = 0 @@ -315,11 +318,15 @@ def generate_match_id(): # print('Match:', txn.date, match[1].date, match[1].date - txn.date, # posting.units, posting.meta['lineno'], match[0].meta['lineno']) match_count += 1 + + account_replace(txn, posting, target_account) + account_replace(match[1], match[0], target_account) + match_id = generate_match_id() if match_metadata or link_transactions else None - account_replace(txn, posting, target_account, - match_id, match_metadata_name) - account_replace(match[1], match[0], target_account, - match_id, match_metadata_name) + + if match_metadata: + metadata_update(txn, posting, match_id, match_metadata_name) + metadata_update(match[1], match[0], match_id, match_metadata_name) if link_transactions: transaction_update(txn, match_id, link_prefix)