Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Support for proration on subscription transaction detail line items #42

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx
- `SubscriptionClient` `preview_update` and `preview_one_time_charge` responses now have `import_meta` property
- Support for `tax_rates_used` on Adjustments
- Added `IPAddressesClient.get_ip_addresses` to support retrieval of Paddle IP addresses
- Support for `proration` on subscription `recurring_transaction_details.line_items[]` and `next_transaction.details.line_items[]`

### Changed

- `paddle_billing.Entities.Shared.CustomData` is no longer a `dataclass`
- `NotificationSettingsClient.delete` now returns `None` for `204 No Content` response
- `TimePeriod` is now aligned to API specification:
- Existing shared `TimePeriod` was renamed to `Duration` (with properties `interval` and `frequency`)
- New shared `TimePeriod` was added (with properties `starts_at` and `ends_at`)
- Replaced `AdjustmentTimePeriod`, `SubscriptionTimePeriod` and `TransactionTimePeriod` with shared `TimePeriod`
- Replaced `AdjustmentProration`, `SubscriptionProration` and `TransactionProration` with shared `Proration`

### Fixed

Expand Down
34 changes: 30 additions & 4 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,56 @@ All breaking changes prior to v1 will be documented in this file to assist with

## v0.3.0

1. `paddle_billing.Entities.Shared.AvailablePaymentMethods` has been replaced by `paddle_billing.Entities.Shared.PaymentMethodType`.
### 1. `paddle_billing.Entities.Shared.AvailablePaymentMethods` has been replaced by `paddle_billing.Entities.Shared.PaymentMethodType`.

`Transaction` `available_payment_methods` will now return a list of `PaymentMethodType`.

All usage of `AvailablePaymentMethods` will need to be replaced with `PaymentMethodType`.

### 2. `paddle_billing.Entities.Shared.TimePeriod` has been aligned to API specification

Existing shared `TimePeriod` was renamed to `Duration` (with properties `interval` and `frequency`), and new `TimePeriod` was added (with properties `starts_at` and `ends_at`).

Existing usages of `paddle_billing.Entities.Shared.TimePeriod` will need to be changed to `paddle_billing.Entities.Shared.Duration`.

`paddle_billing.Entities.Shared.TimePeriod` should be used in place of:
- `paddle_billing.Entities.Shared.AdjustmentTimePeriod`
- `paddle_billing.Entities.Subscriptions.SubscriptionTimePeriod`
- `paddle_billing.Entities.Transactions.TransactionTimePeriod`

`paddle_billing.Notifications.Entities.Shared.TimePeriod` should be used in place of:
- `paddle_billing.Notifications.Entities.Shared.AdjustmentTimePeriod`
- `paddle_billing.Notifications.Entities.Subscriptions.SubscriptionTimePeriod`
- `paddle_billing.Notifications.Entities.Transactions.TransactionTimePeriod`

`paddle_billing.Entities.Shared.Proration` should be used in place of:
- `paddle_billing.Entities.Shared.AdjustmentProration`
- `paddle_billing.Entities.Subscriptions.SubscriptionProration`
- `paddle_billing.Entities.Transactions.TransactionProration`

`paddle_billing.Notifications.Entities.Shared.Proration` should be used in place of:
- `paddle_billing.Notifications.Entities.Shared.AdjustmentProration`
- `paddle_billing.Notifications.Entities.Transactions.TransactionProration`


## v0.2.0

This release includes a few breaking changes. These changes should be limited impact on most integrations but may cause problems in some circumstances.

1. `PaddleStrEnum` has been re-implement to gracefully handle non-existent values, it is no longer using native enums
### 1. `PaddleStrEnum` has been re-implement to gracefully handle non-existent values, it is no longer using native enums

This should not require any implementation changes in your code. The new `PaddleStrEnum` is implemented in a way that minimises this impact.

However, as we have dropped native Enums there maybe native Enum specific behaviour that does not work exactly as before which would require more caution.

2. The `paddle_billing.Entities.Subscriptions.SubscriptionItem` price entity is now using the main `paddle_billing.Entities.Price` entity
### 2. The `paddle_billing.Entities.Subscriptions.SubscriptionItem` price entity is now using the main `paddle_billing.Entities.Price` entity

The change here has again limited impact on runtime behaviour except for having more properties available, however, any instance or type checking at runtime or statically will fail.

As the `paddle_billing.Entities.Subscriptions.SubscriptionPrice` entity has been removed any references of this in the code will fail.

If you're making any of these checks or have the `SubscriptionPrice` imported you will need to update accordingly.

3. Entity factory methods are consistently static now where previously there were implementations as class methods
### 3. Entity factory methods are consistently static now where previously there were implementations as class methods

This should not require any real change with integrations as these factory methods never made use of being class methods but something to be aware of.
6 changes: 3 additions & 3 deletions paddle_billing/Entities/Adjustments/AdjustmentItem.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass

from paddle_billing.Entities.Shared import AdjustmentItemTotals, AdjustmentProration, AdjustmentType
from paddle_billing.Entities.Shared import AdjustmentItemTotals, AdjustmentType, Proration


@dataclass
Expand All @@ -10,7 +10,7 @@ class AdjustmentItem:
item_id: str
type: AdjustmentType
amount: str | None
proration: AdjustmentProration | None
proration: Proration | None
totals: AdjustmentItemTotals


Expand All @@ -21,6 +21,6 @@ def from_dict(data: dict) -> AdjustmentItem:
item_id = data['item_id'],
type = AdjustmentType(data['type']),
amount = data.get('amount'),
proration = AdjustmentProration.from_dict(data['proration']) if data.get('proration') else None,
proration = Proration.from_dict(data['proration']) if data.get('proration') else None,
totals = AdjustmentItemTotals.from_dict(data['totals']),
)
10 changes: 5 additions & 5 deletions paddle_billing/Entities/Price.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
from paddle_billing.Entities.Shared import (
CatalogType,
CustomData,
Duration,
ImportMeta,
Money,
PriceQuantity,
Status,
TaxMode,
TimePeriod,
UnitPriceOverride,
)

Expand All @@ -26,8 +26,8 @@ class Price(Entity):
name: str | None
description: str
type: CatalogType | None
billing_cycle: TimePeriod | None
trial_period: TimePeriod | None
billing_cycle: Duration | None
trial_period: Duration | None
tax_mode: TaxMode
unit_price: Money
unit_price_overrides: list[UnitPriceOverride]
Expand Down Expand Up @@ -55,8 +55,8 @@ def from_dict(data: dict) -> Price:
updated_at = datetime.fromisoformat(data['updated_at']),
unit_price_overrides = [UnitPriceOverride.from_dict(override) for override in data.get('unit_price_overrides', [])],
type = CatalogType(data.get('type')) if data.get('type') else None,
billing_cycle = TimePeriod.from_dict(data['billing_cycle']) if data.get('billing_cycle') else None,
trial_period = TimePeriod.from_dict(data['trial_period']) if data.get('trial_period') else None,
billing_cycle = Duration.from_dict(data['billing_cycle']) if data.get('billing_cycle') else None,
trial_period = Duration.from_dict(data['trial_period']) if data.get('trial_period') else None,
custom_data = CustomData(data['custom_data']) if data.get('custom_data') else None,
import_meta = ImportMeta.from_dict(data['import_meta']) if data.get('import_meta') else None,
product = Product.from_dict(data['product']) if data.get('product') else None,
Expand Down
18 changes: 0 additions & 18 deletions paddle_billing/Entities/Shared/AdjustmentProration.py

This file was deleted.

17 changes: 0 additions & 17 deletions paddle_billing/Entities/Shared/AdjustmentTimePeriod.py

This file was deleted.

6 changes: 3 additions & 3 deletions paddle_billing/Entities/Shared/BillingDetails.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from __future__ import annotations
from dataclasses import dataclass

from paddle_billing.Entities.Shared.TimePeriod import TimePeriod
from paddle_billing.Entities.Shared.Duration import Duration


@dataclass
class BillingDetails:
enable_checkout: bool
payment_terms: TimePeriod
payment_terms: Duration
purchase_order_number: str | None = None
additional_information: str | None = None

Expand All @@ -16,7 +16,7 @@ class BillingDetails:
def from_dict(data: dict) -> BillingDetails:
return BillingDetails(
enable_checkout = data['enable_checkout'],
payment_terms = TimePeriod.from_dict(data['payment_terms']),
payment_terms = Duration.from_dict(data['payment_terms']),
purchase_order_number = data.get('purchase_order_number'),
additional_information = data.get('additional_information'),
)
18 changes: 18 additions & 0 deletions paddle_billing/Entities/Shared/Duration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations
from dataclasses import dataclass

from paddle_billing.Entities.Shared.Interval import Interval


@dataclass
class Duration:
interval: Interval
frequency: int


@staticmethod
def from_dict(data: dict) -> Duration:
return Duration(
interval = Interval(data['interval']),
frequency = data['frequency'],
)
18 changes: 18 additions & 0 deletions paddle_billing/Entities/Shared/Proration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations
from dataclasses import dataclass

from paddle_billing.Entities.Shared.TimePeriod import TimePeriod


@dataclass
class Proration:
rate: str
billing_period: TimePeriod


@staticmethod
def from_dict(data: dict) -> Proration:
return Proration(
rate = data['rate'],
billing_period = TimePeriod.from_dict(data['billing_period']),
)
11 changes: 5 additions & 6 deletions paddle_billing/Entities/Shared/TimePeriod.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from __future__ import annotations
from dataclasses import dataclass

from paddle_billing.Entities.Shared.Interval import Interval
from datetime import datetime


@dataclass
class TimePeriod:
interval: Interval
frequency: int
starts_at: datetime
ends_at: datetime


@staticmethod
def from_dict(data: dict) -> TimePeriod:
return TimePeriod(
interval = Interval(data['interval']),
frequency = data['frequency'],
starts_at = datetime.fromisoformat(data['starts_at']),
ends_at = datetime.fromisoformat(data['ends_at']),
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from paddle_billing.Entities.Product import Product
from paddle_billing.Entities.Shared.Totals import Totals
from paddle_billing.Entities.Shared.UnitTotals import UnitTotals
from paddle_billing.Entities.Shared.Proration import Proration


@dataclass
Expand All @@ -14,6 +15,7 @@ class TransactionLineItemPreview:
unit_totals: UnitTotals
totals: Totals
product: Product
proration: Proration


@staticmethod
Expand All @@ -25,4 +27,5 @@ def from_dict(data: dict) -> TransactionLineItemPreview:
unit_totals = UnitTotals.from_dict(data['unit_totals']),
totals = Totals.from_dict(data['totals']),
product = Product.from_dict(data['product']),
proration = Proration.from_dict(data['proration']),
)
4 changes: 2 additions & 2 deletions paddle_billing/Entities/Shared/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from paddle_billing.Entities.Shared.Action import Action
from paddle_billing.Entities.Shared.AddressPreview import AddressPreview
from paddle_billing.Entities.Shared.AdjustmentItemTotals import AdjustmentItemTotals
from paddle_billing.Entities.Shared.AdjustmentProration import AdjustmentProration
from paddle_billing.Entities.Shared.AdjustmentStatus import AdjustmentStatus
from paddle_billing.Entities.Shared.AdjustmentTimePeriod import AdjustmentTimePeriod
from paddle_billing.Entities.Shared.AdjustmentTotals import AdjustmentTotals
from paddle_billing.Entities.Shared.AdjustmentType import AdjustmentType
from paddle_billing.Entities.Shared.BillingDetails import BillingDetails
Expand All @@ -21,6 +19,7 @@
from paddle_billing.Entities.Shared.CustomData import CustomData
from paddle_billing.Entities.Shared.Data import Data
from paddle_billing.Entities.Shared.Disposition import Disposition
from paddle_billing.Entities.Shared.Duration import Duration
from paddle_billing.Entities.Shared.ErrorCode import ErrorCode
from paddle_billing.Entities.Shared.ImportMeta import ImportMeta
from paddle_billing.Entities.Shared.Interval import Interval
Expand All @@ -34,6 +33,7 @@
from paddle_billing.Entities.Shared.PaymentMethodType import PaymentMethodType
from paddle_billing.Entities.Shared.PayoutTotalsAdjustment import PayoutTotalsAdjustment
from paddle_billing.Entities.Shared.PriceQuantity import PriceQuantity
from paddle_billing.Entities.Shared.Proration import Proration
from paddle_billing.Entities.Shared.Status import Status
from paddle_billing.Entities.Shared.TransactionStatus import TransactionStatus
from paddle_billing.Entities.Shared.TaxCategory import TaxCategory
Expand Down
12 changes: 6 additions & 6 deletions paddle_billing/Entities/Subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
CollectionMode,
CurrencyCode,
CustomData,
Duration,
ImportMeta,
TimePeriod,
)
Expand All @@ -21,7 +22,6 @@
SubscriptionNextTransaction,
SubscriptionScheduledChange,
SubscriptionStatus,
SubscriptionTimePeriod,
)


Expand All @@ -42,9 +42,9 @@ class Subscription(Entity):
canceled_at: datetime | None
discount: SubscriptionDiscount | None
collection_mode: CollectionMode
billing_details: BillingDetails | None
current_billing_period: SubscriptionTimePeriod | None
billing_cycle: TimePeriod
billing_details: BillingDetails | None
current_billing_period: TimePeriod | None
billing_cycle: Duration
scheduled_change: SubscriptionScheduledChange | None
management_urls: SubscriptionManagementUrls | None
items: list[SubscriptionItem]
Expand All @@ -66,9 +66,9 @@ def from_dict(data: dict) -> Subscription:
created_at = datetime.fromisoformat(data['created_at']),
updated_at = datetime.fromisoformat(data['updated_at']),
collection_mode = CollectionMode(data['collection_mode']),
billing_cycle = TimePeriod.from_dict(data['billing_cycle']),
billing_cycle = Duration.from_dict(data['billing_cycle']),
items = [SubscriptionItem.from_dict(item) for item in data['items']],
current_billing_period = SubscriptionTimePeriod.from_dict(data['current_billing_period']) if data.get('current_billing_period') else None,
current_billing_period = TimePeriod.from_dict(data['current_billing_period']) if data.get('current_billing_period') else None,
management_urls = SubscriptionManagementUrls.from_dict(data['management_urls']) if data.get('management_urls') else None,
started_at = datetime.fromisoformat(data['started_at']) if data.get('started_at') else None,
first_billed_at = datetime.fromisoformat(data['first_billed_at']) if data.get('first_billed_at') else None,
Expand Down
Loading