diff --git a/lago_python_client/client.py b/lago_python_client/client.py index 8a6308f..f115f14 100644 --- a/lago_python_client/client.py +++ b/lago_python_client/client.py @@ -80,7 +80,7 @@ def __init__( @property def base_api_url(self) -> str: - return urljoin(self.api_url if self.api_url else Client.BASE_URL, Client.API_PATH) + return urljoin(self.api_url or Client.BASE_URL, Client.API_PATH) @property def base_ingest_api_url(self) -> str: diff --git a/lago_python_client/models/credit_note.py b/lago_python_client/models/credit_note.py index 3a62049..5f6a014 100644 --- a/lago_python_client/models/credit_note.py +++ b/lago_python_client/models/credit_note.py @@ -53,7 +53,7 @@ class CreditNoteResponse(BaseResponseModel): balance_amount_cents: int refund_amount_cents: int offset_amount_cents: int - taxes_amount_cents: str + taxes_amount_cents: int taxes_rate: float sub_total_excluding_taxes_amount_cents: int coupons_adjustment_amount_cents: int @@ -76,7 +76,12 @@ class Items(BaseModel): class CreditNote(BaseModel): + invoice_id: Optional[str] reason: Optional[str] + description: Optional[str] + credit_amount_cents: Optional[int] + refund_amount_cents: Optional[int] + offset_amount_cents: Optional[int] items: Optional[Items] metadata: Optional[Dict[str, Optional[str]]] @@ -117,7 +122,7 @@ class CreditNoteEstimatedResponse(BaseResponseModel): max_creditable_amount_cents: int max_refundable_amount_cents: int max_offsettable_amount_cents: int - taxes_amount_cents: str + taxes_amount_cents: int taxes_rate: float sub_total_excluding_taxes_amount_cents: int coupons_adjustment_amount_cents: int diff --git a/lago_python_client/models/fee.py b/lago_python_client/models/fee.py index bf25923..7c4f547 100644 --- a/lago_python_client/models/fee.py +++ b/lago_python_client/models/fee.py @@ -7,7 +7,6 @@ class Fee(BaseModel): payment_status: Optional[str] - invoice_display_name: Optional[str] class FeeAppliedTax(BaseResponseModel): @@ -46,18 +45,21 @@ class FeeResponse(BaseResponseModel): lago_invoice_id: Optional[str] lago_true_up_fee_id: Optional[str] lago_true_up_parent_fee_id: Optional[str] + lago_original_fee_id: Optional[str] external_subscription_id: Optional[str] + lago_customer_id: Optional[str] + external_customer_id: Optional[str] amount_cents: Optional[int] amount_currency: Optional[str] taxes_amount_cents: Optional[int] taxes_rate: Optional[float] total_amount_cents: Optional[int] - unit_amount_cents: Optional[int] # deprecated precise_unit_amount: Optional[str] sub_total_excluding_taxes_amount_cents: Optional[int] sub_total_excluding_taxes_precise_amount_cents: Optional[str] precise_amount: Optional[str] precise_total_amount: Optional[str] + precise_coupons_amount_cents: Optional[str] taxes_precise_amount: Optional[str] total_amount_currency: Optional[str] units: Optional[str] @@ -68,7 +70,8 @@ class FeeResponse(BaseResponseModel): description: Optional[str] pay_in_advance: Optional[bool] invoiceable: Optional[bool] - invoice_display_name: Optional[str] + self_billed: Optional[bool] + event_transaction_id: Optional[str] succeeded_at: Optional[str] failed_at: Optional[str] refunded_at: Optional[str] @@ -77,7 +80,6 @@ class FeeResponse(BaseResponseModel): amount_details: Optional[Dict[str, Any]] pricing_unit_details: Optional[PricingUnitDetails] presentation_breakdowns: Optional[PresentationBreakdowns] - billing_entity_code: Optional[str] item: Optional[InvoiceItemResponse] applied_taxes: Optional[FeeAppliedTaxes] diff --git a/lago_python_client/models/invoice.py b/lago_python_client/models/invoice.py index 54e7968..02806af 100644 --- a/lago_python_client/models/invoice.py +++ b/lago_python_client/models/invoice.py @@ -136,11 +136,16 @@ class InvoiceResponse(BaseResponseModel): sub_total_including_taxes_amount_cents: int total_amount_cents: int total_due_amount_cents: int + total_paid_amount_cents: Optional[int] + total_offsetted_credit_note_amount_cents: Optional[int] prepaid_credit_amount_cents: int prepaid_granted_credit_amount_cents: Optional[int] prepaid_purchased_credit_amount_cents: Optional[int] + self_billed: Optional[bool] + voided_at: Optional[str] file_url: Optional[str] + xml_url: Optional[str] web_url: Optional[str] customer: Optional[CustomerResponse] billing_periods: Optional[BillingPeriodsResponse] diff --git a/lago_python_client/models/tax.py b/lago_python_client/models/tax.py index 3248649..40760ec 100644 --- a/lago_python_client/models/tax.py +++ b/lago_python_client/models/tax.py @@ -24,6 +24,7 @@ class TaxResponse(BaseResponseModel): rate: float description: Optional[str] applied_to_organization: Optional[bool] + applied_to_billing_entities_codes: Optional[List[str]] created_at: str diff --git a/tests/fixtures/fee.json b/tests/fixtures/fee.json index 8983ba5..1d0678e 100644 --- a/tests/fixtures/fee.json +++ b/tests/fixtures/fee.json @@ -1,13 +1,16 @@ { "fee": { "lago_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", - "billing_entity_code": "test-company", "lago_charge_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", "lago_charge_filter_id": "1a2d9c8d-5875-4688-9854-5ccfd414bc5e", "lago_invoice_id": "1a2d9c8d-5875-4688-9854-5ccfd414bc5e", "lago_true_up_fee_id": "1a2d9c8d-5875-4688-9854-5ccfd414bc5e", "lago_true_up_parent_fee_id": null, - "invoice_display_name": "fee_invoice_display_name", + "lago_original_fee_id": null, + "lago_customer_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "external_customer_id": "external_customer_id", + "self_billed": false, + "event_transaction_id": "transaction_1234567890", "item": { "type": "charge", "code": "fee_code", @@ -28,6 +31,7 @@ "units": "10.0", "sub_total_excluding_taxes_amount_cents": 120, "sub_total_excluding_taxes_precise_amount_cents": "120.00", + "precise_coupons_amount_cents": "0.0", "events_count": 10, "pricing_unit_details": { "lago_pricing_unit_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", diff --git a/tests/fixtures/invoice.json b/tests/fixtures/invoice.json index 72f0ced..0e6688e 100644 --- a/tests/fixtures/invoice.json +++ b/tests/fixtures/invoice.json @@ -26,6 +26,11 @@ "prepaid_purchased_credit_amount_cents": 0, "total_amount_cents": 120, "total_due_amount_cents": 120, + "total_paid_amount_cents": 60, + "total_offsetted_credit_note_amount_cents": 10, + "self_billed": false, + "voided_at": null, + "xml_url": "https://getlago.com/invoice/xml", "metadata": [ { "key": "key", diff --git a/tests/fixtures/invoice_index.json b/tests/fixtures/invoice_index.json index d6ac26a..f005d66 100644 --- a/tests/fixtures/invoice_index.json +++ b/tests/fixtures/invoice_index.json @@ -25,6 +25,11 @@ "prepaid_purchased_credit_amount_cents": 0, "total_amount_cents": 120, "total_due_amount_cents": 120, + "total_paid_amount_cents": 0, + "total_offsetted_credit_note_amount_cents": 0, + "self_billed": false, + "voided_at": null, + "xml_url": null, "applied_taxes": [ { "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", @@ -65,6 +70,11 @@ "prepaid_purchased_credit_amount_cents": 0, "total_amount_cents": 120, "total_due_amount_cents": 120, + "total_paid_amount_cents": 0, + "total_offsetted_credit_note_amount_cents": 0, + "self_billed": false, + "voided_at": null, + "xml_url": null, "applied_taxes": [ { "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", diff --git a/tests/fixtures/one_off_invoice.json b/tests/fixtures/one_off_invoice.json index 01f4916..a710a59 100644 --- a/tests/fixtures/one_off_invoice.json +++ b/tests/fixtures/one_off_invoice.json @@ -24,6 +24,11 @@ "prepaid_purchased_credit_amount_cents": 0, "total_amount_cents": 120, "total_due_amount_cents": 120, + "total_paid_amount_cents": 0, + "total_offsetted_credit_note_amount_cents": 0, + "self_billed": false, + "voided_at": null, + "xml_url": null, "metadata": [ { "key": "key", @@ -97,7 +102,6 @@ "fees": [ { "lago_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", - "invoice_display_name": "fee_invoice_display_name", "item": { "type": "one_off", "code": "one", diff --git a/tests/fixtures/tax.json b/tests/fixtures/tax.json index 9ad663d..6b2e193 100644 --- a/tests/fixtures/tax.json +++ b/tests/fixtures/tax.json @@ -6,6 +6,7 @@ "rate": 15.0, "description": "tax_desc", "applied_to_organization": false, + "applied_to_billing_entities_codes": ["acme_corp", "foo_bar"], "created_at": "2022-04-29T08:59:51Z" } } diff --git a/tests/fixtures/voided_invoice.json b/tests/fixtures/voided_invoice.json index 5ba53e1..f015302 100644 --- a/tests/fixtures/voided_invoice.json +++ b/tests/fixtures/voided_invoice.json @@ -24,6 +24,11 @@ "prepaid_purchased_credit_amount_cents": 0, "total_amount_cents": 120, "total_due_amount_cents": 120, + "total_paid_amount_cents": 0, + "total_offsetted_credit_note_amount_cents": 0, + "self_billed": false, + "voided_at": "2022-04-30T10:00:00Z", + "xml_url": null, "metadata": [ { "key": "key", diff --git a/tests/test_credit_note_client.py b/tests/test_credit_note_client.py index 2102977..bcb9110 100644 --- a/tests/test_credit_note_client.py +++ b/tests/test_credit_note_client.py @@ -19,7 +19,14 @@ def credit_note_object(): item2 = Item(fee="fee_id_2", amount_cents=5) - return CreditNote(lago_id="credit_note_id", reason="other", items=Items(__root__=[item1, item2])) + return CreditNote( + invoice_id="invoice_id", + reason="other", + description="Credit note description", + credit_amount_cents=10, + refund_amount_cents=5, + items=Items(__root__=[item1, item2]), + ) def credit_note_update_object(): diff --git a/tests/test_fee_client.py b/tests/test_fee_client.py index fe37504..3e85e71 100644 --- a/tests/test_fee_client.py +++ b/tests/test_fee_client.py @@ -16,7 +16,6 @@ def mock_response(): def test_valid_find_fee_request(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") identifier = "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba" - fee_invoice_display_name = "fee_invoice_display_name" charge_invoice_display_name = "charge_invoice_display_name" httpx_mock.add_response( @@ -27,7 +26,11 @@ def test_valid_find_fee_request(httpx_mock: HTTPXMock): response = client.fees.find(identifier) assert response.lago_id == identifier - assert response.invoice_display_name == fee_invoice_display_name + assert response.lago_customer_id == "1a901a90-1a90-1a90-1a90-1a901a901a90" + assert response.external_customer_id == "external_customer_id" + assert response.self_billed is False + assert response.event_transaction_id == "transaction_1234567890" + assert response.precise_coupons_amount_cents == "0.0" assert response.item.invoice_display_name == charge_invoice_display_name assert response.presentation_breakdowns.__root__[0].presentation_by["team"] == "engineering" assert response.presentation_breakdowns.__root__[0].units == "10.0" diff --git a/tests/test_invoice_client.py b/tests/test_invoice_client.py index 2cba8cf..c1f43f6 100644 --- a/tests/test_invoice_client.py +++ b/tests/test_invoice_client.py @@ -84,7 +84,6 @@ def test_valid_create_invoice_request(httpx_mock: HTTPXMock): assert response.lago_id == "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba" assert response.invoice_type == "one_off" - assert response.fees.__root__[0].invoice_display_name == "fee_invoice_display_name" assert response.fees.__root__[0].precise_unit_amount == "9.52" assert response.fees.__root__[0].item.invoice_display_name == "one_off_invoice_display_name" assert response.fees.__root__[0].amount_details == {} @@ -171,6 +170,11 @@ def test_valid_find_invoice_request(httpx_mock: HTTPXMock): assert response.lago_id == identifier assert response.purchase_order_number == "PO-123" + assert response.total_paid_amount_cents == 60 + assert response.total_offsetted_credit_note_amount_cents == 10 + assert response.self_billed is False + assert response.xml_url == "https://getlago.com/invoice/xml" + assert response.voided_at is None def test_invalid_find_invoice_request(httpx_mock: HTTPXMock): diff --git a/tests/test_plan_client.py b/tests/test_plan_client.py index c8a65f4..3425dad 100644 --- a/tests/test_plan_client.py +++ b/tests/test_plan_client.py @@ -403,7 +403,7 @@ def test_plan_with_fixed_charges_serialization(): assert plan_dict["fixed_charges"][0]["add_on_id"] == "add_on_123" assert plan_dict["fixed_charges"][0]["charge_model"] == "standard" assert plan_dict["fixed_charges"][0]["invoice_display_name"] == "Setup Fee" - assert plan_dict["fixed_charges"][0]["units"] == 1.0 + assert plan_dict["fixed_charges"][0]["units"] == pytest.approx(1.0) assert plan_dict["fixed_charges"][0]["pay_in_advance"] is True assert plan_dict["fixed_charges"][0]["prorated"] is False assert plan_dict["fixed_charges"][0]["properties"]["amount"] == "500" diff --git a/tests/test_rate_limit.py b/tests/test_rate_limit.py index 5577a4b..4a4a230 100644 --- a/tests/test_rate_limit.py +++ b/tests/test_rate_limit.py @@ -119,8 +119,8 @@ def test_default_config(self): assert config.max_retries == 3 assert config.retry_on_rate_limit is True - assert config.base_backoff_seconds == 1.0 - assert config.backoff_multiplier == 2.0 + assert config.base_backoff_seconds == pytest.approx(1.0) + assert config.backoff_multiplier == pytest.approx(2.0) def test_custom_config(self): """Test custom rate limit retry config.""" @@ -133,8 +133,8 @@ def test_custom_config(self): assert config.max_retries == 5 assert config.retry_on_rate_limit is False - assert config.base_backoff_seconds == 2.0 - assert config.backoff_multiplier == 3.0 + assert config.base_backoff_seconds == pytest.approx(2.0) + assert config.backoff_multiplier == pytest.approx(3.0) def test_exponential_backoff_calculation(self): """Test exponential backoff duration calculation.""" @@ -144,11 +144,11 @@ def test_exponential_backoff_calculation(self): ) # First retry: 1 * 2^0 = 1 - assert config.calculate_backoff(0) == 1.0 + assert config.calculate_backoff(0) == pytest.approx(1.0) # Second retry: 1 * 2^1 = 2 - assert config.calculate_backoff(1) == 2.0 + assert config.calculate_backoff(1) == pytest.approx(2.0) # Third retry: 1 * 2^2 = 4 - assert config.calculate_backoff(2) == 4.0 + assert config.calculate_backoff(2) == pytest.approx(4.0) def test_custom_exponential_backoff(self): """Test custom exponential backoff calculation.""" @@ -158,11 +158,11 @@ def test_custom_exponential_backoff(self): ) # 0.5 * 3^0 = 0.5 - assert config.calculate_backoff(0) == 0.5 + assert config.calculate_backoff(0) == pytest.approx(0.5) # 0.5 * 3^1 = 1.5 - assert config.calculate_backoff(1) == 1.5 + assert config.calculate_backoff(1) == pytest.approx(1.5) # 0.5 * 3^2 = 4.5 - assert config.calculate_backoff(2) == 4.5 + assert config.calculate_backoff(2) == pytest.approx(4.5) class TestClientRateLimitConfig: diff --git a/tests/test_subscription_client.py b/tests/test_subscription_client.py index 17cb2cb..5755174 100644 --- a/tests/test_subscription_client.py +++ b/tests/test_subscription_client.py @@ -394,7 +394,7 @@ def test_valid_lifetime_usage_request(httpx_mock: HTTPXMock): assert response.usage_thresholds[0].amount_cents == 2000 assert response.usage_thresholds[0].completion_ratio == 1 assert response.usage_thresholds[1].amount_cents == 4000 - assert response.usage_thresholds[1].completion_ratio == 0.5 + assert response.usage_thresholds[1].completion_ratio == pytest.approx(0.5) def test_invalid_lifetime_usage_request(httpx_mock: HTTPXMock): @@ -452,7 +452,7 @@ def test_valid_find_all_fixed_charges_request(httpx_mock: HTTPXMock): assert response["fixed_charges"][0].pay_in_advance is True assert response["fixed_charges"][0].prorated is False assert response["fixed_charges"][0].properties.amount == "500" - assert response["fixed_charges"][0].units == 1.0 + assert response["fixed_charges"][0].units == pytest.approx(1.0) assert response["fixed_charges"][1].lago_id == "3c903c90-3c90-3c90-3c90-3c903c903c90" assert response["fixed_charges"][1].charge_model == "graduated" assert response["fixed_charges"][1].properties.graduated_ranges[0].from_value == 0 diff --git a/tests/test_tax_client.py b/tests/test_tax_client.py index d3b0e57..8431273 100644 --- a/tests/test_tax_client.py +++ b/tests/test_tax_client.py @@ -46,6 +46,7 @@ def test_valid_create_tax_request(httpx_mock: HTTPXMock): assert response.lago_id == "b7ab2926-1de8-4428-9bcd-779314ac129b" assert response.code == "tax_code" + assert response.applied_to_billing_entities_codes == ["acme_corp", "foo_bar"] def test_invalid_create_tax_request(httpx_mock: HTTPXMock):