diff --git a/lago_python_client/customers/clients.py b/lago_python_client/customers/clients.py index cae9f1a8..250a37e5 100644 --- a/lago_python_client/customers/clients.py +++ b/lago_python_client/customers/clients.py @@ -10,6 +10,7 @@ ) from ..models.customer import CustomerResponse from ..models.customer_usage import CustomerUsageResponse +from ..models.customer_projected_usage import CustomerProjectedUsageResponse from ..services.request import make_headers, make_url, send_get_request, send_post_request, QueryPairs from ..services.response import ( get_response_data, @@ -56,6 +57,27 @@ def current_usage( data=get_response_data(response=api_response, key="customer_usage"), ) + def projected_usage( + self, resource_id: str, external_subscription_id: str, apply_taxes: Optional[str] = None + ) -> CustomerProjectedUsageResponse: + query_params = {"external_subscription_id": external_subscription_id} + if apply_taxes is not None: + query_params["apply_taxes"] = apply_taxes + + api_response: Response = send_get_request( + url=make_url( + origin=self.base_url, + path_parts=(self.API_RESOURCE, resource_id, "projected_usage"), + query_pairs=query_params, + ), + headers=make_headers(api_key=self.api_key), + ) + + return prepare_object_response( + response_model=CustomerProjectedUsageResponse, + data=get_response_data(response=api_response, key="customer_projected_usage"), + ) + def past_usage( self, resource_id: str, diff --git a/lago_python_client/models/customer_projected_usage.py b/lago_python_client/models/customer_projected_usage.py new file mode 100644 index 00000000..9ec5c3b6 --- /dev/null +++ b/lago_python_client/models/customer_projected_usage.py @@ -0,0 +1,73 @@ +from typing import Dict, List, Optional + +from lago_python_client.base_model import BaseModel + +from ..base_model import BaseResponseModel + + +class Metric(BaseModel): + lago_id: str + name: str + code: str + aggregation_type: str + + +class PricingUnitDetails(BaseModel): + amount_cents: int + short_name: str + conversion_rate: float + + +class ProjectedChargeFilterUsage(BaseModel): + invoice_display_name: Optional[str] + values: Dict[str, List[str]] + units: str + projected_units: str + amount_cents: int + projected_amount_cents: int + events_count: int + pricing_unit_details: Optional[PricingUnitDetails] + + +class ChargeObject(BaseModel): + lago_id: str + charge_model: str + invoice_display_name: Optional[str] + + +class ProjectedGroupedUsage(BaseModel): + amount_cents: int + projected_amount_cents: int + events_count: int + units: str + projected_units: str + grouped_by: Dict[str, str] + filters: List[ProjectedChargeFilterUsage] + pricing_unit_details: Optional[PricingUnitDetails] + + +class ProjectedChargeUsage(BaseModel): + units: str + projected_units: str + events_count: int + amount_cents: int + projected_amount_cents: int + amount_currency: str + charge: ChargeObject + billable_metric: Metric + filters: List[ProjectedChargeFilterUsage] + grouped_usage: Optional[List[ProjectedGroupedUsage]] + pricing_unit_details: Optional[PricingUnitDetails] + + +class CustomerProjectedUsageResponse(BaseResponseModel): + from_datetime: str + to_datetime: str + issuing_date: str + invoice_id: Optional[str] + currency: str + amount_cents: int + projected_amount_cents: int + total_amount_cents: int + taxes_amount_cents: int + charges_usage: List[ProjectedChargeUsage] diff --git a/lago_python_client/models/customer_usage.py b/lago_python_client/models/customer_usage.py index ccfcc72b..5783656c 100644 --- a/lago_python_client/models/customer_usage.py +++ b/lago_python_client/models/customer_usage.py @@ -22,9 +22,7 @@ class ChargeFilterUsage(BaseModel): invoice_display_name: Optional[str] values: Dict[str, List[str]] units: str - projected_units: str amount_cents: int - projected_amount_cents: int events_count: int pricing_unit_details: Optional[PricingUnitDetails] @@ -37,10 +35,8 @@ class ChargeObject(BaseModel): class GroupedUsage(BaseModel): amount_cents: int - projected_amount_cents: int events_count: int units: str - projected_units: str grouped_by: Dict[str, str] filters: List[ChargeFilterUsage] pricing_unit_details: Optional[PricingUnitDetails] @@ -48,10 +44,8 @@ class GroupedUsage(BaseModel): class ChargeUsage(BaseModel): units: str - projected_units: str events_count: int amount_cents: int - projected_amount_cents: int amount_currency: str charge: ChargeObject billable_metric: Metric @@ -67,7 +61,6 @@ class CustomerUsageResponse(BaseResponseModel): invoice_id: Optional[str] currency: str amount_cents: int - projected_amount_cents: int total_amount_cents: int taxes_amount_cents: int charges_usage: List[ChargeUsage] diff --git a/tests/fixtures/customer_past_usage.json b/tests/fixtures/customer_past_usage.json index 839f4b6e..5fb422ac 100644 --- a/tests/fixtures/customer_past_usage.json +++ b/tests/fixtures/customer_past_usage.json @@ -7,16 +7,13 @@ "lago_invoice_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", "currency": "EUR", "amount_cents": 123, - "projected_amount_cents": 0, "total_amount_cents": 123, "taxes_amount_cents": 0, "charges_usage": [ { "units": "1.0", - "projected_units": "0.0", "events_count": 1, "amount_cents": 123, - "projected_amount_cents": 0, "amount_currency": "EUR", "charge": { "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", @@ -36,10 +33,8 @@ "filters": [ { "units": "3.0", - "projected_units": "0.0", "events_count": 1, "amount_cents": 123, - "projected_amount_cents": 0, "values": { "country": [ "france" diff --git a/tests/fixtures/customer_projected_usage.json b/tests/fixtures/customer_projected_usage.json new file mode 100644 index 00000000..235f1bad --- /dev/null +++ b/tests/fixtures/customer_projected_usage.json @@ -0,0 +1,90 @@ +{ + "customer_projected_usage": { + "from_datetime": "2022-07-01T00:00:00Z", + "to_datetime": "2022-07-31T23:59:59Z", + "issuing_date": "2022-08-01", + "invoice_id": null, + "currency": "EUR", + "amount_cents": 123, + "projected_amount_cents": 256, + "total_amount_cents": 123, + "taxes_amount_cents": 0, + "charges_usage": [ + { + "units": "1.0", + "projected_units": "2.0", + "events_count": 1, + "amount_cents": 123, + "projected_amount_cents": 256, + "amount_currency": "EUR", + "charge": { + "lago_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", + "charge_model": "graduated", + "invoice_display_name": "add_on_invoice_display_name" + }, + "billable_metric": { + "lago_id": "99a6094e-199b-4101-896a-54e927ce7bd7", + "name": "Usage metric", + "code": "usage_metric", + "aggregation_type": "sum" + }, + "pricing_unit_details": { + "amount_cents": 123, + "short_name": "CR", + "conversion_rate": 1.0 + }, + "filters": [ + { + "units": "3.0", + "projected_units": "4.0", + "events_count": 1, + "amount_cents": 123, + "projected_amount_cents": 256, + "values": { + "country": [ + "france" + ] + }, + "pricing_unit_details": { + "amount_cents": 123, + "short_name": "CR", + "conversion_rate": 1.0 + } + } + ], + "grouped_usage": [ + { + "amount_cents": 123, + "projected_amount_cents": 256, + "events_count": 1, + "units": "3.0", + "projected_units": "4.0", + "grouped_by": { + "agent_name": "aragorn" + }, + "pricing_unit_details": { + "amount_cents": 456, + "short_name": "CR", + "conversion_rate": 1.2 + }, + "filters": [ + { + "units": "3.0", + "projected_units": "4.0", + "events_count": 1, + "amount_cents": 123, + "projected_amount_cents": 256, + "values": { + "country": [ + "france" + ] + }, + "pricing_unit_details": null + } + ] + } + ] + } + ] + } +} diff --git a/tests/fixtures/customer_usage.json b/tests/fixtures/customer_usage.json index 4a43211c..12f7fb9d 100644 --- a/tests/fixtures/customer_usage.json +++ b/tests/fixtures/customer_usage.json @@ -6,16 +6,13 @@ "invoice_id": null, "currency": "EUR", "amount_cents": 123, - "projected_amount_cents": 256, "total_amount_cents": 123, "taxes_amount_cents": 0, "charges_usage": [ { "units": "1.0", - "projected_units": "2.0", "events_count": 1, "amount_cents": 123, - "projected_amount_cents": 256, "amount_currency": "EUR", "charge": { "lago_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", @@ -36,10 +33,8 @@ "filters": [ { "units": "3.0", - "projected_units": "4.0", "events_count": 1, "amount_cents": 123, - "projected_amount_cents": 256, "values": { "country": [ "france" @@ -55,10 +50,8 @@ "grouped_usage": [ { "amount_cents": 123, - "projected_amount_cents": 256, "events_count": 1, "units": "3.0", - "projected_units": "4.0", "grouped_by": { "agent_name": "aragorn" }, @@ -70,10 +63,8 @@ "filters": [ { "units": "3.0", - "projected_units": "4.0", "events_count": 1, "amount_cents": 123, - "projected_amount_cents": 256, "values": { "country": [ "france" diff --git a/tests/test_customer_client.py b/tests/test_customer_client.py index 39cb04b5..ec0269be 100644 --- a/tests/test_customer_client.py +++ b/tests/test_customer_client.py @@ -131,6 +131,29 @@ def test_valid_current_usage(httpx_mock: HTTPXMock): assert response.charges_usage[0].filters[0].values["country"] == ["france"] +def test_valid_projected_usage(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="GET", + url="https://api.getlago.com/api/v1/customers/external_customer_id/projected_usage?external_subscription_id=123", + content=mock_response("customer_projected_usage"), + ) + response = client.customers.projected_usage("external_customer_id", "123") + + assert response.from_datetime == "2022-07-01T00:00:00Z" + assert len(response.charges_usage) == 1 + assert response.charges_usage[0].units == "1.0" + assert response.charges_usage[0].projected_units == "2.0" + assert response.charges_usage[0].amount_cents == 123 + assert response.charges_usage[0].projected_amount_cents == 256 + assert response.charges_usage[0].amount_currency == "EUR" + assert response.charges_usage[0].charge.lago_id == "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba" + assert response.charges_usage[0].charge.charge_model == "graduated" + assert response.charges_usage[0].charge.invoice_display_name == "add_on_invoice_display_name" + assert response.charges_usage[0].billable_metric.lago_id == "99a6094e-199b-4101-896a-54e927ce7bd7" + + def test_valid_current_usage_without_taxes(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")