xero_gateway-float 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/Gemfile +12 -0
  2. data/LICENSE +14 -0
  3. data/README.textile +357 -0
  4. data/Rakefile +14 -0
  5. data/examples/oauth.rb +25 -0
  6. data/examples/partner_app.rb +36 -0
  7. data/init.rb +1 -0
  8. data/lib/oauth/oauth_consumer.rb +14 -0
  9. data/lib/xero_gateway.rb +39 -0
  10. data/lib/xero_gateway/account.rb +95 -0
  11. data/lib/xero_gateway/accounts_list.rb +87 -0
  12. data/lib/xero_gateway/address.rb +96 -0
  13. data/lib/xero_gateway/bank_transaction.rb +178 -0
  14. data/lib/xero_gateway/ca-certificates.crt +2560 -0
  15. data/lib/xero_gateway/contact.rb +206 -0
  16. data/lib/xero_gateway/credit_note.rb +222 -0
  17. data/lib/xero_gateway/currency.rb +56 -0
  18. data/lib/xero_gateway/dates.rb +30 -0
  19. data/lib/xero_gateway/error.rb +18 -0
  20. data/lib/xero_gateway/exceptions.rb +46 -0
  21. data/lib/xero_gateway/gateway.rb +622 -0
  22. data/lib/xero_gateway/http.rb +138 -0
  23. data/lib/xero_gateway/http_encoding_helper.rb +49 -0
  24. data/lib/xero_gateway/invoice.rb +236 -0
  25. data/lib/xero_gateway/line_item.rb +125 -0
  26. data/lib/xero_gateway/line_item_calculations.rb +55 -0
  27. data/lib/xero_gateway/money.rb +16 -0
  28. data/lib/xero_gateway/oauth.rb +87 -0
  29. data/lib/xero_gateway/organisation.rb +75 -0
  30. data/lib/xero_gateway/partner_app.rb +30 -0
  31. data/lib/xero_gateway/payment.rb +40 -0
  32. data/lib/xero_gateway/phone.rb +77 -0
  33. data/lib/xero_gateway/private_app.rb +17 -0
  34. data/lib/xero_gateway/response.rb +41 -0
  35. data/lib/xero_gateway/tax_rate.rb +63 -0
  36. data/lib/xero_gateway/tracking_category.rb +87 -0
  37. data/test/integration/accounts_list_test.rb +109 -0
  38. data/test/integration/create_bank_transaction_test.rb +38 -0
  39. data/test/integration/create_contact_test.rb +66 -0
  40. data/test/integration/create_credit_note_test.rb +49 -0
  41. data/test/integration/create_invoice_test.rb +49 -0
  42. data/test/integration/get_accounts_test.rb +23 -0
  43. data/test/integration/get_bank_transaction_test.rb +51 -0
  44. data/test/integration/get_bank_transactions_test.rb +88 -0
  45. data/test/integration/get_contact_test.rb +28 -0
  46. data/test/integration/get_contacts_test.rb +40 -0
  47. data/test/integration/get_credit_note_test.rb +48 -0
  48. data/test/integration/get_credit_notes_test.rb +90 -0
  49. data/test/integration/get_currencies_test.rb +25 -0
  50. data/test/integration/get_invoice_test.rb +48 -0
  51. data/test/integration/get_invoices_test.rb +92 -0
  52. data/test/integration/get_organisation_test.rb +24 -0
  53. data/test/integration/get_tax_rates_test.rb +25 -0
  54. data/test/integration/get_tracking_categories_test.rb +27 -0
  55. data/test/integration/update_bank_transaction_test.rb +31 -0
  56. data/test/integration/update_contact_test.rb +31 -0
  57. data/test/integration/update_invoice_test.rb +31 -0
  58. data/test/test_helper.rb +179 -0
  59. data/test/unit/account_test.rb +47 -0
  60. data/test/unit/bank_transaction_test.rb +126 -0
  61. data/test/unit/contact_test.rb +97 -0
  62. data/test/unit/credit_note_test.rb +284 -0
  63. data/test/unit/currency_test.rb +31 -0
  64. data/test/unit/gateway_test.rb +119 -0
  65. data/test/unit/invoice_test.rb +326 -0
  66. data/test/unit/oauth_test.rb +116 -0
  67. data/test/unit/organisation_test.rb +38 -0
  68. data/test/unit/tax_rate_test.rb +38 -0
  69. data/test/unit/tracking_category_test.rb +52 -0
  70. data/xero_gateway.gemspec +15 -0
  71. metadata +164 -0
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper.rb')
2
+
3
+ class CurrencyTest < Test::Unit::TestCase
4
+
5
+ # Tests that a currency can be converted into XML that Xero can understand, and then converted back to a currency
6
+ def test_build_and_parse_xml
7
+ currency = create_test_currency
8
+
9
+ # Generate the XML message
10
+ currency_as_xml = currency.to_xml
11
+
12
+ # Parse the XML message and retrieve the account element
13
+ currency_element = REXML::XPath.first(REXML::Document.new(currency_as_xml), "/Currency")
14
+
15
+ # Build a new account from the XML
16
+ result_currency = XeroGateway::Currency.from_xml(currency_element)
17
+
18
+ # Check the account details
19
+ assert_equal currency, result_currency
20
+ end
21
+
22
+
23
+ private
24
+
25
+ def create_test_currency
26
+ XeroGateway::Currency.new.tap do |currency|
27
+ currency.code = "NZD"
28
+ currency.description = "New Zealand Dollar"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,119 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper.rb')
2
+
3
+ class GatewayTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def setup
7
+ @gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+ end
9
+
10
+ context "with error handling" do
11
+
12
+ should "handle token expired" do
13
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("token_expired"), :code => "401"))
14
+
15
+ assert_raises XeroGateway::OAuth::TokenExpired do
16
+ @gateway.get_accounts
17
+ end
18
+ end
19
+
20
+ should "handle invalid request tokens" do
21
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("invalid_request_token"), :code => "401"))
22
+
23
+ assert_raises XeroGateway::OAuth::TokenInvalid do
24
+ @gateway.get_accounts
25
+ end
26
+ end
27
+
28
+ should "handle invalid consumer key" do
29
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("invalid_consumer_key"), :code => "401"))
30
+
31
+ assert_raises XeroGateway::OAuth::TokenInvalid do
32
+ @gateway.get_accounts
33
+ end
34
+ end
35
+
36
+ should "handle rate limit exceeded" do
37
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("rate_limit_exceeded"), :code => "401"))
38
+
39
+ assert_raises XeroGateway::OAuth::RateLimitExceeded do
40
+ @gateway.get_accounts
41
+ end
42
+ end
43
+
44
+ should "handle unknown errors" do
45
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("bogus_oauth_error"), :code => "401"))
46
+
47
+ assert_raises XeroGateway::OAuth::UnknownError do
48
+ @gateway.get_accounts
49
+ end
50
+ end
51
+
52
+ should "handle ApiExceptions" do
53
+ XeroGateway::OAuth.any_instance.stubs(:put).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "400"))
54
+
55
+ assert_raises XeroGateway::ApiException do
56
+ @gateway.create_invoice(XeroGateway::Invoice.new)
57
+ end
58
+ end
59
+
60
+ should "handle invoices not found" do
61
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
62
+
63
+ assert_raises XeroGateway::InvoiceNotFoundError do
64
+ @gateway.get_invoice('unknown-invoice-id')
65
+ end
66
+ end
67
+
68
+ should "handle bank transactions not found" do
69
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
70
+
71
+ assert_raises XeroGateway::BankTransactionNotFoundError do
72
+ @gateway.get_bank_transaction('unknown-bank-transaction-id')
73
+ end
74
+ end
75
+
76
+ should "handle credit notes not found" do
77
+ XeroGateway::OAuth.any_instance.stubs(:get).returns(stub(:plain_body => get_file_as_string("api_exception.xml"), :code => "404"))
78
+
79
+ assert_raises XeroGateway::CreditNoteNotFoundError do
80
+ @gateway.get_credit_note('unknown-credit-note-id')
81
+ end
82
+ end
83
+
84
+ should "handle random root elements" do
85
+ XeroGateway::OAuth.any_instance.stubs(:put).returns(stub(:plain_body => "<RandomRootElement></RandomRootElement>", :code => "200"))
86
+
87
+ assert_raises XeroGateway::UnparseableResponse do
88
+ @gateway.create_invoice(XeroGateway::Invoice.new)
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ def test_unknown_error_handling
95
+ if STUB_XERO_CALLS
96
+ @gateway.xero_url = "DUMMY_URL"
97
+ @gateway.stubs(:http_get).with {|client, url, params| url =~ /Invoices\/AN_INVALID_ID$/ }.returns(get_file_as_string("unknown_error.xml"))
98
+ end
99
+
100
+ result = @gateway.get_invoice("AN_INVALID_ID")
101
+ assert !result.success?
102
+ assert_equal 1, result.errors.size
103
+ assert !result.errors.first.type.nil?
104
+ assert !result.errors.first.description.nil?
105
+ end
106
+
107
+ def test_object_not_found_error_handling
108
+ if STUB_XERO_CALLS
109
+ @gateway.xero_url = "DUMMY_URL"
110
+ @gateway.stubs(:http_get).with {|client, url, params| url =~ /Invoices\/UNKNOWN_INVOICE_NO$/ }.returns(get_file_as_string("invoice_not_found_error.xml"))
111
+ end
112
+
113
+ result = @gateway.get_invoice("UNKNOWN_INVOICE_NO")
114
+ assert !result.success?
115
+ assert_equal 1, result.errors.size
116
+ assert_equal "Xero.API.Library.Exceptions.ObjectDoesNotExistException", result.errors.first.type
117
+ assert !result.errors.first.description.nil?
118
+ end
119
+ end
@@ -0,0 +1,326 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper.rb')
2
+
3
+ class InvoiceTest < Test::Unit::TestCase
4
+
5
+ context "building and parsing XML" do
6
+ should "work vice versa" do
7
+ invoice = create_test_invoice
8
+
9
+ # Generate the XML message
10
+ invoice_as_xml = invoice.to_xml
11
+
12
+ # Parse the XML message and retrieve the invoice element
13
+ invoice_element = REXML::XPath.first(REXML::Document.new(invoice_as_xml), "/Invoice")
14
+
15
+ # Build a new invoice from the XML
16
+ result_invoice = XeroGateway::Invoice.from_xml(invoice_element)
17
+
18
+ assert_equal(invoice, result_invoice)
19
+ end
20
+
21
+ should "work for optional params" do
22
+ invoice = create_test_invoice(:url => 'http://example.com?with=params&and=more')
23
+ invoice_element = REXML::XPath.first(REXML::Document.new(invoice.to_xml), "/Invoice")
24
+ assert_match /<Url>http:\/\/example.com\?with=params&amp;and=more<\/Url>/, invoice_element.to_s
25
+
26
+ parsed_invoice = XeroGateway::Invoice.from_xml(invoice_element)
27
+ assert_equal 'http://example.com?with=params&and=more', parsed_invoice.url
28
+ end
29
+ end
30
+
31
+ # Tests the sub_total calculation and that setting it manually doesn't modify the data.
32
+ def test_invoice_sub_total_calculation
33
+ invoice = create_test_invoice
34
+ line_item = invoice.line_items.first
35
+
36
+ # Make sure that everything adds up to begin with.
37
+ expected_sub_total = invoice.line_items.inject(BigDecimal.new('0')) { | sum, line_item | line_item.line_amount }
38
+ assert_equal(expected_sub_total, invoice.sub_total)
39
+
40
+ # Change the sub_total and check that it doesn't modify anything.
41
+ invoice.sub_total = expected_sub_total * 10
42
+ assert_equal(expected_sub_total, invoice.sub_total)
43
+
44
+ # Change the amount of the first line item and make sure that
45
+ # everything still continues to add up.
46
+ line_item.unit_amount = line_item.unit_amount + 10
47
+ assert_not_equal(expected_sub_total, invoice.sub_total)
48
+ expected_sub_total = invoice.line_items.inject(BigDecimal.new('0')) { | sum, line_item | line_item.line_amount }
49
+ assert_equal(expected_sub_total, invoice.sub_total)
50
+ end
51
+
52
+ # Tests the total_tax calculation and that setting it manually doesn't modify the data.
53
+ def test_invoice_sub_total_calculation
54
+ invoice = create_test_invoice
55
+ line_item = invoice.line_items.first
56
+
57
+ # Make sure that everything adds up to begin with.
58
+ expected_total_tax = invoice.line_items.inject(BigDecimal.new('0')) { | sum, line_item | line_item.tax_amount }
59
+ assert_equal(expected_total_tax, invoice.total_tax)
60
+
61
+ # Change the total_tax and check that it doesn't modify anything.
62
+ invoice.total_tax = expected_total_tax * 10
63
+ assert_equal(expected_total_tax, invoice.total_tax)
64
+
65
+ # Change the tax_amount of the first line item and make sure that
66
+ # everything still continues to add up.
67
+ line_item.tax_amount = line_item.tax_amount + 10
68
+ assert_not_equal(expected_total_tax, invoice.total_tax)
69
+ expected_total_tax = invoice.line_items.inject(BigDecimal.new('0')) { | sum, line_item | line_item.tax_amount }
70
+ assert_equal(expected_total_tax, invoice.total_tax)
71
+ end
72
+
73
+ # Tests the total calculation and that setting it manually doesn't modify the data.
74
+ def test_invoice_sub_total_calculation
75
+ invoice = create_test_invoice
76
+ line_item = invoice.line_items.first
77
+
78
+ # Make sure that everything adds up to begin with.
79
+ expected_total = invoice.sub_total + invoice.total_tax
80
+ assert_equal(expected_total, invoice.total)
81
+
82
+ # Change the total and check that it doesn't modify anything.
83
+ invoice.total = expected_total * 10
84
+ assert_equal(expected_total, invoice.total)
85
+
86
+ # Change the quantity of the first line item and make sure that
87
+ # everything still continues to add up.
88
+ line_item.quantity = line_item.quantity + 5
89
+ assert_not_equal(expected_total, invoice.total)
90
+ expected_total = invoice.sub_total + invoice.total_tax
91
+ assert_equal(expected_total, invoice.total)
92
+ end
93
+
94
+ # Tests that the LineItem#line_amount calculation is working correctly.
95
+ def test_line_amount_calculation
96
+ invoice = create_test_invoice
97
+ line_item = invoice.line_items.first
98
+
99
+ # Make sure that everything adds up to begin with.
100
+ expected_amount = line_item.quantity * line_item.unit_amount
101
+ assert_equal(expected_amount, line_item.line_amount)
102
+
103
+ # Change the line_amount and check that it doesn't modify anything.
104
+ line_item.line_amount = expected_amount * 10
105
+ assert_equal(expected_amount, line_item.line_amount)
106
+
107
+ # Change the quantity and check that the line_amount has been updated.
108
+ quantity = line_item.quantity + 2
109
+ line_item.quantity = quantity
110
+ assert_not_equal(expected_amount, line_item.line_amount)
111
+ assert_equal(quantity * line_item.unit_amount, line_item.line_amount)
112
+ end
113
+
114
+ # Ensure that the totalling methods don't raise exceptions, even when
115
+ # invoice.line_items is empty.
116
+ def test_totalling_methods_when_line_items_empty
117
+ invoice = create_test_invoice
118
+ invoice.line_items = []
119
+
120
+ assert_nothing_raised(Exception) {
121
+ assert_equal(BigDecimal.new('0'), invoice.sub_total)
122
+ assert_equal(BigDecimal.new('0'), invoice.total_tax)
123
+ assert_equal(BigDecimal.new('0'), invoice.total)
124
+ }
125
+ end
126
+
127
+ def test_invoice_type_helper_methods
128
+ # Test accounts receivable invoices.
129
+ invoice = create_test_invoice({:invoice_type => 'ACCREC'})
130
+ assert_equal(true, invoice.accounts_receivable?, "Accounts RECEIVABLE invoice doesn't think it is.")
131
+ assert_equal(false, invoice.accounts_payable?, "Accounts RECEIVABLE invoice thinks it's payable.")
132
+
133
+ # Test accounts payable invoices.
134
+ invoice = create_test_invoice({:invoice_type => 'ACCPAY'})
135
+ assert_equal(false, invoice.accounts_receivable?, "Accounts PAYABLE invoice doesn't think it is.")
136
+ assert_equal(true, invoice.accounts_payable?, "Accounts PAYABLE invoice thinks it's receivable.")
137
+ end
138
+
139
+
140
+ # Make sure that the create_test_invoice method is working correctly
141
+ # with all the defaults and overrides.
142
+ def test_create_test_invoice_defaults_working
143
+ invoice = create_test_invoice
144
+
145
+ # Test invoice defaults.
146
+ assert_equal('ACCREC', invoice.invoice_type)
147
+ assert_kind_of(Date, invoice.date)
148
+ assert_kind_of(Date, invoice.due_date)
149
+ assert_equal('12345', invoice.invoice_number)
150
+ assert_equal('MY REFERENCE FOR THIS INVOICE', invoice.reference)
151
+ assert_equal("Exclusive", invoice.line_amount_types)
152
+
153
+ # Test the contact defaults.
154
+ assert_equal('00000000-0000-0000-0000-000000000000', invoice.contact.contact_id)
155
+ assert_equal('CONTACT NAME', invoice.contact.name)
156
+
157
+ # Test address defaults.
158
+ assert_equal('DEFAULT', invoice.contact.address.address_type)
159
+ assert_equal('LINE 1 OF THE ADDRESS', invoice.contact.address.line_1)
160
+
161
+ # Test phone defaults.
162
+ assert_equal('DEFAULT', invoice.contact.phone.phone_type)
163
+ assert_equal('12345678', invoice.contact.phone.number)
164
+
165
+ # Test the line_item defaults.
166
+ assert_equal('A LINE ITEM', invoice.line_items.first.description)
167
+ assert_equal('200', invoice.line_items.first.account_code)
168
+ assert_equal(BigDecimal.new('100'), invoice.line_items.first.unit_amount)
169
+ assert_equal(BigDecimal.new('12.5'), invoice.line_items.first.tax_amount)
170
+
171
+ # Test optional params
172
+ assert_nil invoice.url
173
+
174
+ # Test overriding an invoice parameter (assume works for all).
175
+ invoice = create_test_invoice({:invoice_type => 'ACCPAY'})
176
+ assert_equal('ACCPAY', invoice.invoice_type)
177
+
178
+ # Test overriding a contact/address/phone parameter (assume works for all).
179
+ invoice = create_test_invoice({}, {:name => 'OVERRIDDEN NAME', :address => {:line_1 => 'OVERRIDDEN LINE 1'}, :phone => {:number => '999'}})
180
+ assert_equal('OVERRIDDEN NAME', invoice.contact.name)
181
+ assert_equal('OVERRIDDEN LINE 1', invoice.contact.address.line_1)
182
+ assert_equal('999', invoice.contact.phone.number)
183
+
184
+ # Test overriding line_items with hash.
185
+ invoice = create_test_invoice({}, {}, {:description => 'OVERRIDDEN LINE ITEM'})
186
+ assert_equal(1, invoice.line_items.size)
187
+ assert_equal('OVERRIDDEN LINE ITEM', invoice.line_items.first.description)
188
+ assert_equal(BigDecimal.new('100'), invoice.line_items.first.unit_amount)
189
+
190
+ # Test overriding line_items with array of 2 line_items.
191
+ invoice = create_test_invoice({}, {}, [
192
+ {:description => 'OVERRIDDEN ITEM 1'},
193
+ {:description => 'OVERRIDDEN ITEM 2', :account_code => '200', :unit_amount => BigDecimal.new('200'), :tax_amount => '25.0'}
194
+ ])
195
+ assert_equal(2, invoice.line_items.size)
196
+ assert_equal('OVERRIDDEN ITEM 1', invoice.line_items[0].description)
197
+ assert_equal(BigDecimal.new('100'), invoice.line_items[0].unit_amount)
198
+ assert_equal('OVERRIDDEN ITEM 2', invoice.line_items[1].description)
199
+ assert_equal(BigDecimal.new('200'), invoice.line_items[1].unit_amount)
200
+ end
201
+
202
+ def test_auto_creation_of_associated_contact
203
+ invoice = create_test_invoice({}, nil) # no contact
204
+ assert_nil(invoice.instance_variable_get("@contact"))
205
+
206
+ new_contact = invoice.contact
207
+ assert_kind_of(XeroGateway::Contact, new_contact)
208
+ end
209
+
210
+ def test_add_line_item
211
+ invoice = create_test_invoice({}, {}, nil) # no line_items
212
+ assert_equal(0, invoice.line_items.size)
213
+
214
+ line_item_params = {:description => "Test Item 1", :unit_amount => 100}
215
+
216
+ # Test adding line item by hash
217
+ line_item = invoice.add_line_item(line_item_params)
218
+ assert_kind_of(XeroGateway::LineItem, line_item)
219
+ assert_equal(line_item_params[:description], line_item.description)
220
+ assert_equal(line_item_params[:unit_amount], line_item.unit_amount)
221
+ assert_equal(1, invoice.line_items.size)
222
+
223
+ # Test adding line item by XeroGateway::LineItem
224
+ line_item = invoice.add_line_item(line_item_params)
225
+ assert_kind_of(XeroGateway::LineItem, line_item)
226
+ assert_equal(line_item_params[:description], line_item.description)
227
+ assert_equal(line_item_params[:unit_amount], line_item.unit_amount)
228
+ assert_equal(2, invoice.line_items.size)
229
+
230
+ # Test that pushing anything else into add_line_item fails.
231
+ ["invalid", 100, nil, []].each do | invalid_object |
232
+ assert_raise(XeroGateway::Invoice::InvalidLineItemError) { invoice.add_line_item(invalid_object) }
233
+ assert_equal(2, invoice.line_items.size)
234
+ end
235
+ end
236
+
237
+ def test_instantiate_invoice_with_default_line_amount_types
238
+ invoice = XeroGateway::Invoice.new
239
+ assert_equal(invoice.line_amount_types, 'Exclusive')
240
+ end
241
+
242
+ def test_optional_params
243
+ invoice = create_test_invoice(:url => 'http://example.com')
244
+ assert_equal 'http://example.com', invoice.url
245
+ end
246
+
247
+ private
248
+
249
+ def create_test_invoice(invoice_params = {}, contact_params = {}, line_item_params = [])
250
+ unless invoice_params.nil?
251
+ invoice_params = {
252
+ :invoice_type => 'ACCREC',
253
+ :date => Date.today,
254
+ :due_date => Date.today + 10, # 10 days in the future
255
+ :invoice_number => '12345',
256
+ :reference => "MY REFERENCE FOR THIS INVOICE",
257
+ :line_amount_types => "Exclusive"
258
+ }.merge(invoice_params)
259
+ end
260
+ invoice = XeroGateway::Invoice.new(invoice_params || {})
261
+
262
+ unless contact_params.nil?
263
+ # Strip out :address key from contact_params to use as the default address.
264
+ stripped_address = {
265
+ :address_type => 'DEFAULT',
266
+ :line_1 => 'LINE 1 OF THE ADDRESS'
267
+ }.merge(contact_params.delete(:address) || {})
268
+
269
+ # Strip out :phone key from contact_params to use at the default phone.
270
+ stripped_phone = {
271
+ :phone_type => 'DEFAULT',
272
+ :number => '12345678'
273
+ }.merge(contact_params.delete(:phone) || {})
274
+
275
+ contact_params = {
276
+ :contact_id => '00000000-0000-0000-0000-000000000000', # Just any valid GUID
277
+ :name => "CONTACT NAME",
278
+ :first_name => "Bob",
279
+ :last_name => "Builder"
280
+ }.merge(contact_params)
281
+
282
+ # Create invoice.contact from contact_params.
283
+ invoice.contact = XeroGateway::Contact.new(contact_params)
284
+ invoice.contact.address = XeroGateway::Address.new(stripped_address)
285
+ invoice.contact.phone = XeroGateway::Phone.new(stripped_phone)
286
+ end
287
+
288
+ unless line_item_params.nil?
289
+ line_item_params = [line_item_params].flatten # always use an array, even if only a single hash passed in
290
+
291
+ # At least one line item, make first have some defaults.
292
+ line_item_params << {} if line_item_params.size == 0
293
+ line_item_params[0] = {
294
+ :description => "A LINE ITEM",
295
+ :account_code => "200",
296
+ :unit_amount => BigDecimal.new("100"),
297
+ :tax_amount => BigDecimal.new("12.5"),
298
+ :tracking => XeroGateway::TrackingCategory.new(:name => "blah", :options => "hello")
299
+ }.merge(line_item_params[0])
300
+
301
+ # Create invoice.line_items from line_item_params
302
+ line_item_params.each do | line_item |
303
+ invoice.add_line_item(line_item)
304
+ end
305
+ end
306
+
307
+ invoice
308
+ end
309
+
310
+ # NB: Xero no longer appears to provide XSDs for their api, check http://blog.xero.com/developer/api/invoices/
311
+ #
312
+ # context "validating against the Xero XSD" do
313
+ # setup do
314
+ # # @schema = LibXML::XML::Schema.document(LibXML::XML::Document.file(File.join(File.dirname(__FILE__), '../xsd/create_invoice.xsd')))
315
+ # end
316
+ #
317
+ # should "succeed" do
318
+ # invoice = create_test_invoice
319
+ # message = invoice.to_xml
320
+ #
321
+ # # Check that the document matches the XSD
322
+ # assert LibXML::XML::Parser.string(message).parse.validate_schema(@schema), "The XML document generated did not validate against the XSD"
323
+ # end
324
+ # end
325
+
326
+ end