Skip to content

Conversation

@robcsegal
Copy link
Contributor

@robcsegal robcsegal commented Dec 30, 2025

Added missing billing statement attachments endpoints
Even though mixins will be re-organized, I added an attachment mixin because there's almost half a dozen of attachment resources.

https://softwareone.atlassian.net/browse/MPT-16678

Summary by CodeRabbit

  • New Features

    • Added statement attachments support with upload/download and create/list/get/delete operations (sync and async).
    • Added attachments accessors on statements to obtain per-statement attachment services.
  • Refactor

    • Replaced multiple per-operation attachment mixins with unified attachment mixins for consistent behavior across attachment services.
  • Tests

    • Expanded unit tests for statement attachments and added iterate/download checks across attachment services.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds a Statement Attachments API client and accessor on Statements; consolidates multiple attachment-related mixins into unified AttachmentMixin/AsyncAttachmentMixin; updates several attachment service classes to use the new mixins; extends and adds unit tests to validate attachment methods and endpoints.

Changes

Cohort / File(s) Summary
New Statement Attachments
mpt_api_client/resources/billing/statement_attachments.py
New module: StatementAttachment model; StatementAttachmentsServiceConfig with _endpoint = "/public/v1/billing/statements/{statement_id}/attachments", _collection_key = "data", _upload_file_key = "file", _upload_data_key = "attachment"; sync/async services StatementAttachmentsService and AsyncStatementAttachmentsService composed from Attachment/Collection/Service mixins.
Statements service updates
mpt_api_client/resources/billing/statements.py
Added attachments(self, statement_id: str) accessors to StatementsService and AsyncStatementsService returning configured StatementAttachmentsService instances with statement_id endpoint param.
Attachment mixins introduced
mpt_api_client/resources/billing/mixins.py
Added AttachmentMixin[Model] (CreateFile, Update, Delete, DownloadFile, Get) and AsyncAttachmentMixin[Model] (async equivalents) to aggregate attachment-related operations.
Services switched to AttachmentMixin
mpt_api_client/resources/billing/credit_memo_attachments.py, .../custom_ledger_attachments.py, .../invoice_attachments.py, .../journal_attachments.py, .../ledger_attachments.py
Replaced previous combinations of Files/Modifiable/operation-specific mixins with AttachmentMixin/AsyncAttachmentMixin; updated imports and added _upload_file_key = "file" and _upload_data_key = "attachment" to relevant service configs.
Tests — new and extended
tests/unit/resources/billing/test_statement_attachments.py, tests/unit/resources/billing/test_statements.py, tests/unit/resources/billing/test_credit_memo_attachments.py, tests/unit/resources/billing/test_custom_ledger_attachments.py, tests/unit/resources/billing/test_invoice_attachments.py, tests/unit/resources/billing/test_ledger_attachments.py
New unit tests for statement attachments; extended existing attachment tests to expect iterate and download methods in sync and async services; updated statements tests to include attachments service checks.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped to the code with a twitch of my nose,
New attachments arrived in neat little rows.
Sync and async I nudge with a cheer,
Files and endpoints all ready — let's steer! ✨📎

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding missing billing statement attachments endpoints, directly aligned with the primary objective of the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch MPT-16678-add-missing-billing-statements-attachments-endpoints

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9392d9e and 74e3a58.

📒 Files selected for processing (14)
  • mpt_api_client/resources/billing/credit_memo_attachments.py
  • mpt_api_client/resources/billing/custom_ledger_attachments.py
  • mpt_api_client/resources/billing/invoice_attachments.py
  • mpt_api_client/resources/billing/journal_attachments.py
  • mpt_api_client/resources/billing/ledger_attachments.py
  • mpt_api_client/resources/billing/mixins.py
  • mpt_api_client/resources/billing/statement_attachments.py
  • mpt_api_client/resources/billing/statements.py
  • tests/unit/resources/billing/test_credit_memo_attachments.py
  • tests/unit/resources/billing/test_custom_ledger_attachments.py
  • tests/unit/resources/billing/test_invoice_attachments.py
  • tests/unit/resources/billing/test_ledger_attachments.py
  • tests/unit/resources/billing/test_statement_attachments.py
  • tests/unit/resources/billing/test_statements.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/unit/resources/billing/test_custom_ledger_attachments.py
  • mpt_api_client/resources/billing/credit_memo_attachments.py
  • mpt_api_client/resources/billing/statement_attachments.py
  • tests/unit/resources/billing/test_invoice_attachments.py
  • tests/unit/resources/billing/test_statement_attachments.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-12T15:02:20.732Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 160
