Skip to content

Commit 8a860e7

Browse files
authored
Add disabled, disabled_at, edit_history_summary and errors fields to Invoice (#126)
* Add tests confirming that retrieve accepts query params * Add errors, edit_history_summary, disabled, disabled_at and disabled_by to Invoice * Add support for boolean query params
1 parent 4574334 commit 8a860e7

File tree

4 files changed

+195
-19
lines changed

4 files changed

+195
-19
lines changed

chartmogul/api/data_source.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,7 @@ class DataSource(Resource):
3636
'with_auto_churn_subscription_setting',
3737
'with_invoice_handling_setting'
3838
]
39-
_many = namedtuple(
40-
"DataSources",
41-
[_root_key] + _bool_query_params,
42-
defaults=[None, None, None]
43-
)
44-
45-
@classmethod
46-
def _preProcessParams(cls, params):
47-
params = super()._preProcessParams(params)
48-
49-
for query_param in cls._bool_query_params:
50-
if query_param in params and isinstance(params[query_param], bool):
51-
if params[query_param] is True:
52-
params[query_param] = 'true'
53-
else:
54-
del params[query_param]
55-
56-
return params
39+
_many = namedtuple("DataSources", [_root_key])
5740

5841
class _Schema(Schema):
5942
uuid = fields.String()

chartmogul/api/invoice.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class Invoice(Resource):
4040

