From d2c34ad2a536eec27e0a5d8a968d9f96b1540249 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 15 Jul 2026 16:11:08 +0100 Subject: [PATCH] feat: Add support for checkouts report --- CHANGELOG.md | 6 ++++ .../Entities/Reports/CheckoutsReportType.py | 7 ++++ .../Entities/Reports/ReportFilterName.py | 2 ++ paddle_billing/Entities/Reports/ReportType.py | 1 + paddle_billing/Entities/Reports/__init__.py | 1 + .../Entities/Reports/ReportFilterName.py | 2 ++ .../Entities/Reports/ReportType.py | 1 + .../Operations/CreateCheckoutsReport.py | 25 ++++++++++++++ .../Filters/CheckoutCreatedAtFilter.py | 34 +++++++++++++++++++ .../Filters/CustomerCountryCodeFilter.py | 22 ++++++++++++ .../Reports/Operations/Filters/__init__.py | 2 ++ .../Resources/Reports/Operations/__init__.py | 1 + .../request/create_checkouts_basic.json | 3 ++ .../request/create_checkouts_full.json | 23 +++++++++++++ .../Resources/Reports/test_ReportsClient.py | 24 +++++++++++++ 15 files changed, 154 insertions(+) create mode 100644 paddle_billing/Entities/Reports/CheckoutsReportType.py create mode 100644 paddle_billing/Resources/Reports/Operations/CreateCheckoutsReport.py create mode 100644 paddle_billing/Resources/Reports/Operations/Filters/CheckoutCreatedAtFilter.py create mode 100644 paddle_billing/Resources/Reports/Operations/Filters/CustomerCountryCodeFilter.py create mode 100644 tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_basic.json create mode 100644 tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_full.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 29f12b1..9286967 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-python-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. +## Unreleased + +### Added + +- Added support for `checkouts` report type. See [related changelog](https://developer.paddle.com/changelog/2026/checkouts-report?utm_source=dx&utm_medium=paddle-python-sdk) + ## 1.14.1 - 2026-04-21 ### Fixed diff --git a/paddle_billing/Entities/Reports/CheckoutsReportType.py b/paddle_billing/Entities/Reports/CheckoutsReportType.py new file mode 100644 index 0000000..9dad285 --- /dev/null +++ b/paddle_billing/Entities/Reports/CheckoutsReportType.py @@ -0,0 +1,7 @@ +from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta + +from paddle_billing.Entities.Reports.ReportType import ReportType + + +class CheckoutsReportType(PaddleStrEnum, metaclass=PaddleStrEnumMeta): + Checkouts: "CheckoutsReportType" = ReportType.Checkouts.value diff --git a/paddle_billing/Entities/Reports/ReportFilterName.py b/paddle_billing/Entities/Reports/ReportFilterName.py index 4d88d3a..2c2828d 100644 --- a/paddle_billing/Entities/Reports/ReportFilterName.py +++ b/paddle_billing/Entities/Reports/ReportFilterName.py @@ -3,8 +3,10 @@ class ReportFilterName(PaddleStrEnum, metaclass=PaddleStrEnumMeta): Action: "ReportFilterName" = "action" + CheckoutCreatedAt: "ReportFilterName" = "checkout_created_at" CollectionMode: "ReportFilterName" = "collection_mode" CurrencyCode: "ReportFilterName" = "currency_code" + CustomerCountryCode: "ReportFilterName" = "customer_country_code" Origin: "ReportFilterName" = "origin" PriceStatus: "ReportFilterName" = "price_status" PriceType: "ReportFilterName" = "price_type" diff --git a/paddle_billing/Entities/Reports/ReportType.py b/paddle_billing/Entities/Reports/ReportType.py index f2d1c50..403b8fe 100644 --- a/paddle_billing/Entities/Reports/ReportType.py +++ b/paddle_billing/Entities/Reports/ReportType.py @@ -10,3 +10,4 @@ class ReportType(PaddleStrEnum, metaclass=PaddleStrEnumMeta): TransactionLineItems: "ReportType" = "transaction_line_items" Balance: "ReportType" = "balance" PayoutReconciliation: "ReportType" = "payout_reconciliation" + Checkouts: "ReportType" = "checkouts" diff --git a/paddle_billing/Entities/Reports/__init__.py b/paddle_billing/Entities/Reports/__init__.py index 5f0b028..fefd9dd 100644 --- a/paddle_billing/Entities/Reports/__init__.py +++ b/paddle_billing/Entities/Reports/__init__.py @@ -1,5 +1,6 @@ from paddle_billing.Entities.Reports.AdjustmentsReportType import AdjustmentsReportType from paddle_billing.Entities.Reports.BalanceReportType import BalanceReportType +from paddle_billing.Entities.Reports.CheckoutsReportType import CheckoutsReportType from paddle_billing.Entities.Reports.DiscountsReportType import DiscountsReportType from paddle_billing.Entities.Reports.PayoutReconciliationReportType import PayoutReconciliationReportType from paddle_billing.Entities.Reports.ProductsPricesReportType import ProductsPricesReportType diff --git a/paddle_billing/Notifications/Entities/Reports/ReportFilterName.py b/paddle_billing/Notifications/Entities/Reports/ReportFilterName.py index 4d88d3a..2c2828d 100644 --- a/paddle_billing/Notifications/Entities/Reports/ReportFilterName.py +++ b/paddle_billing/Notifications/Entities/Reports/ReportFilterName.py @@ -3,8 +3,10 @@ class ReportFilterName(PaddleStrEnum, metaclass=PaddleStrEnumMeta): Action: "ReportFilterName" = "action" + CheckoutCreatedAt: "ReportFilterName" = "checkout_created_at" CollectionMode: "ReportFilterName" = "collection_mode" CurrencyCode: "ReportFilterName" = "currency_code" + CustomerCountryCode: "ReportFilterName" = "customer_country_code" Origin: "ReportFilterName" = "origin" PriceStatus: "ReportFilterName" = "price_status" PriceType: "ReportFilterName" = "price_type" diff --git a/paddle_billing/Notifications/Entities/Reports/ReportType.py b/paddle_billing/Notifications/Entities/Reports/ReportType.py index f2d1c50..403b8fe 100644 --- a/paddle_billing/Notifications/Entities/Reports/ReportType.py +++ b/paddle_billing/Notifications/Entities/Reports/ReportType.py @@ -10,3 +10,4 @@ class ReportType(PaddleStrEnum, metaclass=PaddleStrEnumMeta): TransactionLineItems: "ReportType" = "transaction_line_items" Balance: "ReportType" = "balance" PayoutReconciliation: "ReportType" = "payout_reconciliation" + Checkouts: "ReportType" = "checkouts" diff --git a/paddle_billing/Resources/Reports/Operations/CreateCheckoutsReport.py b/paddle_billing/Resources/Reports/Operations/CreateCheckoutsReport.py new file mode 100644 index 0000000..67b49b6 --- /dev/null +++ b/paddle_billing/Resources/Reports/Operations/CreateCheckoutsReport.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass, field + +from paddle_billing.Entities.Reports import CheckoutsReportType + +from paddle_billing.Resources.Reports.Operations.Filters import ( + CheckoutCreatedAtFilter, + CustomerCountryCodeFilter, +) + +from paddle_billing.Resources.Reports.Operations.CreateReport import ( + CreateReport, +) + + +@dataclass +class CreateCheckoutsReport(CreateReport): + type: CheckoutsReportType + filters: list[CheckoutCreatedAtFilter | CustomerCountryCodeFilter] = field(default_factory=list) + + @staticmethod + def get_allowed_filters() -> tuple[CheckoutCreatedAtFilter | CustomerCountryCodeFilter]: + return ( + CheckoutCreatedAtFilter, + CustomerCountryCodeFilter, + ) diff --git a/paddle_billing/Resources/Reports/Operations/Filters/CheckoutCreatedAtFilter.py b/paddle_billing/Resources/Reports/Operations/Filters/CheckoutCreatedAtFilter.py new file mode 100644 index 0000000..e5700b4 --- /dev/null +++ b/paddle_billing/Resources/Reports/Operations/Filters/CheckoutCreatedAtFilter.py @@ -0,0 +1,34 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.Entities.DateTime import DateTime + +from paddle_billing.Entities.Reports import ReportFilterOperator + +from paddle_billing.Resources.Reports.Operations.Filters.Filter import Filter + +from paddle_billing.Entities.Reports import ReportFilterName + + +@dataclass +class CheckoutCreatedAtFilter(Filter): + operator: ReportFilterOperator + value: DateTime + + @staticmethod + def get_name() -> ReportFilterName: + return ReportFilterName.CheckoutCreatedAt + + def get_operator(self) -> ReportFilterOperator | None: + return self.operator + + def get_value(self) -> str: + return self.value.format() + + @staticmethod + def gte(value: DateTime) -> CheckoutCreatedAtFilter: + return CheckoutCreatedAtFilter(value=value, operator=ReportFilterOperator.Gte) + + @staticmethod + def lt(value: DateTime) -> CheckoutCreatedAtFilter: + return CheckoutCreatedAtFilter(value=value, operator=ReportFilterOperator.Lt) diff --git a/paddle_billing/Resources/Reports/Operations/Filters/CustomerCountryCodeFilter.py b/paddle_billing/Resources/Reports/Operations/Filters/CustomerCountryCodeFilter.py new file mode 100644 index 0000000..d33ac25 --- /dev/null +++ b/paddle_billing/Resources/Reports/Operations/Filters/CustomerCountryCodeFilter.py @@ -0,0 +1,22 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.EnumStringify import enum_stringify + +from paddle_billing.Resources.Reports.Operations.Filters.Filter import Filter + +from paddle_billing.Entities.Shared.CountryCode import CountryCode + +from paddle_billing.Entities.Reports import ReportFilterName + + +@dataclass +class CustomerCountryCodeFilter(Filter): + country_codes: list[CountryCode] + + @staticmethod + def get_name() -> ReportFilterName: + return ReportFilterName.CustomerCountryCode + + def get_value(self) -> list[str]: + return list(map(enum_stringify, self.country_codes)) diff --git a/paddle_billing/Resources/Reports/Operations/Filters/__init__.py b/paddle_billing/Resources/Reports/Operations/Filters/__init__.py index cca810d..6acb8a2 100644 --- a/paddle_billing/Resources/Reports/Operations/Filters/__init__.py +++ b/paddle_billing/Resources/Reports/Operations/Filters/__init__.py @@ -1,7 +1,9 @@ from paddle_billing.Resources.Reports.Operations.Filters.AdjustmentActionFilter import AdjustmentActionFilter from paddle_billing.Resources.Reports.Operations.Filters.AdjustmentStatusFilter import AdjustmentStatusFilter +from paddle_billing.Resources.Reports.Operations.Filters.CheckoutCreatedAtFilter import CheckoutCreatedAtFilter from paddle_billing.Resources.Reports.Operations.Filters.CollectionModeFilter import CollectionModeFilter from paddle_billing.Resources.Reports.Operations.Filters.CurrencyCodeFilter import CurrencyCodeFilter +from paddle_billing.Resources.Reports.Operations.Filters.CustomerCountryCodeFilter import CustomerCountryCodeFilter from paddle_billing.Resources.Reports.Operations.Filters.DiscountStatusFilter import DiscountStatusFilter from paddle_billing.Resources.Reports.Operations.Filters.DiscountTypeFilter import DiscountTypeFilter from paddle_billing.Resources.Reports.Operations.Filters.PriceStatusFilter import PriceStatusFilter diff --git a/paddle_billing/Resources/Reports/Operations/__init__.py b/paddle_billing/Resources/Reports/Operations/__init__.py index 5a3d9d0..36cee0e 100644 --- a/paddle_billing/Resources/Reports/Operations/__init__.py +++ b/paddle_billing/Resources/Reports/Operations/__init__.py @@ -1,5 +1,6 @@ from paddle_billing.Resources.Reports.Operations.CreateAdjustmentsReport import CreateAdjustmentsReport from paddle_billing.Resources.Reports.Operations.CreateBalanceReport import CreateBalanceReport +from paddle_billing.Resources.Reports.Operations.CreateCheckoutsReport import CreateCheckoutsReport from paddle_billing.Resources.Reports.Operations.CreateDiscountsReport import CreateDiscountsReport from paddle_billing.Resources.Reports.Operations.CreatePayoutReconciliationReport import ( CreatePayoutReconciliationReport, diff --git a/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_basic.json b/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_basic.json new file mode 100644 index 0000000..fc62799 --- /dev/null +++ b/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_basic.json @@ -0,0 +1,3 @@ +{ + "type": "checkouts" +} diff --git a/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_full.json b/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_full.json new file mode 100644 index 0000000..78c948c --- /dev/null +++ b/tests/Functional/Resources/Reports/_fixtures/request/create_checkouts_full.json @@ -0,0 +1,23 @@ +{ + "type": "checkouts", + "filters": [ + { + "name": "checkout_created_at", + "operator": "gte", + "value": "2023-12-30T00:00:00.000000Z" + }, + { + "name": "checkout_created_at", + "operator": "lt", + "value": "2024-12-30T12:30:01.123456Z" + }, + { + "name": "customer_country_code", + "operator": null, + "value": [ + "US", + "GB" + ] + } + ] +} diff --git a/tests/Functional/Resources/Reports/test_ReportsClient.py b/tests/Functional/Resources/Reports/test_ReportsClient.py index 79cf2c9..a73806d 100644 --- a/tests/Functional/Resources/Reports/test_ReportsClient.py +++ b/tests/Functional/Resources/Reports/test_ReportsClient.py @@ -8,6 +8,7 @@ from paddle_billing.Entities.Reports import ( AdjustmentsReportType, BalanceReportType, + CheckoutsReportType, DiscountsReportType, PayoutReconciliationReportType, ProductsPricesReportType, @@ -23,8 +24,10 @@ from paddle_billing.Resources.Reports.Operations.Filters import ( AdjustmentActionFilter, AdjustmentStatusFilter, + CheckoutCreatedAtFilter, CollectionModeFilter, CurrencyCodeFilter, + CustomerCountryCodeFilter, DiscountStatusFilter, DiscountTypeFilter, PriceStatusFilter, @@ -45,6 +48,7 @@ Action, CatalogType, CollectionMode, + CountryCode, CurrencyCode, TransactionOrigin, TransactionStatus, @@ -61,6 +65,7 @@ from paddle_billing.Resources.Reports.Operations import ( CreateAdjustmentsReport, CreateBalanceReport, + CreateCheckoutsReport, CreateDiscountsReport, CreatePayoutReconciliationReport, CreateProductsAndPricesReport, @@ -174,6 +179,23 @@ class TestReportsClient: ReadsFixtures.read_raw_json_fixture("request/create_payout_reconciliation_full"), PayoutReconciliationReportType, ), + ( + CreateCheckoutsReport(type=CheckoutsReportType.Checkouts), + ReadsFixtures.read_raw_json_fixture("request/create_checkouts_basic"), + CheckoutsReportType, + ), + ( + CreateCheckoutsReport( + type=CheckoutsReportType.Checkouts, + filters=[ + CheckoutCreatedAtFilter.gte(DateTime("2023-12-30")), + CheckoutCreatedAtFilter.lt(DateTime("2024-12-30T12:30:01.123456Z")), + CustomerCountryCodeFilter([CountryCode.US, CountryCode.GB]), + ], + ), + ReadsFixtures.read_raw_json_fixture("request/create_checkouts_full"), + CheckoutsReportType, + ), ], ids=[ "Create transaction report with basic data", @@ -185,6 +207,8 @@ class TestReportsClient: "Create balance report with filters", "Create payout reconciliation report with basic data", "Create payout reconciliation report with filters", + "Create checkouts report with basic data", + "Create checkouts report with filters", ], ) def test_create_report_uses_expected_payload(