From 233089193d1cb5fcc44f9f43d10ce6b901e50f7b Mon Sep 17 00:00:00 2001 From: siddick Date: Fri, 28 Mar 2014 19:40:17 +0530 Subject: [PATCH] Support Invoice APIs --- paypalrestsdk/__init__.py | 1 + paypalrestsdk/api.py | 11 ++- paypalrestsdk/invoices.py | 36 ++++++++++ paypalrestsdk/resource.py | 15 ++++ test/unit_tests/invoices_test.py | 118 +++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 paypalrestsdk/invoices.py create mode 100644 test/unit_tests/invoices_test.py diff --git a/paypalrestsdk/__init__.py b/paypalrestsdk/__init__.py index 41c25260..02a16b43 100644 --- a/paypalrestsdk/__init__.py +++ b/paypalrestsdk/__init__.py @@ -1,5 +1,6 @@ from paypalrestsdk.api import Api, set_config, configure from paypalrestsdk.payments import Payment, Sale, Refund, Authorization, Capture +from paypalrestsdk.invoices import Invoice from paypalrestsdk.vault import CreditCard from paypalrestsdk.openid_connect import Tokeninfo, Userinfo from paypalrestsdk.exceptions import ResourceNotFound, UnauthorizedAccess, MissingConfig diff --git a/paypalrestsdk/api.py b/paypalrestsdk/api.py index dc264027..486ecb48 100644 --- a/paypalrestsdk/api.py +++ b/paypalrestsdk/api.py @@ -147,10 +147,10 @@ def http_call(self, url, method, **kwargs): """ Makes a http call. Logs response information. """ - logging.info('Request[%s]: %s' % (method, url)) + logging.info('Request[%s]: %s' % (method, url)) start_time = datetime.datetime.now() - - response = requests.request(method, url, proxies=self.proxies, **kwargs) + + response = requests.request(method, url, proxies=self.proxies, **kwargs) duration = datetime.datetime.now() - start_time logging.info('Response[%d]: %s, Duration: %s.%ss.' % (response.status_code, response.reason, duration.seconds, duration.microseconds)) @@ -224,6 +224,11 @@ def post(self, action, params=None, headers=None, refresh_token=None): """ return self.request(util.join_url(self.endpoint, action), 'POST', body=params or {}, headers=headers or {}, refresh_token=refresh_token) + def put(self, action, params=None, headers=None, refresh_token=None): + """Make PUT request + """ + return self.request(util.join_url(self.endpoint, action), 'PUT', body=params or {}, headers=headers or {}, refresh_token=refresh_token) + def delete(self, action, headers=None): """Make DELETE request """ diff --git a/paypalrestsdk/invoices.py b/paypalrestsdk/invoices.py new file mode 100644 index 00000000..00ed62bb --- /dev/null +++ b/paypalrestsdk/invoices.py @@ -0,0 +1,36 @@ +import paypalrestsdk.util as util +from paypalrestsdk.resource import List, Find, Delete, Create, Update, Post, Resource +from paypalrestsdk.api import default as default_api + +class Invoice(List, Find, Create, Delete, Update, Post): + """Invoice class wrapping the REST v1/invoices/invoice endpoint + + Usage:: + + >>> invoice_histroy = Invoice.all({"count": 5}) + + >>> invoice = Invoice.new({}) + >>> invoice.create() # return True or False + """ + path = "v1/invoices/invoice" + + def send(self): + return self.post('send', {}, self) + + def remind(self, attributes): + return self.post('remind', attributes, self) + + def cancel(self, attributes): + return self.post('cancel', attributes, self) + + @classmethod + def search(cls, params=None, api=None): + api = api or default_api() + params = params or {} + + url = util.join_url(cls.path, 'search') + + return Resource(api.post(url, params), api=api) + +Invoice.convert_resources['invoices'] = Invoice +Invoice.convert_resources['invoice'] = Invoice diff --git a/paypalrestsdk/resource.py b/paypalrestsdk/resource.py index 619d748a..1dd4935e 100644 --- a/paypalrestsdk/resource.py +++ b/paypalrestsdk/resource.py @@ -160,6 +160,21 @@ def create(self, refresh_token=None, correlation_id=None): self.merge(new_attributes) return self.success() +class Update(Resource): + """ Update a resource + + Usage:: + + >>> invoice.update() + """ + + def update(self, attributes=None, refresh_token=None): + attributes = attributes or self.to_dict() + url = util.join_url(self.path, str(self['id'])) + new_attributes = self.api.put(url, attributes, self.http_headers(), refresh_token) + self.error = None + self.merge(new_attributes) + return self.success() class Delete(Resource): diff --git a/test/unit_tests/invoices_test.py b/test/unit_tests/invoices_test.py new file mode 100644 index 00000000..bc9c9e84 --- /dev/null +++ b/test/unit_tests/invoices_test.py @@ -0,0 +1,118 @@ +from test_helper import paypal, unittest +from mock import patch, ANY + +class TestInvoice(unittest.TestCase): + + def setUp(self): + self.invoice_attributes = { + 'merchant_info': { + 'email': 'ppaas_default@paypal.com' + }, + 'billing_info': [ + { 'email': 'example@example.com' } + ], + 'items': [ + { + 'name': 'Sutures', + 'quantity': 100, + 'unit_price': { + 'currency': 'USD', + 'value': 5 + } + } + ] + } + self.invoice = paypal.Invoice(self.invoice_attributes) + self.invoice.id = 'INV2-RUVR-ADWQ-H89Y-ABCD' + + @patch('test_helper.paypal.Api.post', autospec=True) + def test_create(self, mock): + invoice = paypal.Invoice(self.invoice_attributes) + response = invoice.create() + + mock.assert_called_once_with(invoice.api,'v1/invoices/invoice', self.invoice_attributes, {'PayPal-Request-Id' : invoice.request_id}, None) + self.assertEqual(response, True) + + @patch('test_helper.paypal.Api.get', autospec=True) + def test_find(self, mock): + invoice = paypal.Invoice.find(self.invoice.id) + + mock.assert_called_once_with(self.invoice.api, 'v1/invoices/invoice/'+self.invoice.id) + self.assertTrue(isinstance(invoice, paypal.Invoice)) + + @patch('test_helper.paypal.Api.get', autospec=True) + def test_all(self, mock): + mock.return_value = {'total_count': 1, 'invoices': [self.invoice_attributes]} + history = paypal.Invoice.all({'count': 1}) + + mock.assert_called_once_with(self.invoice.api, 'v1/invoices/invoice?count=1') + self.assertEqual(history.total_count, 1) + self.assertTrue(isinstance(history.invoices[0], paypal.Invoice)) + + + + @patch('test_helper.paypal.Api.delete', autospec=True) + def test_delete(self, mock): + response = self.invoice.delete() + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/'+self.invoice.id) + self.assertEqual(response, True) + + @patch('test_helper.paypal.Api.put', autospec=True) + def test_update(self, mock): + response = self.invoice.update(self.invoice_attributes) + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/'+self.invoice.id, self.invoice_attributes, {'PayPal-Request-Id' : self.invoice.request_id}, None) + self.assertEqual(response, True) + + @patch('test_helper.paypal.Api.post', autospec=True) + def test_send(self, mock): + response = self.invoice.send() + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/'+self.invoice.id+'/send', {}, {'PayPal-Request-Id' : ANY}) + self.assertEqual(response, True) + + + @patch('test_helper.paypal.Api.post', autospec=True) + def test_search(self, mock): + search_attributes = { + "start_invoice_date" : "2010-05-10 PST", + "end_invoice_date" : "2013-05-11 PST", + "page" : 1, + "page_size" : 20, + "total_count_required" : True + } + mock.return_value = {'total_count': 1, 'invoices': [self.invoice_attributes]} + + history = paypal.Invoice.search(search_attributes); + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/search', search_attributes) + self.assertEqual(history.total_count, 1) + self.assertTrue(isinstance(history.invoices[0], paypal.Invoice)) + + @patch('test_helper.paypal.Api.post', autospec=True) + def test_remind(self, mock): + remind_attributes = { + 'subject': 'Past due', + 'note': 'Please pay soon', + 'send_to_merchant': True + } + + response = self.invoice.remind(remind_attributes); + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/'+self.invoice.id+'/remind', remind_attributes, {'PayPal-Request-Id' : ANY}) + self.assertEqual(response, True) + + @patch('test_helper.paypal.Api.post', autospec=True) + def test_cancel(self, mock): + cancel_attributes = { + 'subject': 'Past due', + 'note': 'Canceling invoice', + 'send_to_merchant': True, + 'send_to_payer': True + } + + response = self.invoice.cancel(cancel_attributes); + + mock.assert_called_once_with(self.invoice.api,'v1/invoices/invoice/'+self.invoice.id+'/cancel', cancel_attributes, {'PayPal-Request-Id' : ANY}) + self.assertEqual(response, True)