From bdf8d83b04d27d006581063ad936602ed011bc34 Mon Sep 17 00:00:00 2001 From: daabel Date: Thu, 8 Feb 2024 09:10:55 +0100 Subject: [PATCH] implemented logic to account for multiple or no accounts in requisition --- ynabapiimport/accountfetcher.py | 43 +++++++++++++++++++++++++++++ ynabapiimport/gocardlessclient.py | 12 ++++++-- ynabapiimport/models/exceptions.py | 8 ++++++ ynabapiimport/requisitionfetcher.py | 21 -------------- ynabapiimport/ynabapiimport.py | 4 +-- 5 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 ynabapiimport/accountfetcher.py delete mode 100644 ynabapiimport/requisitionfetcher.py diff --git a/ynabapiimport/accountfetcher.py b/ynabapiimport/accountfetcher.py new file mode 100644 index 0000000..6643bf9 --- /dev/null +++ b/ynabapiimport/accountfetcher.py @@ -0,0 +1,43 @@ +from typing import List + +from nordigen import NordigenClient +from requests import HTTPError + +from ynabapiimport.models.exceptions import NoActiveAccountError, MultipleActiveAccountsError, NoRequisitionError + + +class AccountFetcher: + + def __init__(self, client: NordigenClient, reference: str): + self._client = client + self._reference = reference + + @staticmethod + def fetch_by_resource_id(resource_id: str, active_accounts: List[dict]) -> str: + try: + return next(a['id'] for a in active_accounts if a['resourceId'] == resource_id) + except StopIteration: + raise NoActiveAccountError(f"No active account with resource_id. Available accounts: {active_accounts}") + + def fetch(self, resource_id: str = None) -> str: + req = self.fetch_requisition() + + account_dicts = [{**self._client.account_api(id=a).get_details()['account'], + **{'id': a}} for a in req['accounts']] + active_accounts = [a for a in account_dicts if a['status'] == 'enabled'] + + if len(active_accounts) == 0: + raise NoActiveAccountError('No active accounts available in active requisition') + if resource_id: + return self.fetch_by_resource_id(resource_id=resource_id, active_accounts=active_accounts) + if len(active_accounts) > 1: + raise MultipleActiveAccountsError(f"There are multiple active accounts available in active requisition. " + f"Please provide resourceId in import_transaction() call. Available accounts: {active_accounts}") + return next(a['id'] for a in active_accounts) + + def fetch_requisition(self) -> dict: + try: + results = self._client.requisition.get_requisitions()['results'] + return next(r for r in results if r['status'] == 'LN' and r['reference'].split('::')[0] == self._reference) + except (HTTPError, StopIteration): + raise NoRequisitionError() diff --git a/ynabapiimport/gocardlessclient.py b/ynabapiimport/gocardlessclient.py index 1341819..f3da14b 100644 --- a/ynabapiimport/gocardlessclient.py +++ b/ynabapiimport/gocardlessclient.py @@ -3,8 +3,8 @@ from nordigen import NordigenClient +from ynabapiimport.accountfetcher import AccountFetcher from ynabapiimport.models.transaction import Transaction -from ynabapiimport.requisitionfetcher import RequisitionFetcher class GocardlessClient: @@ -12,8 +12,14 @@ def __init__(self, secret_id: str, secret_key: str): self._client = NordigenClient(secret_key=secret_key, secret_id=secret_id) self._client.generate_token() - def fetch_transactions(self, reference: str) -> List[Transaction]: - account_id = RequisitionFetcher(client=self._client).fetch(reference=reference) + def fetch_transactions(self, reference: str, resource_id: str) -> List[Transaction]: + af = AccountFetcher(client=self._client, reference=reference) + + if resource_id: + account_id = af.fetch(resource_id=resource_id) + else: + account_id = af.fetch() + account = self._client.account_api(id=account_id) transaction_dicts = account.get_transactions()['transactions']['booked'] transactions = [Transaction.from_dict(t) for t in transaction_dicts] diff --git a/ynabapiimport/models/exceptions.py b/ynabapiimport/models/exceptions.py index e1e837a..9c6e920 100644 --- a/ynabapiimport/models/exceptions.py +++ b/ynabapiimport/models/exceptions.py @@ -2,3 +2,11 @@ class NoRequisitionError(Exception): pass + + +class NoActiveAccountError(Exception): + pass + + +class MultipleActiveAccountsError(Exception): + pass diff --git a/ynabapiimport/requisitionfetcher.py b/ynabapiimport/requisitionfetcher.py deleted file mode 100644 index 198a2f0..0000000 --- a/ynabapiimport/requisitionfetcher.py +++ /dev/null @@ -1,21 +0,0 @@ -from nordigen import NordigenClient -from requests import HTTPError - -from ynabapiimport.models.exceptions import NoRequisitionError - - -class RequisitionFetcher: - - def __init__(self, client: NordigenClient) -> None: - self._client = client - - def fetch(self, reference: str): - results = self._client.requisition.get_requisitions()['results'] - try: - req = next(r for r in results if r['status'] == 'LN' and r['reference'].split('::')[0] == reference) - account_dicts = [{**self._client.account_api(id=a).get_details()['account'], - **{'id': a}} for a in req['accounts']] - return next(a['id'] for a in account_dicts - if a['status'] == 'enabled') - except (HTTPError, StopIteration): - raise NoRequisitionError() diff --git a/ynabapiimport/ynabapiimport.py b/ynabapiimport/ynabapiimport.py index 4e21736..a8a9b74 100644 --- a/ynabapiimport/ynabapiimport.py +++ b/ynabapiimport/ynabapiimport.py @@ -22,8 +22,8 @@ def from_yaml(cls, path: str): secret_key=config_dict['secret_key'], token=config_dict['token']) - def import_transactions(self, reference: str, budget_id: str, account_id: str): - transactions = self._gocardless_client.fetch_transactions(reference=reference) + def import_transactions(self, reference: str, budget_id: str, account_id: str, resource_id: str = None): + transactions = self._gocardless_client.fetch_transactions(reference=reference, resource_id=resource_id) i = self._ynab_client.insert(transactions, account_id=account_id, budget_id=budget_id) print(f"inserted {i} transactions for {reference} into account {account_id}")