File: tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py:55-58
Timestamp: 2025-12-12T15:02:20.732Z
Learning: In pytest with pytest-asyncio, if a test function uses async fixtures but contains no await, declare the test function as def (synchronous) instead of async def. Pytest-asyncio will resolve the async fixtures automatically; this avoids linter complaints about unnecessary async functions. This pattern applies to any test file under the tests/ directory that uses such fixtures.

Applied to files:

  • tests/unit/resources/billing/test_ledger_attachments.py
  • tests/unit/resources/billing/test_credit_memo_attachments.py
  • tests/unit/resources/billing/test_statements.py
🧬 Code graph analysis (6)
tests/unit/resources/billing/test_ledger_attachments.py (2)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/catalog/test_products_parameters.py (1)
  • test_methods_present (34-37)
mpt_api_client/resources/billing/journal_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
tests/unit/resources/billing/test_statements.py (1)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (33-39)
  • StatementAttachmentsService (24-30)
mpt_api_client/resources/billing/invoice_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
mpt_api_client/resources/billing/mixins.py (1)
mpt_api_client/http/mixins.py (10)
  • AsyncCreateFileMixin (187-216)
  • AsyncDeleteMixin (273-283)
  • AsyncDownloadFileMixin (303-325)
  • AsyncGetMixin (380-395)
  • AsyncUpdateMixin (286-300)
  • CreateFileMixin (115-144)
  • DeleteMixin (29-38)
  • DownloadFileMixin (58-80)
  • GetMixin (361-377)
  • UpdateMixin (41-55)
mpt_api_client/resources/billing/ledger_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (15)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)

41-41: LGTM!

The extended parameterized method list correctly reflects the capabilities provided by the AttachmentMixin (get, create, update, delete, download) and CollectionMixin (iterate). Test coverage appropriately validates method presence for both sync and async services.

Also applies to: 48-48

tests/unit/resources/billing/test_statements.py (3)

3-6: LGTM!

Imports correctly added for the new StatementAttachmentsService and AsyncStatementAttachmentsService classes to support the new test cases.


26-38: LGTM!

Method lists correctly extended to include "attachments" alongside "charges", ensuring test coverage for the new attachments accessor on both sync and async statements services.

Also applies to: 48-60


68-93: LGTM!

Property service tests correctly verify both sync and async attachments services:

  • Validates correct service class instantiation (StatementAttachmentsService / AsyncStatementAttachmentsService)
  • Confirms endpoint_params contains the expected statement_id key

This mirrors the existing pattern for charges and ensures consistent behavior.

tests/unit/resources/billing/test_ledger_attachments.py (1)

40-40: LGTM!

Parameterized method lists correctly extended with "iterate" and "download" to match the capabilities provided by the new AttachmentMixin and AsyncAttachmentMixin. Consistent with updates in other attachment test files.

Also applies to: 47-47

mpt_api_client/resources/billing/journal_attachments.py (1)

7-7: LGTM!

Clean migration to the consolidated AttachmentMixin and AsyncAttachmentMixin. The service configuration correctly defines _upload_file_key and _upload_data_key as required by CreateFileMixin, and the inheritance order is appropriate for proper MRO resolution.

Also applies to: 24-39

mpt_api_client/resources/billing/mixins.py (2)

1-12: LGTM!

Imports correctly added for all required base mixins to support the new AttachmentMixin and AsyncAttachmentMixin compositions.