4141
_path = "/import/customers{/uuid}/invoices"
4242
_root_key = "invoices"
43+
_bool_query_params = [
44+
'include_edit_histories',
45+
'with_disabled'
46+
]
4347
_many = namedtuple(
4448
"Invoices",
4549
[_root_key, "cursor", "has_more", "customer_uuid"],
@@ -58,6 +62,12 @@ class _Schema(Schema):
5862
date = fields.DateTime()
5963
due_date = fields.DateTime(allow_none=True)
6064

65+
disabled = fields.Boolean(allow_none=True)
66+
disabled_at = fields.DateTime(allow_none=True)
67+
disabled_by = fields.String(allow_none=True)
68+
edit_history_summary = fields.Dict(allow_none=True)
69+
errors = fields.Dict(allow_none=True)
70+
6171
line_items = fields.Nested(LineItem._Schema, many=True, unknown=EXCLUDE)
6272
transactions = fields.Nested(Transaction._Schema, many=True, unknown=EXCLUDE)
6373

chartmogul/resource.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ def _preProcessParams(cls, params):
121121
if key in params:
122122
params[replacement] = params[key]
123123
del params[key]
124+
125+
if hasattr(cls, '_bool_query_params'):
126+
for query_param in cls._bool_query_params:
127+
if query_param in params and isinstance(params[query_param], bool):
128+
if params[query_param] is True:
129+
params[query_param] = 'true'
130+
elif params[query_param] is False:
131+
params[query_param] = 'false'
132+
124133
return params
125134

126135
@classmethod

test/api/test_invoice.py

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pylama:ignore=W0212
22
import unittest
3-
from datetime import datetime
3+
from datetime import datetime, timezone
44

55
import requests_mock
66

@@ -175,6 +175,20 @@
175175
"date": "2015-11-01T00:00:00.000Z",
176176
"due_date": "2015-11-15T00:00:00.000Z",
177177
"currency": "USD",
178+
"disabled": False,
179+
"disabled_at": None,
180+
"disabled_by": None,
181+
"edit_history_summary": {
182+
"values_changed": {
183+
"amount_in_cents": {
184+
"original_value": 4500,
185+
"edited_value": 5000
186+
}
187+
},
188+
"latest_edit_author": "admin@example.com",
189+
"latest_edit_performed_at": "2024-01-10T12:00:00.000Z"
190+
},
191+
"errors": None,
178192
"line_items": [
179193
{
180194
"uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56",
@@ -231,6 +245,27 @@
231245
"date": "2015-11-01T00:00:00.000Z",
232246
"due_date": "2015-11-15T00:00:00.000Z",
233247
"currency": "USD",
248+
"disabled": True,
249+
"disabled_at": "2024-01-15T10:30:00.000Z",
250+
"disabled_by": "user@example.com",
251+
"edit_history_summary": {
252+
"values_changed": {
253+
"currency": {
254+
"original_value": "EUR",
255+
"edited_value": "USD"
256+
},
257+
"date": {
258+
"original_value": "2024-01-01T00:00:00.000Z",
259+
"edited_value": "2024-01-02T00:00:00.000Z"
260+
}
261+
},
262+
"latest_edit_author": "editor@example.com",
263+
"latest_edit_performed_at": "2024-01-20T15:45:00.000Z"
264+
},
265+
"errors": {
266+
"currency": ["Currency is invalid", "Currency must be supported"],
267+
"date": ["Date is in the future"]
268+
},
234269
"line_items": [
235270
{
236271
"uuid": "li_d72e6843-5793-41d0-bfdf-0269514c9c56",
@@ -447,3 +482,142 @@ def test_retrieve_invoice(self, mock_requests):
447482
self.assertTrue(isinstance(result, Invoice))
448483

449484
self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")
485+
486+
@requests_mock.mock()
487+
def test_retrieve_invoice_with_validation_type(self, mock_requests):
488+
mock_requests.register_uri(
489+
"GET",
490+
("https://api.chartmogul.com/v1/invoices/inv_22910fc6-c931-48e7-ac12-90d2cb5f0059"
491+
"?validation_type=all"),
492+
request_headers={"Authorization": "Basic dG9rZW46"},
493+
headers={"Content-Type": "application/json"},
494+
status_code=200,
495+
json=retrieveInvoiceExample,
496+
)
497+
498+
config = Config("token")
499+
result = Invoice.retrieve(
500+
config,
501+
uuid="inv_22910fc6-c931-48e7-ac12-90d2cb5f0059",
502+
validation_type="all"
503+
).get()
504+
505+
self.assertEqual(mock_requests.call_count, 1, "expected call")
506+
self.assertEqual(
507+
mock_requests.last_request.qs,
508+
{"validation_type": ["all"]},
509+
)
510+
self.assertTrue(isinstance(result, Invoice))
511+
self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")
512+
513+
@requests_mock.mock()
514+
def test_retrieve_invoice_with_all_params(self, mock_requests):
515+
mock_requests.register_uri(
516+
"GET",
517+
("https://api.chartmogul.com/v1/invoices/inv_22910fc6-c931-48e7-ac12-90d2cb5f0059"
518+
"?validation_type=invalid&include_edit_histories=true&with_disabled=false"),
519+
request_headers={"Authorization": "Basic dG9rZW46"},
520+
headers={"Content-Type": "application/json"},
521+
status_code=200,
522+
json=retrieveInvoiceExample,
523+
)
524+
525+
config = Config("token")
526+
result = Invoice.retrieve(
527+
config,
528+
uuid="inv_22910fc6-c931-48e7-ac12-90d2cb5f0059",
529+
validation_type="invalid",
530+
include_edit_histories=True,
531+
with_disabled=False
532+
).get()
533+
534+
self.assertEqual(mock_requests.call_count, 1, "expected call")
535+
qs = mock_requests.last_request.qs
536+
self.assertEqual(qs["validation_type"], ["invalid"])
537+
self.assertEqual(qs["include_edit_histories"], ["true"])
538+
self.assertEqual(qs["with_disabled"], ["false"])
539+
self.assertTrue(isinstance(result, Invoice))
540+
self.assertEqual(result.uuid, "inv_22910fc6-c931-48e7-ac12-90d2cb5f0059")
541+
self.assertTrue(result.disabled)
542+
self.assertEqual(result.disabled_at, datetime(2024, 1, 15, 10, 30, tzinfo=timezone.utc))
543+
self.assertEqual(result.disabled_by, "user@example.com")
544+
self.assertIsNotNone(result.edit_history_summary)
545+
self.assertIn("values_changed", result.edit_history_summary)
546+
self.assertIn("currency", result.edit_history_summary["values_changed"])
547+
self.assertEqual(
548+
result.edit_history_summary["values_changed"]["currency"]["original_value"],
549+
"EUR"
550+
)
551+
self.assertEqual(
552+
result.edit_history_summary["values_changed"]["currency"]["edited_value"],
553+
"USD"
554+
)
555+
self.assertEqual(
556+
result.edit_history_summary["latest_edit_author"],
557+
"editor@example.com"
558+
)
559+
self.assertEqual(
560+
result.edit_history_summary["latest_edit_performed_at"],
561+
"2024-01-20T15:45:00.000Z"
562+
)
563+
self.assertIsNotNone(result.errors)
564+
self.assertIn("currency", result.errors)
565+
self.assertIsInstance(result.errors["currency"], list)
566+
self.assertEqual(len(result.errors["currency"]), 2)
567+
self.assertEqual(result.errors["currency"][0], "Currency is invalid")
568+
self.assertEqual(result.errors["currency"][1], "Currency must be supported")
569+
self.assertIn("date", result.errors)
570+
self.assertIsInstance(result.errors["date"], list)
571+
self.assertEqual(len(result.errors["date"]), 1)
572+
self.assertEqual(result.errors["date"][0], "Date is in the future")
573+
574+
@requests_mock.mock()
575+
def test_all_invoices_with_validation_type(self, mock_requests):
576+
mock_requests.register_uri(
577+
"GET",
578+
"https://api.chartmogul.com/v1/invoices?validation_type=all",
579+
request_headers={"Authorization": "Basic dG9rZW46"},
580+
headers={"Content-Type": "application/json"},
581+
status_code=200,
582+
json=invoiceListExample,
583+
)
584+
585+
config = Config("token")
586+
result = Invoice.all(config, validation_type="all").get()
587+
588+
self.assertEqual(mock_requests.call_count, 1, "expected call")
589+
self.assertEqual(
590+
mock_requests.last_request.qs,
591+
{"validation_type": ["all"]},
592+
)
593+
594+
self.assertTrue(isinstance(result, Invoice._many))
595+
self.assertEqual(len(result.invoices), 1)
596+
597+
@requests_mock.mock()
598+
def test_all_invoices_with_all_params(self, mock_requests):
599+
mock_requests.register_uri(
600+
"GET",
601+
("https://api.chartmogul.com/v1/invoices"
602+
"?validation_type=valid&include_edit_histories=true&with_disabled=true"),
603+
request_headers={"Authorization": "Basic dG9rZW46"},
604+
headers={"Content-Type": "application/json"},
605+
status_code=200,
606+
json=invoiceListExample,
607+
)
608+
609+
config = Config("token")
610+
result = Invoice.all(
611+
config,
612+
validation_type="valid",
613+
include_edit_histories=True,
614+
with_disabled=True
615+
).get()
616+
617+
self.assertEqual(mock_requests.call_count, 1, "expected call")
618+
qs = mock_requests.last_request.qs
619+
self.assertEqual(qs["validation_type"], ["valid"])
620+
self.assertEqual(qs["include_edit_histories"], ["true"])
621+
self.assertEqual(qs["with_disabled"], ["true"])
622+
self.assertTrue(isinstance(result, Invoice._many))
623+
self.assertEqual(len(result.invoices), 1)

0 commit comments

Comments
 (0)