From 6c54ed92e0f70c68a7e2b71946f79b8ea5a68d39 Mon Sep 17 00:00:00 2001 From: Jonathan Klabunde Tomer Date: Sat, 21 Sep 2024 11:19:24 -0700 Subject: [PATCH] Some minor improvements to the healthequity source (#225) * healthequity: ignore_before option * healthequity: skip balance assertion when multiple transactions in a day beancount only supports one balance assertion per day, so if we create intra-day balance assertions on days with multiple transactions, all but the last of them will be wrong. Skip the balance assertions if there are multiple transactions in a day instead. --- beancount_import/source/healthequity.py | 55 +++++++++++++------ .../test_basic/import_results.beancount | 5 -- .../test_invalid/import_results.beancount | 5 -- .../test_matching/import_results.beancount | 5 -- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/beancount_import/source/healthequity.py b/beancount_import/source/healthequity.py index 4afd4277..8509a102 100644 --- a/beancount_import/source/healthequity.py +++ b/beancount_import/source/healthequity.py @@ -457,9 +457,12 @@ def make_import_result(csv_entry: RawTransaction, accounts: Dict[str, Open], class Source(description_based_source.DescriptionBasedSource): - def __init__(self, directory: str, **kwargs) -> None: + def __init__(self, directory: str, + ignore_before: Optional[datetime.date] = None, + **kwargs) -> None: super().__init__(**kwargs) self.directory = directory + self.ignore_before = ignore_before self.raw_transactions = [ ] # type: List[Union[CashTransaction, FundTransaction]] self.raw_balances = [] # type: List[ImportedBalance] @@ -493,9 +496,15 @@ def convert_account(entry: RawEntry): account_to_id[full_account] = account_id return entry._replace(account=full_account) - balances = [convert_account(entry) for entry in self.raw_balances] + balances = [ + convert_account(entry) + for entry in self.raw_balances + if self.ignore_before is None or self.ignore_before <= entry.date + ] transactions = [ - convert_account(entry) for entry in self.raw_transactions + convert_account(entry) + for entry in self.raw_transactions + if self.ignore_before is None or self.ignore_before <= entry.date ] description_based_source.get_pending_and_invalid_entries( @@ -510,26 +519,36 @@ def convert_account(entry: RawEntry): results=results) balance_entries = collections.OrderedDict( - ) # type: Dict[Tuple[datetime.date, str, str], ImportResult] + ) # type: Dict[Tuple[datetime.date, str, str], Optional[ImportResult]] for entry in transactions: date = entry.date + datetime.timedelta(days=1) - balance_entries[(date, entry.account, - entry.balance.currency)] = ImportResult( - date=date, - entries=[ - Balance( - date=date, - meta=None, - account=entry.account, - amount=entry.balance, - tolerance=None, - diff_amount=None) - ], - info=get_info(entry)) + key = (date, entry.account, entry.balance.currency) + + # When multiple transactions happen on the same day, we can't trust + # the reported balance because we don't know which order to apply + # them in. Just skip it, and put a tombstone in place of the one + # that was already there. + if key in balance_entries: + balance_entries[key] = None + continue + + balance_entries[key] = ImportResult( + date=date, + entries=[ + Balance( + date=date, + meta=None, + account=entry.account, + amount=entry.balance, + tolerance=None, + diff_amount=None) + ], + info=get_info(entry)) for entry in balance_entries.values(): - results.add_pending_entry(entry) + if entry != None: + results.add_pending_entry(entry) for balance in balances: # Skip outputting recent balances --- just output prices. diff --git a/testdata/source/healthequity/test_basic/import_results.beancount b/testdata/source/healthequity/test_basic/import_results.beancount index 4a5b02df..d2b83418 100644 --- a/testdata/source/healthequity/test_basic/import_results.beancount +++ b/testdata/source/healthequity/test_basic/import_results.beancount @@ -220,11 +220,6 @@ source_desc: "Buy" Assets:HSA:HealthEquity:Cash -1936.76 USD -;; date: 2016-03-12 -;; info: {"filename": "/data/1234567/cash-transactions-other.csv", "line": 5, "type": "text/csv"} - -2016-03-12 balance Assets:HSA:HealthEquity:Cash 500.00 USD - ;; date: 2016-03-12 ;; info: {"filename": "/data/1234567/investment-transactions.csv", "line": 3, "type": "text/csv"} diff --git a/testdata/source/healthequity/test_invalid/import_results.beancount b/testdata/source/healthequity/test_invalid/import_results.beancount index b7d1e80a..0b039b6a 100644 --- a/testdata/source/healthequity/test_invalid/import_results.beancount +++ b/testdata/source/healthequity/test_invalid/import_results.beancount @@ -33,11 +33,6 @@ 2016-03-01 balance Assets:HSA:HealthEquity:Cash 836.76 USD -;; date: 2016-03-12 -;; info: {"filename": "/data/1234567/cash-transactions-other.csv", "line": 5, "type": "text/csv"} - -2016-03-12 balance Assets:HSA:HealthEquity:Cash 500.00 USD - ;; date: 2016-03-12 ;; info: {"filename": "/data/1234567/investment-transactions.csv", "line": 3, "type": "text/csv"} diff --git a/testdata/source/healthequity/test_matching/import_results.beancount b/testdata/source/healthequity/test_matching/import_results.beancount index b7d1e80a..0b039b6a 100644 --- a/testdata/source/healthequity/test_matching/import_results.beancount +++ b/testdata/source/healthequity/test_matching/import_results.beancount @@ -33,11 +33,6 @@ 2016-03-01 balance Assets:HSA:HealthEquity:Cash 836.76 USD -;; date: 2016-03-12 -;; info: {"filename": "/data/1234567/cash-transactions-other.csv", "line": 5, "type": "text/csv"} - -2016-03-12 balance Assets:HSA:HealthEquity:Cash 500.00 USD - ;; date: 2016-03-12 ;; info: {"filename": "/data/1234567/investment-transactions.csv", "line": 3, "type": "text/csv"}