lago-python-client 1.46.0__tar.gz → 1.48.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/PKG-INFO +1 -1
  2. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/client.py +4 -1
  3. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/clients.py +26 -0
  4. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/__init__.py +6 -0
  5. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer_projected_usage.py +7 -0
  6. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer_usage.py +12 -0
  7. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/fee.py +2 -0
  8. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/organization.py +1 -0
  9. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/subscription.py +2 -0
  10. lago_python_client-1.48.0/lago_python_client/observability.py +67 -0
  11. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/rate_limit.py +110 -1
  12. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/request.py +10 -1
  13. lago_python_client-1.48.0/lago_python_client/version.py +1 -0
  14. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/PKG-INFO +1 -1
  15. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/SOURCES.txt +2 -0
  16. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_client.py +44 -0
  17. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_fee_client.py +2 -0
  18. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_plan_client.py +27 -2
  19. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_rate_limit.py +283 -1
  20. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_subscription_client.py +5 -0
  21. lago_python_client-1.46.0/lago_python_client/version.py +0 -1
  22. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/LICENSE +0 -0
  23. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/README.md +0 -0
  24. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/__init__.py +0 -0
  25. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/activity_logs/__init__.py +0 -0
  26. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/activity_logs/clients.py +0 -0
  27. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/add_ons/__init__.py +0 -0
  28. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/add_ons/clients.py +0 -0
  29. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/api_logs/__init__.py +0 -0
  30. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/api_logs/clients.py +0 -0
  31. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/base_client.py +0 -0
  32. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/base_model.py +0 -0
  33. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billable_metrics/__init__.py +0 -0
  34. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billable_metrics/clients.py +0 -0
  35. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billing_entities/__init__.py +0 -0
  36. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/billing_entities/clients.py +0 -0
  37. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/coupons/__init__.py +0 -0
  38. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/coupons/clients.py +0 -0
  39. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/credit_notes/__init__.py +0 -0
  40. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/credit_notes/clients.py +0 -0
  41. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/__init__.py +0 -0
  42. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/applied_coupons_client.py +0 -0
  43. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/credit_notes_client.py +0 -0
  44. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/invoices_client.py +0 -0
  45. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payment_methods_client.py +0 -0
  46. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payment_requests_client.py +0 -0
  47. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/payments_client.py +0 -0
  48. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/subscriptions_client.py +0 -0
  49. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallet_client.py +0 -0
  50. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/__init__.py +0 -0
  51. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/alert_client.py +0 -0
  52. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets/metadata_client.py +0 -0
  53. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/customers/wallets_client.py +0 -0
  54. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/events/__init__.py +0 -0
  55. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/events/clients.py +0 -0
  56. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/exceptions.py +0 -0
  57. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/fees/__init__.py +0 -0
  58. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/fees/clients.py +0 -0
  59. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/functools_ext.py +0 -0
  60. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/gross_revenues/__init__.py +0 -0
  61. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/gross_revenues/clients.py +0 -0
  62. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoice_collections/__init__.py +0 -0
  63. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoice_collections/clients.py +0 -0
  64. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoiced_usages/__init__.py +0 -0
  65. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoiced_usages/clients.py +0 -0
  66. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoices/__init__.py +0 -0
  67. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/invoices/clients.py +0 -0
  68. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mixins.py +0 -0
  69. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/activity_log.py +0 -0
  70. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/add_on.py +0 -0
  71. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/alert.py +0 -0
  72. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/api_log.py +0 -0
  73. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/applied_coupon.py +0 -0
  74. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billable_metric.py +0 -0
  75. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billing_entity.py +0 -0
  76. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/billing_period.py +0 -0
  77. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/charge.py +0 -0
  78. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/coupon.py +0 -0
  79. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/credit.py +0 -0
  80. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/credit_note.py +0 -0
  81. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/customer.py +0 -0
  82. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/error_detail.py +0 -0
  83. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/event.py +0 -0
  84. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/fixed_charge.py +0 -0
  85. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/gross_revenue.py +0 -0
  86. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice.py +0 -0
  87. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_collection.py +0 -0
  88. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_custom_section.py +0 -0
  89. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoice_item.py +0 -0
  90. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/invoiced_usage.py +0 -0
  91. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/lifetime_usage.py +0 -0
  92. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/minimum_commitment.py +0 -0
  93. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/mrr.py +0 -0
  94. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/overdue_balance.py +0 -0
  95. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment.py +0 -0
  96. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_method.py +0 -0
  97. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_receipt.py +0 -0
  98. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/payment_request.py +0 -0
  99. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/plan.py +0 -0
  100. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/tax.py +0 -0
  101. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/usage.py +0 -0
  102. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/usage_threshold.py +0 -0
  103. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/wallet.py +0 -0
  104. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/wallet_transaction.py +0 -0
  105. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/models/webhook_endpoint.py +0 -0
  106. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mrrs/__init__.py +0 -0
  107. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/mrrs/clients.py +0 -0
  108. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/organizations/__init__.py +0 -0
  109. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/organizations/clients.py +0 -0
  110. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/overdue_balances/__init__.py +0 -0
  111. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/overdue_balances/clients.py +0 -0
  112. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_receipts/__init__.py +0 -0
  113. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_receipts/clients.py +0 -0
  114. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_requests/__init__.py +0 -0
  115. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payment_requests/clients.py +0 -0
  116. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payments/__init__.py +0 -0
  117. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/payments/clients.py +0 -0
  118. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/__init__.py +0 -0
  119. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/charges_client.py +0 -0
  120. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/clients.py +0 -0
  121. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/plans/fixed_charges_client.py +0 -0
  122. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/py.typed +0 -0
  123. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/__init__.py +0 -0
  124. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/json.py +0 -0
  125. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/services/response.py +0 -0
  126. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/__init__.py +0 -0
  127. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/charges_client.py +0 -0
  128. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/clients.py +0 -0
  129. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/subscriptions/fixed_charges_client.py +0 -0
  130. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/taxes/__init__.py +0 -0
  131. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/taxes/clients.py +0 -0
  132. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/usages/__init__.py +0 -0
  133. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/usages/clients.py +0 -0
  134. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/wallets/__init__.py +0 -0
  135. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/wallets/clients.py +0 -0
  136. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhook_endpoints/__init__.py +0 -0
  137. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhook_endpoints/clients.py +0 -0
  138. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhooks/__init__.py +0 -0
  139. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client/webhooks/clients.py +0 -0
  140. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/dependency_links.txt +0 -0
  141. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/requires.txt +0 -0
  142. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/lago_python_client.egg-info/top_level.txt +0 -0
  143. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/pyproject.toml +0 -0
  144. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/setup.cfg +0 -0
  145. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/__init__.py +0 -0
  146. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_activity_logs_client.py +0 -0
  147. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_add_on_client.py +0 -0
  148. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_api_logs_client.py +0 -0
  149. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_applied_coupon_client.py +0 -0
  150. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_billable_metric_client.py +0 -0
  151. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_billing_entity_client.py +0 -0
  152. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_client.py +0 -0
  153. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_coupon_client.py +0 -0
  154. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_credit_note_client.py +0 -0
  155. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_applied_coupons_client.py +0 -0
  156. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_credit_notes_client.py +0 -0
  157. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_invoices_client.py +0 -0
  158. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payment_methods_client.py +0 -0
  159. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payment_requests_client.py +0 -0
  160. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_payments_client.py +0 -0
  161. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_sbscriptions_client.py +0 -0
  162. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_alert_client.py +0 -0
  163. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_client.py +0 -0
  164. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallet_metadata_client.py +0 -0
  165. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_customer_wallets_client.py +0 -0
  166. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_event_client.py +0 -0
  167. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_functools_ext.py +0 -0
  168. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_gross_revenue_client.py +0 -0
  169. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoice_client.py +0 -0
  170. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoice_collection_client.py +0 -0
  171. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_invoiced_usage_client.py +0 -0
  172. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_json_services.py +0 -0
  173. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_mrr_client.py +0 -0
  174. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_organization_client.py +0 -0
  175. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_overdue_balance_client.py +0 -0
  176. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_client.py +0 -0
  177. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_receipt_client.py +0 -0
  178. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_payment_request_client.py +0 -0
  179. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_request_services.py +0 -0
  180. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_response_services.py +0 -0
  181. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_tax_client.py +0 -0
  182. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_usage_client.py +0 -0
  183. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_wallet_client.py +0 -0
  184. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_wallet_transaction_client.py +0 -0
  185. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_webhook_client.py +0 -0
  186. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/test_webhook_endpoint_client.py +0 -0
  187. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/utils/__init__.py +0 -0
  188. {lago_python_client-1.46.0 → lago_python_client-1.48.0}/tests/utils/mixin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lago-python-client
3
- Version: 1.46.0
3
+ Version: 1.48.0
4
4
  Summary: Lago Python API Client
5
5
  Home-page: https://github.com/getlago/lago-python-client
6
6
  Author: Lovro Colic
@@ -1,3 +1,4 @@
1
+ from typing import Optional
1
2
  from urllib.parse import urljoin
2
3
 
3
4
  from .activity_logs.clients import ActivityLogClient
@@ -32,7 +33,7 @@ from .payments.clients import PaymentClient
32
33
  from .plans.charges_client import PlanChargesClient
33
34
  from .plans.clients import PlanClient
34
35
  from .plans.fixed_charges_client import PlanFixedChargesClient
35
- from .services.rate_limit import RateLimitRetryConfig
36
+ from .services.rate_limit import RateLimitCallback, RateLimitRetryConfig
36
37
  from .subscriptions.charges_client import SubscriptionChargesClient
37
38
  from .subscriptions.clients import SubscriptionClient
38
39
  from .subscriptions.fixed_charges_client import SubscriptionFixedChargesClient
@@ -61,6 +62,7 @@ class Client:
61
62
  ingest_api_url: str = "",
62
63
  max_retries: int = 3,
63
64
  retry_on_rate_limit: bool = True,
65
+ on_rate_limit_info: Optional[RateLimitCallback] = None,
64
66
  ) -> None:
65
67
  self.api_key: str = api_key
66
68
  self.api_url: str = api_url
@@ -69,6 +71,7 @@ class Client:
69
71
  self.rate_limit_retry_config: RateLimitRetryConfig = RateLimitRetryConfig(
70
72
  max_retries=max_retries,
71
73
  retry_on_rate_limit=retry_on_rate_limit,
74
+ on_rate_limit_info=on_rate_limit_info,
72
75
  )
73
76
 
74
77
  @property
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import warnings
2
3
  from collections.abc import Mapping
3
4
  from typing import Any, ClassVar, Optional, Type, Union
4
5
 
@@ -46,17 +47,42 @@ class CustomerClient(
46
47
  filter_by_charge_id: Optional[str] = None,
47
48
  filter_by_charge_code: Optional[str] = None,
48
49
  filter_by_group: Optional[dict] = None,
50
+ filter_by_presentation: Optional[Union[str, list[str]]] = None,
49
51
  full_usage: Optional[bool] = None,
52
+ charge_id: Optional[str] = None,
53
+ charge_code: Optional[str] = None,
54
+ billable_metric_code: Optional[str] = None,
55
+ group: Optional[dict] = None,
50
56
  ) -> CustomerUsageResponse:
51
57
  query_params: dict[str, Union[str, bool]] = {"external_subscription_id": external_subscription_id}
52
58
  if apply_taxes is not None:
53
59
  query_params["apply_taxes"] = apply_taxes
60
+ if charge_id is not None:
61
+ query_params["charge_id"] = charge_id
62
+ if charge_code is not None:
63
+ query_params["charge_code"] = charge_code
64
+ if billable_metric_code is not None:
65
+ query_params["billable_metric_code"] = billable_metric_code
66
+ if group is not None:
67
+ for k, v in group.items():
68
+ query_params[f"group[{k}]"] = v
54
69
  if filter_by_charge_id is not None:
70
+ warnings.warn("filter_by_charge_id is deprecated, use charge_id instead", DeprecationWarning, stacklevel=2)
55
71
  query_params["filter_by_charge_id"] = filter_by_charge_id
56
72
  if filter_by_charge_code is not None:
73
+ warnings.warn(
74
+ "filter_by_charge_code is deprecated, use charge_code instead", DeprecationWarning, stacklevel=2
75
+ )
57
76
  query_params["filter_by_charge_code"] = filter_by_charge_code
58
77
  if filter_by_group is not None:
78
+ warnings.warn("filter_by_group is deprecated, use group instead", DeprecationWarning, stacklevel=2)
59
79
  query_params["filter_by_group"] = json.dumps(filter_by_group)
80
+ if filter_by_presentation is not None:
81
+ query_params["filter_by_presentation"] = (
82
+ filter_by_presentation
83
+ if isinstance(filter_by_presentation, str)
84
+ else json.dumps(filter_by_presentation)
85
+ )
60
86
  if full_usage is not None:
61
87
  query_params["full_usage"] = str(full_usage).lower()
62
88
 
@@ -138,6 +138,12 @@ from .customer_usage import (
138
138
  from .customer_usage import (
139
139
  Metric as Metric,
140
140
  )
141
+ from .customer_usage import (
142
+ PresentationBreakdown as PresentationBreakdown,
143
+ )
144
+ from .customer_usage import (
145
+ PresentationBreakdowns as PresentationBreakdowns,
146
+ )
141
147
  from .event import BatchEvent as BatchEvent
142
148
  from .event import Event as Event
143
149
  from .fee import Fee as Fee
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional
3
3
  from lago_python_client.base_model import BaseModel
4
4
 
5
5
  from ..base_model import BaseResponseModel
6
+ from .customer_usage import PresentationBreakdowns
6
7
 
7
8
 
8
9
  class Metric(BaseModel):
@@ -27,6 +28,8 @@ class ProjectedChargeFilterUsage(BaseModel):
27
28
  projected_amount_cents: int
28
29
  events_count: int
29
30
  pricing_unit_details: Optional[PricingUnitDetails]
31
+ presentation_breakdowns: Optional[PresentationBreakdowns]
32
+ projected_presentation_breakdowns: Optional[PresentationBreakdowns]
30
33
 
31
34
 
32
35
  class ChargeObject(BaseModel):
@@ -44,6 +47,8 @@ class ProjectedGroupedUsage(BaseModel):
44
47
  grouped_by: Dict[str, Optional[str]]
45
48
  filters: List[ProjectedChargeFilterUsage]
46
49
  pricing_unit_details: Optional[PricingUnitDetails]
50
+ presentation_breakdowns: Optional[PresentationBreakdowns]
51
+ projected_presentation_breakdowns: Optional[PresentationBreakdowns]
47
52
 
48
53
 
49
54
  class ProjectedChargeUsage(BaseModel):
@@ -58,6 +63,8 @@ class ProjectedChargeUsage(BaseModel):
58
63
  filters: List[ProjectedChargeFilterUsage]
59
64
  grouped_usage: Optional[List[ProjectedGroupedUsage]]
60
65
  pricing_unit_details: Optional[PricingUnitDetails]
66
+ presentation_breakdowns: Optional[PresentationBreakdowns]
67
+ projected_presentation_breakdowns: Optional[PresentationBreakdowns]
61
68
 
62
69
 
63
70
  class CustomerProjectedUsageResponse(BaseResponseModel):
@@ -18,6 +18,15 @@ class PricingUnitDetails(BaseModel):
18
18
  conversion_rate: float
19
19
 
20
20
 
21
+ class PresentationBreakdown(BaseModel):
22
+ presentation_by: Dict[str, str]
23
+ units: str
24
+
25
+
26
+ class PresentationBreakdowns(BaseModel):
27
+ __root__: List[PresentationBreakdown]
28
+
29
+
21
30
  class ChargeFilterUsage(BaseModel):
22
31
  invoice_display_name: Optional[str]
23
32
  values: Optional[Dict[str, List[str]]]
@@ -26,6 +35,7 @@ class ChargeFilterUsage(BaseModel):
26
35
  amount_cents: int
27
36
  events_count: int
28
37
  pricing_unit_details: Optional[PricingUnitDetails]
38
+ presentation_breakdowns: Optional[PresentationBreakdowns]
29
39
 
30
40
 
31
41
  class ChargeObject(BaseModel):
@@ -42,6 +52,7 @@ class GroupedUsage(BaseModel):
42
52
  grouped_by: Dict[str, Optional[str]]
43
53
  filters: List[ChargeFilterUsage]
44
54
  pricing_unit_details: Optional[PricingUnitDetails]
55
+ presentation_breakdowns: Optional[PresentationBreakdowns]
45
56
 
46
57
 
47
58
  class ChargeUsage(BaseModel):
@@ -55,6 +66,7 @@ class ChargeUsage(BaseModel):
55
66
  filters: List[ChargeFilterUsage]
56
67
  grouped_usage: Optional[List[GroupedUsage]]
57
68
  pricing_unit_details: Optional[PricingUnitDetails]
69
+ presentation_breakdowns: Optional[PresentationBreakdowns]
58
70
 
59
71
 
60
72
  class CustomerUsageResponse(BaseResponseModel):
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Dict, List, Optional
2
2
 
3
3
  from ..base_model import BaseModel, BaseResponseModel
4
+ from .customer_usage import PresentationBreakdowns
4
5
  from .invoice_item import InvoiceItemResponse
5
6
 
6
7
 
@@ -75,6 +76,7 @@ class FeeResponse(BaseResponseModel):
75
76
  to_date: Optional[str]
76
77
  amount_details: Optional[Dict[str, Any]]
77
78
  pricing_unit_details: Optional[PricingUnitDetails]
79
+ presentation_breakdowns: Optional[PresentationBreakdowns]
78
80
  billing_entity_code: Optional[str]
79
81
 
80
82
  item: Optional[InvoiceItemResponse]
@@ -56,3 +56,4 @@ class OrganizationResponse(BaseResponseModel):
56
56
  email_settings: Optional[List[str]]
57
57
  finalize_zero_amount_invoice: Optional[bool]
58
58
  billing_configuration: Optional[OrganizationBillingConfiguration]
59
+ events_store: Optional[str]
@@ -19,6 +19,7 @@ class Subscription(BaseModel):
19
19
  plan_overrides: Optional[PlanOverrides]
20
20
  payment_method: Optional[PaymentMethod]
21
21
  invoice_custom_section: Optional[InvoiceCustomSectionInput]
22
+ consolidate_invoice: Optional[bool]
22
23
 
23
24
 
24
25
  class Subscriptions(BaseModel):
@@ -55,6 +56,7 @@ class SubscriptionResponse(BaseResponseModel):
55
56
  on_termination_invoice: Optional[str]
56
57
  payment_method: Optional[PaymentMethod]
57
58
  applied_invoice_custom_sections: Optional[AppliedInvoiceCustomSections]
59
+ consolidate_invoice: Optional[bool]
58
60
 
59
61
 
60
62
  class SubscriptionsResponse(BaseResponseModel):
@@ -0,0 +1,67 @@
1
+ """
2
+ Optional observability helpers for Lago rate limits.
3
+
4
+ Provides ready-to-use ``on_rate_limit_info`` callbacks so callers can opt into
5
+ rate limit observability without writing one themselves.
6
+
7
+ Example:
8
+ from lago_python_client import Client
9
+ from lago_python_client.observability import LoggingRateLimitObserver
10
+
11
+ client = Client(
12
+ api_key="...",
13
+ on_rate_limit_info=LoggingRateLimitObserver(),
14
+ )
15
+ """
16
+
17
+ import logging
18
+ from typing import Optional, Sequence
19
+
20
+ from .services.rate_limit import RateLimitInfo
21
+
22
+ DEFAULT_THRESHOLDS: Sequence[float] = (0.80, 0.90, 0.95)
23
+
24
+
25
+ class LoggingRateLimitObserver:
26
+ """
27
+ Emits a log line each time rate limit usage crosses a configured threshold.
28
+
29
+ Stateless across calls: every response that lands above any threshold logs.
30
+ If you want one-shot crossings, wrap it with your own state.
31
+
32
+ Args:
33
+ thresholds: Usage fractions (0.0 - 1.0) that should produce a log line.
34
+ Defaults to 80%, 90%, 95%.
35
+ logger: Logger to emit on. Defaults to ``lago_python_client.rate_limit``.
36
+ level: Log level used when a threshold is crossed. Defaults to WARNING.
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ thresholds: Sequence[float] = DEFAULT_THRESHOLDS,
42
+ logger: Optional[logging.Logger] = None,
43
+ level: int = logging.WARNING,
44
+ ) -> None:
45
+ # Sort descending so we report the highest matching threshold first.
46
+ self.thresholds = tuple(sorted(thresholds, reverse=True))
47
+ self.logger = logger or logging.getLogger("lago_python_client.rate_limit")
48
+ self.level = level
49
+
50
+ def __call__(self, info: RateLimitInfo) -> None:
51
+ pct = info.usage_pct
52
+ if pct is None:
53
+ return
54
+
55
+ for threshold in self.thresholds:
56
+ if pct >= threshold:
57
+ self.logger.log(
58
+ self.level,
59
+ "Lago rate limit at %.0f%% (limit=%s, remaining=%s, reset=%ss, %s %s)",
60
+ pct * 100,
61
+ info.limit,
62
+ info.remaining,
63
+ info.reset,
64
+ info.method,
65
+ info.url,
66
+ )
67
+ return
@@ -1,11 +1,87 @@
1
+ import logging
1
2
  import time
2
- from typing import TYPE_CHECKING, Optional
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Callable, Optional
3
5
 
4
6
  if TYPE_CHECKING:
5
7
  from httpx import Response
6
8
 
7
9
  from ..exceptions import LagoRateLimitError
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class RateLimitInfo:
16
+ """
17
+ Parsed rate limit headers from a Lago API response.
18
+
19
+ Provided to ``on_rate_limit_info`` callbacks after every successful request
20
+ so callers can build their own observability (metrics, logs, alerts).
21
+ """
22
+
23
+ limit: Optional[int]
24
+ remaining: Optional[int]
25
+ reset: Optional[int]
26
+ url: str
27
+ method: str
28
+
29
+ @property
30
+ def usage_pct(self) -> Optional[float]:
31
+ """
32
+ Fraction of the rate limit currently used, between 0.0 and 1.0.
33
+
34
+ Returns None when the headers aren't usable (missing, zero limit, etc.).
35
+ """
36
+ if self.limit is None or self.remaining is None or self.limit <= 0:
37
+ return None
38
+ return 1.0 - (self.remaining / self.limit)
39
+
40
+
41
+ RateLimitCallback = Callable[[RateLimitInfo], None]
42
+
43
+
44
+ def parse_rate_limit_info(
45
+ response: "Response",
46
+ method: str,
47
+ url: str,
48
+ ) -> Optional[RateLimitInfo]:
49
+ """
50
+ Parse ``x-ratelimit-*`` headers from a response into a RateLimitInfo.
51
+
52
+ Returns ``None`` when no rate limit headers are present (for example, on a
53
+ self-hosted Lago instance that doesn't enforce limits) so callers can skip
54
+ emission entirely.
55
+ """
56
+ headers = response.headers
57
+ limit: Optional[int] = None
58
+ remaining: Optional[int] = None
59
+ reset: Optional[int] = None
60
+
61
+ try:
62
+ limit = int(headers["x-ratelimit-limit"])
63
+ except (KeyError, ValueError, TypeError):
64
+ pass
65
+ try:
66
+ remaining = int(headers["x-ratelimit-remaining"])
67
+ except (KeyError, ValueError, TypeError):
68
+ pass
69
+ try:
70
+ reset = int(headers["x-ratelimit-reset"])
71
+ except (KeyError, ValueError, TypeError):
72
+ pass
73
+
74
+ if limit is None and remaining is None and reset is None:
75
+ return None
76
+
77
+ return RateLimitInfo(
78
+ limit=limit,
79
+ remaining=remaining,
80
+ reset=reset,
81
+ url=url,
82
+ method=method,
83
+ )
84
+
9
85
 
10
86
  class RateLimitRetryConfig:
11
87
  """Configuration for rate limit retry behavior."""
@@ -17,6 +93,7 @@ class RateLimitRetryConfig:
17
93
  base_backoff_seconds: float = 1.0,
18
94
  backoff_multiplier: float = 2.0,
19
95
  max_retry_delay: float = 20.0,
96
+ on_rate_limit_info: Optional[RateLimitCallback] = None,
20
97
  ):
21
98
  """
22
99
  Initialize rate limit retry configuration.
@@ -27,12 +104,18 @@ class RateLimitRetryConfig:
27
104
  base_backoff_seconds: Initial backoff duration in seconds for exponential backoff.
28
105
  backoff_multiplier: Multiplier for exponential backoff between retries.
29
106
  max_retry_delay: Maximum delay in seconds before a retry (default: 20).
107
+ on_rate_limit_info: Optional callback invoked after every successful
108
+ response with parsed rate limit headers. Use this to build
109
+ observability around the rate limit (warn at 80%, emit metrics,
110
+ etc.). Exceptions raised from the callback are logged and
111
+ swallowed so they never break the request flow.
30
112
  """
31
113
  self.max_retries = max_retries
32
114
  self.retry_on_rate_limit = retry_on_rate_limit
33
115
  self.base_backoff_seconds = base_backoff_seconds
34
116
  self.backoff_multiplier = backoff_multiplier
35
117
  self.max_retry_delay = max_retry_delay
118
+ self.on_rate_limit_info = on_rate_limit_info
36
119
 
37
120
  def calculate_backoff(self, retry_attempt: int) -> float:
38
121
  """
@@ -47,6 +130,32 @@ class RateLimitRetryConfig:
47
130
  return self.base_backoff_seconds * (self.backoff_multiplier**retry_attempt)
48
131
 
49
132
 
133
+ def emit_rate_limit_info(
134
+ response: "Response",
135
+ config: "RateLimitRetryConfig",
136
+ method: str,
137
+ url: str,
138
+ ) -> None:
139
+ """
140
+ Invoke the ``on_rate_limit_info`` callback if configured.
141
+
142
+ Parses rate limit headers off the response and hands them to the user's
143
+ callback. Any exception raised from the callback is logged and swallowed so
144
+ a buggy observer never breaks the underlying request.
145
+ """
146
+ if config.on_rate_limit_info is None:
147
+ return
148
+
149
+ info = parse_rate_limit_info(response, method=method, url=url)
150
+ if info is None:
151
+ return
152
+
153
+ try:
154
+ config.on_rate_limit_info(info)
155
+ except Exception:
156
+ logger.exception("on_rate_limit_info callback raised; suppressing")
157
+
158
+
50
159
  def handle_rate_limit_response(
51
160
  response: "Response",
52
161
  config: RateLimitRetryConfig,
@@ -12,6 +12,7 @@ import httpx
12
12
  from ..version import LAGO_VERSION
13
13
  from .rate_limit import (
14
14
  RateLimitRetryConfig,
15
+ emit_rate_limit_info,
15
16
  handle_rate_limit_response,
16
17
  is_rate_limit_response,
17
18
  wait_for_retry,
@@ -92,12 +93,20 @@ def _create_retry_wrapper(
92
93
  if rate_limit_retry_config is None:
93
94
  rate_limit_retry_config = RateLimitRetryConfig()
94
95
 
96
+ method_name = http_method.__name__.upper()
97
+
95
98
  retry_attempt = 0
96
99
  while True:
97
100
  response = http_method(url, **kwargs)
98
101
 
99
- # If not rate limited, return the response
102
+ # If not rate limited, emit observability info and return the response
100
103
  if not is_rate_limit_response(response):
104
+ emit_rate_limit_info(
105
+ response,
106
+ rate_limit_retry_config,
107
+ method=method_name,
108
+ url=url,
109
+ )
101
110
  return response
102
111
 
103
112
  # Handle rate limit - may raise LagoRateLimitError or return wait duration
@@ -0,0 +1 @@
1
+ LAGO_VERSION = "1.48.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lago-python-client
3
- Version: 1.46.0
3
+ Version: 1.48.0
4
4
  Summary: Lago Python API Client
5
5
  Home-page: https://github.com/getlago/lago-python-client
6
6
  Author: Lovro Colic
@@ -9,6 +9,7 @@ setup.cfg
9
9
  ./lago_python_client/exceptions.py
10
10
  ./lago_python_client/functools_ext.py
11
11
  ./lago_python_client/mixins.py
12
+ ./lago_python_client/observability.py
12
13
  ./lago_python_client/py.typed
13
14
  ./lago_python_client/version.py
14
15
  ./lago_python_client/activity_logs/__init__.py
@@ -185,6 +186,7 @@ lago_python_client/client.py
185
186
  lago_python_client/exceptions.py
186
187
  lago_python_client/functools_ext.py
187
188
  lago_python_client/mixins.py
189
+ lago_python_client/observability.py
188
190
  lago_python_client/py.typed
189
191
  lago_python_client/version.py
190
192
  lago_python_client.egg-info/PKG-INFO
@@ -134,6 +134,11 @@ def test_valid_current_usage(httpx_mock: HTTPXMock):
134
134
  assert response.charges_usage[0].units == "1.0"
135
135
  assert len(response.charges_usage[0].filters) == 1
136
136
  assert response.charges_usage[0].filters[0].values["country"] == ["france"]
137
+ charge_usage = response.charges_usage[0]
138
+ assert charge_usage.presentation_breakdowns.__root__[0].presentation_by["team"] == "engineering"
139
+ assert charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
140
+ assert charge_usage.filters[0].presentation_breakdowns.__root__[0].units == "2.0"
141
+ assert charge_usage.grouped_usage[0].presentation_breakdowns.__root__[0].units == "1.0"
137
142
 
138
143
 
139
144
  def test_valid_projected_usage(httpx_mock: HTTPXMock):
@@ -157,6 +162,11 @@ def test_valid_projected_usage(httpx_mock: HTTPXMock):
157
162
  assert response.charges_usage[0].charge.charge_model == "graduated"
158
163
  assert response.charges_usage[0].charge.invoice_display_name == "add_on_invoice_display_name"
159
164
  assert response.charges_usage[0].billable_metric.lago_id == "99a6094e-199b-4101-896a-54e927ce7bd7"
165
+ charge_usage = response.charges_usage[0]
166
+ assert charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
167
+ assert charge_usage.projected_presentation_breakdowns.__root__[0].units == "6.0"
168
+ assert charge_usage.filters[0].projected_presentation_breakdowns.__root__[0].units == "4.0"
169
+ assert charge_usage.grouped_usage[0].projected_presentation_breakdowns.__root__[0].units == "2.0"
160
170
 
161
171
 
162
172
  def test_valid_current_usage_without_taxes(httpx_mock: HTTPXMock):
@@ -204,6 +214,38 @@ def test_valid_current_usage_with_filters(httpx_mock: HTTPXMock):
204
214
  assert len(response.charges_usage) == 1
205
215
 
206
216
 
217
+ def test_valid_current_usage_with_new_filters(httpx_mock: HTTPXMock):
218
+ client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
219
+ charge_id = "1a901a90-1a90-1a90-1a90-1a901a901a90"
220
+
221
+ httpx_mock.add_response(
222
+ method="GET",
223
+ url=(
224
+ "https://api.getlago.com/api/v1/customers/external_customer_id/current_usage"
225
+ f"?external_subscription_id=123&charge_id={charge_id}"
226
+ "&charge_code=storage"
227
+ "&billable_metric_code=storage"
228
+ "&group%5Bcloud%5D=aws"
229
+ "&filter_by_presentation=%5B%22engineering%22%2C+%22operations%22%5D"
230
+ "&full_usage=true"
231
+ ),
232
+ content=mock_response("customer_usage"),
233
+ )
234
+ response = client.customers.current_usage(
235
+ "external_customer_id",
236
+ "123",
237
+ charge_id=charge_id,
238
+ charge_code="storage",
239
+ billable_metric_code="storage",
240
+ group={"cloud": "aws"},
241
+ filter_by_presentation=["engineering", "operations"],
242
+ full_usage=True,
243
+ )
244
+
245
+ assert response.from_datetime == "2022-07-01T00:00:00Z"
246
+ assert len(response.charges_usage) == 1
247
+
248
+
207
249
  def test_invalid_current_usage(httpx_mock: HTTPXMock):
208
250
  client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
209
251
 
@@ -233,6 +275,8 @@ def test_valid_past_usage(httpx_mock: HTTPXMock):
233
275
  assert len(response["usage_periods"][0].charges_usage) == 1
234
276
  assert response["usage_periods"][0].charges_usage[0].units == "1.0"
235
277
  assert len(response["usage_periods"][0].charges_usage[0].filters) == 1
278
+ past_charge_usage = response["usage_periods"][0].charges_usage[0]
279
+ assert past_charge_usage.presentation_breakdowns.__root__[0].units == "3.0"
236
280
 
237
281
 
238
282
  def test_invalid_past_usage(httpx_mock: HTTPXMock):
@@ -29,6 +29,8 @@ def test_valid_find_fee_request(httpx_mock: HTTPXMock):
29
29
  assert response.lago_id == identifier
30
30
  assert response.invoice_display_name == fee_invoice_display_name
31
31
  assert response.item.invoice_display_name == charge_invoice_display_name
32
+ assert response.presentation_breakdowns.__root__[0].presentation_by["team"] == "engineering"
33
+ assert response.presentation_breakdowns.__root__[0].units == "10.0"
32
34
 
33
35
 
34
36
  def test_fee_response_with_fixed_charge_id():
@@ -25,7 +25,14 @@ def plan_object():
25
25
  id=None,
26
26
  invoice_display_name=None,
27
27
  regroup_paid_fees=None,
28
- properties=None,
28
+ properties={
29
+ "presentation_group_keys": [
30
+ {
31
+ "value": "region",
32
+ "options": {"display_in_invoice": True},
33
+ }
34
+ ]
35
+ },
29
36
  tax_codes=None,
30
37
  billable_metric_id="id",
31
38
  charge_model="standard",
@@ -194,6 +201,8 @@ def test_valid_create_plan_request(httpx_mock: HTTPXMock):
194
201
  assert response.code == "plan_code"
195
202
  assert response.invoice_display_name == "test plan 1"
196
203
  assert response.charges.__root__[0].invoice_display_name == "Setup"
204
+ charge = response.charges.__root__[0]
205
+ assert charge.properties["presentation_group_keys"][0]["value"] == "region"
197
206
  assert response.minimum_commitment.invoice_display_name == "Minimum commitment (C1)"
198
207
  assert response.usage_thresholds.__root__[0].threshold_display_name == "Threshold 1"
199
208
  assert response.metadata == {"key1": "value1", "key2": None}
@@ -276,6 +285,8 @@ def test_valid_find_plan_request(httpx_mock: HTTPXMock):
276
285
  assert response.invoice_display_name == "test plan 1"
277
286
  assert response.charges.__root__[0].charge_model == "standard"
278
287
  assert response.charges.__root__[0].min_amount_cents == 0
288
+ charge = response.charges.__root__[0]
289
+ assert charge.properties["presentation_group_keys"][0]["value"] == "region"
279
290
  assert response.minimum_commitment.amount_cents == 1000
280
291
 
281
292
 
@@ -338,6 +349,8 @@ def test_valid_find_all_plan_request(httpx_mock: HTTPXMock):
338
349
  assert response["plans"][0].invoice_display_name == "test plan 1"
339
350
  assert response["plans"][0].minimum_commitment.invoice_display_name == "Minimum commitment (C2)"
340
351
  assert response["plans"][0].charges.__root__[0].lago_id == "51c1e851-5be6-4343-a0ee-39a81d8b4ee1"
352
+ plan_charge = response["plans"][0].charges.__root__[0]
353
+ assert plan_charge.properties["presentation_group_keys"][0]["value"] == "region"
341
354
  assert response["plans"][0].charges.__root__[0].filters.__root__[0].properties["amount"] == "0.22"
342
355
  assert response["plans"][0].charges.__root__[0].filters.__root__[0].invoice_display_name == "Europe"
343
356
  assert response["meta"]["current_page"] == 1
@@ -506,6 +519,9 @@ def test_valid_find_charge_request(httpx_mock: HTTPXMock):
506
519
  assert response.code == "charge_code"
507
520
  assert response.charge_model == "standard"
508
521
  assert response.invoice_display_name == "Setup"
522
+ presentation_group_key = response.properties["presentation_group_keys"][0]
523
+ assert presentation_group_key["value"] == "region"
524
+ assert presentation_group_key["options"]["display_in_invoice"] is True
509
525
 
510
526
 
511
527
  def test_valid_create_charge_request(httpx_mock: HTTPXMock):
@@ -522,12 +538,21 @@ def test_valid_create_charge_request(httpx_mock: HTTPXMock):
522
538
  charge_model="standard",
523
539
  pay_in_advance=True,
524
540
  invoiceable=True,
525
- properties={"amount": "0.22"},
541
+ properties={
542
+ "amount": "0.22",
543
+ "presentation_group_keys": [
544
+ {
545
+ "value": "region",
546
+ "options": {"display_in_invoice": True},
547
+ }
548
+ ],
549
+ },
526
550
  )
527
551
  response = client.plan_charges.create(plan_code, charge)
528
552
 
529
553
  assert response.lago_id == "51c1e851-5be6-4343-a0ee-39a81d8b4ee1"
530
554
  assert response.charge_model == "standard"
555
+ assert charge.dict()["properties"]["presentation_group_keys"][0]["value"] == "region"
531
556
 
532
557
 
533
558
  def test_valid_update_charge_request(httpx_mock: HTTPXMock):