Skip to content

Commit

Permalink
Merge pull request #257 from recurly/api_version_2_14
Browse files Browse the repository at this point in the history
Bump to 2.8.7 / API version 2.14
  • Loading branch information
bhelx authored Aug 21, 2018
2 parents e2ab480 + 13f88fc commit 81acf6b
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 4 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## Unreleased

## Version 2.8.7 – August 21st, 2018 ##

This version brings us up API version 2.14 but has no breaking changes.

- Support updating an invoice
- Support updating custom fields through Subscription#update_notes

## Version 2.8.6 – July 20th, 2018 ##

- Add gateway_token and gateway_code fields to BillingInfo class
Expand Down
28 changes: 24 additions & 4 deletions recurly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import re
from datetime import datetime
from six.moves.urllib.parse import urljoin
from six import iteritems
from xml.etree import ElementTree

import recurly
Expand All @@ -20,7 +21,7 @@
"""

__version__ = '2.8.6'
__version__ = '2.8.7'
__python_version__ = '.'.join(map(str, sys.version_info[:3]))

cached_rate_limits = {
Expand All @@ -44,7 +45,7 @@
API_KEY = None
"""The API key to use when authenticating API requests."""

API_VERSION = '2.13'
API_VERSION = '2.14'
"""The API version to use when making API requests."""

CA_CERTS_FILE = None
Expand Down Expand Up @@ -91,6 +92,10 @@ class Address(Resource):
nodename = 'address'

attributes = (
'first_name',
'last_name',
'name_on_account',
'company',
'address1',
'address2',
'city',
Expand Down Expand Up @@ -730,6 +735,7 @@ class Invoice(Resource):
'transactions',
'terms_and_conditions',
'customer_notes',
'vat_reverse_charge_notes', # Only shows if reverse charge invoice
'address',
'closed_at',
'collection_method',
Expand Down Expand Up @@ -881,6 +887,12 @@ def enter_offline_payment(self, transaction):
transaction.post(url)
return transaction

def save(self):
if hasattr(self, '_url'):
super(Invoice, self).save()
else:
raise BadRequestError("New invoices cannot be created using Invoice#save")

class InvoiceCollection(Resource):

"""A collection of invoices resulting from some action. Includes
Expand Down Expand Up @@ -1068,8 +1080,16 @@ def preview(self):
return self.post(url)

def update_notes(self, **kwargs):
"""Updates the notes on the subscription without generating a change"""
for key, val in kwargs.iteritems():
"""
Updates the notes on the subscription without generating a change
This endpoint also allows you to update custom fields:
`
sub.custom_fields[0].value = 'A new value'
sub.update_notes()
`
"""
for key, val in iteritems(kwargs):
setattr(self, key, val)
url = urljoin(self._url, '/notes')
self.put(url)
Expand Down
113 changes: 113 additions & 0 deletions tests/fixtures/invoice/update-invoice.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
PUT https://api.recurly.com/v2/invoices/6019 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

<?xml version="1.0" encoding="UTF-8"?>
<invoice>
<po_number>1234</po_number>
<terms_and_conditions>School staff is not responsible for items left at Hogwarts School of Witchcraft and Wizardry.</terms_and_conditions>
<customer_notes>It's levi-O-sa, not levio-SA!</customer_notes>
<vat_reverse_charge_notes>can't be changed when invoice was not a reverse charge</vat_reverse_charge_notes>
<address>
<first_name>Harry</first_name>
<last_name>Potter</last_name>
<name_on_account>Albus Dumbledore</name_on_account>
<company>Hogwarts</company>
<address1>4 Privet Drive</address1>
<address2>Little Whinging</address2>
<city>Surrey</city>
<state>England</state>
<zip>YO8 9FX</zip>
<country>Great Britain</country>
<phone>781-452-4077</phone>
</address>
<net_terms type="integer">1</net_terms>
</invoice>


HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<invoice href="https://api.recurly.com/v2/invoices/6019">
<account href="https://api.recurly.com/v2/accounts/aa463d59-a618-4b71-b1f4-0410f835fe74"/>
<address>
<first_name>Harry</first_name>
<last_name>Potter</last_name>
<name_on_account>Albus Dumbledore</name_on_account>
<company>Hogwarts</company>
<address1>4 Privet Drive</address1>
<address2>Little Whinging</address2>
<city>Surrey</city>
<state>England</state>
<zip>YO8 9FX</zip>
<country>Great Britain</country>
<phone>781-452-4077</phone>
</address>
<uuid>46036dca820357dae40c94420ab52632</uuid>
<state>pending</state>
<invoice_number_prefix></invoice_number_prefix>
<invoice_number type="integer">6019</invoice_number>
<vat_number nil="nil"></vat_number>
<vat_reverse_charge_notes>can't be changed when invoice was not a reverse charge</vat_reverse_charge_notes>
<tax_in_cents type="integer">0</tax_in_cents>
<total_in_cents type="integer">5000</total_in_cents>
<currency>USD</currency>
<created_at type="datetime">2018-07-13T17:13:57Z</created_at>
<updated_at type="datetime">2018-07-13T17:13:57Z</updated_at>
<attempt_next_collection_at type="datetime">2018-07-14T17:13:57Z</attempt_next_collection_at>
<closed_at nil="nil"></closed_at>
<customer_notes>It's levi-O-sa, not levio-SA!</customer_notes>
<recovery_reason nil="nil"></recovery_reason>
<subtotal_before_discount_in_cents type="integer">5000</subtotal_before_discount_in_cents>
<subtotal_in_cents type="integer">5000</subtotal_in_cents>
<discount_in_cents type="integer">0</discount_in_cents>
<due_on type="datetime">2018-07-14T17:13:57Z</due_on>
<balance_in_cents type="integer">5000</balance_in_cents>
<type>charge</type>
<origin>purchase</origin>
<credit_invoices href="https://api.recurly.com/v2/invoices/6019/credit_invoices"/>
<refundable_total_in_cents type="integer">5000</refundable_total_in_cents>
<credit_payments type="array">
</credit_payments>
<net_terms type="integer">1</net_terms>
<collection_method>manual</collection_method>
<po_number>1234</po_number>
<terms_and_conditions>School staff is not responsible for items left at Hogwarts School of Witchcraft and Wizardry.</terms_and_conditions>
<line_items type="array">
<adjustment href="https://api.recurly.com/v2/adjustments/46036dc9823500f96f43ef44769df449" type="charge">
<account href="https://api.recurly.com/v2/accounts/aa463d59-a618-4b71-b1f4-0410f835fe74"/>
<invoice href="https://api.recurly.com/v2/invoices/6019"/>
<credit_adjustments href="https://api.recurly.com/v2/adjustments/46036dc9823500f96f43ef44769df449/credit_adjustments"/>
<refundable_total_in_cents type="integer">5000</refundable_total_in_cents>
<uuid>46036dc9823500f96f43ef44769df449</uuid>
<state>invoiced</state>
<description>Charge for extra bandwidth</description>
<accounting_code>bandwidth</accounting_code>
<product_code nil="nil"></product_code>
<origin>debit</origin>
<unit_amount_in_cents type="integer">5000</unit_amount_in_cents>
<quantity type="integer">1</quantity>
<discount_in_cents type="integer">0</discount_in_cents>
<tax_in_cents type="integer">0</tax_in_cents>
<total_in_cents type="integer">5000</total_in_cents>
<currency>USD</currency>
<proration_rate nil="nil"></proration_rate>
<taxable type="boolean">false</taxable>
<tax_exempt type="boolean">false</tax_exempt>
<tax_code nil="nil"></tax_code>
<start_date type="datetime">2018-07-13T17:13:57Z</start_date>
<end_date nil="nil"></end_date>
<created_at type="datetime">2018-07-13T17:13:57Z</created_at>
<updated_at type="datetime">2018-07-13T17:13:57Z</updated_at>
<revenue_schedule_type></revenue_schedule_type>
</adjustment>
</line_items>
<transactions type="array">
</transactions>
<a name="mark_successful" href="https://api.recurly.com/v2/invoices/6019/mark_successful" method="put"/>
<a name="mark_failed" href="https://api.recurly.com/v2/invoices/6019/mark_failed" method="put"/>
</invoice>
50 changes: 50 additions & 0 deletions tests/fixtures/subscription/subscribe-custom-fields-notes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
PUT https://api.recurly.com/v2/subscriptions/123456789012345678901234567890ab/notes 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

<?xml version="1.0" encoding="UTF-8"?>
<subscription>
<custom_fields>
<custom_field>
<name>my_sub_field</name>
<value>A new sub value</value>
</custom_field>
</custom_fields>
</subscription>

HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Location: https://api.recurly.com/v2/plans/basicplan

<?xml version="1.0" encoding="UTF-8"?>
<subscription
href="https://api.recurly.com/v2/subscriptions/123456789012345678901234567890ab">
<uuid>123456789012345678901234567890ab</uuid>
<account href="https://api.recurly.com/v2/accounts/subscribe-mock-2"/>
<plan href="https://api.recurly.com/v2/plans/basicplan">
<plan_code>basicplan</plan_code>
<name>Basic Plan</name>
</plan>
<state>active</state>
<quantity type="integer">1</quantity>
<currency>EUR</currency>
<unit_amount_in_cents type="integer">1000</unit_amount_in_cents>
<activated_at type="datetime">2011-05-27T07:00:00Z</activated_at>
<canceled_at nil="nil"></canceled_at>
<expires_at nil="nil"></expires_at>
<current_period_started_at type="datetime">2011-06-27T07:00:00Z</current_period_started_at>
<current_period_ends_at type="datetime">2010-07-27T07:00:00Z</current_period_ends_at>
<trial_started_at nil="nil"></trial_started_at>
<trial_ends_at nil="nil"></trial_ends_at>
<subscription_add_ons type="array">
</subscription_add_ons>
<custom_fields type="array">
<custom_field>
<name>my_sub_field</name>
<value>A new sub value</value>
</custom_field>
</custom_fields>
</subscription>
57 changes: 57 additions & 0 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,54 @@ def test_invoice_offline_payment(self):

self.assertIsInstance(transaction, Transaction)

def test_invoice_create(self):
# Invoices should not be created with save method
invoice = Invoice()
self.assertRaises(BadRequestError, invoice.save)

def test_invoice_update(self):
with self.mock_request('invoice/show-invoice.xml'):
invoice = Invoice.get("6019")

self.assertIsInstance(invoice, Invoice)

with self.mock_request('invoice/update-invoice.xml'):
invoice.address = recurly.Address(
first_name = 'Harry',
last_name = 'Potter',
company = 'Hogwarts',
name_on_account = 'Albus Dumbledore',
address1 = '4 Privet Drive',
address2 = 'Little Whinging',
city = 'Surrey',
state = 'England',
zip = 'YO8 9FX',
country = 'Great Britain',
phone = '781-452-4077'
)
invoice.po_number = '1234'
invoice.terms_and_conditions = 'School staff is not responsible for items left at Hogwarts School of Witchcraft and Wizardry.'
invoice.customer_notes = "It's levi-O-sa, not levio-SA!"
invoice.vat_reverse_charge_notes = "can't be changed when invoice was not a reverse charge"
invoice.net_terms = 1
invoice.save()

self.assertEqual(invoice.address.first_name, 'Harry')
self.assertEqual(invoice.address.last_name, 'Potter')
self.assertEqual(invoice.address.name_on_account, 'Albus Dumbledore')
self.assertEqual(invoice.address.company, 'Hogwarts')
self.assertEqual(invoice.address.address1, '4 Privet Drive')
self.assertEqual(invoice.address.address2, 'Little Whinging')
self.assertEqual(invoice.address.city, 'Surrey')
self.assertEqual(invoice.address.state, 'England')
self.assertEqual(invoice.address.zip, 'YO8 9FX')
self.assertEqual(invoice.address.country, 'Great Britain')
self.assertEqual(invoice.address.phone, '781-452-4077')
self.assertEqual(invoice.po_number, '1234')
self.assertEqual(invoice.terms_and_conditions, 'School staff is not responsible for items left at Hogwarts School of Witchcraft and Wizardry.')
self.assertEqual(invoice.customer_notes, "It's levi-O-sa, not levio-SA!")
self.assertEqual(invoice.vat_reverse_charge_notes, "can't be changed when invoice was not a reverse charge")
self.assertEqual(invoice.net_terms, 1)

def test_build_invoice(self):
account = Account(account_code='invoice%s' % self.test_id)
Expand Down Expand Up @@ -1403,6 +1451,15 @@ def test_subscription_custom_fields(self):
self.assertEquals(sub.custom_fields[0].name, 'my_sub_field')
self.assertEquals(sub.custom_fields[0].value, 'definitely sub value')

cfs = sub.custom_fields
cfs[0].value = 'A new sub value'
sub.custom_fields = cfs

with self.mock_request('subscription/subscribe-custom-fields-notes.xml'):
sub.update_notes()

self.assertEquals(sub.custom_fields[0].value, 'A new sub value')

def test_account_notes(self):
account1 = Account(account_code='note%s' % self.test_id)
account2 = Account(account_code='note%s' % self.test_id)
Expand Down

0 comments on commit 81acf6b

Please sign in to comment.