diff --git a/recurly/__init__.py b/recurly/__init__.py
index ad525ac7..1e2f48c5 100644
--- a/recurly/__init__.py
+++ b/recurly/__init__.py
@@ -1210,7 +1210,24 @@ def refund_amount(self, amount_in_cents, refund_options = {}):
if 'refund_method' not in refund_options:
refund_options = { 'refund_method': 'credit_first' }
- amount_element = self._refund_open_amount_xml(amount_in_cents,
+ amount_element = self._refund_open_amount_xml('amount_in_cents',
+ amount_in_cents,
+ refund_options)
+ return self._create_refund_invoice(amount_element)
+
+ def refund_percentage(self, percentage, refund_options = {}):
+ # For backwards compatibility
+ # TODO the consequent branch of this conditional should eventually be removed
+ # and we should document that as a breaking change in the changelog.
+ # The same change should be applied to the refund() method
+ if (isinstance(refund_options, six.string_types)):
+ refund_options = { 'refund_method': refund_options }
+ else:
+ if 'refund_method' not in refund_options:
+ refund_options = { 'refund_method': 'credit_first' }
+
+ amount_element = self._refund_open_amount_xml('percentage',
+ percentage,
refund_options)
return self._create_refund_invoice(amount_element)
@@ -1229,10 +1246,10 @@ def refund(self, adjustments, refund_options = {}):
refund_options)
return self._create_refund_invoice(adjustments_element)
- def _refund_open_amount_xml(self, amount_in_cents, refund_options):
+ def _refund_open_amount_xml(self, refund_type, refund_amount, refund_options):
elem = ElementTreeBuilder.Element(self.nodename)
- elem.append(Resource.element_for_value('amount_in_cents',
- amount_in_cents))
+ elem.append(Resource.element_for_value(refund_type,
+ refund_amount))
# Need to sort the keys for tests to pass in python 2 and 3
# Can remove `sorted` when we drop python 2 support
@@ -1247,15 +1264,22 @@ def _refund_line_items_xml(self, line_items, refund_options):
for item in line_items:
adj_elem = ElementTreeBuilder.Element('adjustment')
- adj_elem.append(Resource.element_for_value('uuid',
- item['adjustment'].uuid))
- adj_elem.append(Resource.element_for_value('quantity',
- item['quantity']))
+ adj_elem.append(Resource.element_for_value('uuid', item['adjustment'].uuid))
+
+ if 'quantity' in item:
+ adj_elem.append(Resource.element_for_value('quantity', item['quantity']))
if 'quantity_decimal' in item:
adj_elem.append(Resource.element_for_value('quantity_decimal', item['quantity_decimal']))
- adj_elem.append(Resource.element_for_value('prorate', item['prorate']))
+ if 'percentage' in item:
+ adj_elem.append(Resource.element_for_value('percentage', item['percentage']))
+
+ if 'amount_in_cents' in item:
+ adj_elem.append(Resource.element_for_value('amount_in_cents', item['amount_in_cents']))
+
+ if 'prorate' in item:
+ adj_elem.append(Resource.element_for_value('prorate', item['prorate']))
line_items_elem.append(adj_elem)
elem.append(line_items_elem)
diff --git a/tests/fixtures/invoice/line-item-refunded-amount-in-cents.xml b/tests/fixtures/invoice/line-item-refunded-amount-in-cents.xml
new file mode 100644
index 00000000..cac366c0
--- /dev/null
+++ b/tests/fixtures/invoice/line-item-refunded-amount-in-cents.xml
@@ -0,0 +1,39 @@
+POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
+X-Api-Version: {api-version}
+Accept: application/xml
+Authorization: Basic YXBpa2V5Og==
+User-Agent: {user-agent}
+Content-Type: application/xml; charset=utf-8
+
+
+
+
+
+ 2bc3cf4cb513049c6aec1b419c97b508
+ 500
+ false
+
+
+ Credit Customer Notes
+ Description
+ credit_first
+
+
+HTTP/1.1 201 Created
+Content-Type: application/xml; charset=utf-8
+Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
+
+
+
+ 4ba1531325014b4f969cd13676f514d8
+ invoicemock
+ 1001
+
+ -500
+ 0
+ -500
+ 2009-11-03T23:27:46-08:00
+
+ test charge
+ 2009-11-03T23:27:46-08:00
+
diff --git a/tests/fixtures/invoice/line-item-refunded-percentage.xml b/tests/fixtures/invoice/line-item-refunded-percentage.xml
new file mode 100644
index 00000000..9ed3ad66
--- /dev/null
+++ b/tests/fixtures/invoice/line-item-refunded-percentage.xml
@@ -0,0 +1,39 @@
+POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
+X-Api-Version: {api-version}
+Accept: application/xml
+Authorization: Basic YXBpa2V5Og==
+User-Agent: {user-agent}
+Content-Type: application/xml; charset=utf-8
+
+
+
+
+
+ 2bc3cf4cb513049c6aec1b419c97b508
+ 50
+ false
+
+
+ Credit Customer Notes
+ Description
+ credit_first
+
+
+HTTP/1.1 201 Created
+Content-Type: application/xml; charset=utf-8
+Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
+
+
+
+ 4ba1531325014b4f969cd13676f514d8
+ invoicemock
+ 1001
+
+ -500
+ 0
+ -500
+ 2009-11-03T23:27:46-08:00
+
+ test charge
+ 2009-11-03T23:27:46-08:00
+
diff --git a/tests/fixtures/invoice/refunded-percentage.xml b/tests/fixtures/invoice/refunded-percentage.xml
new file mode 100644
index 00000000..b97d0bac
--- /dev/null
+++ b/tests/fixtures/invoice/refunded-percentage.xml
@@ -0,0 +1,33 @@
+POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
+X-Api-Version: {api-version}
+Accept: application/xml
+Authorization: Basic YXBpa2V5Og==
+User-Agent: {user-agent}
+Content-Type: application/xml; charset=utf-8
+
+
+
+ 50
+ Credit Customer Notes
+ Description
+ credit_first
+
+
+HTTP/1.1 201 Created
+Content-Type: application/xml; charset=utf-8
+Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
+
+
+
+ 4ba1531325014b4f969cd13676f514d8
+ invoicemock
+ 1001
+
+ -50
+ 0
+ -50
+ 2009-11-03T23:27:46-08:00
+
+ test charge
+ 2009-11-03T23:27:46-08:00
+
diff --git a/tests/test_resources.py b/tests/test_resources.py
index cc33cb02..42620cba 100644
--- a/tests/test_resources.py
+++ b/tests/test_resources.py
@@ -1678,6 +1678,23 @@ def test_invoice_refund_amount(self):
refund_invoice = invoice.refund_amount(1000, options)
self.assertEqual(refund_invoice.subtotal_in_cents, -1000)
+ def test_invoice_refund_percentage(self):
+ account = Account(account_code='invoice%s' % self.test_id)
+ with self.mock_request('invoice/account-created.xml'):
+ account.save()
+
+ with self.mock_request('invoice/invoiced.xml'):
+ invoice = account.invoice().charge_invoice
+
+ with self.mock_request('invoice/refunded-percentage.xml'):
+ options = {
+ 'refund_method': 'credit_first',
+ 'credit_customer_notes': 'Credit Customer Notes',
+ 'description': 'Description'
+ }
+ refund_invoice = invoice.refund_percentage(50, options)
+ self.assertEqual(refund_invoice.subtotal_in_cents, -50)
+
def test_invoice_refund(self):
account = Account(account_code='invoice%s' % self.test_id)
with self.mock_request('invoice/account-created.xml'):
@@ -1697,6 +1714,42 @@ def test_invoice_refund(self):
refund_invoice = invoice.refund(line_items, options)
self.assertEqual(refund_invoice.subtotal_in_cents, -1000)
+ def test_invoice_refund_line_item_percentage(self):
+ account = Account(account_code='invoice%s' % self.test_id)
+ with self.mock_request('invoice/account-created.xml'):
+ account.save()
+
+ with self.mock_request('invoice/invoiced-line-items.xml'):
+ invoice = account.invoice().charge_invoice
+
+ with self.mock_request('invoice/line-item-refunded-percentage.xml'):
+ line_items = [{ 'adjustment': invoice.line_items[0], 'percentage': 50, 'prorate': False }]
+ options = {
+ 'refund_method': 'credit_first',
+ 'credit_customer_notes': 'Credit Customer Notes',
+ 'description': 'Description'
+ }
+ refund_invoice = invoice.refund(line_items, options)
+ self.assertEqual(refund_invoice.subtotal_in_cents, -500)
+
+ def test_invoice_refund_line_item_amount_in_cents(self):
+ account = Account(account_code='invoice%s' % self.test_id)
+ with self.mock_request('invoice/account-created.xml'):
+ account.save()
+
+ with self.mock_request('invoice/invoiced-line-items.xml'):
+ invoice = account.invoice().charge_invoice
+
+ with self.mock_request('invoice/line-item-refunded-amount-in-cents.xml'):
+ line_items = [{ 'adjustment': invoice.line_items[0], 'amount_in_cents': 500, 'prorate': False }]
+ options = {
+ 'refund_method': 'credit_first',
+ 'credit_customer_notes': 'Credit Customer Notes',
+ 'description': 'Description'
+ }
+ refund_invoice = invoice.refund(line_items, options)
+ self.assertEqual(refund_invoice.subtotal_in_cents, -500)
+
def test_invoice_collect(self):
with self.mock_request('invoice/show-invoice.xml'):
invoice = Invoice.get("6019")