From 6165f7743891387a3e8dc52dde41879437071b94 Mon Sep 17 00:00:00 2001 From: op3 Date: Mon, 20 Jan 2025 20:27:39 +0100 Subject: [PATCH] Fix(split_income): Convert positions to weight before subtracting taxes (#8) Previously, the code assumed that income and taxes used the same currency. If the postings had an additional associated cost or price, this was not considered. Now, to obtain the correct amount, the beancount.core.convert.get_weight function is used. It provides the amount that will need to be balanced from a posting of a transaction. Appropriate tests are included. Fixes #7 --- fava_plugins/split_income.py | 6 ++- tests/test_split_income.py | 74 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/fava_plugins/split_income.py b/fava_plugins/split_income.py index 2a892d1..d215367 100644 --- a/fava_plugins/split_income.py +++ b/fava_plugins/split_income.py @@ -54,6 +54,7 @@ import copy import re +from beancount.core import convert from beancount.core import data from beancount.core import getters from beancount.core.inventory import Inventory @@ -112,14 +113,15 @@ def split_income(entries, options_map, config_str): income = collections.defaultdict(Inventory) taxes = collections.defaultdict(Decimal) for posting in list(entry.postings): + weight = convert.get_weight(posting) if posting.account.startswith(config["income"]): new_entry.postings.append(posting) entry.postings.remove(posting) - income[posting.account].add_amount(posting.units) + income[posting.account].add_amount(weight) elif re.match(config["taxes"], posting.account): new_entry.postings.append(posting) entry.postings.remove(posting) - taxes[posting.units.currency] += posting.units.number + taxes[weight.currency] += weight.number for account, inv in income.items(): net_account = account.replace( diff --git a/tests/test_split_income.py b/tests/test_split_income.py index 1db9865..93d98cf 100644 --- a/tests/test_split_income.py +++ b/tests/test_split_income.py @@ -99,3 +99,77 @@ def test_split_income_config(load_doc): assert len(entries) == 9 assert len([e for e in entries if isinstance(e, data.Open)]) == 7 + + +def test_split_income_mixed_currency_income(load_doc): + """ + plugin "fava_plugins.split_income" "" + plugin "beancount.plugins.auto_accounts" + + 2025-01-01 * "Employer" "Income" + Income:Work -10 USD @@ 9 EUR + Expenses:Taxes 1 EUR + Assets:Account 8 EUR + """ + + entries, errors, __ = load_doc + + entries_after, _, __ = load_string( + """ + 2025-01-01 * "Employer" "Income" + Assets:Account 8 EUR + Income:Net:Work -8 EUR + + 2025-01-01 * "Employer" "Income" #pretax + Income:Work -10 USD @ 0.9 EUR + Expenses:Taxes 1 EUR + Income:Net:Work 8 EUR + """, + dedent=True, + ) + + assert not errors + assert "pretax" in entries[5].tags + + _compare_postings(entries[5], entries_after[1]) + _compare_postings(entries[4], entries_after[0]) + + assert len(entries) == 6 + assert len([e for e in entries if isinstance(e, data.Open)]) == 4 + + +def test_split_income_mixed_currency_others(load_doc): + """ + plugin "fava_plugins.split_income" "" + plugin "beancount.plugins.auto_accounts" + + 2025-01-01 * "Employer" "Income" + Income:Work -11 USD + Expenses:Taxes 1 EUR @ 1.1 USD + Assets:Account 9 EUR @ 1.1 USD + """ + + entries, errors, __ = load_doc + + entries_after, _, __ = load_string( + """ + 2025-01-01 * "Employer" "Income" + Assets:Account 9 EUR @ 1.1 USD + Income:Net:Work -9.9 USD + + 2025-01-01 * "Employer" "Income" #pretax + Income:Work -11 USD + Expenses:Taxes 1 EUR @ 1.1 USD + Income:Net:Work 9.9 USD + """, + dedent=True, + ) + + assert not errors + assert "pretax" in entries[5].tags + + _compare_postings(entries[5], entries_after[1]) + _compare_postings(entries[4], entries_after[0]) + + assert len(entries) == 6 + assert len([e for e in entries if isinstance(e, data.Open)]) == 4