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/customer.py b/chartmogul/api/customer.py index 2c5b4fe..9282781 100644 --- a/chartmogul/api/customer.py +++ b/chartmogul/api/customer.py @@ -13,6 +13,7 @@ from .customer_note import CustomerNote from .opportunity import Opportunity from .task import Task +from .customers.subscription import CustomerSubscription class Address(DataObject): @@ -80,6 +81,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/chartmogul/api/customers/subscription.py b/chartmogul/api/customers/subscription.py index a85336a..8f34203 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,22 @@ 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 ef9bdf1..ec7700e 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( @@ -518,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", }, ] } @@ -547,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", }, ] }