Repository: bvanelli/actualpy
Pull request #132 (2025-07-19 13:44:35)

fix: Use correct payee name when using Simplefin.
Adds compatibility of payee name for SimpleFin transactions.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/bvanelli/actualpy/pull/132?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Brunno+Vanelli) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 97.94%. Comparing base [(`a934f6e`)](https://app.codecov.io/gh/bvanelli/actualpy/commit/a934f6eed3c5247661346fdbaf457f869aab3ec7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Brunno+Vanelli) to head [(`9ce6491`)](https://app.codecov.io/gh/bvanelli/actualpy/commit/9ce6491840b32d80c4533407ef014ca05faf9fc9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Brunno+Vanelli).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #132   +/-   ##
=======================================
  Coverage   97.94%   97.94%           
=======================================
  Files          18       18           
  Lines        2432     2436    +4     
=======================================
+ Hits         2382     2386    +4     
  Misses         50       50           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/bvanelli/actualpy/pull/132?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Brunno+Vanelli).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Brunno+Vanelli).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/actual/__init__.py b/actual/__init__.py
index 57a6fa3..2b5df6e 100644
--- a/actual/__init__.py
+++ b/actual/__init__.py
@@ -545,7 +545,7 @@ class Actual(ActualServer):
         for transaction in new_transactions[::-1]:
             if not transaction.booked:
                 continue
-            payee = transaction.payee_name or "" if sync_method == "goCardless" else transaction.notes
+            payee = transaction.payee_name or ""
             reconciled = reconcile_transaction(
                 self.session,
                 transaction.date,
diff --git a/actual/api/__init__.py b/actual/api/__init__.py
index f0a73db..821550f 100644
--- a/actual/api/__init__.py
+++ b/actual/api/__init__.py
@@ -267,6 +267,15 @@ class ActualServer:
         )
         return StatusDTO.model_validate(response.json())
 
+    def reset_password(self, new_password: str) -> StatusDTO:
+        """Resets the password for the user. You need to be logged in to reset your password, as the old
+        password does not need to be provided."""
+        response = self._requests_session.post(
+            f"{self.api_url}/{Endpoints.RESET_PASSWORD}",
+            json={"password": new_password, "token": self._token},
+        )
+        return StatusDTO.model_validate(response.json())
+
     def sync_sync(self, request: SyncRequest) -> SyncResponse:
         """Calls the sync endpoint with a request and returns the response. Both the request and response are
         protobuf models. The request and response are not standard REST, but rather protobuf binary serialized data.
diff --git a/actual/api/models.py b/actual/api/models.py
index 480de3c..e752473 100644
--- a/actual/api/models.py
+++ b/actual/api/models.py
@@ -18,6 +18,7 @@ class Endpoints(enum.Enum):
     ACCOUNT_VALIDATE = "account/validate"
     NEEDS_BOOTSTRAP = "account/needs-bootstrap"
     BOOTSTRAP = "account/bootstrap"
+    RESET_PASSWORD = "account/change-password"
     SYNC = "sync/sync"
     LIST_USER_FILES = "sync/list-user-files"
     GET_USER_FILE_INFO = "sync/get-user-file-info"


Test patch:
diff --git a/tests/test_bank_sync.py b/tests/test_bank_sync.py
index 7e9492e..2c8c65f 100644
--- a/tests/test_bank_sync.py
+++ b/tests/test_bank_sync.py
@@ -163,13 +163,13 @@ def test_full_bank_sync_go_simplefin(session, bank_sync_data_match):
         assert imported_transactions[0].financial_id == "a2c2fafe-334a-46a6-8d05-200c2e41397b"
         assert imported_transactions[0].get_date() == datetime.date(2024, 6, 13)
         assert imported_transactions[0].get_amount() == decimal.Decimal("-7.77")
-        assert imported_transactions[0].payee.name == "Payment"  # simplefin uses the wrong field
+        assert imported_transactions[0].payee.name == "Institution Gmbh (DE12 XXX 6789)"
         assert imported_transactions[0].notes == "Payment"
 
         assert imported_transactions[1].financial_id == "208584e9-343f-4831-8095-7b9f4a34a77e"
         assert imported_transactions[1].get_date() == datetime.date(2024, 6, 13)
         assert imported_transactions[1].get_amount() == decimal.Decimal("9.26")
-        assert imported_transactions[1].payee.name == "Transferring Money"  # simplefin uses the wrong field
+        assert imported_transactions[1].payee.name == "John Doe"
         assert imported_transactions[1].notes == "Transferring Money"
 
 
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 23de629..920bcd2 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -156,6 +156,21 @@ def test_redownload_file(actual_server, tmp_path):
             pass
 
 
+def test_reset_password(actual_server):
+    port = actual_server.get_exposed_port(5006)
+    with Actual(f"http://localhost:{port}", password="mypass", bootstrap=True) as actual:
+        actual.create_budget("My Budget")
+        actual.upload_budget()
+        actual.reset_password("mynewpass")
+        response = actual.list_user_files()
+        assert len(response.data) == 1
+    with Actual(f"http://localhost:{port}", password="mynewpass"):
+        assert len(actual.list_user_files().data) == 1
+        with pytest.raises(AuthorizationError):
+            # login with old password should fail
+            actual.login("mypass")
+
+
 def test_models(actual_server):
     port = actual_server.get_exposed_port(5006)
     with Actual(f"http://localhost:{port}", password="mypass", encryption_password="mypass", bootstrap=True) as actual: