From fd4ac7535f1f6f5fbe82df4b9778aee020ed14f9 Mon Sep 17 00:00:00 2001 From: dnbasta Date: Fri, 26 Apr 2024 07:54:16 +0200 Subject: [PATCH 1/4] added transfer_transaction_id to transaction model --- tests/conftest.py | 3 ++- tests/test_transaction.py | 8 +++++--- ynabtransactionadjuster/models/transaction.py | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 64f590d..9cd34f6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -40,7 +40,8 @@ def mock_original_transaction(request): import_payee_name_original='ipno', transaction_date=date(2024, 1, 1), approved=False, - cleared='uncleared') + cleared='uncleared', + transfer_transaction_id=None) @pytest.fixture diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 0f0c371..a73c167 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -9,8 +9,8 @@ def mock_transaction_dict(): return dict(id='id', amount=1000, date='2024-01-01', category_name='category', category_id='categoryid', payee_name='payee', payee_id='payeeid', flag_color=None, memo=None, subtransactions=[], - import_payee_name_original=None, import_payee_name=None, transfer_account_id=None, - approved=False, cleared='uncleared') + import_payee_name_original=None, import_payee_name=None, transfer_account_id='transfer_account_id', + approved=False, cleared='uncleared', transfer_transaction_id='transfer_transaction_id') def test_from_dict(mock_transaction_dict): @@ -19,13 +19,15 @@ def test_from_dict(mock_transaction_dict): assert o.amount == mock_transaction_dict['amount'] assert o.transaction_date == datetime.strptime(mock_transaction_dict['date'], '%Y-%m-%d').date() assert o.category == Category(id=mock_transaction_dict['category_id'], name=mock_transaction_dict['category_name']) - assert o.payee == Payee(id=mock_transaction_dict['payee_id'], name=mock_transaction_dict['payee_name']) + assert o.payee == Payee(id=mock_transaction_dict['payee_id'], name=mock_transaction_dict['payee_name'], + transfer_account_id=mock_transaction_dict['transfer_account_id']) assert o.flag_color == mock_transaction_dict['flag_color'] assert o.memo == mock_transaction_dict['memo'] assert o.import_payee_name_original == mock_transaction_dict['import_payee_name_original'] assert o.import_payee_name == mock_transaction_dict['import_payee_name'] assert o.approved == mock_transaction_dict['approved'] assert o.cleared == mock_transaction_dict['cleared'] + assert o.transfer_transaction_id == mock_transaction_dict['transfer_transaction_id'] assert not o.subtransactions diff --git a/ynabtransactionadjuster/models/transaction.py b/ynabtransactionadjuster/models/transaction.py index 3074c28..12abc83 100644 --- a/ynabtransactionadjuster/models/transaction.py +++ b/ynabtransactionadjuster/models/transaction.py @@ -22,6 +22,7 @@ class Transaction: :ivar import_payee_name_original: The original payee or memo as recorded by the bank :ivar approved: approval status of the original transaction :ivar cleared: clearance state of the original transaction + :ivar transfer_transaction_id: id of the originating transaction if transaction is transfer """ id: str transaction_date: date @@ -35,6 +36,7 @@ class Transaction: subtransactions: Tuple[SubTransaction, ...] cleared: Literal['uncleared', 'cleared', 'reconciled'] approved: bool + transfer_transaction_id: Optional[str] @classmethod def from_dict(cls, t_dict: dict) -> 'Transaction': @@ -64,7 +66,8 @@ def build_subtransaction(s_dict: dict) -> SubTransaction: subtransactions=tuple([build_subtransaction(st) for st in t_dict['subtransactions']]), amount=t_dict['amount'], approved=t_dict['approved'], - cleared=t_dict['cleared']) + cleared=t_dict['cleared'], + transfer_transaction_id=t_dict['transfer_transaction_id']) def as_dict(self) -> dict: return asdict(self) From bf8f1231c4e05339edcd76fb781ea2c43f42930d Mon Sep 17 00:00:00 2001 From: dnbasta Date: Fri, 26 Apr 2024 07:55:05 +0200 Subject: [PATCH 2/4] made check_signatures() method private --- ynabtransactionadjuster/adjuster.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ynabtransactionadjuster/adjuster.py b/ynabtransactionadjuster/adjuster.py index 60e75dc..732be27 100644 --- a/ynabtransactionadjuster/adjuster.py +++ b/ynabtransactionadjuster/adjuster.py @@ -75,7 +75,7 @@ def dry_run(self, pretty_print: bool = False) -> List[ModifiedTransaction]: :raises AdjustError: if there is any error during the adjust process :raises HTTPError: if there is any error with the YNAB API (e.g. wrong credentials) """ - self.check_signatures() + self._check_signatures() filtered_transactions = self.filter(self.transactions) s = Serializer(transactions=filtered_transactions, adjust_func=self.adjust, categories=self.categories) modified_transactions = s.run() @@ -92,7 +92,7 @@ def run(self) -> int: :raises AdjustError: if there is any error during the adjust process :raises HTTPError: if there is any error with the YNAB API (e.g. wrong credentials) """ - self.check_signatures() + self._check_signatures() filtered_transactions = self.filter(self.transactions) s = Serializer(transactions=filtered_transactions, adjust_func=self.adjust, categories=self.categories) modified_transactions = s.run() @@ -102,6 +102,6 @@ def run(self) -> int: return updated return 0 - def check_signatures(self): + def _check_signatures(self): SignatureChecker(func=self.filter, parent_func=Adjuster.filter).check() SignatureChecker(func=self.adjust, parent_func=Adjuster.adjust).check() From 269bb7e09be53d0ad5b98af5f3c3aab2c2259743 Mon Sep 17 00:00:00 2001 From: dnbasta Date: Fri, 26 Apr 2024 08:07:17 +0200 Subject: [PATCH 3/4] moved_mock_transaction_dict to conftest --- tests/conftest.py | 8 ++++++++ tests/test_transaction.py | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9cd34f6..512480e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -49,3 +49,11 @@ def mock_category_repo(): return CategoryRepo(categories=[ CategoryGroup(name='group1', categories=frozenset([Category(id='cid1', name='c_name')])), CategoryGroup(name='group2', categories=frozenset([Category(id='cid2', name='c_name')]))]) + + +@pytest.fixture +def mock_transaction_dict(): + return dict(id='id', amount=1000, date='2024-01-01', category_name='category', category_id='categoryid', + payee_name='payee', payee_id='payeeid', flag_color=None, memo=None, subtransactions=[], + import_payee_name_original=None, import_payee_name=None, transfer_account_id='transfer_account_id', + approved=False, cleared='uncleared', transfer_transaction_id='transfer_transaction_id') diff --git a/tests/test_transaction.py b/tests/test_transaction.py index a73c167..b393fdb 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -5,14 +5,6 @@ from ynabtransactionadjuster.models import Transaction, Category, Payee -@pytest.fixture -def mock_transaction_dict(): - return dict(id='id', amount=1000, date='2024-01-01', category_name='category', category_id='categoryid', - payee_name='payee', payee_id='payeeid', flag_color=None, memo=None, subtransactions=[], - import_payee_name_original=None, import_payee_name=None, transfer_account_id='transfer_account_id', - approved=False, cleared='uncleared', transfer_transaction_id='transfer_transaction_id') - - def test_from_dict(mock_transaction_dict): o = Transaction.from_dict(mock_transaction_dict) assert o.id == mock_transaction_dict['id'] From 1c906e89da6320b960e73cd0e6de708092c92cce Mon Sep 17 00:00:00 2001 From: dnbasta Date: Fri, 26 Apr 2024 09:30:07 +0200 Subject: [PATCH 4/4] added fetch_transaction method to adjuster --- tests/test_client.py | 14 ++++++++++++++ ynabtransactionadjuster/adjuster.py | 8 ++++++++ ynabtransactionadjuster/client.py | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 256f769..16179bd 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -2,6 +2,7 @@ from requests import Response +from ynabtransactionadjuster import Transaction from ynabtransactionadjuster.client import Client from ynabtransactionadjuster.models.payee import Payee @@ -46,3 +47,16 @@ def test_fetch_payees(mock_get): assert p_list[0].name == 'p_name' assert p_list[0].id == 'p_id' assert p_list[0].transfer_account_id == 't_id' + + +@patch('ynabtransactionadjuster.client.requests.get') +def test_fetch_transaction(mock_get, mock_transaction_dict): + # Arrange + resp = MagicMock(spec=Response) + resp.json.return_value = {'data': {'transaction': mock_transaction_dict}} + mock_get.return_value = resp + client = Client(token=MagicMock(), budget=MagicMock(), account=MagicMock()) + + # Act + t = client.fetch_transaction('transaction_id') + assert isinstance(t, Transaction) diff --git a/ynabtransactionadjuster/adjuster.py b/ynabtransactionadjuster/adjuster.py index 732be27..6f550ab 100644 --- a/ynabtransactionadjuster/adjuster.py +++ b/ynabtransactionadjuster/adjuster.py @@ -105,3 +105,11 @@ def run(self) -> int: def _check_signatures(self): SignatureChecker(func=self.filter, parent_func=Adjuster.filter).check() SignatureChecker(func=self.adjust, parent_func=Adjuster.adjust).check() + + def fetch_transaction(self, transaction_id: str) -> Transaction: + """Fetches an individual transaction from the YNAB account + + :param transaction_id: Transaction ID of the transaction to be fetched + """ + client = Client.from_credentials(credentials=self.credentials) + return client.fetch_transaction(transaction_id=transaction_id) diff --git a/ynabtransactionadjuster/client.py b/ynabtransactionadjuster/client.py index 32d98a7..5a7ea55 100644 --- a/ynabtransactionadjuster/client.py +++ b/ynabtransactionadjuster/client.py @@ -56,6 +56,11 @@ def fetch_transactions(self) -> List[Transaction]: transactions = [Transaction.from_dict(t) for t in transaction_dicts] return transactions + def fetch_transaction(self, transaction_id: str) -> Transaction: + r = requests.get(f'{YNAB_BASE_URL}/budgets/{self._budget}/transactions/{transaction_id}', headers=self._header) + r.raise_for_status() + return Transaction.from_dict(r.json()['data']['transaction']) + def update_transactions(self, transactions: List[ModifiedTransaction]) -> int: """Updates transactions in YNAB. The updates are done in bulk.