From a008a09477a2fd788e7ecc06702eae2fa16fb043 2020-05-30 12:30:07 From: Brett Smith Date: 2020-05-30 12:30:07 Subject: [PATCH] filters: Add remove_opening_balance_txn. --- diff --git a/conservancy_beancount/filters.py b/conservancy_beancount/filters.py index 5bfba62c9b738fdcb766c532dbf334e73363d117..107f9f470e803197806733e145ec76e02c7a2b73 100644 --- a/conservancy_beancount/filters.py +++ b/conservancy_beancount/filters.py @@ -16,17 +16,24 @@ import re +from beancount.core import data as bc_data + from . import data from . import rtutil from typing import ( + cast, Iterable, + Optional, Pattern, Union, ) from .beancount_types import ( + Directive, + Entries, MetaKey, MetaValue, + Transaction, ) Postings = Iterable[data.Posting] @@ -56,3 +63,29 @@ def filter_for_rt_id(postings: Postings, ticket_id: Union[int, str]) -> Postings """ regexp = rtutil.RT.metadata_regexp(ticket_id, first_link_only=True) return filter_meta_match(postings, 'rt-id', regexp) + +def remove_opening_balance_txn(entries: Entries) -> Optional[Transaction]: + """Remove an opening balance transaction from entries returned by Beancount + + Returns the removed transaction if found, or None if not. + Note that it modifies the ``entries`` argument in-place. + + This function is useful for tools like accrual-report that are more + focused on finding and reporting related transactions than providing + total account balances, etc. Since the opening balance transaction does not + provide the same metadata documentation as typical transactions, it's + typically easiest to filter it out before cross-referencing transactions by + metadata. + + Note that this function only removes a single transaction, because that's + fastest for the common case. + """ + for index, entry in enumerate(entries): + if isinstance(entry, bc_data.Transaction): + entry = cast(Transaction, entry) + if data.is_opening_balance_txn(entry): + break + else: + return None + del entries[index] + return entry diff --git a/tests/test_filters.py b/tests/test_filters.py index 24b02c4edd7d461f94adb95bbc4f4736f1c68240..9ba4bc4ddf538b5080972676d7a3996a7f8e8f40 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -134,3 +134,24 @@ def test_filter_for_rt_id_uses_first_link_only(): postings = data.Posting.from_entries(entries) actual = filters.filter_for_rt_id(postings, 350) check_filter(actual, entries, ()), + +@pytest.mark.parametrize('opening_txn', [ + testutil.OpeningBalance(), + None, +]) +def test_remove_opening_balance_txn(opening_txn): + entries = [ + testutil.Transaction(postings=[ + (account, amount), + ('Assets:Checking', -amount), + ]) + for account, amount in [ + ('Income:Donations', -50), + ('Expenses:Other', 75), + ]] + if opening_txn is not None: + entries.insert(1, opening_txn) + actual = filters.remove_opening_balance_txn(entries) + assert actual is opening_txn + assert len(entries) == 2 + assert opening_txn not in entries