diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c766a1b1..08ebb64c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.18.2" + ".": "3.19.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 1e64c91b..88277273 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-95a3d7780935a38e0cf076d4ad2d68bd1a5641bced8398d972db2e92751d364a.yml -openapi_spec_hash: 9ebe818c4ad4f2d9c4e473b5192d7544 -config_hash: 54edf41f0377bc235f622fdaa7405f22 +configured_endpoints: 106 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-e8dad7eee5621fe2ba948dfd00dabf170d9d92ce615a9f04b0f546f4d8bf39ba.yml +openapi_spec_hash: 3f6a98e3a1b3a47acebd67a960090ebf +config_hash: 7e523cf79552b8936bd772f2e1025e5f diff --git a/CHANGELOG.md b/CHANGELOG.md index a915617c..050ce307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 3.19.0 (2025-04-08) + +Full Changelog: [v3.18.2...v3.19.0](https://github.com/orbcorp/orb-python/compare/v3.18.2...v3.19.0) + +### Features + +* **api:** api update ([#614](https://github.com/orbcorp/orb-python/issues/614)) ([900d258](https://github.com/orbcorp/orb-python/commit/900d258b7af45e1c777b20b561373b9438e42abb)) + + +### Chores + +* **internal:** slight transform perf improvement ([#616](https://github.com/orbcorp/orb-python/issues/616)) ([de565f2](https://github.com/orbcorp/orb-python/commit/de565f252353fa7f951ad8cc10574f82919e35aa)) +* **tests:** improve enum examples ([#617](https://github.com/orbcorp/orb-python/issues/617)) ([a0d4366](https://github.com/orbcorp/orb-python/commit/a0d4366a82d86555d6ac41a912ce22414b74dd50)) + ## 3.18.2 (2025-04-07) Full Changelog: [v3.18.1...v3.18.2](https://github.com/orbcorp/orb-python/compare/v3.18.1...v3.18.2) diff --git a/api.md b/api.md index da2a7bc1..2a1498a9 100644 --- a/api.md +++ b/api.md @@ -410,3 +410,21 @@ Methods: Methods: - client.dimensional_price_groups.external_dimensional_price_group_id.retrieve(external_dimensional_price_group_id) -> DimensionalPriceGroup + +# SubscriptionChanges + +Types: + +```python +from orb.types import ( + SubscriptionChangeRetrieveResponse, + SubscriptionChangeApplyResponse, + SubscriptionChangeCancelResponse, +) +``` + +Methods: + +- client.subscription_changes.retrieve(subscription_change_id) -> SubscriptionChangeRetrieveResponse +- client.subscription_changes.apply(subscription_change_id, \*\*params) -> SubscriptionChangeApplyResponse +- client.subscription_changes.cancel(subscription_change_id) -> SubscriptionChangeCancelResponse diff --git a/pyproject.toml b/pyproject.toml index e4fc5098..8c6f97d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "orb-billing" -version = "3.18.2" +version = "3.19.0" description = "The official Python library for the orb API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/orb/_client.py b/src/orb/_client.py index d9737190..c4b5d59c 100644 --- a/src/orb/_client.py +++ b/src/orb/_client.py @@ -35,6 +35,7 @@ credit_notes, subscriptions, invoice_line_items, + subscription_changes, ) from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OrbError, APIStatusError @@ -69,6 +70,7 @@ class Orb(SyncAPIClient): webhooks: webhooks.Webhooks alerts: alerts.Alerts dimensional_price_groups: dimensional_price_groups.DimensionalPriceGroups + subscription_changes: subscription_changes.SubscriptionChanges with_raw_response: OrbWithRawResponse with_streaming_response: OrbWithStreamedResponse @@ -151,6 +153,7 @@ def __init__( self.webhooks = webhooks.Webhooks(self) self.alerts = alerts.Alerts(self) self.dimensional_price_groups = dimensional_price_groups.DimensionalPriceGroups(self) + self.subscription_changes = subscription_changes.SubscriptionChanges(self) self.with_raw_response = OrbWithRawResponse(self) self.with_streaming_response = OrbWithStreamedResponse(self) @@ -325,6 +328,7 @@ class AsyncOrb(AsyncAPIClient): webhooks: webhooks.AsyncWebhooks alerts: alerts.AsyncAlerts dimensional_price_groups: dimensional_price_groups.AsyncDimensionalPriceGroups + subscription_changes: subscription_changes.AsyncSubscriptionChanges with_raw_response: AsyncOrbWithRawResponse with_streaming_response: AsyncOrbWithStreamedResponse @@ -407,6 +411,7 @@ def __init__( self.webhooks = webhooks.AsyncWebhooks(self) self.alerts = alerts.AsyncAlerts(self) self.dimensional_price_groups = dimensional_price_groups.AsyncDimensionalPriceGroups(self) + self.subscription_changes = subscription_changes.AsyncSubscriptionChanges(self) self.with_raw_response = AsyncOrbWithRawResponse(self) self.with_streaming_response = AsyncOrbWithStreamedResponse(self) @@ -583,6 +588,7 @@ def __init__(self, client: Orb) -> None: self.dimensional_price_groups = dimensional_price_groups.DimensionalPriceGroupsWithRawResponse( client.dimensional_price_groups ) + self.subscription_changes = subscription_changes.SubscriptionChangesWithRawResponse(client.subscription_changes) class AsyncOrbWithRawResponse: @@ -603,6 +609,9 @@ def __init__(self, client: AsyncOrb) -> None: self.dimensional_price_groups = dimensional_price_groups.AsyncDimensionalPriceGroupsWithRawResponse( client.dimensional_price_groups ) + self.subscription_changes = subscription_changes.AsyncSubscriptionChangesWithRawResponse( + client.subscription_changes + ) class OrbWithStreamedResponse: @@ -623,6 +632,9 @@ def __init__(self, client: Orb) -> None: self.dimensional_price_groups = dimensional_price_groups.DimensionalPriceGroupsWithStreamingResponse( client.dimensional_price_groups ) + self.subscription_changes = subscription_changes.SubscriptionChangesWithStreamingResponse( + client.subscription_changes + ) class AsyncOrbWithStreamedResponse: @@ -645,6 +657,9 @@ def __init__(self, client: AsyncOrb) -> None: self.dimensional_price_groups = dimensional_price_groups.AsyncDimensionalPriceGroupsWithStreamingResponse( client.dimensional_price_groups ) + self.subscription_changes = subscription_changes.AsyncSubscriptionChangesWithStreamingResponse( + client.subscription_changes + ) Client = Orb diff --git a/src/orb/_utils/_transform.py b/src/orb/_utils/_transform.py index 7ac2e17f..3ec62081 100644 --- a/src/orb/_utils/_transform.py +++ b/src/orb/_utils/_transform.py @@ -142,6 +142,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: return key +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + def _transform_recursive( data: object, *, @@ -184,6 +188,15 @@ def _transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): @@ -332,6 +345,15 @@ async def _async_transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): diff --git a/src/orb/_version.py b/src/orb/_version.py index fe3ad45f..f4c31f1c 100644 --- a/src/orb/_version.py +++ b/src/orb/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "orb" -__version__ = "3.18.2" # x-release-please-version +__version__ = "3.19.0" # x-release-please-version diff --git a/src/orb/resources/__init__.py b/src/orb/resources/__init__.py index ccb3f489..80634301 100644 --- a/src/orb/resources/__init__.py +++ b/src/orb/resources/__init__.py @@ -108,6 +108,14 @@ InvoiceLineItemsWithStreamingResponse, AsyncInvoiceLineItemsWithStreamingResponse, ) +from .subscription_changes import ( + SubscriptionChanges, + AsyncSubscriptionChanges, + SubscriptionChangesWithRawResponse, + AsyncSubscriptionChangesWithRawResponse, + SubscriptionChangesWithStreamingResponse, + AsyncSubscriptionChangesWithStreamingResponse, +) from .dimensional_price_groups import ( DimensionalPriceGroups, AsyncDimensionalPriceGroups, @@ -204,4 +212,10 @@ "AsyncDimensionalPriceGroupsWithRawResponse", "DimensionalPriceGroupsWithStreamingResponse", "AsyncDimensionalPriceGroupsWithStreamingResponse", + "SubscriptionChanges", + "AsyncSubscriptionChanges", + "SubscriptionChangesWithRawResponse", + "AsyncSubscriptionChangesWithRawResponse", + "SubscriptionChangesWithStreamingResponse", + "AsyncSubscriptionChangesWithStreamingResponse", ] diff --git a/src/orb/resources/invoices.py b/src/orb/resources/invoices.py index 22c82be0..eff301a9 100644 --- a/src/orb/resources/invoices.py +++ b/src/orb/resources/invoices.py @@ -104,8 +104,9 @@ def create( by setting the value to `null`, and the entire metadata mapping can be cleared by setting `metadata` to `null`. - will_auto_issue: When true, this invoice will automatically be issued upon creation. When false, - the resulting invoice will require manual review to issue. Defaulted to false. + will_auto_issue: When true, this invoice will be submitted for issuance upon creation. When + false, the resulting invoice will require manual review to issue. Defaulted to + false. extra_headers: Send extra headers @@ -643,8 +644,9 @@ async def create( by setting the value to `null`, and the entire metadata mapping can be cleared by setting `metadata` to `null`. - will_auto_issue: When true, this invoice will automatically be issued upon creation. When false, - the resulting invoice will require manual review to issue. Defaulted to false. + will_auto_issue: When true, this invoice will be submitted for issuance upon creation. When + false, the resulting invoice will require manual review to issue. Defaulted to + false. extra_headers: Send extra headers diff --git a/src/orb/resources/subscription_changes.py b/src/orb/resources/subscription_changes.py new file mode 100644 index 00000000..645b12dd --- /dev/null +++ b/src/orb/resources/subscription_changes.py @@ -0,0 +1,418 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from .. import _legacy_response +from ..types import subscription_change_apply_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._base_client import make_request_options +from ..types.subscription_change_apply_response import SubscriptionChangeApplyResponse +from ..types.subscription_change_cancel_response import SubscriptionChangeCancelResponse +from ..types.subscription_change_retrieve_response import SubscriptionChangeRetrieveResponse + +__all__ = ["SubscriptionChanges", "AsyncSubscriptionChanges"] + + +class SubscriptionChanges(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SubscriptionChangesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/orbcorp/orb-python#accessing-raw-response-data-eg-headers + """ + return SubscriptionChangesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SubscriptionChangesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/orbcorp/orb-python#with_streaming_response + """ + return SubscriptionChangesWithStreamingResponse(self) + + def retrieve( + self, + subscription_change_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubscriptionChangeRetrieveResponse: + """ + This endpoint returns a subscription change given an identifier. + + A subscription change is created by including + `Create-Pending-Subscription-Change: True` in the header of a subscription + mutation API call (e.g. + [create subscription endpoint](/api-reference/subscription/create-subscription), + [schedule plan change endpoint](/api-reference/subscription/schedule-plan-change), + ...). The subscription change will be referenced by the + `pending_subscription_change` field in the response. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return self._get( + f"/subscription_changes/{subscription_change_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubscriptionChangeRetrieveResponse, + ) + + def apply( + self, + subscription_change_id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + previously_collected_amount: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> SubscriptionChangeApplyResponse: + """Apply a subscription change to perform the intended action. + + If a positive amount + is passed with a request to this endpoint, any eligible invoices that were + created will be issued immediately if they only contain in-advance fees. + + Args: + description: Description to apply to the balance transaction representing this credit. + + previously_collected_amount: Amount already collected to apply to the customer's balance. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return self._post( + f"/subscription_changes/{subscription_change_id}/apply", + body=maybe_transform( + { + "description": description, + "previously_collected_amount": previously_collected_amount, + }, + subscription_change_apply_params.SubscriptionChangeApplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=SubscriptionChangeApplyResponse, + ) + + def cancel( + self, + subscription_change_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> SubscriptionChangeCancelResponse: + """Cancel a subscription change. + + The change can no longer be applied. A + subscription can only have one "pending" change at a time - use this endpoint to + cancel an existing change before creating a new one. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return self._post( + f"/subscription_changes/{subscription_change_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=SubscriptionChangeCancelResponse, + ) + + +class AsyncSubscriptionChanges(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSubscriptionChangesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/orbcorp/orb-python#accessing-raw-response-data-eg-headers + """ + return AsyncSubscriptionChangesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSubscriptionChangesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/orbcorp/orb-python#with_streaming_response + """ + return AsyncSubscriptionChangesWithStreamingResponse(self) + + async def retrieve( + self, + subscription_change_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubscriptionChangeRetrieveResponse: + """ + This endpoint returns a subscription change given an identifier. + + A subscription change is created by including + `Create-Pending-Subscription-Change: True` in the header of a subscription + mutation API call (e.g. + [create subscription endpoint](/api-reference/subscription/create-subscription), + [schedule plan change endpoint](/api-reference/subscription/schedule-plan-change), + ...). The subscription change will be referenced by the + `pending_subscription_change` field in the response. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return await self._get( + f"/subscription_changes/{subscription_change_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubscriptionChangeRetrieveResponse, + ) + + async def apply( + self, + subscription_change_id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + previously_collected_amount: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> SubscriptionChangeApplyResponse: + """Apply a subscription change to perform the intended action. + + If a positive amount + is passed with a request to this endpoint, any eligible invoices that were + created will be issued immediately if they only contain in-advance fees. + + Args: + description: Description to apply to the balance transaction representing this credit. + + previously_collected_amount: Amount already collected to apply to the customer's balance. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return await self._post( + f"/subscription_changes/{subscription_change_id}/apply", + body=await async_maybe_transform( + { + "description": description, + "previously_collected_amount": previously_collected_amount, + }, + subscription_change_apply_params.SubscriptionChangeApplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=SubscriptionChangeApplyResponse, + ) + + async def cancel( + self, + subscription_change_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + idempotency_key: str | None = None, + ) -> SubscriptionChangeCancelResponse: + """Cancel a subscription change. + + The change can no longer be applied. A + subscription can only have one "pending" change at a time - use this endpoint to + cancel an existing change before creating a new one. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + if not subscription_change_id: + raise ValueError( + f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" + ) + return await self._post( + f"/subscription_changes/{subscription_change_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=SubscriptionChangeCancelResponse, + ) + + +class SubscriptionChangesWithRawResponse: + def __init__(self, subscription_changes: SubscriptionChanges) -> None: + self._subscription_changes = subscription_changes + + self.retrieve = _legacy_response.to_raw_response_wrapper( + subscription_changes.retrieve, + ) + self.apply = _legacy_response.to_raw_response_wrapper( + subscription_changes.apply, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + subscription_changes.cancel, + ) + + +class AsyncSubscriptionChangesWithRawResponse: + def __init__(self, subscription_changes: AsyncSubscriptionChanges) -> None: + self._subscription_changes = subscription_changes + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + subscription_changes.retrieve, + ) + self.apply = _legacy_response.async_to_raw_response_wrapper( + subscription_changes.apply, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + subscription_changes.cancel, + ) + + +class SubscriptionChangesWithStreamingResponse: + def __init__(self, subscription_changes: SubscriptionChanges) -> None: + self._subscription_changes = subscription_changes + + self.retrieve = to_streamed_response_wrapper( + subscription_changes.retrieve, + ) + self.apply = to_streamed_response_wrapper( + subscription_changes.apply, + ) + self.cancel = to_streamed_response_wrapper( + subscription_changes.cancel, + ) + + +class AsyncSubscriptionChangesWithStreamingResponse: + def __init__(self, subscription_changes: AsyncSubscriptionChanges) -> None: + self._subscription_changes = subscription_changes + + self.retrieve = async_to_streamed_response_wrapper( + subscription_changes.retrieve, + ) + self.apply = async_to_streamed_response_wrapper( + subscription_changes.apply, + ) + self.cancel = async_to_streamed_response_wrapper( + subscription_changes.cancel, + ) diff --git a/src/orb/types/__init__.py b/src/orb/types/__init__.py index 74c64801..42bb839f 100644 --- a/src/orb/types/__init__.py +++ b/src/orb/types/__init__.py @@ -77,13 +77,16 @@ from .subscription_fetch_costs_params import SubscriptionFetchCostsParams as SubscriptionFetchCostsParams from .subscription_fetch_usage_params import SubscriptionFetchUsageParams as SubscriptionFetchUsageParams from .alert_create_for_customer_params import AlertCreateForCustomerParams as AlertCreateForCustomerParams +from .subscription_change_apply_params import SubscriptionChangeApplyParams as SubscriptionChangeApplyParams from .subscription_update_trial_params import SubscriptionUpdateTrialParams as SubscriptionUpdateTrialParams from .invoice_line_item_create_response import InvoiceLineItemCreateResponse as InvoiceLineItemCreateResponse from .subscription_fetch_costs_response import SubscriptionFetchCostsResponse as SubscriptionFetchCostsResponse from .subscription_trigger_phase_params import SubscriptionTriggerPhaseParams as SubscriptionTriggerPhaseParams +from .subscription_change_apply_response import SubscriptionChangeApplyResponse as SubscriptionChangeApplyResponse from .subscription_fetch_schedule_params import SubscriptionFetchScheduleParams as SubscriptionFetchScheduleParams from .subscription_update_trial_response import SubscriptionUpdateTrialResponse as SubscriptionUpdateTrialResponse from .dimensional_price_group_list_params import DimensionalPriceGroupListParams as DimensionalPriceGroupListParams +from .subscription_change_cancel_response import SubscriptionChangeCancelResponse as SubscriptionChangeCancelResponse from .subscription_price_intervals_params import SubscriptionPriceIntervalsParams as SubscriptionPriceIntervalsParams from .subscription_trigger_phase_response import SubscriptionTriggerPhaseResponse as SubscriptionTriggerPhaseResponse from .alert_create_for_subscription_params import AlertCreateForSubscriptionParams as AlertCreateForSubscriptionParams @@ -92,6 +95,9 @@ from .dimensional_price_group_create_params import ( DimensionalPriceGroupCreateParams as DimensionalPriceGroupCreateParams, ) +from .subscription_change_retrieve_response import ( + SubscriptionChangeRetrieveResponse as SubscriptionChangeRetrieveResponse, +) from .subscription_price_intervals_response import ( SubscriptionPriceIntervalsResponse as SubscriptionPriceIntervalsResponse, ) diff --git a/src/orb/types/invoice_create_params.py b/src/orb/types/invoice_create_params.py index 78e22ea8..c8e55475 100644 --- a/src/orb/types/invoice_create_params.py +++ b/src/orb/types/invoice_create_params.py @@ -62,7 +62,7 @@ class InvoiceCreateParams(TypedDict, total=False): """ will_auto_issue: bool - """When true, this invoice will automatically be issued upon creation. + """When true, this invoice will be submitted for issuance upon creation. When false, the resulting invoice will require manual review to issue. Defaulted to false. diff --git a/src/orb/types/plan_create_params.py b/src/orb/types/plan_create_params.py index 817ea7f3..ae82aaf8 100644 --- a/src/orb/types/plan_create_params.py +++ b/src/orb/types/plan_create_params.py @@ -433,13 +433,13 @@ class PriceNewPlanMatrixPrice(TypedDict, total=False): class PriceNewPlanTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class PriceNewPlanTieredPriceTieredConfig(TypedDict, total=False): @@ -535,10 +535,10 @@ class PriceNewPlanTieredBpsPriceTieredBpsConfigTier(TypedDict, total=False): """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" diff --git a/src/orb/types/price.py b/src/orb/types/price.py index f04b2874..a53d02c4 100644 --- a/src/orb/types/price.py +++ b/src/orb/types/price.py @@ -710,13 +710,13 @@ class TieredPriceMinimum(BaseModel): class TieredPriceTieredConfigTier(BaseModel): first_unit: float - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: str """Amount per unit""" last_unit: Optional[float] = None - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class TieredPriceTieredConfig(BaseModel): @@ -841,10 +841,10 @@ class TieredBpsPriceTieredBpsConfigTier(BaseModel): """Per-event basis point rate""" minimum_amount: str - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] = None - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] = None """Per unit maximum to charge""" diff --git a/src/orb/types/price_create_params.py b/src/orb/types/price_create_params.py index 3c41a7db..9116a686 100644 --- a/src/orb/types/price_create_params.py +++ b/src/orb/types/price_create_params.py @@ -561,13 +561,13 @@ class NewFloatingTieredPrice(TypedDict, total=False): class NewFloatingTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class NewFloatingTieredPriceTieredConfig(TypedDict, total=False): @@ -660,10 +660,10 @@ class NewFloatingTieredBpsPriceTieredBpsConfigTier(TypedDict, total=False): """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" diff --git a/src/orb/types/subscription.py b/src/orb/types/subscription.py index 85454c04..51911875 100644 --- a/src/orb/types/subscription.py +++ b/src/orb/types/subscription.py @@ -27,6 +27,7 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", @@ -346,6 +347,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -537,6 +542,9 @@ class Subscription(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be diff --git a/src/orb/types/subscription_cancel_response.py b/src/orb/types/subscription_cancel_response.py index 700b020c..7481099d 100644 --- a/src/orb/types/subscription_cancel_response.py +++ b/src/orb/types/subscription_cancel_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionCancelResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionCancelResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionCancelResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionCancelResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_change_apply_params.py b/src/orb/types/subscription_change_apply_params.py new file mode 100644 index 00000000..a0f098fd --- /dev/null +++ b/src/orb/types/subscription_change_apply_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["SubscriptionChangeApplyParams"] + + +class SubscriptionChangeApplyParams(TypedDict, total=False): + description: Optional[str] + """Description to apply to the balance transaction representing this credit.""" + + previously_collected_amount: Optional[str] + """Amount already collected to apply to the customer's balance.""" diff --git a/src/orb/types/subscription_change_apply_response.py b/src/orb/types/subscription_change_apply_response.py new file mode 100644 index 00000000..625a60a7 --- /dev/null +++ b/src/orb/types/subscription_change_apply_response.py @@ -0,0 +1,610 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .plan import Plan +from .price import Price +from .._utils import PropertyInfo +from .invoice import Invoice +from .._models import BaseModel +from .customer import Customer +from .credit_note import CreditNote + +__all__ = [ + "SubscriptionChangeApplyResponse", + "Subscription", + "SubscriptionAdjustmentInterval", + "SubscriptionAdjustmentIntervalAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment", + "SubscriptionBillingCycleAnchorConfiguration", + "SubscriptionDiscountInterval", + "SubscriptionDiscountIntervalAmountDiscountInterval", + "SubscriptionDiscountIntervalPercentageDiscountInterval", + "SubscriptionDiscountIntervalUsageDiscountInterval", + "SubscriptionFixedFeeQuantitySchedule", + "SubscriptionMaximumInterval", + "SubscriptionMinimumInterval", + "SubscriptionPendingSubscriptionChange", + "SubscriptionPriceInterval", + "SubscriptionPriceIntervalFixedFeeQuantityTransition", + "SubscriptionRedeemedCoupon", + "SubscriptionTrialInfo", + "SubscriptionChangedResources", +] + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["usage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + usage_discount: float + """ + The number of usage units by which to discount the price this adjustment applies + to in a given billing period. + """ + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["amount_discount"] + + amount_discount: str + """ + The amount by which to discount the prices this adjustment applies to in a given + billing period. + """ + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["percentage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + percentage_discount: float + """ + The percentage (as a value between 0 and 1) by which to discount the price + intervals this adjustment applies to in a given billing period. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["minimum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + item_id: str + """The item ID that revenue from this minimum will be attributed to.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["maximum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +SubscriptionAdjustmentIntervalAdjustment: TypeAlias = Annotated[ + Union[ + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment, + ], + PropertyInfo(discriminator="adjustment_type"), +] + + +class SubscriptionAdjustmentInterval(BaseModel): + id: str + + adjustment: SubscriptionAdjustmentIntervalAdjustment + + applies_to_price_interval_ids: List[str] + """The price interval IDs that this adjustment applies to.""" + + end_date: Optional[datetime] = None + """The end date of the adjustment interval.""" + + start_date: datetime + """The start date of the adjustment interval.""" + + +class SubscriptionBillingCycleAnchorConfiguration(BaseModel): + day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + month: Optional[int] = None + """The month on which the billing cycle is anchored (e.g. + + a quarterly price anchored in February would have cycles starting February, May, + August, and November). + """ + + year: Optional[int] = None + """The year on which the billing cycle is anchored (e.g. + + a 2 year billing cycle anchored on 2021 would have cycles starting on 2021, + 2023, 2025, etc.). + """ + + +class SubscriptionDiscountIntervalAmountDiscountInterval(BaseModel): + amount_discount: str + """Only available if discount_type is `amount`.""" + + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["amount"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalPercentageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["percentage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + percentage_discount: float + """ + Only available if discount_type is `percentage`.This is a number between 0 + and 1. + """ + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalUsageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["usage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + usage_discount: float + """Only available if discount_type is `usage`. + + Number of usage units that this discount is for + """ + + +SubscriptionDiscountInterval: TypeAlias = Annotated[ + Union[ + SubscriptionDiscountIntervalAmountDiscountInterval, + SubscriptionDiscountIntervalPercentageDiscountInterval, + SubscriptionDiscountIntervalUsageDiscountInterval, + ], + PropertyInfo(discriminator="discount_type"), +] + + +class SubscriptionFixedFeeQuantitySchedule(BaseModel): + end_date: Optional[datetime] = None + + price_id: str + + quantity: float + + start_date: datetime + + +class SubscriptionMaximumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this maximum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this maximum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the maximum interval.""" + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the price intervals + this transform applies to. + """ + + start_date: datetime + """The start date of the maximum interval.""" + + +class SubscriptionMinimumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this minimum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this minimum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the minimum interval.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the price intervals + this minimum applies to. + """ + + start_date: datetime + """The start date of the minimum interval.""" + + +class SubscriptionPendingSubscriptionChange(BaseModel): + id: str + + +class SubscriptionPriceIntervalFixedFeeQuantityTransition(BaseModel): + effective_date: datetime + + price_id: str + + quantity: int + + +class SubscriptionPriceInterval(BaseModel): + id: str + + billing_cycle_day: int + """The day of the month that Orb bills for this price""" + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is exactly the + end of the billing period. Set to null if this price interval is not currently + active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if this price interval is not currently active. + """ + + end_date: Optional[datetime] = None + """The end date of the price interval. + + This is the date that Orb stops billing for this price. + """ + + filter: Optional[str] = None + """An additional filter to apply to usage queries.""" + + fixed_fee_quantity_transitions: Optional[List[SubscriptionPriceIntervalFixedFeeQuantityTransition]] = None + """The fixed fee quantity transitions for this price interval. + + This is only relevant for fixed fees. + """ + + price: Price + """ + The Price resource represents a price that can be billed on a subscription, + resulting in a charge on an invoice in the form of an invoice line item. Prices + take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models + is serialized differently in a given Price object. The model_type field + determines the key for the configuration object that is present. + + For more on the types of prices, see + [the core concepts documentation](/core-concepts#plan-and-price) + """ + + start_date: datetime + """The start date of the price interval. + + This is the date that Orb starts billing for this price. + """ + + usage_customer_ids: Optional[List[str]] = None + """ + A list of customer IDs whose usage events will be aggregated and billed under + this price interval. + """ + + +class SubscriptionRedeemedCoupon(BaseModel): + coupon_id: str + + end_date: Optional[datetime] = None + + start_date: datetime + + +class SubscriptionTrialInfo(BaseModel): + end_date: Optional[datetime] = None + + +class SubscriptionChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + +class Subscription(BaseModel): + id: str + + active_plan_phase_order: Optional[int] = None + """ + The current plan phase that is active, only if the subscription's plan has + phases. + """ + + adjustment_intervals: List[SubscriptionAdjustmentInterval] + """ + The adjustment intervals for this subscription sorted by the start_date of the + adjustment interval. + """ + + auto_collection: Optional[bool] = None + """ + Determines whether issued invoices for this subscription will automatically be + charged with the saved payment method on the due date. This property defaults to + the plan's behavior. If null, defaults to the customer's setting. + """ + + billing_cycle_anchor_configuration: SubscriptionBillingCycleAnchorConfiguration + + billing_cycle_day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + created_at: datetime + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is not part of + the billing period. Set to null for subscriptions that are not currently active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if the subscription is not currently active. + """ + + customer: Customer + """ + A customer is a buyer of your products, and the other party to the billing + relationship. + + In Orb, customers are assigned system generated identifiers automatically, but + it's often desirable to have these match existing identifiers in your system. To + avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further + information about how these aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a + payment provider solution like Stripe. Use the `payment_provider_id` and the + `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard + [IANA timezone database](https://www.iana.org/time-zones)), which defaults to + your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + + default_invoice_memo: Optional[str] = None + """Determines the default memo on this subscriptions' invoices. + + Note that if this is not provided, it is determined by the plan configuration. + """ + + discount_intervals: List[SubscriptionDiscountInterval] + """The discount intervals for this subscription sorted by the start_date.""" + + end_date: Optional[datetime] = None + """The date Orb stops billing for this subscription.""" + + fixed_fee_quantity_schedule: List[SubscriptionFixedFeeQuantitySchedule] + + invoicing_threshold: Optional[str] = None + + maximum_intervals: List[SubscriptionMaximumInterval] + """The maximum intervals for this subscription sorted by the start_date.""" + + metadata: Dict[str, str] + """User specified key-value pairs for the resource. + + If not present, this defaults to an empty dictionary. Individual keys can be + removed by setting the value to `null`, and the entire metadata mapping can be + cleared by setting `metadata` to `null`. + """ + + minimum_intervals: List[SubscriptionMinimumInterval] + """The minimum intervals for this subscription sorted by the start_date.""" + + net_terms: int + """ + Determines the difference between the invoice issue date for subscription + invoices as the date that they are due. A value of `0` here represents that the + invoice is due on issue, whereas a value of `30` represents that the customer + has a month to pay the invoice. + """ + + pending_subscription_change: Optional[SubscriptionPendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + + plan: Plan + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be + subscribed to by a customer. Plans define the billing behavior of the + subscription. You can see more about how to configure prices in the + [Price resource](/reference/price). + """ + + price_intervals: List[SubscriptionPriceInterval] + """The price intervals for this subscription.""" + + redeemed_coupon: Optional[SubscriptionRedeemedCoupon] = None + + start_date: datetime + """The date Orb starts billing for this subscription.""" + + status: Literal["active", "ended", "upcoming"] + + trial_info: SubscriptionTrialInfo + + changed_resources: Optional[SubscriptionChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ + + +class SubscriptionChangeApplyResponse(BaseModel): + id: str + + expiration_time: datetime + """ + Subscription change will be cancelled at this time and can no longer be applied. + """ + + status: Literal["pending", "applied", "cancelled"] + + subscription: Optional[Subscription] = None + + applied_at: Optional[datetime] = None + """When this change was applied.""" + + cancelled_at: Optional[datetime] = None + """When this change was cancelled.""" diff --git a/src/orb/types/subscription_change_cancel_response.py b/src/orb/types/subscription_change_cancel_response.py new file mode 100644 index 00000000..4bb41909 --- /dev/null +++ b/src/orb/types/subscription_change_cancel_response.py @@ -0,0 +1,610 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .plan import Plan +from .price import Price +from .._utils import PropertyInfo +from .invoice import Invoice +from .._models import BaseModel +from .customer import Customer +from .credit_note import CreditNote + +__all__ = [ + "SubscriptionChangeCancelResponse", + "Subscription", + "SubscriptionAdjustmentInterval", + "SubscriptionAdjustmentIntervalAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment", + "SubscriptionBillingCycleAnchorConfiguration", + "SubscriptionDiscountInterval", + "SubscriptionDiscountIntervalAmountDiscountInterval", + "SubscriptionDiscountIntervalPercentageDiscountInterval", + "SubscriptionDiscountIntervalUsageDiscountInterval", + "SubscriptionFixedFeeQuantitySchedule", + "SubscriptionMaximumInterval", + "SubscriptionMinimumInterval", + "SubscriptionPendingSubscriptionChange", + "SubscriptionPriceInterval", + "SubscriptionPriceIntervalFixedFeeQuantityTransition", + "SubscriptionRedeemedCoupon", + "SubscriptionTrialInfo", + "SubscriptionChangedResources", +] + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["usage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + usage_discount: float + """ + The number of usage units by which to discount the price this adjustment applies + to in a given billing period. + """ + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["amount_discount"] + + amount_discount: str + """ + The amount by which to discount the prices this adjustment applies to in a given + billing period. + """ + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["percentage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + percentage_discount: float + """ + The percentage (as a value between 0 and 1) by which to discount the price + intervals this adjustment applies to in a given billing period. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["minimum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + item_id: str + """The item ID that revenue from this minimum will be attributed to.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["maximum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +SubscriptionAdjustmentIntervalAdjustment: TypeAlias = Annotated[ + Union[ + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment, + ], + PropertyInfo(discriminator="adjustment_type"), +] + + +class SubscriptionAdjustmentInterval(BaseModel): + id: str + + adjustment: SubscriptionAdjustmentIntervalAdjustment + + applies_to_price_interval_ids: List[str] + """The price interval IDs that this adjustment applies to.""" + + end_date: Optional[datetime] = None + """The end date of the adjustment interval.""" + + start_date: datetime + """The start date of the adjustment interval.""" + + +class SubscriptionBillingCycleAnchorConfiguration(BaseModel): + day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + month: Optional[int] = None + """The month on which the billing cycle is anchored (e.g. + + a quarterly price anchored in February would have cycles starting February, May, + August, and November). + """ + + year: Optional[int] = None + """The year on which the billing cycle is anchored (e.g. + + a 2 year billing cycle anchored on 2021 would have cycles starting on 2021, + 2023, 2025, etc.). + """ + + +class SubscriptionDiscountIntervalAmountDiscountInterval(BaseModel): + amount_discount: str + """Only available if discount_type is `amount`.""" + + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["amount"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalPercentageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["percentage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + percentage_discount: float + """ + Only available if discount_type is `percentage`.This is a number between 0 + and 1. + """ + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalUsageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["usage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + usage_discount: float + """Only available if discount_type is `usage`. + + Number of usage units that this discount is for + """ + + +SubscriptionDiscountInterval: TypeAlias = Annotated[ + Union[ + SubscriptionDiscountIntervalAmountDiscountInterval, + SubscriptionDiscountIntervalPercentageDiscountInterval, + SubscriptionDiscountIntervalUsageDiscountInterval, + ], + PropertyInfo(discriminator="discount_type"), +] + + +class SubscriptionFixedFeeQuantitySchedule(BaseModel): + end_date: Optional[datetime] = None + + price_id: str + + quantity: float + + start_date: datetime + + +class SubscriptionMaximumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this maximum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this maximum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the maximum interval.""" + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the price intervals + this transform applies to. + """ + + start_date: datetime + """The start date of the maximum interval.""" + + +class SubscriptionMinimumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this minimum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this minimum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the minimum interval.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the price intervals + this minimum applies to. + """ + + start_date: datetime + """The start date of the minimum interval.""" + + +class SubscriptionPendingSubscriptionChange(BaseModel): + id: str + + +class SubscriptionPriceIntervalFixedFeeQuantityTransition(BaseModel): + effective_date: datetime + + price_id: str + + quantity: int + + +class SubscriptionPriceInterval(BaseModel): + id: str + + billing_cycle_day: int + """The day of the month that Orb bills for this price""" + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is exactly the + end of the billing period. Set to null if this price interval is not currently + active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if this price interval is not currently active. + """ + + end_date: Optional[datetime] = None + """The end date of the price interval. + + This is the date that Orb stops billing for this price. + """ + + filter: Optional[str] = None + """An additional filter to apply to usage queries.""" + + fixed_fee_quantity_transitions: Optional[List[SubscriptionPriceIntervalFixedFeeQuantityTransition]] = None + """The fixed fee quantity transitions for this price interval. + + This is only relevant for fixed fees. + """ + + price: Price + """ + The Price resource represents a price that can be billed on a subscription, + resulting in a charge on an invoice in the form of an invoice line item. Prices + take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models + is serialized differently in a given Price object. The model_type field + determines the key for the configuration object that is present. + + For more on the types of prices, see + [the core concepts documentation](/core-concepts#plan-and-price) + """ + + start_date: datetime + """The start date of the price interval. + + This is the date that Orb starts billing for this price. + """ + + usage_customer_ids: Optional[List[str]] = None + """ + A list of customer IDs whose usage events will be aggregated and billed under + this price interval. + """ + + +class SubscriptionRedeemedCoupon(BaseModel): + coupon_id: str + + end_date: Optional[datetime] = None + + start_date: datetime + + +class SubscriptionTrialInfo(BaseModel): + end_date: Optional[datetime] = None + + +class SubscriptionChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + +class Subscription(BaseModel): + id: str + + active_plan_phase_order: Optional[int] = None + """ + The current plan phase that is active, only if the subscription's plan has + phases. + """ + + adjustment_intervals: List[SubscriptionAdjustmentInterval] + """ + The adjustment intervals for this subscription sorted by the start_date of the + adjustment interval. + """ + + auto_collection: Optional[bool] = None + """ + Determines whether issued invoices for this subscription will automatically be + charged with the saved payment method on the due date. This property defaults to + the plan's behavior. If null, defaults to the customer's setting. + """ + + billing_cycle_anchor_configuration: SubscriptionBillingCycleAnchorConfiguration + + billing_cycle_day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + created_at: datetime + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is not part of + the billing period. Set to null for subscriptions that are not currently active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if the subscription is not currently active. + """ + + customer: Customer + """ + A customer is a buyer of your products, and the other party to the billing + relationship. + + In Orb, customers are assigned system generated identifiers automatically, but + it's often desirable to have these match existing identifiers in your system. To + avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further + information about how these aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a + payment provider solution like Stripe. Use the `payment_provider_id` and the + `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard + [IANA timezone database](https://www.iana.org/time-zones)), which defaults to + your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + + default_invoice_memo: Optional[str] = None + """Determines the default memo on this subscriptions' invoices. + + Note that if this is not provided, it is determined by the plan configuration. + """ + + discount_intervals: List[SubscriptionDiscountInterval] + """The discount intervals for this subscription sorted by the start_date.""" + + end_date: Optional[datetime] = None + """The date Orb stops billing for this subscription.""" + + fixed_fee_quantity_schedule: List[SubscriptionFixedFeeQuantitySchedule] + + invoicing_threshold: Optional[str] = None + + maximum_intervals: List[SubscriptionMaximumInterval] + """The maximum intervals for this subscription sorted by the start_date.""" + + metadata: Dict[str, str] + """User specified key-value pairs for the resource. + + If not present, this defaults to an empty dictionary. Individual keys can be + removed by setting the value to `null`, and the entire metadata mapping can be + cleared by setting `metadata` to `null`. + """ + + minimum_intervals: List[SubscriptionMinimumInterval] + """The minimum intervals for this subscription sorted by the start_date.""" + + net_terms: int + """ + Determines the difference between the invoice issue date for subscription + invoices as the date that they are due. A value of `0` here represents that the + invoice is due on issue, whereas a value of `30` represents that the customer + has a month to pay the invoice. + """ + + pending_subscription_change: Optional[SubscriptionPendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + + plan: Plan + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be + subscribed to by a customer. Plans define the billing behavior of the + subscription. You can see more about how to configure prices in the + [Price resource](/reference/price). + """ + + price_intervals: List[SubscriptionPriceInterval] + """The price intervals for this subscription.""" + + redeemed_coupon: Optional[SubscriptionRedeemedCoupon] = None + + start_date: datetime + """The date Orb starts billing for this subscription.""" + + status: Literal["active", "ended", "upcoming"] + + trial_info: SubscriptionTrialInfo + + changed_resources: Optional[SubscriptionChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ + + +class SubscriptionChangeCancelResponse(BaseModel): + id: str + + expiration_time: datetime + """ + Subscription change will be cancelled at this time and can no longer be applied. + """ + + status: Literal["pending", "applied", "cancelled"] + + subscription: Optional[Subscription] = None + + applied_at: Optional[datetime] = None + """When this change was applied.""" + + cancelled_at: Optional[datetime] = None + """When this change was cancelled.""" diff --git a/src/orb/types/subscription_change_retrieve_response.py b/src/orb/types/subscription_change_retrieve_response.py new file mode 100644 index 00000000..04068bf2 --- /dev/null +++ b/src/orb/types/subscription_change_retrieve_response.py @@ -0,0 +1,610 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .plan import Plan +from .price import Price +from .._utils import PropertyInfo +from .invoice import Invoice +from .._models import BaseModel +from .customer import Customer +from .credit_note import CreditNote + +__all__ = [ + "SubscriptionChangeRetrieveResponse", + "Subscription", + "SubscriptionAdjustmentInterval", + "SubscriptionAdjustmentIntervalAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment", + "SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment", + "SubscriptionBillingCycleAnchorConfiguration", + "SubscriptionDiscountInterval", + "SubscriptionDiscountIntervalAmountDiscountInterval", + "SubscriptionDiscountIntervalPercentageDiscountInterval", + "SubscriptionDiscountIntervalUsageDiscountInterval", + "SubscriptionFixedFeeQuantitySchedule", + "SubscriptionMaximumInterval", + "SubscriptionMinimumInterval", + "SubscriptionPendingSubscriptionChange", + "SubscriptionPriceInterval", + "SubscriptionPriceIntervalFixedFeeQuantityTransition", + "SubscriptionRedeemedCoupon", + "SubscriptionTrialInfo", + "SubscriptionChangedResources", +] + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["usage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + usage_discount: float + """ + The number of usage units by which to discount the price this adjustment applies + to in a given billing period. + """ + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["amount_discount"] + + amount_discount: str + """ + The amount by which to discount the prices this adjustment applies to in a given + billing period. + """ + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment(BaseModel): + id: str + + adjustment_type: Literal["percentage_discount"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + percentage_discount: float + """ + The percentage (as a value between 0 and 1) by which to discount the price + intervals this adjustment applies to in a given billing period. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["minimum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + item_id: str + """The item ID that revenue from this minimum will be attributed to.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +class SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment(BaseModel): + id: str + + adjustment_type: Literal["maximum"] + + applies_to_price_ids: List[str] + """The price IDs that this adjustment applies to.""" + + is_invoice_level: bool + """ + True for adjustments that apply to an entire invocice, false for adjustments + that apply to only one price. + """ + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the prices this + adjustment applies to. + """ + + plan_phase_order: Optional[int] = None + """The plan phase in which this adjustment is active.""" + + reason: Optional[str] = None + """The reason for the adjustment.""" + + +SubscriptionAdjustmentIntervalAdjustment: TypeAlias = Annotated[ + Union[ + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseUsageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseAmountDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhasePercentageDiscountAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMinimumAdjustment, + SubscriptionAdjustmentIntervalAdjustmentPlanPhaseMaximumAdjustment, + ], + PropertyInfo(discriminator="adjustment_type"), +] + + +class SubscriptionAdjustmentInterval(BaseModel): + id: str + + adjustment: SubscriptionAdjustmentIntervalAdjustment + + applies_to_price_interval_ids: List[str] + """The price interval IDs that this adjustment applies to.""" + + end_date: Optional[datetime] = None + """The end date of the adjustment interval.""" + + start_date: datetime + """The start date of the adjustment interval.""" + + +class SubscriptionBillingCycleAnchorConfiguration(BaseModel): + day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + month: Optional[int] = None + """The month on which the billing cycle is anchored (e.g. + + a quarterly price anchored in February would have cycles starting February, May, + August, and November). + """ + + year: Optional[int] = None + """The year on which the billing cycle is anchored (e.g. + + a 2 year billing cycle anchored on 2021 would have cycles starting on 2021, + 2023, 2025, etc.). + """ + + +class SubscriptionDiscountIntervalAmountDiscountInterval(BaseModel): + amount_discount: str + """Only available if discount_type is `amount`.""" + + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["amount"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalPercentageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["percentage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + percentage_discount: float + """ + Only available if discount_type is `percentage`.This is a number between 0 + and 1. + """ + + start_date: datetime + """The start date of the discount interval.""" + + +class SubscriptionDiscountIntervalUsageDiscountInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this discount interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this discount interval applies to.""" + + discount_type: Literal["usage"] + + end_date: Optional[datetime] = None + """The end date of the discount interval.""" + + start_date: datetime + """The start date of the discount interval.""" + + usage_discount: float + """Only available if discount_type is `usage`. + + Number of usage units that this discount is for + """ + + +SubscriptionDiscountInterval: TypeAlias = Annotated[ + Union[ + SubscriptionDiscountIntervalAmountDiscountInterval, + SubscriptionDiscountIntervalPercentageDiscountInterval, + SubscriptionDiscountIntervalUsageDiscountInterval, + ], + PropertyInfo(discriminator="discount_type"), +] + + +class SubscriptionFixedFeeQuantitySchedule(BaseModel): + end_date: Optional[datetime] = None + + price_id: str + + quantity: float + + start_date: datetime + + +class SubscriptionMaximumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this maximum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this maximum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the maximum interval.""" + + maximum_amount: str + """ + The maximum amount to charge in a given billing period for the price intervals + this transform applies to. + """ + + start_date: datetime + """The start date of the maximum interval.""" + + +class SubscriptionMinimumInterval(BaseModel): + applies_to_price_ids: List[str] + """The price ids that this minimum interval applies to.""" + + applies_to_price_interval_ids: List[str] + """The price interval ids that this minimum interval applies to.""" + + end_date: Optional[datetime] = None + """The end date of the minimum interval.""" + + minimum_amount: str + """ + The minimum amount to charge in a given billing period for the price intervals + this minimum applies to. + """ + + start_date: datetime + """The start date of the minimum interval.""" + + +class SubscriptionPendingSubscriptionChange(BaseModel): + id: str + + +class SubscriptionPriceIntervalFixedFeeQuantityTransition(BaseModel): + effective_date: datetime + + price_id: str + + quantity: int + + +class SubscriptionPriceInterval(BaseModel): + id: str + + billing_cycle_day: int + """The day of the month that Orb bills for this price""" + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is exactly the + end of the billing period. Set to null if this price interval is not currently + active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if this price interval is not currently active. + """ + + end_date: Optional[datetime] = None + """The end date of the price interval. + + This is the date that Orb stops billing for this price. + """ + + filter: Optional[str] = None + """An additional filter to apply to usage queries.""" + + fixed_fee_quantity_transitions: Optional[List[SubscriptionPriceIntervalFixedFeeQuantityTransition]] = None + """The fixed fee quantity transitions for this price interval. + + This is only relevant for fixed fees. + """ + + price: Price + """ + The Price resource represents a price that can be billed on a subscription, + resulting in a charge on an invoice in the form of an invoice line item. Prices + take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models + is serialized differently in a given Price object. The model_type field + determines the key for the configuration object that is present. + + For more on the types of prices, see + [the core concepts documentation](/core-concepts#plan-and-price) + """ + + start_date: datetime + """The start date of the price interval. + + This is the date that Orb starts billing for this price. + """ + + usage_customer_ids: Optional[List[str]] = None + """ + A list of customer IDs whose usage events will be aggregated and billed under + this price interval. + """ + + +class SubscriptionRedeemedCoupon(BaseModel): + coupon_id: str + + end_date: Optional[datetime] = None + + start_date: datetime + + +class SubscriptionTrialInfo(BaseModel): + end_date: Optional[datetime] = None + + +class SubscriptionChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + +class Subscription(BaseModel): + id: str + + active_plan_phase_order: Optional[int] = None + """ + The current plan phase that is active, only if the subscription's plan has + phases. + """ + + adjustment_intervals: List[SubscriptionAdjustmentInterval] + """ + The adjustment intervals for this subscription sorted by the start_date of the + adjustment interval. + """ + + auto_collection: Optional[bool] = None + """ + Determines whether issued invoices for this subscription will automatically be + charged with the saved payment method on the due date. This property defaults to + the plan's behavior. If null, defaults to the customer's setting. + """ + + billing_cycle_anchor_configuration: SubscriptionBillingCycleAnchorConfiguration + + billing_cycle_day: int + """The day of the month on which the billing cycle is anchored. + + If the maximum number of days in a month is greater than this value, the last + day of the month is the billing cycle day (e.g. billing_cycle_day=31 for April + means the billing period begins on the 30th. + """ + + created_at: datetime + + current_billing_period_end_date: Optional[datetime] = None + """The end of the current billing period. + + This is an exclusive timestamp, such that the instant returned is not part of + the billing period. Set to null for subscriptions that are not currently active. + """ + + current_billing_period_start_date: Optional[datetime] = None + """The start date of the current billing period. + + This is an inclusive timestamp; the instant returned is exactly the beginning of + the billing period. Set to null if the subscription is not currently active. + """ + + customer: Customer + """ + A customer is a buyer of your products, and the other party to the billing + relationship. + + In Orb, customers are assigned system generated identifiers automatically, but + it's often desirable to have these match existing identifiers in your system. To + avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further + information about how these aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a + payment provider solution like Stripe. Use the `payment_provider_id` and the + `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard + [IANA timezone database](https://www.iana.org/time-zones)), which defaults to + your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + + default_invoice_memo: Optional[str] = None + """Determines the default memo on this subscriptions' invoices. + + Note that if this is not provided, it is determined by the plan configuration. + """ + + discount_intervals: List[SubscriptionDiscountInterval] + """The discount intervals for this subscription sorted by the start_date.""" + + end_date: Optional[datetime] = None + """The date Orb stops billing for this subscription.""" + + fixed_fee_quantity_schedule: List[SubscriptionFixedFeeQuantitySchedule] + + invoicing_threshold: Optional[str] = None + + maximum_intervals: List[SubscriptionMaximumInterval] + """The maximum intervals for this subscription sorted by the start_date.""" + + metadata: Dict[str, str] + """User specified key-value pairs for the resource. + + If not present, this defaults to an empty dictionary. Individual keys can be + removed by setting the value to `null`, and the entire metadata mapping can be + cleared by setting `metadata` to `null`. + """ + + minimum_intervals: List[SubscriptionMinimumInterval] + """The minimum intervals for this subscription sorted by the start_date.""" + + net_terms: int + """ + Determines the difference between the invoice issue date for subscription + invoices as the date that they are due. A value of `0` here represents that the + invoice is due on issue, whereas a value of `30` represents that the customer + has a month to pay the invoice. + """ + + pending_subscription_change: Optional[SubscriptionPendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + + plan: Plan + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be + subscribed to by a customer. Plans define the billing behavior of the + subscription. You can see more about how to configure prices in the + [Price resource](/reference/price). + """ + + price_intervals: List[SubscriptionPriceInterval] + """The price intervals for this subscription.""" + + redeemed_coupon: Optional[SubscriptionRedeemedCoupon] = None + + start_date: datetime + """The date Orb starts billing for this subscription.""" + + status: Literal["active", "ended", "upcoming"] + + trial_info: SubscriptionTrialInfo + + changed_resources: Optional[SubscriptionChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ + + +class SubscriptionChangeRetrieveResponse(BaseModel): + id: str + + expiration_time: datetime + """ + Subscription change will be cancelled at this time and can no longer be applied. + """ + + status: Literal["pending", "applied", "cancelled"] + + subscription: Optional[Subscription] = None + + applied_at: Optional[datetime] = None + """When this change was applied.""" + + cancelled_at: Optional[datetime] = None + """When this change was cancelled.""" diff --git a/src/orb/types/subscription_create_params.py b/src/orb/types/subscription_create_params.py index 1caf93ce..cf34ff18 100644 --- a/src/orb/types/subscription_create_params.py +++ b/src/orb/types/subscription_create_params.py @@ -829,13 +829,13 @@ class AddPricePriceNewSubscriptionMatrixPrice(TypedDict, total=False): class AddPricePriceNewSubscriptionTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class AddPricePriceNewSubscriptionTieredPriceTieredConfig(TypedDict, total=False): @@ -937,10 +937,10 @@ class AddPricePriceNewSubscriptionTieredBpsPriceTieredBpsConfigTier(TypedDict, t """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" @@ -3473,13 +3473,13 @@ class ReplacePricePriceNewSubscriptionMatrixPrice(TypedDict, total=False): class ReplacePricePriceNewSubscriptionTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class ReplacePricePriceNewSubscriptionTieredPriceTieredConfig(TypedDict, total=False): @@ -3581,10 +3581,10 @@ class ReplacePricePriceNewSubscriptionTieredBpsPriceTieredBpsConfigTier(TypedDic """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" diff --git a/src/orb/types/subscription_create_response.py b/src/orb/types/subscription_create_response.py index ab422df8..485dae87 100644 --- a/src/orb/types/subscription_create_response.py +++ b/src/orb/types/subscription_create_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionCreateResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionCreateResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionCreateResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionCreateResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_price_intervals_params.py b/src/orb/types/subscription_price_intervals_params.py index ce28d218..b2b29880 100644 --- a/src/orb/types/subscription_price_intervals_params.py +++ b/src/orb/types/subscription_price_intervals_params.py @@ -602,13 +602,13 @@ class AddPriceNewFloatingMatrixWithAllocationPrice(TypedDict, total=False): class AddPriceNewFloatingTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class AddPriceNewFloatingTieredPriceTieredConfig(TypedDict, total=False): @@ -701,10 +701,10 @@ class AddPriceNewFloatingTieredBpsPriceTieredBpsConfigTier(TypedDict, total=Fals """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" diff --git a/src/orb/types/subscription_price_intervals_response.py b/src/orb/types/subscription_price_intervals_response.py index b2652d91..0cea78b4 100644 --- a/src/orb/types/subscription_price_intervals_response.py +++ b/src/orb/types/subscription_price_intervals_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionPriceIntervalsResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionPriceIntervalsResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionPriceIntervalsResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionPriceIntervalsResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_schedule_plan_change_params.py b/src/orb/types/subscription_schedule_plan_change_params.py index 2961e733..ef1afbc4 100644 --- a/src/orb/types/subscription_schedule_plan_change_params.py +++ b/src/orb/types/subscription_schedule_plan_change_params.py @@ -829,13 +829,13 @@ class AddPricePriceNewSubscriptionMatrixPrice(TypedDict, total=False): class AddPricePriceNewSubscriptionTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class AddPricePriceNewSubscriptionTieredPriceTieredConfig(TypedDict, total=False): @@ -937,10 +937,10 @@ class AddPricePriceNewSubscriptionTieredBpsPriceTieredBpsConfigTier(TypedDict, t """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" @@ -3473,13 +3473,13 @@ class ReplacePricePriceNewSubscriptionMatrixPrice(TypedDict, total=False): class ReplacePricePriceNewSubscriptionTieredPriceTieredConfigTier(TypedDict, total=False): first_unit: Required[float] - """Inclusive tier starting value""" + """Exclusive tier starting value""" unit_amount: Required[str] """Amount per unit""" last_unit: Optional[float] - """Exclusive tier ending value. If null, this is treated as the last tier""" + """Inclusive tier ending value. If null, this is treated as the last tier""" class ReplacePricePriceNewSubscriptionTieredPriceTieredConfig(TypedDict, total=False): @@ -3581,10 +3581,10 @@ class ReplacePricePriceNewSubscriptionTieredBpsPriceTieredBpsConfigTier(TypedDic """Per-event basis point rate""" minimum_amount: Required[str] - """Inclusive tier starting value""" + """Exclusive tier starting value""" maximum_amount: Optional[str] - """Exclusive tier ending value""" + """Inclusive tier ending value""" per_unit_maximum: Optional[str] """Per unit maximum to charge""" diff --git a/src/orb/types/subscription_schedule_plan_change_response.py b/src/orb/types/subscription_schedule_plan_change_response.py index 669fe675..5bcadf14 100644 --- a/src/orb/types/subscription_schedule_plan_change_response.py +++ b/src/orb/types/subscription_schedule_plan_change_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionSchedulePlanChangeResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionSchedulePlanChangeResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionSchedulePlanChangeResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionSchedulePlanChangeResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_trigger_phase_response.py b/src/orb/types/subscription_trigger_phase_response.py index 55c038c5..bab4f432 100644 --- a/src/orb/types/subscription_trigger_phase_response.py +++ b/src/orb/types/subscription_trigger_phase_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionTriggerPhaseResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionTriggerPhaseResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionTriggerPhaseResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionTriggerPhaseResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_unschedule_cancellation_response.py b/src/orb/types/subscription_unschedule_cancellation_response.py index a6629fd9..beca6a53 100644 --- a/src/orb/types/subscription_unschedule_cancellation_response.py +++ b/src/orb/types/subscription_unschedule_cancellation_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionUnscheduleCancellationResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionUnscheduleCancellationResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionUnscheduleCancellationResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionUnscheduleCancellationResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_unschedule_fixed_fee_quantity_updates_response.py b/src/orb/types/subscription_unschedule_fixed_fee_quantity_updates_response.py index 910e45d4..d17a4423 100644 --- a/src/orb/types/subscription_unschedule_fixed_fee_quantity_updates_response.py +++ b/src/orb/types/subscription_unschedule_fixed_fee_quantity_updates_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionUnscheduleFixedFeeQuantityUpdatesResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionUnscheduleFixedFeeQuantityUpdatesResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionUnscheduleFixedFeeQuantityUpdatesResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionUnscheduleFixedFeeQuantityUpdatesResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_unschedule_pending_plan_changes_response.py b/src/orb/types/subscription_unschedule_pending_plan_changes_response.py index 9d7342a9..96a76755 100644 --- a/src/orb/types/subscription_unschedule_pending_plan_changes_response.py +++ b/src/orb/types/subscription_unschedule_pending_plan_changes_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionUnschedulePendingPlanChangesResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionUnschedulePendingPlanChangesResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionUnschedulePendingPlanChangesResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionUnschedulePendingPlanChangesResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_update_fixed_fee_quantity_response.py b/src/orb/types/subscription_update_fixed_fee_quantity_response.py index 74110564..2b86b5c4 100644 --- a/src/orb/types/subscription_update_fixed_fee_quantity_response.py +++ b/src/orb/types/subscription_update_fixed_fee_quantity_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionUpdateFixedFeeQuantityResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionUpdateFixedFeeQuantityResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionUpdateFixedFeeQuantityResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionUpdateFixedFeeQuantityResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/src/orb/types/subscription_update_trial_response.py b/src/orb/types/subscription_update_trial_response.py index 2b8ae20e..53e38304 100644 --- a/src/orb/types/subscription_update_trial_response.py +++ b/src/orb/types/subscription_update_trial_response.py @@ -7,8 +7,10 @@ from .plan import Plan from .price import Price from .._utils import PropertyInfo +from .invoice import Invoice from .._models import BaseModel from .customer import Customer +from .credit_note import CreditNote __all__ = [ "SubscriptionUpdateTrialResponse", @@ -27,10 +29,12 @@ "FixedFeeQuantitySchedule", "MaximumInterval", "MinimumInterval", + "PendingSubscriptionChange", "PriceInterval", "PriceIntervalFixedFeeQuantityTransition", "RedeemedCoupon", "TrialInfo", + "ChangedResources", ] @@ -346,6 +350,10 @@ class MinimumInterval(BaseModel): """The start date of the minimum interval.""" +class PendingSubscriptionChange(BaseModel): + id: str + + class PriceIntervalFixedFeeQuantityTransition(BaseModel): effective_date: datetime @@ -429,6 +437,20 @@ class TrialInfo(BaseModel): end_date: Optional[datetime] = None +class ChangedResources(BaseModel): + created_credit_notes: List[CreditNote] + """The credit notes that were created as part of this operation.""" + + created_invoices: List[Invoice] + """The invoices that were created as part of this operation.""" + + voided_credit_notes: List[CreditNote] + """The credit notes that were voided as part of this operation.""" + + voided_invoices: List[Invoice] + """The invoices that were voided as part of this operation.""" + + class SubscriptionUpdateTrialResponse(BaseModel): id: str @@ -537,6 +559,9 @@ class SubscriptionUpdateTrialResponse(BaseModel): has a month to pay the invoice. """ + pending_subscription_change: Optional[PendingSubscriptionChange] = None + """A pending subscription change if one exists on this subscription.""" + plan: Plan """ The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be @@ -556,3 +581,10 @@ class SubscriptionUpdateTrialResponse(BaseModel): status: Literal["active", "ended", "upcoming"] trial_info: TrialInfo + + changed_resources: Optional[ChangedResources] = None + """The resources that were changed as part of this operation. + + Only present when fetched through the subscription changes API or if the + `include_changed_resources` parameter was passed in the request. + """ diff --git a/tests/api_resources/test_subscription_changes.py b/tests/api_resources/test_subscription_changes.py new file mode 100644 index 00000000..c362679e --- /dev/null +++ b/tests/api_resources/test_subscription_changes.py @@ -0,0 +1,284 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from orb import Orb, AsyncOrb +from orb.types import ( + SubscriptionChangeApplyResponse, + SubscriptionChangeCancelResponse, + SubscriptionChangeRetrieveResponse, +) +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSubscriptionChanges: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Orb) -> None: + subscription_change = client.subscription_changes.retrieve( + "subscription_change_id", + ) + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Orb) -> None: + response = client.subscription_changes.with_raw_response.retrieve( + "subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Orb) -> None: + with client.subscription_changes.with_streaming_response.retrieve( + "subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Orb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + client.subscription_changes.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_apply(self, client: Orb) -> None: + subscription_change = client.subscription_changes.apply( + subscription_change_id="subscription_change_id", + ) + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + def test_method_apply_with_all_params(self, client: Orb) -> None: + subscription_change = client.subscription_changes.apply( + subscription_change_id="subscription_change_id", + description="description", + previously_collected_amount="previously_collected_amount", + ) + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + def test_raw_response_apply(self, client: Orb) -> None: + response = client.subscription_changes.with_raw_response.apply( + subscription_change_id="subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + def test_streaming_response_apply(self, client: Orb) -> None: + with client.subscription_changes.with_streaming_response.apply( + subscription_change_id="subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_apply(self, client: Orb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + client.subscription_changes.with_raw_response.apply( + subscription_change_id="", + ) + + @parametrize + def test_method_cancel(self, client: Orb) -> None: + subscription_change = client.subscription_changes.cancel( + "subscription_change_id", + ) + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: Orb) -> None: + response = client.subscription_changes.with_raw_response.cancel( + "subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: Orb) -> None: + with client.subscription_changes.with_streaming_response.cancel( + "subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: Orb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + client.subscription_changes.with_raw_response.cancel( + "", + ) + + +class TestAsyncSubscriptionChanges: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOrb) -> None: + subscription_change = await async_client.subscription_changes.retrieve( + "subscription_change_id", + ) + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOrb) -> None: + response = await async_client.subscription_changes.with_raw_response.retrieve( + "subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOrb) -> None: + async with async_client.subscription_changes.with_streaming_response.retrieve( + "subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = await response.parse() + assert_matches_type(SubscriptionChangeRetrieveResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOrb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + await async_client.subscription_changes.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_apply(self, async_client: AsyncOrb) -> None: + subscription_change = await async_client.subscription_changes.apply( + subscription_change_id="subscription_change_id", + ) + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + async def test_method_apply_with_all_params(self, async_client: AsyncOrb) -> None: + subscription_change = await async_client.subscription_changes.apply( + subscription_change_id="subscription_change_id", + description="description", + previously_collected_amount="previously_collected_amount", + ) + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + async def test_raw_response_apply(self, async_client: AsyncOrb) -> None: + response = await async_client.subscription_changes.with_raw_response.apply( + subscription_change_id="subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + @parametrize + async def test_streaming_response_apply(self, async_client: AsyncOrb) -> None: + async with async_client.subscription_changes.with_streaming_response.apply( + subscription_change_id="subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = await response.parse() + assert_matches_type(SubscriptionChangeApplyResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_apply(self, async_client: AsyncOrb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + await async_client.subscription_changes.with_raw_response.apply( + subscription_change_id="", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncOrb) -> None: + subscription_change = await async_client.subscription_changes.cancel( + "subscription_change_id", + ) + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOrb) -> None: + response = await async_client.subscription_changes.with_raw_response.cancel( + "subscription_change_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription_change = response.parse() + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOrb) -> None: + async with async_client.subscription_changes.with_streaming_response.cancel( + "subscription_change_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription_change = await response.parse() + assert_matches_type(SubscriptionChangeCancelResponse, subscription_change, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOrb) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `subscription_change_id` but received ''" + ): + await async_client.subscription_changes.with_raw_response.cancel( + "", + ) diff --git a/tests/api_resources/test_subscriptions.py b/tests/api_resources/test_subscriptions.py index dbd2dc56..1235356b 100644 --- a/tests/api_resources/test_subscriptions.py +++ b/tests/api_resources/test_subscriptions.py @@ -59,7 +59,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: { "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -153,7 +153,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: "replaces_price_id": "replaces_price_id", "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -580,7 +580,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -731,7 +731,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: { "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -820,7 +820,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: "replaces_price_id": "replaces_price_id", "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -1205,7 +1205,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No { "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -1299,7 +1299,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No "replaces_price_id": "replaces_price_id", "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -1726,7 +1726,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -1877,7 +1877,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A { "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, @@ -1966,7 +1966,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A "replaces_price_id": "replaces_price_id", "allocation_price": { "amount": "10.00", - "cadence": "one_time", + "cadence": "monthly", "currency": "USD", "expires_at_end_of_cadence": True, }, diff --git a/tests/test_transform.py b/tests/test_transform.py index dd25243b..c505b930 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -432,3 +432,15 @@ async def test_base64_file_input(use_async: bool) -> None: assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3]