xero_gateway-n8vision 2.0.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) 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 +41 -0
  10. data/lib/xero_gateway/account.rb +86 -0
  11. data/lib/xero_gateway/accounts_list.rb +73 -0
  12. data/lib/xero_gateway/address.rb +96 -0
  13. data/lib/xero_gateway/bank_transaction.rb +175 -0
  14. data/lib/xero_gateway/ca-certificates.crt +2560 -0
  15. data/lib/xero_gateway/contact.rb +203 -0
  16. data/lib/xero_gateway/credit_note.rb +220 -0
  17. data/lib/xero_gateway/currency.rb +56 -0
  18. data/lib/xero_gateway/dates.rb +25 -0
  19. data/lib/xero_gateway/error.rb +18 -0
  20. data/lib/xero_gateway/exceptions.rb +51 -0
  21. data/lib/xero_gateway/gateway.rb +698 -0
  22. data/lib/xero_gateway/http.rb +135 -0
  23. data/lib/xero_gateway/http_encoding_helper.rb +49 -0
  24. data/lib/xero_gateway/invoice.rb +238 -0
  25. data/lib/xero_gateway/journal_line.rb +102 -0
  26. data/lib/xero_gateway/line_item.rb +125 -0
  27. data/lib/xero_gateway/line_item_calculations.rb +51 -0
  28. data/lib/xero_gateway/manual_journal.rb +163 -0
  29. data/lib/xero_gateway/money.rb +16 -0
  30. data/lib/xero_gateway/oauth.rb +92 -0
  31. data/lib/xero_gateway/organisation.rb +75 -0
  32. data/lib/xero_gateway/partner_app.rb +30 -0
  33. data/lib/xero_gateway/payment.rb +43 -0
  34. data/lib/xero_gateway/phone.rb +77 -0
  35. data/lib/xero_gateway/private_app.rb +17 -0
  36. data/lib/xero_gateway/response.rb +43 -0
  37. data/lib/xero_gateway/tax_rate.rb +63 -0
  38. data/lib/xero_gateway/tracking_category.rb +87 -0
  39. data/test/integration/accounts_list_test.rb +109 -0
  40. data/test/integration/create_bank_transaction_test.rb +38 -0
  41. data/test/integration/create_contact_test.rb +66 -0
  42. data/test/integration/create_credit_note_test.rb +49 -0
  43. data/test/integration/create_invoice_test.rb +49 -0
  44. data/test/integration/create_manual_journal_test.rb +35 -0
  45. data/test/integration/get_accounts_test.rb +23 -0
  46. data/test/integration/get_bank_transaction_test.rb +51 -0
  47. data/test/integration/get_bank_transactions_test.rb +88 -0
  48. data/test/integration/get_contact_test.rb +28 -0
  49. data/test/integration/get_contacts_test.rb +40 -0
  50. data/test/integration/get_credit_note_test.rb +48 -0
  51. data/test/integration/get_credit_notes_test.rb +90 -0
  52. data/test/integration/get_currencies_test.rb +25 -0
  53. data/test/integration/get_invoice_test.rb +48 -0
  54. data/test/integration/get_invoices_test.rb +92 -0
  55. data/test/integration/get_manual_journal_test.rb +50 -0
  56. data/test/integration/get_manual_journals_test.rb +88 -0
  57. data/test/integration/get_organisation_test.rb +24 -0
  58. data/test/integration/get_tax_rates_test.rb +25 -0
  59. data/test/integration/get_tracking_categories_test.rb +27 -0
  60. data/test/integration/update_bank_transaction_test.rb +31 -0
  61. data/test/integration/update_contact_test.rb +31 -0
  62. data/test/integration/update_invoice_test.rb +31 -0
  63. data/test/integration/update_manual_journal_test.rb +31 -0
  64. data/test/test_helper.rb +217 -0
  65. data/test/unit/account_test.rb +47 -0
  66. data/test/unit/bank_transaction_test.rb +126 -0
  67. data/test/unit/contact_test.rb +97 -0
  68. data/test/unit/credit_note_test.rb +284 -0
  69. data/test/unit/currency_test.rb +31 -0
  70. data/test/unit/gateway_test.rb +119 -0
  71. data/test/unit/invoice_test.rb +326 -0
  72. data/test/unit/manual_journal_test.rb +93 -0
  73. data/test/unit/oauth_test.rb +116 -0
  74. data/test/unit/organisation_test.rb +38 -0
  75. data/test/unit/tax_rate_test.rb +38 -0
  76. data/test/unit/tracking_category_test.rb +52 -0
  77. data/xero_gateway-n8vision.gemspec +15 -0
  78. metadata +178 -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::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