409-426: LGTM!

Well-designed consolidation of attachment operations into unified mixins. The composition provides:

  • create (via CreateFileMixin / AsyncCreateFileMixin)
  • update (via UpdateMixin / AsyncUpdateMixin)
  • delete (via DeleteMixin / AsyncDeleteMixin)
  • download (via DownloadFileMixin / AsyncDownloadFileMixin)
  • get (via GetMixin / AsyncGetMixin)

This reduces duplication across attachment services and provides a consistent interface. The TODO comments at lines 16-18 appropriately note future reorganization opportunities.

mpt_api_client/resources/billing/statements.py (2)

12-15: LGTM!

Imports correctly added for the new StatementAttachmentsService and AsyncStatementAttachmentsService classes.


51-56: LGTM!

The attachments() methods correctly follow the established pattern used by charges():

  • Return the appropriate sync/async service type
  • Pass http_client for request handling
  • Configure endpoint_params with statement_id matching the {statement_id} placeholder in the endpoint path

Also applies to: 76-81

mpt_api_client/resources/billing/invoice_attachments.py (1)

7-7: LGTM!

Clean migration to consolidated AttachmentMixin / AsyncAttachmentMixin. The addition of _upload_file_key and _upload_data_key configuration is consistent with other attachment services in this PR.

Also applies to: 20-21, 24-39

mpt_api_client/resources/billing/ledger_attachments.py (1)

7-7: LGTM!

Consistent with the migration pattern applied across all attachment services. The LedgerAttachmentsService and AsyncLedgerAttachmentsService now correctly use the consolidated AttachmentMixin / AsyncAttachmentMixin with properly configured upload keys.

Also applies to: 20-21, 24-39

mpt_api_client/resources/billing/custom_ledger_attachments.py (3)

24-39: LGTM: Service classes correctly updated.

Both sync and async service classes have been updated to use the new attachment mixins while maintaining the proper inheritance order and consistency between variants.


20-21: Configuration keys are correctly configured.

The _upload_file_key = "file" and _upload_data_key = "attachment" values match the pattern used by all other attachment services in the billing module (ledger, statement, journal, credit memo, and invoice attachments), confirming correct alignment with the custom ledger attachments API endpoint.


7-7: Consolidation of attachment mixins is correct.

The new AttachmentMixin and AsyncAttachmentMixin properly consolidate functionality by inheriting from CreateFileMixin, UpdateMixin, DeleteMixin, GetMixin, and DownloadFileMixin. All methods from the previous separate mixins are preserved.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Dec 30, 2025

Warnings
⚠️

Some newly added source files do not have corresponding tests in the tests/ folder with matching structure and the test_ prefix.

✅ Found Jira issue key in the title: MPT-16678

Tests mirroring check (created files only)

Added source file Expected test (added in this PR)
mpt_api_client/resources/billing/statement_attachments.py tests/resources/billing/test_statement_attachments.py

Generated by 🚫 dangerJS against 74e3a58

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/unit/resources/billing/test_statement_attachments.py (1)

23-38: Consider simplifying the assertion pattern.

The tests use an intermediate variable to store the comparison result before asserting. This can be simplified to direct assertions for better readability.

🔎 Proposed simplification
 def test_endpoint(statement_attachments_service) -> None:
-    result = (
-        statement_attachments_service.path
-        == "/public/v1/billing/statements/STM-0000-0001/attachments"
-    )
-
-    assert result is True
+    assert (
+        statement_attachments_service.path
+        == "/public/v1/billing/statements/STM-0000-0001/attachments"
+    )


 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
+    assert (
+        async_statement_attachments_service.path
+        == "/public/v1/billing/statements/STM-0000-0001/attachments"
+    )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91901a2 and 3657841.

📒 Files selected for processing (4)
  • mpt_api_client/resources/billing/statement_attachments.py
  • mpt_api_client/resources/billing/statements.py
  • tests/unit/resources/billing/test_statement_attachments.py
  • tests/unit/resources/billing/test_statements.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-12T15:02:20.732Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 160
File: tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py:55-58
Timestamp: 2025-12-12T15:02:20.732Z
Learning: In pytest with pytest-asyncio, if a test function uses async fixtures but contains no await, declare the test function as def (synchronous) instead of async def. Pytest-asyncio will resolve the async fixtures automatically; this avoids linter complaints about unnecessary async functions. This pattern applies to any test file under the tests/ directory that uses such fixtures.

Applied to files:

  • tests/unit/resources/billing/test_statement_attachments.py
  • tests/unit/resources/billing/test_statements.py
🧬 Code graph analysis (3)
mpt_api_client/resources/billing/statements.py (8)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (46-56)
  • StatementAttachmentsService (33-43)
mpt_api_client/resources/billing/journals.py (2)
  • attachments (79-84)
  • attachments (137-142)
mpt_api_client/resources/commerce/agreements.py (2)
  • attachments (43-55)
  • attachments (68-80)
mpt_api_client/resources/catalog/pricing_policies.py (2)
  • attachments (38-43)
  • attachments (80-85)
mpt_api_client/resources/billing/credit_memos.py (2)
  • attachments (41-46)
  • attachments (59-64)
mpt_api_client/resources/billing/invoices.py (2)
  • attachments (41-46)
  • attachments (59-64)
mpt_api_client/resources/billing/custom_ledgers.py (2)
  • attachments (59-64)
  • attachments (90-95)
mpt_api_client/resources/billing/ledgers.py (2)
  • attachments (49-54)
  • attachments (73-78)
tests/unit/resources/billing/test_statement_attachments.py (3)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (46-56)
  • StatementAttachmentsService (33-43)
tests/unit/conftest.py (2)
  • http_client (17-18)
  • async_http_client (22-23)
mpt_api_client/http/base_service.py (1)
  • path (28-30)
tests/unit/resources/billing/test_statements.py (2)
tests/unit/resources/billing/test_billing.py (1)
  • billing (23-24)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (46-56)
  • StatementAttachmentsService (33-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (10)
tests/unit/resources/billing/test_statement_attachments.py (2)

9-20: LGTM!

The fixtures correctly instantiate both synchronous and asynchronous statement attachments services with the required endpoint parameters.


41-52: LGTM!

The parametrized tests correctly verify the presence of all six expected methods (get, create, update, delete, iterate, download) on both synchronous and asynchronous services. This aligns with the mixins used in the service definitions.

tests/unit/resources/billing/test_statements.py (2)

3-65: LGTM!

The imports and parametrization updates correctly integrate the new statement attachments functionality into the existing test suite. The changes follow the established patterns and ensure comprehensive coverage.


68-93: LGTM!

The test cases for the attachments accessor methods correctly verify service instantiation and parameter passing for both synchronous and asynchronous variants. The implementation mirrors the existing charges tests.

mpt_api_client/resources/billing/statements.py (2)

12-15: LGTM!

The imports are correctly placed and necessary for the new attachments accessor methods.


51-81: LGTM!

The attachments accessor methods are correctly implemented for both synchronous and asynchronous services. The implementation follows the established pattern used by the charges methods and is consistent with other attachment accessors throughout the codebase (ledgers, journals, invoices, etc.).

mpt_api_client/resources/billing/statement_attachments.py (4)

1-16: LGTM!

All necessary imports are present for implementing the statement attachments service with full CRUD and file operations support.


19-20: LGTM!

The StatementAttachment model follows the standard pattern used for attachment resources throughout the codebase.


23-30: LGTM!

The service configuration is correctly defined with the appropriate endpoint pattern and standard configuration values for file upload operations.


33-56: LGTM!

Both synchronous and asynchronous service classes are correctly implemented with all necessary mixins for complete CRUD and file operations (create, read, update, delete, iterate, download). The mixin composition and inheritance order follow established patterns and match the test expectations.

@robcsegal robcsegal force-pushed the MPT-16678-add-missing-billing-statements-attachments-endpoints branch from 3657841 to 9392d9e Compare December 31, 2025 14:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mpt_api_client/resources/billing/credit_memo_attachments.py (1)

14-19: Missing upload configuration keys required by CreateFileMixin.

The CreditMemoAttachmentsServiceConfig class inherits from AttachmentMixin, which includes CreateFileMixin. The CreateFileMixin.create() method requires _upload_file_key and _upload_data_key attributes to be defined, but they are missing from this config class. This will cause an AttributeError at runtime if the create() method is called with a file parameter.

JournalAttachmentsServiceConfig and StatementAttachmentsServiceConfig both define these keys as _upload_file_key = "file" and _upload_data_key = "attachment". Add the same configuration to CreditMemoAttachmentsServiceConfig, or remove CreateFileMixin from AttachmentMixin if credit memo attachments do not support file uploads.

Note: This issue also affects InvoiceAttachmentsServiceConfig, LedgerAttachmentsServiceConfig, and CustomLedgerAttachmentsServiceConfig.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3657841 and 9392d9e.

📒 Files selected for processing (14)
  • mpt_api_client/resources/billing/credit_memo_attachments.py
  • mpt_api_client/resources/billing/custom_ledger_attachments.py
  • mpt_api_client/resources/billing/invoice_attachments.py
  • mpt_api_client/resources/billing/journal_attachments.py
  • mpt_api_client/resources/billing/ledger_attachments.py
  • mpt_api_client/resources/billing/mixins.py
  • mpt_api_client/resources/billing/statement_attachments.py
  • mpt_api_client/resources/billing/statements.py
  • tests/unit/resources/billing/test_credit_memo_attachments.py
  • tests/unit/resources/billing/test_custom_ledger_attachments.py
  • tests/unit/resources/billing/test_invoice_attachments.py
  • tests/unit/resources/billing/test_ledger_attachments.py
  • tests/unit/resources/billing/test_statement_attachments.py
  • tests/unit/resources/billing/test_statements.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-12T15:02:20.732Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 160
File: tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py:55-58
Timestamp: 2025-12-12T15:02:20.732Z
Learning: In pytest with pytest-asyncio, if a test function uses async fixtures but contains no await, declare the test function as def (synchronous) instead of async def. Pytest-asyncio will resolve the async fixtures automatically; this avoids linter complaints about unnecessary async functions. This pattern applies to any test file under the tests/ directory that uses such fixtures.

Applied to files:

  • tests/unit/resources/billing/test_statements.py
  • tests/unit/resources/billing/test_custom_ledger_attachments.py
  • tests/unit/resources/billing/test_ledger_attachments.py
  • tests/unit/resources/billing/test_credit_memo_attachments.py
  • tests/unit/resources/billing/test_invoice_attachments.py
  • tests/unit/resources/billing/test_statement_attachments.py
🧬 Code graph analysis (14)
tests/unit/resources/billing/test_statements.py (1)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (33-39)
  • StatementAttachmentsService (24-30)
tests/unit/resources/billing/test_custom_ledger_attachments.py (16)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_ledger_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/commerce/test_subscriptions.py (1)
  • test_methods_present (22-25)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/catalog/test_product_term_variants.py (1)
  • test_methods_present (47-50)
tests/unit/resources/catalog/test_product_terms.py (1)
  • test_methods_present (42-45)
tests/unit/resources/catalog/test_products_documents.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_media.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_item_groups.py (1)
  • test_methods_present (34-37)
tests/unit/resources/catalog/test_products_parameter_groups.py (1)
  • test_methods_present (39-42)
tests/unit/resources/catalog/test_products_parameters.py (1)
  • test_methods_present (34-37)
tests/unit/resources/commerce/test_agreements_attachments.py (1)
  • test_methods_present (36-39)
mpt_api_client/resources/billing/credit_memo_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
mpt_api_client/resources/billing/custom_ledger_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
mpt_api_client/resources/billing/journal_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
mpt_api_client/resources/billing/ledger_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
mpt_api_client/resources/billing/statements.py (4)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (33-39)
  • StatementAttachmentsService (24-30)
mpt_api_client/resources/billing/journals.py (2)
  • attachments (79-84)
  • attachments (137-142)
mpt_api_client/resources/billing/invoices.py (2)
  • attachments (41-46)
  • attachments (59-64)
mpt_api_client/resources/billing/ledgers.py (2)
  • attachments (49-54)
  • attachments (73-78)
tests/unit/resources/billing/test_ledger_attachments.py (16)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_custom_ledger_attachments.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/commerce/test_subscriptions.py (1)
  • test_methods_present (22-25)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/catalog/test_product_term_variants.py (1)
  • test_methods_present (47-50)
tests/unit/resources/catalog/test_product_terms.py (1)
  • test_methods_present (42-45)
tests/unit/resources/catalog/test_products_documents.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_media.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_item_groups.py (1)
  • test_methods_present (34-37)
tests/unit/resources/catalog/test_products_parameter_groups.py (1)
  • test_methods_present (39-42)
tests/unit/resources/catalog/test_products_parameters.py (1)
  • test_methods_present (34-37)
tests/unit/resources/commerce/test_agreements_attachments.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_credit_memo_attachments.py (16)
tests/unit/resources/billing/test_custom_ledger_attachments.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_ledger_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/commerce/test_subscriptions.py (1)
  • test_methods_present (22-25)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/catalog/test_product_term_variants.py (1)
  • test_methods_present (47-50)
tests/unit/resources/catalog/test_product_terms.py (1)
  • test_methods_present (42-45)
tests/unit/resources/catalog/test_products_documents.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_media.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_item_groups.py (1)
  • test_methods_present (34-37)
tests/unit/resources/catalog/test_products_parameter_groups.py (1)
  • test_methods_present (39-42)
tests/unit/resources/catalog/test_products_parameters.py (1)
  • test_methods_present (34-37)
tests/unit/resources/commerce/test_agreements_attachments.py (1)
  • test_methods_present (36-39)
mpt_api_client/resources/billing/mixins.py (2)
mpt_api_client/http/mixins.py (10)
  • AsyncCreateFileMixin (187-216)
  • AsyncDeleteMixin (273-283)
  • AsyncDownloadFileMixin (303-325)
  • AsyncGetMixin (380-395)
  • AsyncUpdateMixin (286-300)
  • CreateFileMixin (115-144)
  • DeleteMixin (29-38)
  • DownloadFileMixin (58-80)
  • GetMixin (361-377)
  • UpdateMixin (41-55)
mpt_api_client/models/model.py (1)
  • Model (65-125)
mpt_api_client/resources/billing/invoice_attachments.py (1)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
tests/unit/resources/billing/test_invoice_attachments.py (16)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_custom_ledger_attachments.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_ledger_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/commerce/test_subscriptions.py (1)
  • test_methods_present (22-25)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/catalog/test_product_term_variants.py (1)
  • test_methods_present (47-50)
tests/unit/resources/catalog/test_product_terms.py (1)
  • test_methods_present (42-45)
tests/unit/resources/catalog/test_products_documents.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_media.py (1)
  • test_methods_present (37-40)
tests/unit/resources/catalog/test_products_item_groups.py (1)
  • test_methods_present (34-37)
tests/unit/resources/catalog/test_products_parameter_groups.py (1)
  • test_methods_present (39-42)
tests/unit/resources/catalog/test_products_parameters.py (1)
  • test_methods_present (34-37)
tests/unit/resources/commerce/test_agreements_attachments.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_statement_attachments.py (3)
mpt_api_client/resources/billing/statement_attachments.py (2)
  • AsyncStatementAttachmentsService (33-39)
  • StatementAttachmentsService (24-30)
tests/unit/conftest.py (2)
  • http_client (17-18)
  • async_http_client (22-23)
mpt_api_client/http/base_service.py (1)
  • path (28-30)
mpt_api_client/resources/billing/statement_attachments.py (5)
mpt_api_client/http/async_service.py (1)
  • AsyncService (12-80)
mpt_api_client/http/service.py (1)
  • Service (12-80)
mpt_api_client/http/mixins.py (2)
  • AsyncCollectionMixin (578-645)
  • CollectionMixin (509-575)
mpt_api_client/models/model.py (1)
  • Model (65-125)
mpt_api_client/resources/billing/mixins.py (2)
  • AsyncAttachmentMixin (419-426)
  • AttachmentMixin (409-416)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (21)
tests/unit/resources/billing/test_invoice_attachments.py (1)

40-51: LGTM!

The expanded parametrization correctly tests for the newly added iterate and download methods from the AttachmentMixin. The test structure is consistent with other attachment test files across the codebase.

tests/unit/resources/billing/test_custom_ledger_attachments.py (1)

39-50: LGTM!

The test coverage correctly extends to include iterate and download methods, aligning with the AttachmentMixin capabilities now used by the custom ledger attachments service.

tests/unit/resources/billing/test_credit_memo_attachments.py (1)

41-52: LGTM!

Test parametrization correctly extended to verify iterate and download methods for both sync and async credit memo attachments services.

tests/unit/resources/billing/test_statement_attachments.py (1)

1-52: LGTM!

Well-structured new test file for statement attachments. The test coverage includes:

  • Endpoint path verification for both sync and async services
  • Method presence checks for all attachment operations (get, create, update, delete, iterate, download)

The file follows the established testing patterns from other attachment service tests.

tests/unit/resources/billing/test_statements.py (3)

3-6: LGTM!

Correctly imports the new StatementAttachmentsService and AsyncStatementAttachmentsService classes to support the new test cases.


26-38: LGTM!

The parametrized method lists are properly extended to include attachments and charges, verifying the statement services expose these accessor methods.

Also applies to: 48-60


72-93: LGTM!

The property service tests correctly verify that:

  • attachments() returns the appropriate StatementAttachmentsService / AsyncStatementAttachmentsService
  • The endpoint_params are correctly set with statement_id
tests/unit/resources/billing/test_ledger_attachments.py (1)

40-51: LGTM!

Test parametrization correctly extended to verify iterate and download methods for ledger attachments services, consistent with the new AttachmentMixin capabilities.

mpt_api_client/resources/billing/mixins.py (2)

1-12: LGTM!

Clean import additions for the mixin components that will be composed into the new AttachmentMixin and AsyncAttachmentMixin classes.


409-426: LGTM!

The new AttachmentMixin and AsyncAttachmentMixin classes effectively consolidate attachment-related operations:

  • CreateFileMixin / AsyncCreateFileMixin for file uploads
  • UpdateMixin / AsyncUpdateMixin for metadata updates
  • DeleteMixin / AsyncDeleteMixin for removal
  • DownloadFileMixin / AsyncDownloadFileMixin for file downloads
  • GetMixin / AsyncGetMixin for retrieval

This reduces duplication across multiple attachment service implementations.

mpt_api_client/resources/billing/invoice_attachments.py (2)

7-7: LGTM!

Correctly imports the new consolidated attachment mixins from the billing mixins module.


22-37: LGTM!

The service classes now use the consolidated AttachmentMixin and AsyncAttachmentMixin, which simplifies the inheritance chain while maintaining full attachment functionality. The generic type parameter InvoiceAttachment is correctly applied.

mpt_api_client/resources/billing/credit_memo_attachments.py (1)

22-28: Mixin refactor looks good.

The switch from individual mixins to the consolidated AttachmentMixin and AsyncAttachmentMixin is consistent with the pattern established in mpt_api_client/resources/billing/mixins.py (lines 408-425). This provides a cleaner inheritance hierarchy while maintaining the same functionality (create, update, delete, download, get).

Also applies to: 31-37

mpt_api_client/resources/billing/statements.py (2)

12-15: Import addition looks correct.

The import of StatementAttachmentsService and AsyncStatementAttachmentsService aligns with the new statement attachments module being introduced.


51-56: Attachments accessor methods follow established patterns.

Both synchronous and asynchronous attachments() methods mirror the existing charges() method pattern and are consistent with similar accessors in other billing modules (e.g., ledgers.py:48-53, journals.py:78-83, invoices.py:40-45). The implementation correctly passes http_client and endpoint_params with the appropriate statement_id key.

Also applies to: 76-81

mpt_api_client/resources/billing/journal_attachments.py (1)

7-7: Clean refactor to AttachmentMixin.

The migration from individual file-operation mixins to the unified AttachmentMixin and AsyncAttachmentMixin simplifies the inheritance chain while maintaining equivalent functionality. The configuration properly includes _upload_file_key and _upload_data_key for file upload support.

Also applies to: 24-30, 33-39

mpt_api_client/resources/billing/statement_attachments.py (1)

1-39: Well-structured new attachment module.

The new statement_attachments.py module follows the established patterns for billing attachment services:

  • Complete configuration with _endpoint, _model_class, _collection_key, _upload_file_key, and _upload_data_key
  • Proper inheritance from AttachmentMixin/AsyncAttachmentMixin, CollectionMixin/AsyncCollectionMixin, and Service/AsyncService
  • Consistent with other attachment modules like journal_attachments.py
mpt_api_client/resources/billing/custom_ledger_attachments.py (2)

14-19: Potential missing upload configuration keys.

Similar to CreditMemoAttachmentsServiceConfig, this config is missing _upload_file_key and _upload_data_key. The AttachmentMixin includes CreateFileMixin which may require these keys for file upload operations. Compare with JournalAttachmentsServiceConfig and StatementAttachmentsServiceConfig which define these keys.

Verify whether this is intentional or if the keys should be added.


22-28: Mixin refactor is consistent with other modules.

The service classes correctly inherit from the consolidated AttachmentMixin and AsyncAttachmentMixin, maintaining consistency with the broader refactoring effort.

Also applies to: 31-37

mpt_api_client/resources/billing/ledger_attachments.py (2)

14-19: Missing upload configuration keys - same pattern as other attachment configs.

This is the third attachment config missing _upload_file_key and _upload_data_key (along with CreditMemoAttachmentsServiceConfig and CustomLedgerAttachmentsServiceConfig). If the CreateFileMixin from AttachmentMixin requires these keys for file upload, they should be added for consistency.

🔎 Suggested addition if uploads are supported
 class LedgerAttachmentsServiceConfig:
     """Ledger Attachments service configuration."""
 
     _endpoint = "/public/v1/billing/ledgers/{ledger_id}/attachments"
     _model_class = LedgerAttachment
     _collection_key = "data"
+    _upload_file_key = "file"
+    _upload_data_key = "attachment"

22-28: Mixin migration is correct.

The refactor to use AttachmentMixin and AsyncAttachmentMixin is implemented correctly and consistently with other attachment modules.

Also applies to: 31-37

@robcsegal robcsegal force-pushed the MPT-16678-add-missing-billing-statements-attachments-endpoints branch from 9392d9e to 74e3a58 Compare December 31, 2025 15:14
@sonarqubecloud
Copy link

@d3rky d3rky merged commit 9d77bd1 into main Jan 2, 2026
6 checks passed
@d3rky d3rky deleted the MPT-16678-add-missing-billing-statements-attachments-endpoints branch January 2, 2026 09:12
robcsegal pushed a commit that referenced this pull request Jan 2, 2026
Added missing billing statement attachments endpoints
Even though mixins will be re-organized, I added an attachment mixin
because there's almost half a dozen of attachment resources.

https://softwareone.atlassian.net/browse/MPT-16678

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

* **New Features**
* Added statement attachments support with upload/download and
create/list/get/delete operations (sync and async).
* Added attachments accessors on statements to obtain per-statement
attachment services.

* **Refactor**
* Replaced multiple per-operation attachment mixins with unified
attachment mixins for consistent behavior across attachment services.

* **Tests**
* Expanded unit tests for statement attachments and added
iterate/download checks across attachment services.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants