From 74e3a58acefcb1b4ca4cd2bae635d085bc199f5c Mon Sep 17 00:00:00 2001 From: Robert Segal Date: Tue, 30 Dec 2025 15:33:05 -0700 Subject: [PATCH] Added missing billing statement attachments endpoints --- .../billing/credit_memo_attachments.py | 13 ++--- .../billing/custom_ledger_attachments.py | 13 ++--- .../resources/billing/invoice_attachments.py | 13 ++--- .../resources/billing/journal_attachments.py | 23 ++------ .../resources/billing/ledger_attachments.py | 13 ++--- mpt_api_client/resources/billing/mixins.py | 32 ++++++++++++ .../billing/statement_attachments.py | 39 ++++++++++++++ .../resources/billing/statements.py | 18 +++++++ .../billing/test_credit_memo_attachments.py | 4 +- .../billing/test_custom_ledger_attachments.py | 4 +- .../billing/test_invoice_attachments.py | 4 +- .../billing/test_ledger_attachments.py | 4 +- .../billing/test_statement_attachments.py | 52 +++++++++++++++++++ .../unit/resources/billing/test_statements.py | 34 +++++++++++- 14 files changed, 204 insertions(+), 62 deletions(-) create mode 100644 mpt_api_client/resources/billing/statement_attachments.py create mode 100644 tests/unit/resources/billing/test_statement_attachments.py diff --git a/mpt_api_client/resources/billing/credit_memo_attachments.py b/mpt_api_client/resources/billing/credit_memo_attachments.py index 3805efe2..ed194f90 100644 --- a/mpt_api_client/resources/billing/credit_memo_attachments.py +++ b/mpt_api_client/resources/billing/credit_memo_attachments.py @@ -1,13 +1,10 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncFilesOperationsMixin, - AsyncModifiableResourceMixin, CollectionMixin, - FilesOperationsMixin, - ModifiableResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin class CreditMemoAttachment(Model): @@ -20,11 +17,12 @@ class CreditMemoAttachmentsServiceConfig: _endpoint = "/public/v1/billing/credit-memos/{credit_memo_id}/attachments" _model_class = CreditMemoAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class CreditMemoAttachmentsService( - FilesOperationsMixin[CreditMemoAttachment], - ModifiableResourceMixin[CreditMemoAttachment], + AttachmentMixin[CreditMemoAttachment], CollectionMixin[CreditMemoAttachment], Service[CreditMemoAttachment], CreditMemoAttachmentsServiceConfig, @@ -33,8 +31,7 @@ class CreditMemoAttachmentsService( class AsyncCreditMemoAttachmentsService( - AsyncFilesOperationsMixin[CreditMemoAttachment], - AsyncModifiableResourceMixin[CreditMemoAttachment], + AsyncAttachmentMixin[CreditMemoAttachment], AsyncCollectionMixin[CreditMemoAttachment], AsyncService[CreditMemoAttachment], CreditMemoAttachmentsServiceConfig, diff --git a/mpt_api_client/resources/billing/custom_ledger_attachments.py b/mpt_api_client/resources/billing/custom_ledger_attachments.py index 45fb3918..59d42389 100644 --- a/mpt_api_client/resources/billing/custom_ledger_attachments.py +++ b/mpt_api_client/resources/billing/custom_ledger_attachments.py @@ -1,13 +1,10 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncFilesOperationsMixin, - AsyncModifiableResourceMixin, CollectionMixin, - FilesOperationsMixin, - ModifiableResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin class CustomLedgerAttachment(Model): @@ -20,11 +17,12 @@ class CustomLedgerAttachmentsServiceConfig: _endpoint = "/public/v1/billing/custom-ledgers/{custom_ledger_id}/attachments" _model_class = CustomLedgerAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class CustomLedgerAttachmentsService( - FilesOperationsMixin[CustomLedgerAttachment], - ModifiableResourceMixin[CustomLedgerAttachment], + AttachmentMixin[CustomLedgerAttachment], CollectionMixin[CustomLedgerAttachment], Service[CustomLedgerAttachment], CustomLedgerAttachmentsServiceConfig, @@ -33,8 +31,7 @@ class CustomLedgerAttachmentsService( class AsyncCustomLedgerAttachmentsService( - AsyncFilesOperationsMixin[CustomLedgerAttachment], - AsyncModifiableResourceMixin[CustomLedgerAttachment], + AsyncAttachmentMixin[CustomLedgerAttachment], AsyncCollectionMixin[CustomLedgerAttachment], AsyncService[CustomLedgerAttachment], CustomLedgerAttachmentsServiceConfig, diff --git a/mpt_api_client/resources/billing/invoice_attachments.py b/mpt_api_client/resources/billing/invoice_attachments.py index 7966f731..49650037 100644 --- a/mpt_api_client/resources/billing/invoice_attachments.py +++ b/mpt_api_client/resources/billing/invoice_attachments.py @@ -1,13 +1,10 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncFilesOperationsMixin, - AsyncModifiableResourceMixin, CollectionMixin, - FilesOperationsMixin, - ModifiableResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin class InvoiceAttachment(Model): @@ -20,11 +17,12 @@ class InvoiceAttachmentsServiceConfig: _endpoint = "/public/v1/billing/invoices/{invoice_id}/attachments" _model_class = InvoiceAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class InvoiceAttachmentsService( - FilesOperationsMixin[InvoiceAttachment], - ModifiableResourceMixin[InvoiceAttachment], + AttachmentMixin[InvoiceAttachment], CollectionMixin[InvoiceAttachment], Service[InvoiceAttachment], InvoiceAttachmentsServiceConfig, @@ -33,8 +31,7 @@ class InvoiceAttachmentsService( class AsyncInvoiceAttachmentsService( - AsyncFilesOperationsMixin[InvoiceAttachment], - AsyncModifiableResourceMixin[InvoiceAttachment], + AsyncAttachmentMixin[InvoiceAttachment], AsyncCollectionMixin[InvoiceAttachment], AsyncService[InvoiceAttachment], InvoiceAttachmentsServiceConfig, diff --git a/mpt_api_client/resources/billing/journal_attachments.py b/mpt_api_client/resources/billing/journal_attachments.py index f9ee3599..e1ebafd6 100644 --- a/mpt_api_client/resources/billing/journal_attachments.py +++ b/mpt_api_client/resources/billing/journal_attachments.py @@ -1,19 +1,10 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncCreateFileMixin, - AsyncDeleteMixin, - AsyncDownloadFileMixin, - AsyncGetMixin, - AsyncUpdateMixin, CollectionMixin, - CreateFileMixin, - DeleteMixin, - DownloadFileMixin, - GetMixin, - UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin class JournalAttachment(Model): @@ -31,11 +22,7 @@ class JournalAttachmentsServiceConfig: class JournalAttachmentsService( - CreateFileMixin[JournalAttachment], - UpdateMixin[JournalAttachment], - DownloadFileMixin[JournalAttachment], - DeleteMixin, - GetMixin[JournalAttachment], + AttachmentMixin[JournalAttachment], CollectionMixin[JournalAttachment], Service[JournalAttachment], JournalAttachmentsServiceConfig, @@ -44,11 +31,7 @@ class JournalAttachmentsService( class AsyncJournalAttachmentsService( - AsyncCreateFileMixin[JournalAttachment], - AsyncUpdateMixin[JournalAttachment], - AsyncDownloadFileMixin[JournalAttachment], - AsyncDeleteMixin, - AsyncGetMixin[JournalAttachment], + AsyncAttachmentMixin[JournalAttachment], AsyncCollectionMixin[JournalAttachment], AsyncService[JournalAttachment], JournalAttachmentsServiceConfig, diff --git a/mpt_api_client/resources/billing/ledger_attachments.py b/mpt_api_client/resources/billing/ledger_attachments.py index 836ac718..e88144c5 100644 --- a/mpt_api_client/resources/billing/ledger_attachments.py +++ b/mpt_api_client/resources/billing/ledger_attachments.py @@ -1,13 +1,10 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncFilesOperationsMixin, - AsyncModifiableResourceMixin, CollectionMixin, - FilesOperationsMixin, - ModifiableResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin class LedgerAttachment(Model): @@ -20,11 +17,12 @@ class LedgerAttachmentsServiceConfig: _endpoint = "/public/v1/billing/ledgers/{ledger_id}/attachments" _model_class = LedgerAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class LedgerAttachmentsService( - FilesOperationsMixin[LedgerAttachment], - ModifiableResourceMixin[LedgerAttachment], + AttachmentMixin[LedgerAttachment], CollectionMixin[LedgerAttachment], Service[LedgerAttachment], LedgerAttachmentsServiceConfig, @@ -33,8 +31,7 @@ class LedgerAttachmentsService( class AsyncLedgerAttachmentsService( - AsyncFilesOperationsMixin[LedgerAttachment], - AsyncModifiableResourceMixin[LedgerAttachment], + AsyncAttachmentMixin[LedgerAttachment], AsyncCollectionMixin[LedgerAttachment], AsyncService[LedgerAttachment], LedgerAttachmentsServiceConfig, diff --git a/mpt_api_client/resources/billing/mixins.py b/mpt_api_client/resources/billing/mixins.py index ee7ac75d..bda8a7c7 100644 --- a/mpt_api_client/resources/billing/mixins.py +++ b/mpt_api_client/resources/billing/mixins.py @@ -1,3 +1,15 @@ +from mpt_api_client.http.mixins import ( + AsyncCreateFileMixin, + AsyncDeleteMixin, + AsyncDownloadFileMixin, + AsyncGetMixin, + AsyncUpdateMixin, + CreateFileMixin, + DeleteMixin, + DownloadFileMixin, + GetMixin, + UpdateMixin, +) from mpt_api_client.models import ResourceData @@ -392,3 +404,23 @@ async def queue(self, resource_id: str, resource_data: ResourceData | None = Non return await self._resource_action( # type: ignore[attr-defined, no-any-return] resource_id, "POST", "queue", json=resource_data ) + + +class AttachmentMixin[Model]( + CreateFileMixin[Model], + UpdateMixin[Model], + DeleteMixin, + DownloadFileMixin[Model], + GetMixin[Model], +): + """Attachment mixin.""" + + +class AsyncAttachmentMixin[Model]( + AsyncCreateFileMixin[Model], + AsyncUpdateMixin[Model], + AsyncDeleteMixin, + AsyncDownloadFileMixin[Model], + AsyncGetMixin[Model], +): + """Async Attachment mixin.""" diff --git a/mpt_api_client/resources/billing/statement_attachments.py b/mpt_api_client/resources/billing/statement_attachments.py new file mode 100644 index 00000000..c3ab9dae --- /dev/null +++ b/mpt_api_client/resources/billing/statement_attachments.py @@ -0,0 +1,39 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCollectionMixin, + CollectionMixin, +) +from mpt_api_client.models import Model +from mpt_api_client.resources.billing.mixins import AsyncAttachmentMixin, AttachmentMixin + + +class StatementAttachment(Model): + """Statement Attachment resource.""" + + +class StatementAttachmentsServiceConfig: + """Statement Attachments service configuration.""" + + _endpoint = "/public/v1/billing/statements/{statement_id}/attachments" + _model_class = StatementAttachment + _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" + + +class StatementAttachmentsService( + AttachmentMixin[StatementAttachment], + CollectionMixin[StatementAttachment], + Service[StatementAttachment], + StatementAttachmentsServiceConfig, +): + """Statement Attachments service.""" + + +class AsyncStatementAttachmentsService( + AsyncAttachmentMixin[StatementAttachment], + AsyncCollectionMixin[StatementAttachment], + AsyncService[StatementAttachment], + StatementAttachmentsServiceConfig, +): + """Statement Attachments service.""" diff --git a/mpt_api_client/resources/billing/statements.py b/mpt_api_client/resources/billing/statements.py index 5125b5a6..b70eeb0b 100644 --- a/mpt_api_client/resources/billing/statements.py +++ b/mpt_api_client/resources/billing/statements.py @@ -9,6 +9,10 @@ ) from mpt_api_client.models import Model from mpt_api_client.resources.billing.mixins import AsyncIssuableMixin, IssuableMixin +from mpt_api_client.resources.billing.statement_attachments import ( + AsyncStatementAttachmentsService, + StatementAttachmentsService, +) from mpt_api_client.resources.billing.statement_charges import ( AsyncStatementChargesService, StatementChargesService, @@ -44,6 +48,13 @@ def charges(self, statement_id: str) -> StatementChargesService: endpoint_params={"statement_id": statement_id}, ) + def attachments(self, statement_id: str) -> StatementAttachmentsService: + """Return statement attachments service.""" + return StatementAttachmentsService( + http_client=self.http_client, + endpoint_params={"statement_id": statement_id}, + ) + class AsyncStatementsService( AsyncUpdateMixin[Statement], @@ -61,3 +72,10 @@ def charges(self, statement_id: str) -> AsyncStatementChargesService: http_client=self.http_client, endpoint_params={"statement_id": statement_id}, ) + + def attachments(self, statement_id: str) -> AsyncStatementAttachmentsService: + """Return statement attachments service.""" + return AsyncStatementAttachmentsService( + http_client=self.http_client, + endpoint_params={"statement_id": statement_id}, + ) diff --git a/tests/unit/resources/billing/test_credit_memo_attachments.py b/tests/unit/resources/billing/test_credit_memo_attachments.py index 4c83f7cc..56cc6fdd 100644 --- a/tests/unit/resources/billing/test_credit_memo_attachments.py +++ b/tests/unit/resources/billing/test_credit_memo_attachments.py @@ -38,14 +38,14 @@ def test_async_endpoint(async_credit_memo_attachments_service): assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(credit_memo_attachments_service, method: str): result = hasattr(credit_memo_attachments_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_credit_memo_attachments_service, method: str): result = hasattr(async_credit_memo_attachments_service, method) diff --git a/tests/unit/resources/billing/test_custom_ledger_attachments.py b/tests/unit/resources/billing/test_custom_ledger_attachments.py index ff30dadc..69b37ce8 100644 --- a/tests/unit/resources/billing/test_custom_ledger_attachments.py +++ b/tests/unit/resources/billing/test_custom_ledger_attachments.py @@ -36,14 +36,14 @@ def test_async_endpoint(async_custom_ledger_attachments_service): assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(custom_ledger_attachments_service, method: str): result = hasattr(custom_ledger_attachments_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_custom_ledger_attachments_service, method: str): result = hasattr(async_custom_ledger_attachments_service, method) diff --git a/tests/unit/resources/billing/test_invoice_attachments.py b/tests/unit/resources/billing/test_invoice_attachments.py index 3c0cd1f7..a43dc56f 100644 --- a/tests/unit/resources/billing/test_invoice_attachments.py +++ b/tests/unit/resources/billing/test_invoice_attachments.py @@ -37,14 +37,14 @@ def test_async_endpoint(async_invoice_attachments_service): assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(invoice_attachments_service, method: str): result = hasattr(invoice_attachments_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_invoice_attachments_service, method: str): result = hasattr(async_invoice_attachments_service, method) diff --git a/tests/unit/resources/billing/test_ledger_attachments.py b/tests/unit/resources/billing/test_ledger_attachments.py index dbc7f8c4..d4321c6d 100644 --- a/tests/unit/resources/billing/test_ledger_attachments.py +++ b/tests/unit/resources/billing/test_ledger_attachments.py @@ -37,14 +37,14 @@ def test_async_endpoint(async_ledger_attachments_service) -> None: assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(ledger_attachments_service, method: str) -> None: result = hasattr(ledger_attachments_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_ledger_attachments_service, method: str) -> None: result = hasattr(async_ledger_attachments_service, method) diff --git a/tests/unit/resources/billing/test_statement_attachments.py b/tests/unit/resources/billing/test_statement_attachments.py new file mode 100644 index 00000000..be1573df --- /dev/null +++ b/tests/unit/resources/billing/test_statement_attachments.py @@ -0,0 +1,52 @@ +import pytest + +from mpt_api_client.resources.billing.statement_attachments import ( + AsyncStatementAttachmentsService, + StatementAttachmentsService, +) + + +@pytest.fixture +def statement_attachments_service(http_client) -> StatementAttachmentsService: + return StatementAttachmentsService( + http_client=http_client, endpoint_params={"statement_id": "STM-0000-0001"} + ) + + +@pytest.fixture +def async_statement_attachments_service(async_http_client) -> AsyncStatementAttachmentsService: + return AsyncStatementAttachmentsService( + http_client=async_http_client, endpoint_params={"statement_id": "STM-0000-0001"} + ) + + +def test_endpoint(statement_attachments_service) -> None: + result = ( + statement_attachments_service.path + == "/public/v1/billing/statements/STM-0000-0001/attachments" + ) + + assert result is True + + +def test_async_endpoint(async_statement_attachments_service) -> None: + result = ( + async_statement_attachments_service.path + == "/public/v1/billing/statements/STM-0000-0001/attachments" + ) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) +def test_methods_present(statement_attachments_service, method: str) -> None: + result = hasattr(statement_attachments_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) +def test_async_methods_present(async_statement_attachments_service, method: str) -> None: + result = hasattr(async_statement_attachments_service, method) + + assert result is True diff --git a/tests/unit/resources/billing/test_statements.py b/tests/unit/resources/billing/test_statements.py index dd9ab797..cf53c8e0 100644 --- a/tests/unit/resources/billing/test_statements.py +++ b/tests/unit/resources/billing/test_statements.py @@ -1,5 +1,9 @@ import pytest +from mpt_api_client.resources.billing.statement_attachments import ( + AsyncStatementAttachmentsService, + StatementAttachmentsService, +) from mpt_api_client.resources.billing.statement_charges import ( AsyncStatementChargesService, StatementChargesService, @@ -19,7 +23,19 @@ def async_statements_service(async_http_client): @pytest.mark.parametrize( "method", - ["get", "update", "issue", "cancel", "error", "pending", "queue", "retry", "recalculate"], + [ + "get", + "update", + "issue", + "cancel", + "error", + "pending", + "queue", + "retry", + "recalculate", + "attachments", + "charges", + ], ) def test_mixins_present(statements_service, method): result = hasattr(statements_service, method) @@ -29,7 +45,19 @@ def test_mixins_present(statements_service, method): @pytest.mark.parametrize( "method", - ["get", "update", "issue", "cancel", "error", "pending", "queue", "retry", "recalculate"], + [ + "get", + "update", + "issue", + "cancel", + "error", + "pending", + "queue", + "retry", + "recalculate", + "attachments", + "charges", + ], ) def test_async_mixins_present(async_statements_service, method): result = hasattr(async_statements_service, method) @@ -41,6 +69,7 @@ def test_async_mixins_present(async_statements_service, method): ("service_method", "expected_service_class"), [ ("charges", StatementChargesService), + ("attachments", StatementAttachmentsService), ], ) def test_property_services(statements_service, service_method, expected_service_class): @@ -54,6 +83,7 @@ def test_property_services(statements_service, service_method, expected_service_ ("service_method", "expected_service_class"), [ ("charges", AsyncStatementChargesService), + ("attachments", AsyncStatementAttachmentsService), ], ) def test_async_property_services(async_statements_service, service_method, expected_service_class):