diff --git a/lago_python_client/models/__init__.py b/lago_python_client/models/__init__.py index b19fd87f..bf1c43ac 100644 --- a/lago_python_client/models/__init__.py +++ b/lago_python_client/models/__init__.py @@ -25,6 +25,12 @@ from .alert import ( AlertThresholdResponseList as AlertThresholdResponseList, ) +from .alert import ( + TriggeredAlert as TriggeredAlert, +) +from .alert import ( + ResolvedAlert as ResolvedAlert, +) from .api_log import ApiLog as ApiLog from .api_log import ApiLogResponse as ApiLogResponse from .applied_coupon import AppliedCoupon as AppliedCoupon @@ -144,6 +150,30 @@ from .customer_usage import ( PresentationBreakdowns as PresentationBreakdowns, ) +from .entitlement import ( + PlanEntitlementPrivilegeResponse as PlanEntitlementPrivilegeResponse, +) +from .entitlement import ( + PlanEntitlementPrivilegesResponse as PlanEntitlementPrivilegesResponse, +) +from .entitlement import ( + PlanEntitlementResponse as PlanEntitlementResponse, +) +from .entitlement import ( + PlanEntitlementsResponse as PlanEntitlementsResponse, +) +from .entitlement import ( + SubscriptionEntitlementPrivilegeResponse as SubscriptionEntitlementPrivilegeResponse, +) +from .entitlement import ( + SubscriptionEntitlementPrivilegesResponse as SubscriptionEntitlementPrivilegesResponse, +) +from .entitlement import ( + SubscriptionEntitlementResponse as SubscriptionEntitlementResponse, +) +from .entitlement import ( + SubscriptionEntitlementsResponse as SubscriptionEntitlementsResponse, +) from .event import BatchEvent as BatchEvent from .event import Event as Event from .fee import Fee as Fee @@ -289,6 +319,12 @@ from .tax import ( TaxResponse as TaxResponse, ) +from .usage_threshold import ( + ApplicableUsageThresholdResponse as ApplicableUsageThresholdResponse, +) +from .usage_threshold import ( + ApplicableUsageThresholdsResponse as ApplicableUsageThresholdsResponse, +) from .usage_threshold import ( UsageThreshold as UsageThreshold, ) diff --git a/lago_python_client/models/alert.py b/lago_python_client/models/alert.py index f7b8fb92..301d7bb4 100644 --- a/lago_python_client/models/alert.py +++ b/lago_python_client/models/alert.py @@ -57,3 +57,36 @@ class AlertResponse(BaseResponseModel): class AlertsResponseList(BaseResponseModel): alerts: List[AlertResponse] + + +class AlertWebhookBase(BaseResponseModel): + lago_id: str + lago_organization_id: str + lago_alert_id: str + lago_subscription_id: Optional[str] + external_subscription_id: Optional[str] + lago_wallet_id: Optional[str] + external_customer_id: Optional[str] + billable_metric_code: Optional[str] + alert_name: Optional[str] + alert_code: str + alert_type: str + current_value: Optional[str] + previous_value: Optional[str] + + +class TriggeredAlert(AlertWebhookBase): + crossed_thresholds: AlertThresholdResponseList + triggered_at: str + # Deprecated + subscription_external_id: Optional[str] + # Deprecated + customer_external_id: Optional[str] + + +class ResolvedAlert(AlertWebhookBase): + recovered_thresholds: AlertThresholdResponseList + in_alarm_thresholds: List[str] + fully_resolved: bool + reason: Optional[str] + resolved_at: str diff --git a/lago_python_client/models/billing_entity.py b/lago_python_client/models/billing_entity.py index 16c501bb..e92b2a69 100644 --- a/lago_python_client/models/billing_entity.py +++ b/lago_python_client/models/billing_entity.py @@ -38,6 +38,7 @@ class BillingEntity(BaseModel): tax_identification_number: Optional[str] email_settings: Optional[List[str]] billing_configuration: Optional[BillingEntityBillingConfiguration] + einvoicing: Optional[bool] class BillingEntityUpdate(BaseModel): @@ -65,6 +66,7 @@ class BillingEntityUpdate(BaseModel): email_settings: Optional[List[str]] billing_configuration: Optional[BillingEntityBillingConfiguration] invoice_custom_section_codes: Optional[List[str]] + einvoicing: Optional[bool] class BillingEntityResponse(BaseResponseModel): @@ -99,5 +101,6 @@ class BillingEntityResponse(BaseResponseModel): email_settings: Optional[List[str]] taxes: Optional[TaxesResponse] selected_invoice_custom_sections: Optional[InvoiceCustomSectionsResponseList] + einvoicing: Optional[bool] created_at: datetime updated_at: datetime diff --git a/lago_python_client/models/customer.py b/lago_python_client/models/customer.py index 8919b98a..77cf91ce 100644 --- a/lago_python_client/models/customer.py +++ b/lago_python_client/models/customer.py @@ -35,6 +35,7 @@ class IntegrationCustomer(BaseModel): external_customer_id: Optional[str] integration_type: Optional[str] integration_code: Optional[str] + code: Optional[str] subsidiary_id: Optional[str] sync_with_provider: Optional[bool] @@ -44,6 +45,9 @@ class IntegrationCustomerResponse(BaseModel): external_customer_id: Optional[str] type: Optional[str] integration_code: Optional[str] + code: Optional[str] + is_default: Optional[bool] + category: Optional[str] subsidiary_id: Optional[str] sync_with_provider: Optional[bool] @@ -80,6 +84,7 @@ class MetadataResponseList(BaseModel): class Customer(BaseModel): external_id: str + account_type: Optional[str] address_line1: Optional[str] address_line2: Optional[str] city: Optional[str] @@ -89,6 +94,7 @@ class Customer(BaseModel): legal_name: Optional[str] legal_number: Optional[str] net_payment_term: Optional[int] + external_salesforce_id: Optional[str] tax_identification_number: Optional[str] logo_url: Optional[str] name: Optional[str] @@ -114,6 +120,9 @@ class Customer(BaseModel): class CustomerResponse(BaseResponseModel): lago_id: str external_id: str + account_type: Optional[str] + sequential_id: Optional[int] + slug: Optional[str] address_line1: Optional[str] address_line2: Optional[str] city: Optional[str] @@ -125,6 +134,7 @@ class CustomerResponse(BaseResponseModel): legal_name: Optional[str] legal_number: Optional[str] net_payment_term: Optional[int] + external_salesforce_id: Optional[str] tax_identification_number: Optional[str] logo_url: Optional[str] name: Optional[str] diff --git a/lago_python_client/models/entitlement.py b/lago_python_client/models/entitlement.py new file mode 100644 index 00000000..ea359603 --- /dev/null +++ b/lago_python_client/models/entitlement.py @@ -0,0 +1,52 @@ +from typing import Any, Dict, List, Optional + +from ..base_model import BaseResponseModel + + +class PlanEntitlementPrivilegeResponse(BaseResponseModel): + code: Optional[str] + name: Optional[str] + value_type: Optional[str] + config: Optional[Dict[str, Any]] + value: Any + + +class PlanEntitlementPrivilegesResponse(BaseResponseModel): + __root__: List[PlanEntitlementPrivilegeResponse] + + +class PlanEntitlementResponse(BaseResponseModel): + code: Optional[str] + name: Optional[str] + description: Optional[str] + privileges: Optional[PlanEntitlementPrivilegesResponse] + + +class PlanEntitlementsResponse(BaseResponseModel): + __root__: List[PlanEntitlementResponse] + + +class SubscriptionEntitlementPrivilegeResponse(BaseResponseModel): + code: Optional[str] + name: Optional[str] + value_type: Optional[str] + config: Optional[Dict[str, Any]] + value: Any + plan_value: Any + override_value: Any + + +class SubscriptionEntitlementPrivilegesResponse(BaseResponseModel): + __root__: List[SubscriptionEntitlementPrivilegeResponse] + + +class SubscriptionEntitlementResponse(BaseResponseModel): + code: Optional[str] + name: Optional[str] + description: Optional[str] + privileges: Optional[SubscriptionEntitlementPrivilegesResponse] + overrides: Optional[Dict[str, Any]] + + +class SubscriptionEntitlementsResponse(BaseResponseModel): + __root__: List[SubscriptionEntitlementResponse] diff --git a/lago_python_client/models/organization.py b/lago_python_client/models/organization.py index 258b5374..6f17a04c 100644 --- a/lago_python_client/models/organization.py +++ b/lago_python_client/models/organization.py @@ -32,9 +32,11 @@ class Organization(BaseModel): email_settings: Optional[List[str]] finalize_zero_amount_invoice: Optional[bool] billing_configuration: Optional[OrganizationBillingConfiguration] + slug: Optional[str] class OrganizationResponse(BaseResponseModel): + lago_id: Optional[str] name: str created_at: str webhook_url: Optional[str] @@ -57,3 +59,4 @@ class OrganizationResponse(BaseResponseModel): finalize_zero_amount_invoice: Optional[bool] billing_configuration: Optional[OrganizationBillingConfiguration] events_store: Optional[str] + slug: Optional[str] diff --git a/lago_python_client/models/payment.py b/lago_python_client/models/payment.py index 869b209e..76f82e24 100644 --- a/lago_python_client/models/payment.py +++ b/lago_python_client/models/payment.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Any, Dict, List, Optional from ..base_model import BaseModel, BaseResponseModel @@ -12,14 +12,24 @@ class Payment(BaseModel): class PaymentResponse(BaseResponseModel): lago_id: str + lago_customer_id: Optional[str] + external_customer_id: Optional[str] invoice_ids: List[str] invoice_numbers: List[str] + lago_payable_id: Optional[str] + payable_type: Optional[str] amount_cents: int amount_currency: str + status: Optional[str] payment_status: str type: str reference: str + payment_provider_code: Optional[str] + payment_provider_type: Optional[str] external_payment_id: Optional[str] + provider_payment_id: Optional[str] + provider_customer_id: Optional[str] + next_action: Optional[Dict[str, Any]] created_at: str diff --git a/lago_python_client/models/plan.py b/lago_python_client/models/plan.py index e429d59c..d1613018 100644 --- a/lago_python_client/models/plan.py +++ b/lago_python_client/models/plan.py @@ -4,6 +4,7 @@ from ..base_model import BaseResponseModel from .charge import Charges, ChargesOverrides, ChargesResponse +from .entitlement import PlanEntitlementsResponse from .fixed_charge import ( FixedCharges, FixedChargesOverrides, @@ -16,6 +17,7 @@ ) from .tax import TaxesResponse from .usage_threshold import ( + ApplicableUsageThresholdsResponse, UsageThresholds, UsageThresholdsOverrides, UsageThresholdsResponse, @@ -63,6 +65,9 @@ class PlanResponse(BaseResponseModel): usage_thresholds: Optional[UsageThresholdsResponse] taxes: Optional[TaxesResponse] metadata: Optional[Dict[str, Optional[str]]] + pending_deletion: Optional[bool] + entitlements: Optional[PlanEntitlementsResponse] + applicable_usage_thresholds: Optional[ApplicableUsageThresholdsResponse] class PlanOverrides(BaseModel): diff --git a/lago_python_client/models/subscription.py b/lago_python_client/models/subscription.py index 2c3ea759..96b73140 100644 --- a/lago_python_client/models/subscription.py +++ b/lago_python_client/models/subscription.py @@ -3,9 +3,11 @@ from lago_python_client.base_model import BaseModel from ..base_model import BaseResponseModel +from .entitlement import SubscriptionEntitlementsResponse from .invoice_custom_section import AppliedInvoiceCustomSections, InvoiceCustomSectionInput from .payment_method import PaymentMethod from .plan import PlanOverrides +from .usage_threshold import ApplicableUsageThresholdsResponse, UsageThresholdsOverrides class ActivationRuleInput(BaseModel): @@ -28,6 +30,8 @@ class Subscription(BaseModel): billing_entity_code: Optional[str] purchase_order_number: Optional[str] activation_rules: Optional[List[ActivationRuleInput]] + progressive_billing_disabled: Optional[bool] + usage_thresholds: Optional[UsageThresholdsOverrides] class Subscriptions(BaseModel): @@ -63,7 +67,6 @@ class SubscriptionResponse(BaseResponseModel): terminated_at: Optional[str] ending_at: Optional[str] trial_ended_at: Optional[str] - subscription_date: Optional[str] subscription_at: Optional[str] previous_plan_code: Optional[str] next_plan_code: Optional[str] @@ -80,6 +83,9 @@ class SubscriptionResponse(BaseResponseModel): cancellation_reason: Optional[str] activated_at: Optional[str] activation_rules: Optional[List[ActivationRuleResponse]] + progressive_billing_disabled: Optional[bool] + entitlements: Optional[SubscriptionEntitlementsResponse] + applicable_usage_thresholds: Optional[ApplicableUsageThresholdsResponse] class SubscriptionsResponse(BaseResponseModel): diff --git a/lago_python_client/models/usage_threshold.py b/lago_python_client/models/usage_threshold.py index b50bd618..61b86fa8 100644 --- a/lago_python_client/models/usage_threshold.py +++ b/lago_python_client/models/usage_threshold.py @@ -29,6 +29,16 @@ class UsageThresholdsResponse(BaseResponseModel): __root__: List[UsageThresholdResponse] +class ApplicableUsageThresholdResponse(BaseResponseModel): + threshold_display_name: Optional[str] + amount_cents: Optional[int] + recurring: Optional[bool] + + +class ApplicableUsageThresholdsResponse(BaseResponseModel): + __root__: List[ApplicableUsageThresholdResponse] + + class UsageThresholdOverrides(BaseModel): amount_cents: Optional[int] threshold_display_name: Optional[str] diff --git a/lago_python_client/models/wallet.py b/lago_python_client/models/wallet.py index fa7c0285..ee3e2eb4 100644 --- a/lago_python_client/models/wallet.py +++ b/lago_python_client/models/wallet.py @@ -68,6 +68,7 @@ class Wallet(BaseModel): name: Optional[str] code: Optional[str] priority: Optional[int] + transaction_priority: Optional[int] paid_credits: Optional[str] granted_credits: Optional[str] expiration_at: Optional[str] diff --git a/lago_python_client/models/wallet_transaction.py b/lago_python_client/models/wallet_transaction.py index 620c2187..5d69c570 100644 --- a/lago_python_client/models/wallet_transaction.py +++ b/lago_python_client/models/wallet_transaction.py @@ -19,6 +19,8 @@ class WalletTransaction(BaseModel): purchase_order_number: Optional[str] payment_method: Optional[PaymentMethod] invoice_custom_section: Optional[InvoiceCustomSectionInput] + priority: Optional[int] + voided_transaction_id: Optional[str] class WalletTransactionResponse(BaseResponseModel): diff --git a/lago_python_client/wallets/clients.py b/lago_python_client/wallets/clients.py index 85c6f7a7..3a02515f 100644 --- a/lago_python_client/wallets/clients.py +++ b/lago_python_client/wallets/clients.py @@ -109,7 +109,7 @@ def create(self, input_object: BaseModel) -> Mapping[str, Any]: origin=self.base_url, path_parts=(self.API_RESOURCE,), ), - content=to_json({"wallet_transaction": input_object.dict()}), + content=to_json({"wallet_transaction": input_object.dict(exclude_none=True)}), headers=make_headers(api_key=self.api_key), ) diff --git a/tests/fixtures/alert_resolved_webhook.json b/tests/fixtures/alert_resolved_webhook.json new file mode 100644 index 00000000..fc23ed7b --- /dev/null +++ b/tests/fixtures/alert_resolved_webhook.json @@ -0,0 +1,31 @@ +{ + "webhook_type": "alert.resolved", + "object_type": "resolved_alert", + "organization_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "resolved_alert": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a94", + "lago_organization_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "lago_alert_id": "1a901a90-1a90-1a90-1a90-1a901a901a92", + "lago_subscription_id": "1a901a90-1a90-1a90-1a90-1a901a901a93", + "external_subscription_id": "sub_1234", + "lago_wallet_id": null, + "external_customer_id": "cust_1234", + "billable_metric_code": "storage", + "alert_name": "Storage usage", + "alert_code": "storage_alert", + "alert_type": "billable_metric_current_usage_amount", + "current_value": "15000.0", + "previous_value": "25000.0", + "recovered_thresholds": [ + { + "code": null, + "value": "10000.0", + "recurring": false + } + ], + "in_alarm_thresholds": ["20000.0"], + "fully_resolved": false, + "reason": "recovered", + "resolved_at": "2026-10-28T13:00:00Z" + } +} diff --git a/tests/fixtures/alert_triggered_webhook.json b/tests/fixtures/alert_triggered_webhook.json new file mode 100644 index 00000000..6a2738fe --- /dev/null +++ b/tests/fixtures/alert_triggered_webhook.json @@ -0,0 +1,35 @@ +{ + "webhook_type": "alert.triggered", + "object_type": "triggered_alert", + "organization_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "triggered_alert": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a91", + "lago_organization_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "lago_alert_id": "1a901a90-1a90-1a90-1a90-1a901a901a92", + "lago_subscription_id": "1a901a90-1a90-1a90-1a90-1a901a901a93", + "external_subscription_id": "sub_1234", + "lago_wallet_id": null, + "external_customer_id": "cust_1234", + "billable_metric_code": "storage", + "alert_name": "Storage usage", + "alert_code": "storage_alert", + "alert_type": "billable_metric_current_usage_amount", + "current_value": "25000.0", + "previous_value": "5000.0", + "crossed_thresholds": [ + { + "code": null, + "value": "10000.0", + "recurring": false + }, + { + "code": "warn", + "value": "20000.0", + "recurring": true + } + ], + "triggered_at": "2026-10-28T12:00:00Z", + "subscription_external_id": "sub_1234", + "customer_external_id": "cust_1234" + } +} diff --git a/tests/fixtures/billing_entity.json b/tests/fixtures/billing_entity.json index eecea36d..73e34327 100644 --- a/tests/fixtures/billing_entity.json +++ b/tests/fixtures/billing_entity.json @@ -52,6 +52,7 @@ "payment_receipt.created" ], "is_default": true, + "einvoicing": false, "created_at": "2024-03-20T10:00:00Z", "updated_at": "2024-03-20T10:00:00Z" } diff --git a/tests/fixtures/billing_entity_index.json b/tests/fixtures/billing_entity_index.json index 2862048d..906329ca 100644 --- a/tests/fixtures/billing_entity_index.json +++ b/tests/fixtures/billing_entity_index.json @@ -33,6 +33,7 @@ "payment_receipt.created" ], "is_default": true, + "einvoicing": false, "created_at": "2024-03-20T10:00:00Z", "updated_at": "2024-03-20T10:00:00Z" }, @@ -66,6 +67,7 @@ "invoice.finalized" ], "is_default": false, + "einvoicing": false, "created_at": "2024-03-20T10:00:00Z", "updated_at": "2024-03-20T10:00:00Z" } diff --git a/tests/fixtures/customer.json b/tests/fixtures/customer.json index 87908257..7261552a 100644 --- a/tests/fixtures/customer.json +++ b/tests/fixtures/customer.json @@ -2,6 +2,7 @@ "customer": { "lago_id": "99a6094e-199b-4101-896a-54e927ce7bd7", "external_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", + "account_type": "customer", "billing_entity_code": "test-company", "address_line1": "5230 Penfield Ave", "address_line2": null, @@ -18,6 +19,8 @@ "firstname": "Gavin", "lastname": "Belson", "customer_type": "individual", + "sequential_id": 42, + "slug": "LAG-1234-042", "phone": "1-171-883-3711 x245", "state": "CA", "url": "http://hooli.com", @@ -26,6 +29,7 @@ "timezone": "Europe/Paris", "applicable_timezone": "Europe/Paris", "net_payment_term": null, + "external_salesforce_id": "sf_001", "finalize_zero_amount_invoice": "inherit", "billing_configuration": { "invoice_grace_period": 3, @@ -52,6 +56,9 @@ "external_customer_id": "test-12345", "type": "netsuite", "integration_code": "test", + "code": "netsuite-main", + "is_default": true, + "category": "accounting", "subsidiary_id": "2", "sync_with_provider": true } diff --git a/tests/fixtures/organization.json b/tests/fixtures/organization.json index 9b8857ca..f0126b30 100644 --- a/tests/fixtures/organization.json +++ b/tests/fixtures/organization.json @@ -1,6 +1,8 @@ { "organization": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", "name": "Hooli", + "slug": "hooli", "created_at": "2022-05-02T13:04:09Z", "webhook_url": "https://test-example.example", "webhook_urls": ["https://test-example.example", "https://test-example2.example"], diff --git a/tests/fixtures/payment.json b/tests/fixtures/payment.json index 9ab94a4a..0d5bdf5e 100644 --- a/tests/fixtures/payment.json +++ b/tests/fixtures/payment.json @@ -1,14 +1,33 @@ { "payment": { "lago_id": "8e5d5ec2-bdc7-4c43-a944-5ababae775d4", - "invoice_ids": ["f8e194df-5d90-4382-b146-c881d2c67f28"], - "invoice_numbers": ["LAG-1234-567-890"], + "lago_customer_id": "99a6094e-199b-4101-896a-54e927ce7bd7", + "external_customer_id": "customer_external_id", + "invoice_ids": [ + "f8e194df-5d90-4382-b146-c881d2c67f28" + ], + "invoice_numbers": [ + "LAG-1234-567-890" + ], + "lago_payable_id": "f8e194df-5d90-4382-b146-c881d2c67f28", + "payable_type": "Invoice", "amount_cents": 100, "amount_currency": "USD", + "status": "succeeded", "payment_status": "succeeded", - "type": "manual", + "type": "provider", "reference": "123", - "external_payment_id": null, + "payment_provider_code": "stripe_prod", + "payment_provider_type": "stripe", + "external_payment_id": "pi_123", + "provider_payment_id": "pi_123", + "provider_customer_id": "cus_123", + "next_action": { + "type": "redirect_to_url", + "redirect_to_url": { + "url": "https://example.com" + } + }, "created_at": "2025-02-18T18:31:13Z" } } diff --git a/tests/fixtures/payment_index.json b/tests/fixtures/payment_index.json index 71ba597b..0cf34e57 100644 --- a/tests/fixtures/payment_index.json +++ b/tests/fixtures/payment_index.json @@ -2,18 +2,59 @@ "payments": [ { "lago_id": "8e5d5ec2-bdc7-4c43-a944-5ababae775d4", + "lago_customer_id": "99a6094e-199b-4101-896a-54e927ce7bd7", + "external_customer_id": "customer_external_id", "invoice_ids": [ "ed267c66-8170-4d23-83e8-6d6e4fc735ef" ], "invoice_numbers": [ "LAG-1234-567-891" ], + "lago_payable_id": "ed267c66-8170-4d23-83e8-6d6e4fc735ef", + "payable_type": "Invoice", "amount_cents": 100, "amount_currency": "USD", + "status": "succeeded", "payment_status": "succeeded", - "type": "manual", + "type": "provider", "reference": "123", + "payment_provider_code": "stripe_prod", + "payment_provider_type": "stripe", + "external_payment_id": "pi_123", + "provider_payment_id": "pi_123", + "provider_customer_id": "cus_123", + "next_action": { + "type": "redirect_to_url", + "redirect_to_url": { + "url": "https://example.com" + } + }, + "created_at": "2025-02-18T18:31:13Z" + }, + { + "lago_id": "5b3a2c1d-7e8f-4a9b-8c7d-6e5f4a3b2c1d", + "lago_customer_id": "99a6094e-199b-4101-896a-54e927ce7bd7", + "external_customer_id": "customer_external_id", + "invoice_ids": [ + "ed267c66-8170-4d23-83e8-6d6e4fc735ef" + ], + "invoice_numbers": [ + "LAG-1234-567-891" + ], + "lago_payable_id": "ed267c66-8170-4d23-83e8-6d6e4fc735ef", + "payable_type": "Invoice", + "amount_cents": 100, + "amount_currency": "USD", + "status": "succeeded", + "payment_status": "succeeded", + "type": "manual", + "reference": "456", + "payment_provider_code": null, + "payment_provider_type": null, "external_payment_id": null, + "provider_payment_id": null, + "provider_customer_id": null, + "next_action": {}, "created_at": "2025-02-18T18:31:13Z" } ], diff --git a/tests/fixtures/plan.json b/tests/fixtures/plan.json index 1423c644..2d0ea07e 100644 --- a/tests/fixtures/plan.json +++ b/tests/fixtures/plan.json @@ -90,6 +90,46 @@ "metadata": { "key1": "value1", "key2": null - } + }, + "pending_deletion": false, + "entitlements": [ + { + "code": "seats", + "name": "Seats", + "description": "Number of seats", + "privileges": [ + { + "code": "max", + "name": "Maximum", + "value_type": "integer", + "config": {}, + "value": 10 + }, + { + "code": "sso", + "name": "SSO", + "value_type": "boolean", + "config": {}, + "value": true + }, + { + "code": "tier", + "name": "Tier", + "value_type": "select", + "config": { + "select_options": ["5", "10"] + }, + "value": "10" + } + ] + } + ], + "applicable_usage_thresholds": [ + { + "amount_cents": 20, + "threshold_display_name": "Applicable threshold", + "recurring": false + } + ] } } diff --git a/tests/fixtures/subscription.json b/tests/fixtures/subscription.json index 53cf87a2..a698ed35 100644 --- a/tests/fixtures/subscription.json +++ b/tests/fixtures/subscription.json @@ -17,6 +17,7 @@ "on_termination_invoice": "skip", "consolidate_invoice": false, "purchase_order_number": "PO-123", + "progressive_billing_disabled": false, "subscription_at": "2022-04-29T08:59:51Z", "previous_plan_code": null, "next_plan_code": null, @@ -51,6 +52,34 @@ "created_at": "2022-04-29T08:59:51Z", "updated_at": "2022-04-29T08:59:51Z" } + ], + "entitlements": [ + { + "code": "seats", + "name": "Seats", + "description": "Number of seats", + "privileges": [ + { + "code": "max", + "name": "Maximum", + "value_type": "integer", + "value": 20, + "plan_value": 10, + "override_value": 20, + "config": {} + } + ], + "overrides": { + "max": 20 + } + } + ], + "applicable_usage_thresholds": [ + { + "threshold_display_name": "Applicable threshold", + "amount_cents": 20, + "recurring": false + } ] } } diff --git a/tests/test_alert_webhook_models.py b/tests/test_alert_webhook_models.py new file mode 100644 index 00000000..8ff8c764 --- /dev/null +++ b/tests/test_alert_webhook_models.py @@ -0,0 +1,124 @@ +import json + +from lago_python_client.models import ResolvedAlert, TriggeredAlert + +from .utils.mixin import mock_response + + +def triggered_alert_payload(): + return json.loads(mock_response("alert_triggered_webhook"))["triggered_alert"] + + +def resolved_alert_payload(): + return json.loads(mock_response("alert_resolved_webhook"))["resolved_alert"] + + +def test_triggered_alert_parsing(): + alert = TriggeredAlert.parse_obj(triggered_alert_payload()) + + assert alert.lago_id == "1a901a90-1a90-1a90-1a90-1a901a901a91" + assert alert.lago_organization_id == "1a901a90-1a90-1a90-1a90-1a901a901a90" + assert alert.lago_alert_id == "1a901a90-1a90-1a90-1a90-1a901a901a92" + assert alert.lago_subscription_id == "1a901a90-1a90-1a90-1a90-1a901a901a93" + assert alert.external_subscription_id == "sub_1234" + assert alert.lago_wallet_id is None + assert alert.external_customer_id == "cust_1234" + assert alert.billable_metric_code == "storage" + assert alert.alert_name == "Storage usage" + assert alert.alert_code == "storage_alert" + assert alert.alert_type == "billable_metric_current_usage_amount" + assert alert.current_value == "25000.0" + assert alert.previous_value == "5000.0" + assert alert.triggered_at == "2026-10-28T12:00:00Z" + assert alert.subscription_external_id == "sub_1234" + assert alert.customer_external_id == "cust_1234" + + thresholds = alert.crossed_thresholds.__root__ + assert len(thresholds) == 2 + assert thresholds[0].code is None + assert thresholds[0].value == "10000.0" + assert type(thresholds[0].value) is str + assert thresholds[0].recurring is False + assert thresholds[1].code == "warn" + assert thresholds[1].value == "20000.0" + assert type(thresholds[1].value) is str + assert thresholds[1].recurring is True + + +def test_triggered_alert_parsing_without_previous_value(): + payload = triggered_alert_payload() + payload["previous_value"] = None + + alert = TriggeredAlert.parse_obj(payload) + + assert alert.previous_value is None + assert alert.current_value == "25000.0" + + +def test_resolved_alert_parsing(): + alert = ResolvedAlert.parse_obj(resolved_alert_payload()) + + assert alert.lago_id == "1a901a90-1a90-1a90-1a90-1a901a901a94" + assert alert.lago_organization_id == "1a901a90-1a90-1a90-1a90-1a901a901a90" + assert alert.lago_alert_id == "1a901a90-1a90-1a90-1a90-1a901a901a92" + assert alert.lago_subscription_id == "1a901a90-1a90-1a90-1a90-1a901a901a93" + assert alert.external_subscription_id == "sub_1234" + assert alert.lago_wallet_id is None + assert alert.external_customer_id == "cust_1234" + assert alert.billable_metric_code == "storage" + assert alert.alert_name == "Storage usage" + assert alert.alert_code == "storage_alert" + assert alert.alert_type == "billable_metric_current_usage_amount" + assert alert.current_value == "15000.0" + assert alert.previous_value == "25000.0" + assert alert.in_alarm_thresholds == ["20000.0"] + assert alert.fully_resolved is False + assert alert.reason == "recovered" + assert alert.resolved_at == "2026-10-28T13:00:00Z" + + thresholds = alert.recovered_thresholds.__root__ + assert len(thresholds) == 1 + assert thresholds[0].code is None + assert thresholds[0].value == "10000.0" + assert type(thresholds[0].value) is str + assert thresholds[0].recurring is False + + +def test_resolved_alert_parsing_without_previous_value(): + payload = resolved_alert_payload() + payload["previous_value"] = None + + alert = ResolvedAlert.parse_obj(payload) + + assert alert.previous_value is None + assert alert.fully_resolved is False + + +def wallet_alert_payload(payload): + payload["lago_subscription_id"] = None + payload["external_subscription_id"] = None + payload["billable_metric_code"] = None + payload["lago_wallet_id"] = "1a901a90-1a90-1a90-1a90-1a901a901a95" + payload.pop("subscription_external_id", None) + payload.pop("customer_external_id", None) + return payload + + +def test_triggered_alert_parsing_for_wallet_alert(): + alert = TriggeredAlert.parse_obj(wallet_alert_payload(triggered_alert_payload())) + + assert alert.lago_wallet_id == "1a901a90-1a90-1a90-1a90-1a901a901a95" + assert alert.lago_subscription_id is None + assert alert.external_subscription_id is None + assert alert.billable_metric_code is None + assert alert.subscription_external_id is None + assert alert.customer_external_id is None + + +def test_resolved_alert_parsing_for_wallet_alert(): + alert = ResolvedAlert.parse_obj(wallet_alert_payload(resolved_alert_payload())) + + assert alert.lago_wallet_id == "1a901a90-1a90-1a90-1a90-1a901a901a95" + assert alert.lago_subscription_id is None + assert alert.external_subscription_id is None + assert alert.billable_metric_code is None diff --git a/tests/test_billing_entity_client.py b/tests/test_billing_entity_client.py index 745a031e..0d2656ba 100644 --- a/tests/test_billing_entity_client.py +++ b/tests/test_billing_entity_client.py @@ -1,3 +1,4 @@ +import json import os import pytest @@ -98,6 +99,24 @@ def test_valid_create_billing_entity_request(httpx_mock: HTTPXMock): assert response is not None assert response.lago_id == "123e4567-e89b-12d3-a456-426614174000" assert response.code == "test-company" + assert response.einvoicing is False + assert "einvoicing" not in json.loads(httpx_mock.get_request().content)["billing_entity"] + + +def test_valid_create_billing_entity_request_with_einvoicing(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + billing_entity_input = create_billing_entity_input() + billing_entity_input.einvoicing = True + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/billing_entities", + content=mock_response(), + ) + client.billing_entities.create(billing_entity_input) + + body = json.loads(httpx_mock.get_request().content) + assert body["billing_entity"]["einvoicing"] is True def test_invalid_create_billing_entity_request(httpx_mock: HTTPXMock): @@ -160,6 +179,7 @@ def test_valid_find_all_billing_entities_request(httpx_mock: HTTPXMock): assert response["billing_entities"][0].code == "test-company-1" assert response["billing_entities"][1].lago_id == "123e4567-e89b-12d3-a456-426614174001" assert response["billing_entities"][1].code == "test-company-2" + assert response["billing_entities"][1].einvoicing is False def test_invalid_find_all_billing_entities_request(httpx_mock: HTTPXMock): @@ -189,6 +209,25 @@ def test_valid_update_billing_entity_request(httpx_mock: HTTPXMock): assert response.lago_id == "123e4567-e89b-12d3-a456-426614174000" assert response.code == "test-company" + assert response.einvoicing is False + assert "einvoicing" not in json.loads(httpx_mock.get_request().content)["billing_entity"] + + +def test_valid_update_billing_entity_request_with_einvoicing(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + billing_entity_code = "test-company" + billing_entity_input = update_billing_entity_input() + billing_entity_input.einvoicing = False + + httpx_mock.add_response( + method="PUT", + url="https://api.getlago.com/api/v1/billing_entities/" + billing_entity_code, + content=mock_response(), + ) + client.billing_entities.update(billing_entity_input, billing_entity_code) + + body = json.loads(httpx_mock.get_request().content) + assert body["billing_entity"]["einvoicing"] is False def test_invalid_update_billing_entity_request(httpx_mock: HTTPXMock): diff --git a/tests/test_customer_client.py b/tests/test_customer_client.py index 34011ebc..c1e5c88c 100644 --- a/tests/test_customer_client.py +++ b/tests/test_customer_client.py @@ -1,3 +1,4 @@ +import json import os import pytest @@ -125,12 +126,64 @@ def test_valid_create_customers_request(httpx_mock: HTTPXMock): assert response.shipping_address.state == "XZ" assert response.integration_customers.__root__[0].external_customer_id == "test-12345" assert response.integration_customers.__root__[0].type == "netsuite" + assert response.integration_customers.__root__[0].code == "netsuite-main" + assert response.integration_customers.__root__[0].is_default is True + assert response.integration_customers.__root__[0].category == "accounting" + assert response.account_type == "customer" + assert response.external_salesforce_id == "sf_001" + assert response.sequential_id == 42 + assert response.slug == "LAG-1234-042" assert response.applicable_invoice_custom_sections.__root__[0].code == "custom_section_code" assert response.metadata.__root__[0].lago_id == "12345" assert response.metadata.__root__[0].key == "key" assert response.metadata.__root__[0].value == "value" +def test_valid_create_customers_request_with_account_type_and_salesforce_id(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/customers", + content=mock_response(), + ) + customer = Customer( + external_id="5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", + account_type="partner", + external_salesforce_id="sf_123", + integration_customers=IntegrationCustomersList( + __root__=[ + IntegrationCustomer( + integration_type="anrok", + integration_code="anrok-1", + code="anrok", + ) + ] + ), + ) + client.customers.create(customer) + + body = json.loads(httpx_mock.get_request().content) + assert body["customer"]["account_type"] == "partner" + assert body["customer"]["external_salesforce_id"] == "sf_123" + assert body["customer"]["integration_customers"][0]["code"] == "anrok" + + +def test_create_customers_request_without_account_type_and_salesforce_id(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/customers", + content=mock_response(), + ) + client.customers.create(Customer(external_id="5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba")) + + body = json.loads(httpx_mock.get_request().content) + assert "account_type" not in body["customer"] + assert "external_salesforce_id" not in body["customer"] + + def test_invalid_create_customers_request(httpx_mock: HTTPXMock): client = Client(api_key="invalid") diff --git a/tests/test_organization_client.py b/tests/test_organization_client.py index b51df79e..9bb598cf 100644 --- a/tests/test_organization_client.py +++ b/tests/test_organization_client.py @@ -1,3 +1,4 @@ +import json import os import pytest @@ -44,6 +45,26 @@ def test_valid_update_organization_request(httpx_mock: HTTPXMock): assert response.tax_identification_number == "EU123456789" assert response.document_number_prefix == "ORG-1234" assert response.net_payment_term == 0 + assert "slug" not in json.loads(httpx_mock.get_request().content)["organization"] + + +def test_valid_update_organization_request_with_slug(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + organization = organization_object() + organization.slug = "acme-corp" + + httpx_mock.add_response( + method="PUT", + url="https://api.getlago.com/api/v1/organizations", + content=mock_response(), + ) + response = client.organizations.update(organization) + + body = json.loads(httpx_mock.get_request().content) + assert body["organization"]["slug"] == "acme-corp" + assert body["organization"]["default_currency"] == "EUR" + assert response.lago_id == "1a901a90-1a90-1a90-1a90-1a901a901a90" + assert response.slug == "hooli" def test_invalid_update_organization_request(httpx_mock: HTTPXMock): diff --git a/tests/test_payment_client.py b/tests/test_payment_client.py index 6daf6f7d..d6193d2d 100644 --- a/tests/test_payment_client.py +++ b/tests/test_payment_client.py @@ -33,6 +33,36 @@ def mock_collection_response(): return payments_response.read() +def assert_new_payment_fields(response): + assert response.status == "succeeded" + assert response.lago_customer_id == "99a6094e-199b-4101-896a-54e927ce7bd7" + assert response.external_customer_id == "customer_external_id" + assert response.lago_payable_id == "f8e194df-5d90-4382-b146-c881d2c67f28" + assert response.payable_type == "Invoice" + assert response.payment_provider_code == "stripe_prod" + assert response.payment_provider_type == "stripe" + assert response.provider_payment_id == "pi_123" + assert response.provider_customer_id == "cus_123" + assert isinstance(response.next_action, dict) + assert response.next_action["type"] == "redirect_to_url" + assert response.next_action["redirect_to_url"]["url"] == "https://example.com" + + +def test_valid_find_payment_request(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + identifier = "8e5d5ec2-bdc7-4c43-a944-5ababae775d4" + + httpx_mock.add_response( + method="GET", + url="https://api.getlago.com/api/v1/payments/" + identifier, + content=mock_response(), + ) + response = client.payments.find(identifier) + + assert response.lago_id == identifier + assert_new_payment_fields(response) + + def test_valid_find_all_payments_request(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") @@ -44,6 +74,15 @@ def test_valid_find_all_payments_request(httpx_mock: HTTPXMock): response = client.payments.find_all() assert response["payments"][0].lago_id == "8e5d5ec2-bdc7-4c43-a944-5ababae775d4" + assert response["payments"][0].payment_provider_type == "stripe" + assert response["payments"][0].next_action["type"] == "redirect_to_url" + assert response["payments"][1].status == "succeeded" + assert response["payments"][1].payable_type == "Invoice" + assert response["payments"][1].payment_provider_code is None + assert response["payments"][1].payment_provider_type is None + assert response["payments"][1].provider_payment_id is None + assert response["payments"][1].provider_customer_id is None + assert response["payments"][1].next_action == {} assert response["meta"]["current_page"] == 1 @@ -94,6 +133,7 @@ def test_valid_create_payment_request(httpx_mock: HTTPXMock): assert response.invoice_ids[0] == "f8e194df-5d90-4382-b146-c881d2c67f28" assert len(response.invoice_numbers) == 1 assert response.invoice_numbers[0] == "LAG-1234-567-890" + assert_new_payment_fields(response) def test_invalid_create_payment_request(httpx_mock: HTTPXMock): diff --git a/tests/test_plan_client.py b/tests/test_plan_client.py index 3425dad8..f2aa9f41 100644 --- a/tests/test_plan_client.py +++ b/tests/test_plan_client.py @@ -15,6 +15,7 @@ FixedCharges, MinimumCommitment, Plan, + PlanEntitlementPrivilegeResponse, UsageThreshold, UsageThresholds, ) @@ -206,6 +207,9 @@ def test_valid_create_plan_request(httpx_mock: HTTPXMock): assert response.minimum_commitment.invoice_display_name == "Minimum commitment (C1)" assert response.usage_thresholds.__root__[0].threshold_display_name == "Threshold 1" assert response.metadata == {"key1": "value1", "key2": None} + assert response.pending_deletion is False + assert response.entitlements.__root__[0].code == "seats" + assert response.applicable_usage_thresholds.__root__[0].amount_cents == 20 def test_valid_create_graduated_plan_request(httpx_mock: HTTPXMock): @@ -288,6 +292,38 @@ def test_valid_find_plan_request(httpx_mock: HTTPXMock): charge = response.charges.__root__[0] assert charge.properties["presentation_group_keys"][0]["value"] == "region" assert response.minimum_commitment.amount_cents == 1000 + assert response.pending_deletion is False + + entitlement = response.entitlements.__root__[0] + assert entitlement.code == "seats" + assert entitlement.name == "Seats" + assert entitlement.description == "Number of seats" + + integer_privilege, boolean_privilege, select_privilege = entitlement.privileges.__root__ + assert integer_privilege.code == "max" + assert integer_privilege.name == "Maximum" + assert integer_privilege.value_type == "integer" + assert type(integer_privilege.value) is int + assert integer_privilege.value == 10 + assert boolean_privilege.value_type == "boolean" + assert boolean_privilege.value is True + assert select_privilege.value_type == "select" + assert type(select_privilege.value) is str + assert select_privilege.value == "10" + assert isinstance(select_privilege.config, dict) + assert select_privilege.config == {"select_options": ["5", "10"]} + + applicable_threshold = response.applicable_usage_thresholds.__root__[0] + assert applicable_threshold.threshold_display_name == "Applicable threshold" + assert applicable_threshold.amount_cents == 20 + assert applicable_threshold.recurring is False + + +def test_plan_entitlement_privilege_keeps_string_value(): + privilege = PlanEntitlementPrivilegeResponse.parse_obj({"code": "x", "value": "10"}) + + assert type(privilege.value) is str + assert privilege.value == "10" def test_invalid_find_plan_request(httpx_mock: HTTPXMock): diff --git a/tests/test_subscription_client.py b/tests/test_subscription_client.py index 57551746..5b44a7b9 100644 --- a/tests/test_subscription_client.py +++ b/tests/test_subscription_client.py @@ -1,3 +1,4 @@ +import json import os import httpx @@ -17,6 +18,8 @@ Subscription, ) from lago_python_client.models.alert import Alert, AlertsList, AlertThreshold +from lago_python_client.models.subscription import SubscriptionResponse +from lago_python_client.models.usage_threshold import UsageThresholdOverrides, UsageThresholdsOverrides def create_subscription(): @@ -90,6 +93,19 @@ def test_valid_create_subscriptions_request(httpx_mock: HTTPXMock): assert response.consolidate_invoice is False assert response.purchase_order_number == "PO-123" + entitlement = response.entitlements.__root__[0] + assert entitlement.code == "seats" + assert entitlement.overrides == {"max": 20} + privilege = entitlement.privileges.__root__[0] + assert privilege.value == 20 + assert privilege.plan_value == 10 + assert privilege.override_value == 20 + + applicable_threshold = response.applicable_usage_thresholds.__root__[0] + assert applicable_threshold.threshold_display_name == "Applicable threshold" + assert applicable_threshold.amount_cents == 20 + assert applicable_threshold.recurring is False + def test_valid_create_subscriptions_request_with_payment_method(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") @@ -130,6 +146,54 @@ def test_valid_create_subscriptions_request_with_activation_rules(httpx_mock: HT assert response.activation_rules[0].status == "pending" +def test_valid_create_subscriptions_request_with_progressive_billing(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/subscriptions", + match_json={ + "subscription": { + "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba", + "plan_code": "eartha lynch", + "external_id": "code", + "billing_time": "anniversary", + "subscription_at": "2022-04-29T08:59:51Z", + "ending_at": "2022-08-29T08:59:51Z", + "consolidate_invoice": False, + "progressive_billing_disabled": True, + "usage_thresholds": [ + {"amount_cents": 10000, "threshold_display_name": "Alert", "recurring": False}, + ], + } + }, + content=mock_response(), + ) + subscription = create_subscription() + subscription.progressive_billing_disabled = True + subscription.usage_thresholds = UsageThresholdsOverrides( + __root__=[UsageThresholdOverrides(amount_cents=10000, threshold_display_name="Alert", recurring=False)] + ) + response = client.subscriptions.create(subscription) + + assert response.progressive_billing_disabled is False + + +def test_valid_create_subscriptions_request_without_progressive_billing(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/subscriptions", + content=mock_response(), + ) + client.subscriptions.create(create_subscription()) + + body = json.loads(httpx_mock.get_request().content)["subscription"] + assert "progressive_billing_disabled" not in body + assert "usage_thresholds" not in body + + def test_invalid_create_subscriptions_request(httpx_mock: HTTPXMock): client = Client(api_key="invalid") @@ -184,6 +248,34 @@ def test_valid_update_subscription_request(httpx_mock: HTTPXMock): assert response.subscription_at == "2022-04-29T08:59:51Z" +def test_valid_update_subscription_request_with_progressive_billing(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + identifier = "sub_id" + + httpx_mock.add_response( + method="PUT", + url="https://api.getlago.com/api/v1/subscriptions/" + identifier, + match_json={ + "subscription": { + "progressive_billing_disabled": False, + "usage_thresholds": [ + {"amount_cents": 5000, "threshold_display_name": "Recurring", "recurring": True}, + ], + } + }, + content=mock_response(), + ) + subscription = Subscription( + progressive_billing_disabled=False, + usage_thresholds=UsageThresholdsOverrides( + __root__=[UsageThresholdOverrides(amount_cents=5000, threshold_display_name="Recurring", recurring=True)] + ), + ) + response = client.subscriptions.update(subscription, identifier) + + assert response.progressive_billing_disabled is False + + def test_valid_update_subscription_request_with_payment_method(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") identifier = "sub_id" @@ -538,6 +630,16 @@ def mock_subscription_alerts_response(): return alerts_response.read() +def test_subscription_response_has_no_subscription_date(): + assert "subscription_date" not in SubscriptionResponse.__fields__ + + response = SubscriptionResponse.parse_obj(json.loads(mock_response())["subscription"]) + + assert not hasattr(response, "subscription_date") + assert response.subscription_at == "2022-04-29T08:59:51Z" + assert response.progressive_billing_disabled is False + + def test_valid_create_alerts_request(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") external_id = "sub_1234" diff --git a/tests/test_wallet_client.py b/tests/test_wallet_client.py index 9457c96c..9db70ab6 100644 --- a/tests/test_wallet_client.py +++ b/tests/test_wallet_client.py @@ -122,6 +122,53 @@ def test_valid_create_wallet_request(httpx_mock: HTTPXMock): assert response.recurring_transaction_rules.__root__[0].purchase_order_number == "PO-RULE-123" +def test_valid_create_wallet_request_with_transaction_priority(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/wallets", + content=mock_response(), + match_json={ + "wallet": { + "external_customer_id": "12345", + "rate_amount": "1", + "name": "name", + "code": "wallet_code", + "priority": 30, + "transaction_priority": 10, + "paid_credits": "10", + "granted_credits": "10", + "paid_top_up_max_amount_cents": 10000, + "paid_top_up_min_amount_cents": 500, + "recurring_transaction_rules": [ + { + "interval": "monthly", + "trigger": "interval", + "method": "target", + "paid_credits": "105.0", + "granted_credits": "105.0", + "target_ongoing_balance": "105.0", + "transaction_name": "Recurring Transaction Rule", + "purchase_order_number": "PO-RULE-123", + "ignore_paid_top_up_limits": True, + "grants_target_top_up": True, + } + ], + "invoice_requires_successful_payment": False, + "transaction_name": "Transaction Name", + "purchase_order_number": "PO-123", + "applies_to": {"fee_types": ["charge"], "billable_metric_codes": ["usage"]}, + } + }, + ) + wallet = wallet_object() + wallet.transaction_priority = 10 + response = client.wallets.create(wallet) + + assert response.lago_id == "b7ab2926-1de8-4428-9bcd-779314ac129b" + + def test_valid_create_wallet_request_with_payment_method_on_wallet(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") payment_method = PaymentMethod(payment_method_type="card", payment_method_id="pm_123") diff --git a/tests/test_wallet_transaction_client.py b/tests/test_wallet_transaction_client.py index 76c07127..9fc91b82 100644 --- a/tests/test_wallet_transaction_client.py +++ b/tests/test_wallet_transaction_client.py @@ -80,6 +80,107 @@ def test_valid_create_wallet_transaction_request(httpx_mock: HTTPXMock): assert response["wallet_transactions"][2].priority == 50 +def test_valid_create_wallet_transaction_request_with_priority_and_voided_transaction_id(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/wallet_transactions", + match_json={ + "wallet_transaction": { + "wallet_id": "123", + "paid_credits": "10", + "granted_credits": "10", + "voided_credits": "0", + "invoice_requires_successful_payment": False, + "name": "Transaction Name", + "purchase_order_number": "PO-123", + "priority": 25, + "voided_transaction_id": "b7ab2926-1de8-4428-9bcd-779314ac1111", + } + }, + content=mock_response(), + ) + transaction = wallet_transaction_object() + transaction.priority = 25 + transaction.voided_transaction_id = "b7ab2926-1de8-4428-9bcd-779314ac1111" + response = client.wallet_transactions.create(transaction) + + assert response["wallet_transactions"][0].lago_id == "b7ab2926-1de8-4428-9bcd-779314ac1111" + assert response["wallet_transactions"][0].priority == 50 + + +def test_valid_create_wallet_transaction_request_with_zero_priority(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/wallet_transactions", + match_json={ + "wallet_transaction": { + "wallet_id": "123", + "paid_credits": "10", + "granted_credits": "10", + "voided_credits": "0", + "invoice_requires_successful_payment": False, + "name": "Transaction Name", + "purchase_order_number": "PO-123", + "priority": 0, + } + }, + content=mock_response(), + ) + transaction = wallet_transaction_object() + transaction.priority = 0 + response = client.wallet_transactions.create(transaction) + + assert response["wallet_transactions"][0].lago_id == "b7ab2926-1de8-4428-9bcd-779314ac1111" + assert response["wallet_transactions"][0].priority == 50 + + +def test_valid_create_wallet_transaction_request_without_priority_and_voided_transaction_id(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/wallet_transactions", + match_json={ + "wallet_transaction": { + "wallet_id": "123", + "paid_credits": "10", + "granted_credits": "10", + "voided_credits": "0", + "invoice_requires_successful_payment": False, + "name": "Transaction Name", + "purchase_order_number": "PO-123", + } + }, + content=mock_response(), + ) + response = client.wallet_transactions.create(wallet_transaction_object()) + + assert response["wallet_transactions"][0].lago_id == "b7ab2926-1de8-4428-9bcd-779314ac1111" + + +def test_valid_create_wallet_transaction_request_voiding_credits_without_priority(httpx_mock: HTTPXMock): + client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") + + httpx_mock.add_response( + method="POST", + url="https://api.getlago.com/api/v1/wallet_transactions", + match_json={ + "wallet_transaction": { + "wallet_id": "123", + "voided_credits": "5", + } + }, + content=mock_response(), + ) + response = client.wallet_transactions.create(WalletTransaction(wallet_id="123", voided_credits="5")) + + assert response["wallet_transactions"][0].lago_id == "b7ab2926-1de8-4428-9bcd-779314ac1111" + + def test_valid_create_wallet_transaction_request_with_payment_method(httpx_mock: HTTPXMock): client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d") payment_method = PaymentMethod(payment_method_type="card", payment_method_id="pm_123")