From 928b84df28b3b9e245b017ba81cbd916ff47e967 Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Sat, 27 Jul 2024 15:36:46 +0400 Subject: [PATCH 1/5] [Revolut] Import the fees from the CSV This work uses https://github.com/tarioch/beancounttools/pull/105 by Dr-Nuke --- .../importers/revolut/importer.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/tariochbctools/importers/revolut/importer.py b/src/tariochbctools/importers/revolut/importer.py index 3dad225..2a07a19 100644 --- a/src/tariochbctools/importers/revolut/importer.py +++ b/src/tariochbctools/importers/revolut/importer.py @@ -13,10 +13,11 @@ class Importer(identifier.IdentifyMixin, importer.ImporterProtocol): """An importer for Revolut CSV files.""" - def __init__(self, regexps, account, currency): + def __init__(self, regexps, account, currency, fee=None): identifier.IdentifyMixin.__init__(self, matchers=[("filename", regexps)]) self.account = account self.currency = currency + self._fee = fee def name(self): return super().name() + self.account @@ -53,10 +54,25 @@ def extract(self, file, existing_entries): amt = amount.Amount(amount_raw, row["Currency"]) balance = amount.Amount(bal, self.currency) book_date = parse(row["Completed Date"].strip()).date() + fee_amt_raw = D(row["Fee"].replace("'", "").strip()) + fee = amount.Amount(-fee_amt_raw, row["Currency"]) except Exception as e: logging.warning(e) continue + postings = [ + data.Posting(self.account, amt, None, None, None, None), + ] + if self._fee is not None and self.is_non_zero(fee_amt_raw): + postings.extend( + [ + data.Posting(self.account, fee, None, None, None, None), + data.Posting( + self._fee["account"], -fee, None, None, None, None + ), + ] + ) + entry = data.Transaction( data.new_metadata(file.name, 0, {}), book_date, @@ -65,9 +81,7 @@ def extract(self, file, existing_entries): row["Description"].strip(), data.EMPTY_SET, data.EMPTY_SET, - [ - data.Posting(self.account, amt, None, None, None, None), - ], + postings, ) entries.append(entry) @@ -87,3 +101,7 @@ def extract(self, file, existing_entries): pass return entries + + @staticmethod + def _is_non_zero(raw): + return abs(float(raw) - 0.00) > 1e-9 From ec38f57bb4413b1548296013b359d561697733b6 Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Sat, 27 Jul 2024 15:37:57 +0400 Subject: [PATCH 2/5] [Revolut] Better comparison with ZERO --- src/tariochbctools/importers/revolut/importer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tariochbctools/importers/revolut/importer.py b/src/tariochbctools/importers/revolut/importer.py index 2a07a19..b12e363 100644 --- a/src/tariochbctools/importers/revolut/importer.py +++ b/src/tariochbctools/importers/revolut/importer.py @@ -4,7 +4,7 @@ from io import StringIO from beancount.core import amount, data -from beancount.core.number import D +from beancount.core.number import ZERO, D from beancount.ingest import importer from beancount.ingest.importers.mixins import identifier from dateutil.parser import parse @@ -103,5 +103,5 @@ def extract(self, file, existing_entries): return entries @staticmethod - def _is_non_zero(raw): - return abs(float(raw) - 0.00) > 1e-9 + def is_non_zero(raw): + return raw != ZERO From cf4cf9730054873377942429c4c9ef585201f148 Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Thu, 1 Aug 2024 23:02:36 +0400 Subject: [PATCH 3/5] [Revolut] Capture the fee from the CSV into a new transaction --- .../importers/revolut/importer.py | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/tariochbctools/importers/revolut/importer.py b/src/tariochbctools/importers/revolut/importer.py index b12e363..dee9d5d 100644 --- a/src/tariochbctools/importers/revolut/importer.py +++ b/src/tariochbctools/importers/revolut/importer.py @@ -60,48 +60,49 @@ def extract(self, file, existing_entries): logging.warning(e) continue + is_fee_mode = self._fee is not None + if is_fee_mode and fee_amt_raw == ZERO: + continue + postings = [ data.Posting(self.account, amt, None, None, None, None), ] - if self._fee is not None and self.is_non_zero(fee_amt_raw): - postings.extend( - [ - data.Posting(self.account, fee, None, None, None, None), - data.Posting( - self._fee["account"], -fee, None, None, None, None - ), - ] - ) + description = row["Description"].strip() + if is_fee_mode: + postings = [data.Posting(self.account, fee, None, None, None, None), + data.Posting( + self._fee["account"], -fee, None, None, None, None + )] + description = f"Fees for {description}" + + assert isinstance(description, str), "Actual type of description is " + str(type(description)) entry = data.Transaction( data.new_metadata(file.name, 0, {}), book_date, "*", "", - row["Description"].strip(), + description, data.EMPTY_SET, data.EMPTY_SET, postings, ) entries.append(entry) - - # only add balance after the last (newest) transaction - try: - book_date = book_date + timedelta(days=1) - entry = data.Balance( - data.new_metadata(file.name, 0, {}), - book_date, - self.account, - balance, - None, - None, - ) - entries.append(entry) - except NameError: - pass + + if not is_fee_mode: + # only add balance after the last (newest) transaction + try: + book_date = book_date + timedelta(days=1) + entry = data.Balance( + data.new_metadata(file.name, 0, {}), + book_date, + self.account, + balance, + None, + None, + ) + entries.append(entry) + except NameError: + pass return entries - - @staticmethod - def is_non_zero(raw): - return raw != ZERO From c758eae89c9e4ac5474d0ad72a52874bb2d2bb23 Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Thu, 1 Aug 2024 23:04:57 +0400 Subject: [PATCH 4/5] Update importer.py --- src/tariochbctools/importers/revolut/importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tariochbctools/importers/revolut/importer.py b/src/tariochbctools/importers/revolut/importer.py index dee9d5d..4437450 100644 --- a/src/tariochbctools/importers/revolut/importer.py +++ b/src/tariochbctools/importers/revolut/importer.py @@ -47,6 +47,7 @@ def extract(self, file, existing_entries): skipinitialspace=True, ) next(reader) + is_fee_mode = self._fee is not None for row in reader: try: bal = D(row["Balance"].replace("'", "").strip()) @@ -60,7 +61,6 @@ def extract(self, file, existing_entries): logging.warning(e) continue - is_fee_mode = self._fee is not None if is_fee_mode and fee_amt_raw == ZERO: continue From 76e9737a2598bb5fb01c365fb71f000d66db4b1e Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Mon, 5 Aug 2024 09:48:04 +0400 Subject: [PATCH 5/5] Fixes from pre-commit --- src/tariochbctools/importers/revolut/importer.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/tariochbctools/importers/revolut/importer.py b/src/tariochbctools/importers/revolut/importer.py index 4437450..a9bf965 100644 --- a/src/tariochbctools/importers/revolut/importer.py +++ b/src/tariochbctools/importers/revolut/importer.py @@ -69,13 +69,17 @@ def extract(self, file, existing_entries): ] description = row["Description"].strip() if is_fee_mode: - postings = [data.Posting(self.account, fee, None, None, None, None), - data.Posting( - self._fee["account"], -fee, None, None, None, None - )] + postings = [ + data.Posting(self.account, fee, None, None, None, None), + data.Posting( + self._fee["account"], -fee, None, None, None, None + ), + ] description = f"Fees for {description}" - assert isinstance(description, str), "Actual type of description is " + str(type(description)) + assert isinstance( + description, str + ), "Actual type of description is " + str(type(description)) entry = data.Transaction( data.new_metadata(file.name, 0, {}), @@ -88,7 +92,7 @@ def extract(self, file, existing_entries): postings, ) entries.append(entry) - + if not is_fee_mode: # only add balance after the last (newest) transaction try: