Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lago_python_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 7 additions & 2 deletions lago_python_client/models/credit_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]]]

Expand Down Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions lago_python_client/models/fee.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

class Fee(BaseModel):
payment_status: Optional[str]
invoice_display_name: Optional[str]


class FeeAppliedTax(BaseResponseModel):
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand Down
5 changes: 5 additions & 0 deletions lago_python_client/models/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions lago_python_client/models/tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
8 changes: 6 additions & 2 deletions tests/fixtures/fee.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/invoice.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions tests/fixtures/invoice_index.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion tests/fixtures/one_off_invoice.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/tax.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
5 changes: 5 additions & 0 deletions tests/fixtures/voided_invoice.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 8 additions & 1 deletion tests/test_credit_note_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
7 changes: 5 additions & 2 deletions tests/test_fee_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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"
Expand Down
6 changes: 5 additions & 1 deletion tests/test_invoice_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 == {}
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_plan_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 10 additions & 10 deletions tests/test_rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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."""
Expand All @@ -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."""
Expand All @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_subscription_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/test_tax_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading