From 6ebf5e134fac1b9d9371612b3ca6ae8752d72eb7 Mon Sep 17 00:00:00 2001 From: Wiktor Plaga Date: Thu, 25 Dec 2025 15:47:33 +0100 Subject: [PATCH 1/3] Add Customer.subscriptions method --- chartmogul/api/customer.py | 5 ++- test/api/test_customer.py | 79 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/chartmogul/api/customer.py b/chartmogul/api/customer.py index 2c5b4fe..7c774bc 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -13,7 +13,7 @@ from .customer_note import CustomerNote from .opportunity import Opportunity from .task import Task - +from .customers.subscription import CustomerSubscription class Address(DataObject): class _Schema(Schema): @@ -80,6 +80,9 @@ def make(self, data, **kwargs): Customer.search = Customer._method("all", "get", "/customers/search") Customer.merge = Customer._method("merge", "post", "/customers/merges") Customer.unmerge = Customer._method("unmerge", "post", "/customers/unmerges") +Customer.subscriptions = CustomerSubscription._method( + "all", "get", "/customers/{uuid}/subscriptions", useCallerClass=True +) Customer.connectSubscriptions = Customer._method( "create", "post", "/customers/{uuid}/connect_subscriptions" ) diff --git a/test/api/test_customer.py b/test/api/test_customer.py index ef9bdf1..b1f3025 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -1,6 +1,7 @@ import unittest from chartmogul import Customer, Contact, Config, CustomerNote, Opportunity, Task from chartmogul.api.customer import Attributes, Address +from chartmogul.api.customers.subscription import CustomerSubscription from datetime import datetime from chartmogul import APIError import requests_mock @@ -394,6 +395,66 @@ allTasks = {"entries": [taskEntry], "cursor": "cursor==", "has_more": False} +allSubscriptions = { + "entries": [ + { + "id": 5322874574, + "external_id": "cbdemo_ZpbKpmKUbu83EsNv", + "subscription_set_external_id": "cbdemo_ZpbKpmKUbu83EsNv", + "quantity": 1, + "uuid": "8d80f275-a494-4957-8968-6cb68acdcfab", + "mrr": 18100, + "arr": 217200, + "status": "active", + "plan": "Professional Suite Annual(cbdemo_omnisupport-solutions)", + "billing-cycle": "year", + "billing-cycle-count": 1, + "start-date": "2024-11-22T17:51:46+00:00", + "end-date": "2026-11-23T17:51:44+00:00", + "currency": "PLN", + "currency-sign": "zł" + }, + { + "id": 5322874575, + "external_id": "cbdemo_ZpbKpmKUbu83EsNv_cbdemo_workforce-optimizer-addon-annual", + "subscription_set_external_id": "cbdemo_ZpbKpmKUbu83EsNv", + "quantity": 1, + "uuid": "77867070-2435-4da1-8bde-014f6817bd49", + "mrr": 9048, + "arr": 108576, + "status": "active", + "plan": "Workforce Optimizer Add-on Annual(cbdemo_omnisupport-solutions)", + "billing-cycle": "year", + "billing-cycle-count": 1, + "start-date": "2024-11-22T17:51:46+00:00", + "end-date": "2026-11-23T17:51:44+00:00", + "currency": "PLN", + "currency-sign": "zł" + }, + { + "id": 5322874576, + "external_id": "169vEGV551MI2J0", + "subscription_set_external_id": "169vEGV551MI2J0", + "quantity": 1, + "uuid": "a8640a5a-0d43-41c7-803e-76fc042267b0", + "mrr": 19432, + "arr": 233184, + "status": "active", + "plan": "Professional Suite Monthly(cbdemo_omnisupport-solutions)", + "billing-cycle": "month", + "billing-cycle-count": 1, + "start-date": "2025-12-11T14:09:32+00:00", + "end-date": "2026-01-11T14:09:32+00:00", + "currency": "PLN", + "currency-sign": "zł" + } + ], + "has_more": False, + "per_page": 200, + "page": 1, + "cursor": "c3Vic2NyaXB0aW9uc19uZXh0X3BhZ2U9Mg==" +} + class CustomerTestCase(unittest.TestCase): """ @@ -506,6 +567,24 @@ def test_unmerge(self, mock_requests): self.assertEqual(mock_requests.last_request.json(), jsonRequest) self.assertEqual(result, None) + @requests_mock.mock() + def test_subscriptions(self, mock_requests): + mock_requests.register_uri( + "GET", + "https://api.chartmogul.com/v1/customers/cus_5915ee5a-babd-406b-b8ce-d207133fb4cb/subscriptions", + status_code=200, + json=allSubscriptions, + ) + + config = Config("token") + result = Customer.subscriptions( + config, uuid="cus_5915ee5a-babd-406b-b8ce-d207133fb4cb" + ).get() + self.assertEqual(mock_requests.call_count, 1, "expected call") + self.assertEqual(mock_requests.last_request.qs, {}) + self.assertEqual(mock_requests.last_request.text, None) + self.assertTrue(isinstance(result, CustomerSubscription._many)) + @requests_mock.mock() def test_connectSubscriptions(self, mock_requests): mock_requests.register_uri( From 124d8b5e8bcd9692093cdb7925a2266c922baa4b Mon Sep 17 00:00:00 2001 From: Wiktor Plaga Date: Fri, 26 Dec 2025 10:03:40 +0100 Subject: [PATCH 2/3] Add deprecation warning and update docs --- README.md | 10 ++++++---- chartmogul/api/customers/subscription.py | 14 +++++++++++++- test/api/test_customer.py | 8 ++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 041b52a..be5bbde 100644 --- a/README.md +++ b/README.md @@ -149,15 +149,16 @@ chartmogul.Customer.modify(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4c "state": "CA", }) chartmogul.Customer.destroy(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb') +chartmogul.Customer.subscriptions(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb') chartmogul.Customer.connectSubscriptions(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb', data={ 'subscriptions': [ { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0" + "uuid": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0" }, { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4" + "uuid": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4" } ] }) @@ -165,11 +166,11 @@ chartmogul.Customer.disconnectSubscriptions(config, uuid='cus_5915ee5a-babd-406b 'subscriptions': [ { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0" + "uuid": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0" }, { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4" + "uuid": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4" } ] }) @@ -351,6 +352,7 @@ chartmogul.SubscriptionEvent.destroy_with_params(config, data={ ```python import chartmogul +# DEPRECATED: use chartmogul.Customer.subscriptions() instead chartmogul.Subscription.list_imported(config, uuid='cus_5915ee5a-babd-406b-b8ce-d207133fb4cb') chartmogul.Subscription.cancel(config, uuid='sub_3995ee5a-bbdb-406b-a8ca-d207133fb9bb' data={'cancelled_at': ''}) chartmogul.Subscription.modify(config, uuid='sub_3995ee5a-bbdb-406b-a8ca-d207133fb9bb' data={'cancellation_dates': []}) diff --git a/chartmogul/api/customers/subscription.py b/chartmogul/api/customers/subscription.py index a85336a..d1e0b38 100644 --- a/chartmogul/api/customers/subscription.py +++ b/chartmogul/api/customers/subscription.py @@ -1,6 +1,7 @@ from marshmallow import Schema, fields, post_load, EXCLUDE from chartmogul.resource import Resource from collections import namedtuple +import warnings class CustomerSubscription(Resource): @@ -61,9 +62,20 @@ def _loadJSON(cls, jsonObj): # /import namespace -CustomerSubscription.list_imported = CustomerSubscription._method( +_original_list_imported = CustomerSubscription._method( "list_imported", "get", "/import/customers{/uuid}/subscriptions" ) + +@classmethod +def _deprecated_list_imported(cls, config, **kwargs): + warnings.warn( + "CustomerSubscription.list_imported() is deprecated. Use Customer.subscriptions() instead.", + DeprecationWarning, + stacklevel=2 + ) + return _original_list_imported.__func__(cls, config, **kwargs) + +CustomerSubscription.list_imported = _deprecated_list_imported CustomerSubscription.cancel = CustomerSubscription._method( "cancel", "patch", "/import/subscriptions{/uuid}" ) diff --git a/test/api/test_customer.py b/test/api/test_customer.py index b1f3025..ec7700e 100644 --- a/test/api/test_customer.py +++ b/test/api/test_customer.py @@ -597,11 +597,11 @@ def test_connectSubscriptions(self, mock_requests): "subscriptions": [ { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0", + "uuid": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0", }, { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4", + "uuid": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4", }, ] } @@ -626,11 +626,11 @@ def test_disconnectSubscriptions(self, mock_requests): "subscriptions": [ { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0", + "uuid": "d1c0c885-add0-48db-8fa9-0bdf5017d6b0", }, { "data_source_uuid": "ds_ade45e52-47a4-231a-1ed2-eb6b9e541213", - "external_id": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4", + "uuid": "9db5f4a1-1695-44c0-8bd4-de7ce4d0f1d4", }, ] } From 899c84236e21c69f266845b34fc346bbb8925de9 Mon Sep 17 00:00:00 2001 From: Wiktor Plaga Date: Fri, 26 Dec 2025 10:37:03 +0100 Subject: [PATCH 3/3] Fix flake8 violations --- chartmogul/api/customer.py | 1 + chartmogul/api/customers/subscription.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/chartmogul/api/customer.py b/chartmogul/api/customer.py index 7c774bc..9282781 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -15,6 +15,7 @@ from .task import Task from .customers.subscription import CustomerSubscription + class Address(DataObject): class _Schema(Schema): address_zip = fields.String(allow_none=True) diff --git a/chartmogul/api/customers/subscription.py b/chartmogul/api/customers/subscription.py index d1e0b38..8f34203 100644 --- a/chartmogul/api/customers/subscription.py +++ b/chartmogul/api/customers/subscription.py @@ -66,6 +66,7 @@ def _loadJSON(cls, jsonObj): "list_imported", "get", "/import/customers{/uuid}/subscriptions" ) + @classmethod def _deprecated_list_imported(cls, config, **kwargs): warnings.warn( @@ -75,6 +76,7 @@ def _deprecated_list_imported(cls, config, **kwargs): ) return _original_list_imported.__func__(cls, config, **kwargs) + CustomerSubscription.list_imported = _deprecated_list_imported CustomerSubscription.cancel = CustomerSubscription._method( "cancel", "patch", "/import/subscriptions{/uuid}"