Skip to content

Commit 95e6f28

Browse files
Merge pull request #699 from recurly/line-item-refunds
Add percentage and amount_in_cents to line items
2 parents 7f547e5 + 952a1c4 commit 95e6f28

File tree

5 files changed

+197
-9
lines changed

5 files changed

+197
-9
lines changed

recurly/__init__.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,7 +1210,24 @@ def refund_amount(self, amount_in_cents, refund_options = {}):
12101210
if 'refund_method' not in refund_options:
12111211
refund_options = { 'refund_method': 'credit_first' }
12121212

1213-
amount_element = self._refund_open_amount_xml(amount_in_cents,
1213+
amount_element = self._refund_open_amount_xml('amount_in_cents',
1214+
amount_in_cents,
1215+
refund_options)
1216+
return self._create_refund_invoice(amount_element)
1217+
1218+
def refund_percentage(self, percentage, refund_options = {}):
1219+
# For backwards compatibility
1220+
# TODO the consequent branch of this conditional should eventually be removed
1221+
# and we should document that as a breaking change in the changelog.
1222+
# The same change should be applied to the refund() method
1223+
if (isinstance(refund_options, six.string_types)):
1224+
refund_options = { 'refund_method': refund_options }
1225+
else:
1226+
if 'refund_method' not in refund_options:
1227+
refund_options = { 'refund_method': 'credit_first' }
1228+
1229+
amount_element = self._refund_open_amount_xml('percentage',
1230+
percentage,
12141231
refund_options)
12151232
return self._create_refund_invoice(amount_element)
12161233

@@ -1229,10 +1246,10 @@ def refund(self, adjustments, refund_options = {}):
12291246
refund_options)
12301247
return self._create_refund_invoice(adjustments_element)
12311248

1232-
def _refund_open_amount_xml(self, amount_in_cents, refund_options):
1249+
def _refund_open_amount_xml(self, refund_type, refund_amount, refund_options):
12331250
elem = ElementTreeBuilder.Element(self.nodename)
1234-
elem.append(Resource.element_for_value('amount_in_cents',
1235-
amount_in_cents))
1251+
elem.append(Resource.element_for_value(refund_type,
1252+
refund_amount))
12361253

12371254
# Need to sort the keys for tests to pass in python 2 and 3
12381255
# Can remove `sorted` when we drop python 2 support
@@ -1247,15 +1264,22 @@ def _refund_line_items_xml(self, line_items, refund_options):
12471264

12481265
for item in line_items:
12491266
adj_elem = ElementTreeBuilder.Element('adjustment')
1250-
adj_elem.append(Resource.element_for_value('uuid',
1251-
item['adjustment'].uuid))
1252-
adj_elem.append(Resource.element_for_value('quantity',
1253-
item['quantity']))
1267+
adj_elem.append(Resource.element_for_value('uuid', item['adjustment'].uuid))
1268+
1269+
if 'quantity' in item:
1270+
adj_elem.append(Resource.element_for_value('quantity', item['quantity']))
12541271

12551272
if 'quantity_decimal' in item:
12561273
adj_elem.append(Resource.element_for_value('quantity_decimal', item['quantity_decimal']))
12571274

1258-
adj_elem.append(Resource.element_for_value('prorate', item['prorate']))
1275+
if 'percentage' in item:
1276+
adj_elem.append(Resource.element_for_value('percentage', item['percentage']))
1277+
1278+
if 'amount_in_cents' in item:
1279+
adj_elem.append(Resource.element_for_value('amount_in_cents', item['amount_in_cents']))
1280+
1281+
if 'prorate' in item:
1282+
adj_elem.append(Resource.element_for_value('prorate', item['prorate']))
12591283
line_items_elem.append(adj_elem)
12601284

12611285
elem.append(line_items_elem)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
2+
X-Api-Version: {api-version}
3+
Accept: application/xml
4+
Authorization: Basic YXBpa2V5Og==
5+
User-Agent: {user-agent}
6+
Content-Type: application/xml; charset=utf-8
7+
8+
<?xml version="1.0" encoding="UTF-8"?>
9+
<invoice>
10+
<line_items>
11+
<adjustment>
12+
<uuid>2bc3cf4cb513049c6aec1b419c97b508</uuid>
13+
<amount_in_cents type="integer">500</amount_in_cents>
14+
<prorate type="boolean">false</prorate>
15+
</adjustment>
16+
</line_items>
17+
<credit_customer_notes>Credit Customer Notes</credit_customer_notes>
18+
<description>Description</description>
19+
<refund_method>credit_first</refund_method>
20+
</invoice>
21+

22+
HTTP/1.1 201 Created
23+
Content-Type: application/xml; charset=utf-8
24+
Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
25+
26+
<?xml version="1.0" encoding="UTF-8"?>
27+
<invoice href="https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8">
28+
<uuid>4ba1531325014b4f969cd13676f514d8</uuid>
29+
<account_code>invoicemock</account_code>
30+
<invoice_number type="integer">1001</invoice_number>
31+
<invoice_number_prefix></invoice_number_prefix>
32+
<subtotal_in_cents type="integer">-500</subtotal_in_cents>
33+
<tax_in_cents type="integer">0</tax_in_cents>
34+
<total_in_cents type="integer">-500</total_in_cents>
35+
<start_date type="datetime">2009-11-03T23:27:46-08:00</start_date>
36+
<end_date type="datetime"></end_date>
37+
<description>test charge</description>
38+
<created_at type="datetime">2009-11-03T23:27:46-08:00</created_at>
39+
</invoice>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
2+
X-Api-Version: {api-version}
3+
Accept: application/xml
4+
Authorization: Basic YXBpa2V5Og==
5+
User-Agent: {user-agent}
6+
Content-Type: application/xml; charset=utf-8
7+
8+
<?xml version="1.0" encoding="UTF-8"?>
9+
<invoice>
10+
<line_items>
11+
<adjustment>
12+
<uuid>2bc3cf4cb513049c6aec1b419c97b508</uuid>
13+
<percentage type="integer">50</percentage>
14+
<prorate type="boolean">false</prorate>
15+
</adjustment>
16+
</line_items>
17+
<credit_customer_notes>Credit Customer Notes</credit_customer_notes>
18+
<description>Description</description>
19+
<refund_method>credit_first</refund_method>
20+
</invoice>
21+

22+
HTTP/1.1 201 Created
23+
Content-Type: application/xml; charset=utf-8
24+
Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
25+
26+
<?xml version="1.0" encoding="UTF-8"?>
27+
<invoice href="https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8">
28+
<uuid>4ba1531325014b4f969cd13676f514d8</uuid>
29+
<account_code>invoicemock</account_code>
30+
<invoice_number type="integer">1001</invoice_number>
31+
<invoice_number_prefix></invoice_number_prefix>
32+
<subtotal_in_cents type="integer">-500</subtotal_in_cents>
33+
<tax_in_cents type="integer">0</tax_in_cents>
34+
<total_in_cents type="integer">-500</total_in_cents>
35+
<start_date type="datetime">2009-11-03T23:27:46-08:00</start_date>
36+
<end_date type="datetime"></end_date>
37+
<description>test charge</description>
38+
<created_at type="datetime">2009-11-03T23:27:46-08:00</created_at>
39+
</invoice>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
POST https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8/refund HTTP/1.1
2+
X-Api-Version: {api-version}
3+
Accept: application/xml
4+
Authorization: Basic YXBpa2V5Og==
5+
User-Agent: {user-agent}
6+
Content-Type: application/xml; charset=utf-8
7+
8+
<?xml version="1.0" encoding="UTF-8"?>
9+
<invoice>
10+
<percentage type="integer">50</percentage>
11+
<credit_customer_notes>Credit Customer Notes</credit_customer_notes>
12+
<description>Description</description>
13+
<refund_method>credit_first</refund_method>
14+
</invoice>
15+

16+
HTTP/1.1 201 Created
17+
Content-Type: application/xml; charset=utf-8
18+
Location: https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8
19+
20+
<?xml version="1.0" encoding="UTF-8"?>
21+
<invoice href="https://api.recurly.com/v2/invoices/4ba1531325014b4f969cd13676f514d8">
22+
<uuid>4ba1531325014b4f969cd13676f514d8</uuid>
23+
<account_code>invoicemock</account_code>
24+
<invoice_number type="integer">1001</invoice_number>
25+
<invoice_number_prefix></invoice_number_prefix>
26+
<subtotal_in_cents type="integer">-50</subtotal_in_cents>
27+
<tax_in_cents type="integer">0</tax_in_cents>
28+
<total_in_cents type="integer">-50</total_in_cents>
29+
<start_date type="datetime">2009-11-03T23:27:46-08:00</start_date>
30+
<end_date type="datetime"></end_date>
31+
<description>test charge</description>
32+
<created_at type="datetime">2009-11-03T23:27:46-08:00</created_at>
33+
</invoice>

tests/test_resources.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,23 @@ def test_invoice_refund_amount(self):
16781678
refund_invoice = invoice.refund_amount(1000, options)
16791679
self.assertEqual(refund_invoice.subtotal_in_cents, -1000)
16801680

1681+
def test_invoice_refund_percentage(self):
1682+
account = Account(account_code='invoice%s' % self.test_id)
1683+
with self.mock_request('invoice/account-created.xml'):
1684+
account.save()
1685+
1686+
with self.mock_request('invoice/invoiced.xml'):
1687+
invoice = account.invoice().charge_invoice
1688+
1689+
with self.mock_request('invoice/refunded-percentage.xml'):
1690+
options = {
1691+
'refund_method': 'credit_first',
1692+
'credit_customer_notes': 'Credit Customer Notes',
1693+
'description': 'Description'
1694+
}
1695+
refund_invoice = invoice.refund_percentage(50, options)
1696+
self.assertEqual(refund_invoice.subtotal_in_cents, -50)
1697+
16811698
def test_invoice_refund(self):
16821699
account = Account(account_code='invoice%s' % self.test_id)
16831700
with self.mock_request('invoice/account-created.xml'):
@@ -1697,6 +1714,42 @@ def test_invoice_refund(self):
16971714
refund_invoice = invoice.refund(line_items, options)
16981715
self.assertEqual(refund_invoice.subtotal_in_cents, -1000)
16991716

1717+
def test_invoice_refund_line_item_percentage(self):
1718+
account = Account(account_code='invoice%s' % self.test_id)
1719+
with self.mock_request('invoice/account-created.xml'):
1720+
account.save()
1721+
1722+
with self.mock_request('invoice/invoiced-line-items.xml'):
1723+
invoice = account.invoice().charge_invoice
1724+
1725+
with self.mock_request('invoice/line-item-refunded-percentage.xml'):
1726+
line_items = [{ 'adjustment': invoice.line_items[0], 'percentage': 50, 'prorate': False }]
1727+
options = {
1728+
'refund_method': 'credit_first',
1729+
'credit_customer_notes': 'Credit Customer Notes',
1730+
'description': 'Description'
1731+
}
1732+
refund_invoice = invoice.refund(line_items, options)
1733+
self.assertEqual(refund_invoice.subtotal_in_cents, -500)
1734+
1735+
def test_invoice_refund_line_item_amount_in_cents(self):
1736+
account = Account(account_code='invoice%s' % self.test_id)
1737+
with self.mock_request('invoice/account-created.xml'):
1738+
account.save()
1739+
1740+
with self.mock_request('invoice/invoiced-line-items.xml'):
1741+
invoice = account.invoice().charge_invoice
1742+
1743+
with self.mock_request('invoice/line-item-refunded-amount-in-cents.xml'):
1744+
line_items = [{ 'adjustment': invoice.line_items[0], 'amount_in_cents': 500, 'prorate': False }]
1745+
options = {
1746+
'refund_method': 'credit_first',
1747+
'credit_customer_notes': 'Credit Customer Notes',
1748+
'description': 'Description'
1749+
}
1750+
refund_invoice = invoice.refund(line_items, options)
1751+
self.assertEqual(refund_invoice.subtotal_in_cents, -500)
1752+
17001753
def test_invoice_collect(self):
17011754
with self.mock_request('invoice/show-invoice.xml'):
17021755
invoice = Invoice.get("6019")

0 commit comments

Comments
 (0)