xeroizer 2.18.1 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +86 -63
  3. data/lib/xeroizer.rb +1 -0
  4. data/lib/xeroizer/generic_application.rb +2 -1
  5. data/lib/xeroizer/http.rb +2 -0
  6. data/lib/xeroizer/models/bank_account.rb +1 -0
  7. data/lib/xeroizer/models/branding_theme.rb +49 -9
  8. data/lib/xeroizer/models/credit_note.rb +3 -2
  9. data/lib/xeroizer/models/from_bank_account.rb +1 -0
  10. data/lib/xeroizer/models/invoice.rb +9 -2
  11. data/lib/xeroizer/models/line_item.rb +9 -2
  12. data/lib/xeroizer/models/organisation.rb +2 -0
  13. data/lib/xeroizer/models/payment_service.rb +22 -0
  14. data/lib/xeroizer/models/prepayment.rb +1 -0
  15. data/lib/xeroizer/models/purchase_order.rb +6 -6
  16. data/lib/xeroizer/models/schedule.rb +1 -0
  17. data/lib/xeroizer/models/to_bank_account.rb +1 -0
  18. data/lib/xeroizer/record/base.rb +1 -1
  19. data/lib/xeroizer/record/base_model_http_proxy.rb +3 -0
  20. data/lib/xeroizer/record/model_definition_helper.rb +1 -1
  21. data/lib/xeroizer/record/xml_helper.rb +2 -2
  22. data/lib/xeroizer/report/aged_receivables_by_contact.rb +1 -1
  23. data/lib/xeroizer/report/cell_xml_helper.rb +1 -1
  24. data/lib/xeroizer/version.rb +1 -1
  25. data/test/acceptance/about_fetching_bank_transactions_test.rb +2 -2
  26. data/test/stub_responses/organisations.xml +10 -0
  27. data/test/stub_responses/payment_service.xml +15 -0
  28. data/test/unit/http_test.rb +5 -5
  29. data/test/unit/models/line_item_test.rb +8 -0
  30. data/test/unit/models/organisation_test.rb +14 -0
  31. data/test/unit/models/payment_service_test.rb +29 -0
  32. data/test/unit/models/repeating_invoice_test.rb +1 -1
  33. data/test/unit/record/base_test.rb +10 -0
  34. metadata +8 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ea62d6d6a33c98a88703fe9f1c6b675fcfa6731e
4
- data.tar.gz: cf3504a3fafc2b1a8d65e274b03373a7dd466d75
3
+ metadata.gz: 1ca2b2dffb8f864a2f4c82295bbfddcdeea61c1b
4
+ data.tar.gz: 4b9f39d61e7027ba84df0d548826cccbc0695d08
5
5
  SHA512:
6
- metadata.gz: 74c217092e28ee42009e40461d99e4b542a09531cba94cfe22f07544dfc5cf7fe35c10d95691a91ffbc152738d67a07470c043b061aa963688b970095db0011e
7
- data.tar.gz: 35af4e22733df6241464e95b2a33b7020fbe5590df8e84292ceabdc3f2b0f830174f69d59693af9e09c77cd318c4132767ac2dbd542f7c8c09e9219a45f72cc3
6
+ metadata.gz: 1e27f5e7d6b66833b4ef226ce27c9fd67b26ba434eaa35b2ea750106db4ad6c334e566132d4779192b3ccfa964811657b9ef123b552d8a4a185be0d9f78c899d
7
+ data.tar.gz: 6ea509718550d68b1560507fc0204c19406df17924c44ba2ebcc2ac0f27d7652929b7fd9716b9a489e564f274304a3d4cb6e72556031186b7150c304aad5a7cc
data/README.md CHANGED
@@ -5,7 +5,7 @@ Xeroizer API Library ![Project status](http://stillmaintained.com/waynerobinson/
5
5
  **Git**: [git://github.com/waynerobinson/xeroizer.git](git://github.com/waynerobinson/xeroizer.git)
6
6
  **Github**: [https://github.com/waynerobinson/xeroizer](https://github.com/waynerobinson/xeroizer)
7
7
  **Author**: Wayne Robinson [http://www.wayne-robinson.com](http://www.wayne-robinson.com)
8
- **Contributors**: See Contributors section below
8
+ **Contributors**: See Contributors section below
9
9
  **Copyright**: 2007-2013
10
10
  **License**: MIT License
11
11
 
@@ -34,7 +34,7 @@ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CON
34
34
  # Retrieve list of contacts (note: all communication must be made through the client).
35
35
  contacts = client.Contact.all(:order => 'Name')
36
36
  ```
37
-
37
+
38
38
  Authentication
39
39
  --------------
40
40
 
@@ -44,13 +44,13 @@ There are three methods of authentication detailed below:
44
44
 
45
45
  ### All: Consumer Key/Secret
46
46
 
47
- All methods of authentication require your OAuth consumer key and secret. This can be found for your application
47
+ All methods of authentication require your OAuth consumer key and secret. This can be found for your application
48
48
  in the API management console at [http://api.xero.com](http://api.xero.com).
49
49
 
50
50
  ### Public Applications
51
51
 
52
- Public applications use a 3-legged authorisation process. A user will need to authorise your
53
- application against each organisation that you want access to. Your application can have access
52
+ Public applications use a 3-legged authorisation process. A user will need to authorise your
53
+ application against each organisation that you want access to. Your application can have access
54
54
  to many organisations at once by going through the authorisation process for each organisation.
55
55
 
56
56
  The access token received will expire after 30 minutes. If you want access for longer you will need
@@ -75,69 +75,69 @@ redirect_to request_token.authorize_url
75
75
 
76
76
  # 3. Exchange RequestToken for AccessToken.
77
77
  # This access token will be used for all subsequent requests but it is stored within the client
78
- # application so you don't have to record it.
78
+ # application so you don't have to record it.
79
79
  #
80
80
  # Note: This example assumes the callback URL is a Rails action.
81
81
  client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
82
82
  ```
83
-
83
+
84
84
  You can now use the client to access the Xero API methods, e.g.
85
85
 
86
86
  ```ruby
87
87
  contacts = client.Contact.all
88
88
  ```
89
-
89
+
90
90
  #### Example Rails Controller
91
91
 
92
92
  ```ruby
93
93
  class XeroSessionController < ApplicationController
94
94
 
95
95
  before_filter :get_xero_client
96
-
96
+
97
97
  public
98
-
98
+
99
99
  def new
100
100
  request_token = @xero_client.request_token(:oauth_callback => 'http://yourapp.com/xero_session/create')
101
101
  session[:request_token] = request_token.token
102
102
  session[:request_secret] = request_token.secret
103
-
103
+
104
104
  redirect_to request_token.authorize_url
105
105
  end
106
-
106
+
107
107
  def create
108
108
  @xero_client.authorize_from_request(
109
- session[:request_token],
110
- session[:request_secret],
109
+ session[:request_token],
110
+ session[:request_secret],
111
111
  :oauth_verifier => params[:oauth_verifier] )
112
-
112
+
113
113
  session[:xero_auth] = {
114
114
  :access_token => @xero_client.access_token.token,
115
115
  :access_key => @xero_client.access_token.secret }
116
-
116
+
117
117
  session.data.delete(:request_token)
118
118
  session.data.delete(:request_secret)
119
119
  end
120
-
120
+
121
121
  def destroy
122
122
  session.data.delete(:xero_auth)
123
123
  end
124
-
124
+
125
125
  private
126
-
126
+
127
127
  def get_xero_client
128
128
  @xero_client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
129
-
129
+
130
130
  # Add AccessToken if authorised previously.
131
131
  if session[:xero_auth]
132
132
  @xero_client.authorize_from_access(
133
- session[:xero_auth][:access_token],
133
+ session[:xero_auth][:access_token],
134
134
  session[:xero_auth][:access_key] )
135
135
  end
136
136
  end
137
137
  end
138
138
  ```
139
-
140
- #### Storing AccessToken
139
+
140
+ #### Storing AccessToken
141
141
 
142
142
  You can store the access token/secret pair so you can access the API again without user intervention. Currently these
143
143
  tokens are only valid for 30 minutes and will raise a `Xeroizer::OAuth::TokenExpired` exception if you try to access
@@ -152,9 +152,9 @@ access_secret = client.access_token.secret
152
152
 
153
153
  ### Private Applications
154
154
 
155
- Private applications use a 2-legged authorisation process. When you register your application, you will select
156
- the organisation that is authorised to your application. This cannot be changed afterwards, although you can
157
- register another private application if you have multiple organisations.
155
+ Private applications use a 2-legged authorisation process. When you register your application, you will select
156
+ the organisation that is authorised to your application. This cannot be changed afterwards, although you can
157
+ register another private application if you have multiple organisations.
158
158
 
159
159
  Note: You can only register organisations you are authorised to yourself.
160
160
 
@@ -178,7 +178,7 @@ contacts = client.Contact.all
178
178
 
179
179
  Partner applications use a combination of 3-legged authorisation and private key message signing.
180
180
 
181
- You will need to contact Xero (network@xero.com) to get permission to create a partner application.
181
+ Visit the [https://developer.xero.com/partner/app-partner](Becoming an app partner) page to get permission to create a partner application.
182
182
 
183
183
  After you have followed the instructions provided by Xero for partner applications and uploaded your certificate you can
184
184
  access the partner application in a similar way to public applications.
@@ -188,7 +188,7 @@ Authentication occcurs in 3 steps:
188
188
  ```ruby
189
189
  client = Xeroizer::PartnerApplication.new(
190
190
  YOUR_OAUTH_CONSUMER_KEY,
191
- YOUR_OAUTH_CONSUMER_SECRET,
191
+ YOUR_OAUTH_CONSUMER_SECRET,
192
192
  "/path/to/privatekey.pem"
193
193
  )
194
194
 
@@ -206,7 +206,7 @@ redirect_to request_token.authorize_url
206
206
 
207
207
  # 3. Exchange RequestToken for AccessToken.
208
208
  # This access token will be used for all subsequent requests but it is stored within the client
209
- # application so you don't have to record it.
209
+ # application so you don't have to record it.
210
210
  #
211
211
  # Note: This example assumes the callback URL is a Rails action.
212
212
  client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
@@ -221,7 +221,7 @@ session_handle = client.session_handle
221
221
  access_key = client.access_token.token
222
222
  access_secret = client.access_token.secret
223
223
  ```
224
-
224
+
225
225
  Two other interesting attributes of the PartnerApplication client are:
226
226
 
227
227
  > **`#expires_at`**: Time this AccessToken will expire (usually 30 minutes into the future).
@@ -245,6 +245,10 @@ session.
245
245
 
246
246
  If you lose these details at any stage you can always reauthorise by redirecting the user back to the Xero OAuth gateway.
247
247
 
248
+ #### Branding Themes API
249
+
250
+ Once you are approved as a Xero Partner you can request unofficial documentation to do with customizing Payment Services and Branding Themes using the API. There is more info on that [here]( https://github.com/waynerobinson/xeroizer/pull/439).
251
+
248
252
  Retrieving Data
249
253
  ---------------
250
254
 
@@ -264,10 +268,10 @@ saved = new_contact.save
264
268
 
265
269
  ### \#all([options])
266
270
 
267
- Retrieves list of all records with matching options.
271
+ Retrieves list of all records with matching options.
268
272
 
269
273
  **Note:** Some records (Invoice, CreditNote) only return summary information for the contact and no line items
270
- when returning them this list operation. This library takes care of automatically retrieving the
274
+ when returning them this list operation. This library takes care of automatically retrieving the
271
275
  contact and line items from Xero on first access however, this first access has a large performance penalty
272
276
  and will count as an extra query towards your 5,000/day and 60/minute request per organisation limit.
273
277
 
@@ -281,6 +285,18 @@ Valid options are:
281
285
 
282
286
  > Field to order by. Should be formatted as Xero-based field (e.g. 'Name', 'ContactID', etc)
283
287
 
288
+ > **:status**
289
+
290
+ > Status field for PurchaseOrder. Should be a valid Xero purchase order status.
291
+
292
+ > **:date_from**
293
+
294
+ > DateFrom field for PurchaseOrder. Should be in YYYY-MM-DD format.
295
+
296
+ > **:date_to**
297
+
298
+ > DateTo field for PurchaseOrder. Should be in YYYY-MM-DD format.
299
+
284
300
  > **:where**
285
301
 
286
302
  > __See *Where Filters* section below.__
@@ -305,11 +321,11 @@ You can specify find filters by providing the :where option with a hash. For exa
305
321
  ```ruby
306
322
  invoices = Xero.Invoice.all(:where => {:type => 'ACCREC', :amount_due_is_not => 0})
307
323
  ```
308
-
324
+
309
325
  will automatically create the Xero string:
310
326
 
311
327
  Type=="ACCREC"&&AmountDue<>0
312
-
328
+
313
329
  The default method for filtering is the equality '==' operator however, these can be overridden
314
330
  by modifying the postfix of the attribute name (as you can see for the :amount\_due field above).
315
331
 
@@ -318,9 +334,9 @@ by modifying the postfix of the attribute name (as you can see for the :amount\_
318
334
  \{attribute_name}_is_greater_than_or_equal_to will use '>='
319
335
  \{attribute_name}_is_less_than will use '<'
320
336
  \{attribute_name}_is_less_than_or_equal_to will use '<='
321
-
337
+
322
338
  The default is '=='
323
-
339
+
324
340
  **Note:** Currently, the hash-conversion library only allows for AND-based criteria and doesn't
325
341
  take into account associations. For these, please use the custom filter method below.
326
342
 
@@ -332,15 +348,15 @@ in the resulting response, including all nested XML elements.
332
348
  **Example 1: Retrieve all invoices for a specific contact ID:**
333
349
 
334
350
  invoices = xero.Invoice.all(:where => 'Contact.ContactID.ToString()=="cd09aa49-134d-40fb-a52b-b63c6a91d712"')
335
-
351
+
336
352
  **Example 2: Retrieve all unpaid ACCREC Invoices against a particular Contact Name:**
337
-
353
+
338
354
  invoices = xero.Invoice.all(:where => 'Contact.Name=="Basket Case" && Type=="ACCREC" && AmountDue<>0')
339
-
355
+
340
356
  **Example 3: Retrieve all Invoices PAID between certain dates**
341
-
357
+
342
358
  invoices = xero.Invoice.all(:where => 'FullyPaidOnDate>=DateTime.Parse("2010-01-01T00:00:00")&&FullyPaidOnDate<=DateTime.Parse("2010-01-08T00:00:00")')
343
-
359
+
344
360
  **Example 4: Retrieve all Invoices using Paging (batches of 100)**
345
361
 
346
362
  invoices = xero.Invoice.find_in_batches({page_number: 1}) do |invoice_batch|
@@ -348,15 +364,15 @@ in the resulting response, including all nested XML elements.
348
364
  ...
349
365
  end
350
366
  end
351
-
367
+
352
368
  **Example 5: Retrieve all Bank Accounts:**
353
-
369
+
354
370
  accounts = xero.Account.all(:where => 'Type=="BANK"')
355
-
371
+
356
372
  **Example 6: Retrieve all DELETED or VOIDED Invoices:**
357
-
373
+
358
374
  invoices = xero.Invoice.all(:where => 'Status=="VOIDED" OR Status=="DELETED"')
359
-
375
+
360
376
  **Example 7: Retrieve all contacts with specific text in the contact name:**
361
377
 
362
378
  contacts = xero.Contact.all(:where => 'Name.Contains("Peter")')
@@ -376,7 +392,7 @@ invoice.line_items.each do | line_item |
376
392
  puts "Line Description: #{line_item.description}"
377
393
  end
378
394
  ```
379
-
395
+
380
396
  **belongs\_to example:**
381
397
 
382
398
  ```ruby
@@ -430,21 +446,21 @@ contact.add_phone(:type => 'DEFAULT', :area_code => '07', :number => '3033 1234'
430
446
  contact.add_phone(:type => 'MOBILE', :number => '0412 123 456')
431
447
  contact.save
432
448
  ```
433
-
449
+
434
450
  To add to a `has_many` association use the `add_{association}` method. For example:
435
451
 
436
452
  ```ruby
437
453
  contact.add_address(:type => 'STREET', :line1 => '12 Testing Lane', :city => 'Brisbane')
438
454
  ```
439
-
455
+
440
456
  To add to a `belongs_to` association use the `build_{association}` method. For example:
441
-
457
+
442
458
  ```ruby
443
459
  invoice.build_contact(:name => 'ABC Company')
444
460
  ```
445
461
 
446
462
  ### Updating
447
-
463
+
448
464
  If the primary GUID for the record is present, the library will attempt to update the record instead of
449
465
  creating it. It is important that this record is downloaded from the Xero API first before attempting
450
466
  an update. For example:
@@ -454,10 +470,17 @@ contact = xero.Contact.find("cd09aa49-134d-40fb-a52b-b63c6a91d712")
454
470
  contact.name = "Another Name Change"
455
471
  contact.save
456
472
  ```
457
-
458
- Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
473
+
474
+ Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
459
475
  minimum validation requirements for each of the record types.
460
476
 
477
+ Some Xero endpoints, such as Payment, will only accept specific attributes for updates. Because the library does not have this knowledge encoded (and doesn't do dirty tracking of attributes), it's necessary to construct new objects instead of using the ones retrieved from Xero:
478
+
479
+ ```ruby
480
+ delete_payment = gateway.Payment.build(id: payment.id, status: 'DELETED')
481
+ delete_payment.save
482
+ ```
483
+
461
484
  ### Bulk Creates & Updates
462
485
 
463
486
  Xero has a hard daily limit on the number of API requests you can make (currently 5,000 requests
@@ -501,7 +524,7 @@ saved = contact.save
501
524
 
502
525
  # contact.errors will contain [[:name, "can't be blank"]]
503
526
  ```
504
-
527
+
505
528
  \#errors\_for(:attribute\_name) is a helper method to return just the errors associated with
506
529
  that attribute. For example:
507
530
 
@@ -530,7 +553,7 @@ invoice.save
530
553
  # An invoice created without a status will default to 'DRAFT'
531
554
  invoice.approved?
532
555
 
533
- # Payments can only be created against 'AUTHROISED' invoices
556
+ # Payments can only be created against 'AUTHORISED' invoices
534
557
  invoice.approve!
535
558
 
536
559
  # Find the first bank account
@@ -582,16 +605,16 @@ trial_balance.rows.each do | row |
582
605
  case row
583
606
  when Xeroizer::Report::HeaderRow
584
607
  # do something with header
585
-
608
+
586
609
  when Xeroizer::Report::SectionRow
587
610
  # do something with section, will need to step into the rows for this section
588
-
611
+
589
612
  when Xeroizer::Report::Row
590
613
  # do something for standard report rows
591
-
614
+
592
615
  when Xeroizer::Report::SummaryRow
593
616
  # do something for summary rows
594
-
617
+
595
618
  end
596
619
  end
597
620
  ```
@@ -660,7 +683,7 @@ throttling and logging:
660
683
  Xeroizer::PublicApplication.new(
661
684
  credentials[:key], credentials[:secret],
662
685
  before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
663
- after_request: ->(request, response) { puts "Got this response: #{response.code}" }
686
+ after_request: ->(request, response) { puts "Got this response: #{response.code}" },
664
687
  around_request: -> (request, &block) { puts "About to send request"; block.call; puts "After request"}
665
688
  )
666
689
  ```
@@ -686,7 +709,7 @@ This option adds the unitdp=4 query string parameter to all requests for models
686
709
  Tests
687
710
  -----
688
711
 
689
- The tests within the repository can be run by setting up a [Private App](https://developer.xero.com/documentation/auth-and-limits/private-applications). You can create a Private App in the [developer portal](https://developer.xero.com/myapps/), it's suggested that you create it against the [Demo Company](https://developer.xero.com/documentation/getting-started/development-accounts) (note: the Demo Company expires after 28 days, so you will need to reset it and create a new Private App if you Demo Company has expired).
712
+ The tests within the repository can be run by setting up a [Private App](https://developer.xero.com/documentation/auth-and-limits/private-applications). You can create a Private App in the [developer portal](https://developer.xero.com/myapps/), it's suggested that you create it against the [Demo Company (AU)](https://developer.xero.com/documentation/getting-started/development-accounts). Demo Company expires after 28 days, so you will need to reset it and create a new Private App if you Demo Company has expired. Make sure you create the Demo Company in Australia region.
690
713
 
691
714
  Once you have created your Private App, set these environment variables:
692
715
  ```
@@ -704,6 +727,6 @@ rake test
704
727
 
705
728
 
706
729
  ### Contributors
707
- Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
708
- and Nik Wakelin and portions of the networking and authentication code are based completely off
730
+ Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
731
+ and Nik Wakelin and portions of the networking and authentication code are based completely off
709
732
  this project. Copyright for these components remains held in the name of Tim Connor.
@@ -56,6 +56,7 @@ require 'xeroizer/models/manual_journal_line'
56
56
  require 'xeroizer/models/option'
57
57
  require 'xeroizer/models/organisation'
58
58
  require 'xeroizer/models/payment'
59
+ require 'xeroizer/models/payment_service'
59
60
  require 'xeroizer/models/prepayment'
60
61
  require 'xeroizer/models/overpayment'
61
62
  require 'xeroizer/models/phone'
@@ -32,6 +32,7 @@ module Xeroizer
32
32
  record :ManualJournal
33
33
  record :Organisation
34
34
  record :Payment
35
+ record :PaymentService
35
36
  record :Prepayment
36
37
  record :Overpayment
37
38
  record :PurchaseOrder
@@ -80,6 +81,6 @@ module Xeroizer
80
81
  xero_client.xero_url = options[:xero_url] || "https://api.xero.com/payroll.xro/1.0"
81
82
  @payroll ||= PayrollApplication.new(xero_client)
82
83
  end
83
-
84
+
84
85
  end
85
86
  end
@@ -112,6 +112,8 @@ module Xeroizer
112
112
  case response.code.to_i
113
113
  when 200
114
114
  response.plain_body
115
+ when 204
116
+ nil
115
117
  when 400
116
118
  handle_error!(response, body)
117
119
  when 401
@@ -7,6 +7,7 @@ module Xeroizer
7
7
  class BankAccount < Base
8
8
  guid :account_id
9
9
  string :code
10
+ string :name
10
11
  end
11
12
  end
12
13
  end
@@ -1,22 +1,62 @@
1
+ require "xeroizer/models/payment_service"
2
+
1
3
  module Xeroizer
2
4
  module Record
3
-
5
+
4
6
  class BrandingThemeModel < BaseModel
5
-
6
- set_permissions :read
7
-
7
+
8
+ set_permissions :read, :write
9
+
10
+ public
11
+
12
+ def payment_services(id)
13
+ @payment_services ||= @application.http_get(@application.client, payment_services_endpoint(id))
14
+ end
15
+
16
+ def add_payment_service(id:, payment_service_id:)
17
+ xml = {
18
+ PaymentService: {
19
+ PaymentServiceID: payment_service_id
20
+ }
21
+ }.to_xml
22
+
23
+ @application.http_post(@application.client, payment_services_endpoint(id), xml)
24
+ end
25
+
26
+ private
27
+
28
+ def payment_services_endpoint(id)
29
+ "#{url}/#{id}/PaymentServices"
30
+ end
31
+
8
32
  end
9
-
33
+
10
34
  class BrandingTheme < Base
11
-
35
+
12
36
  set_primary_key :branding_theme_id
13
-
37
+
14
38
  guid :branding_theme_id
15
39
  string :name
16
40
  integer :sort_order
17
41
  datetime_utc :created_date_utc, :api_name => 'CreatedDateUTC'
18
42
 
43
+ # Unfortunately, this part of the API does not work the same as the rest.
44
+ # You cannot POST child records to Branding Themes.
45
+ #
46
+ # The endpoints are:
47
+ # GET /BrandingThemes/{BrandingThemeID}/PaymentServices
48
+ # POST /BrandingThemes/{BrandingThemeID}/PaymentServices
49
+ #
50
+ # has_one :payment_service, :model_name => 'PaymentService', :list_complete => true
51
+
52
+ def payment_services
53
+ parent.payment_services(id)
54
+ end
55
+
56
+ def add_payment_service(payment_service_id)
57
+ parent.add_payment_service(id: id, payment_service_id: payment_service_id)
58
+ end
19
59
  end
20
-
60
+
21
61
  end
22
- end
62
+ end
@@ -66,6 +66,7 @@ module Xeroizer
66
66
  datetime :fully_paid_on_date
67
67
  boolean :sent_to_contact
68
68
  decimal :remaining_credit
69
+ decimal :applied_amount
69
70
  boolean :has_attachments
70
71
 
71
72
  belongs_to :contact
@@ -100,7 +101,7 @@ module Xeroizer
100
101
  # Calculate sub_total from line_items.
101
102
  def sub_total(always_summary = false)
102
103
  if !always_summary && (new_record? || (!new_record? && line_items && line_items.size > 0))
103
- overall_sum = (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.line_amount }
104
+ overall_sum = (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.line_amount }
104
105
 
105
106
  # If the default amount types are inclusive of 'tax' then remove the tax amount from this sub-total.
106
107
  overall_sum -= total_tax if line_amount_types == 'Inclusive'
@@ -113,7 +114,7 @@ module Xeroizer
113
114
  # Calculate total_tax from line_items.
114
115
  def total_tax(always_summary = false)
115
116
  if !always_summary && (new_record? || (!new_record? && line_items && line_items.size > 0))
116
- (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.tax_amount }
117
+ (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.tax_amount }
117
118
  else
118
119
  attributes[:total_tax]
119
120
  end
@@ -7,6 +7,7 @@ module Xeroizer
7
7
  class FromBankAccount < Base
8
8
  guid :account_id
9
9
  string :code
10
+ string :name
10
11
  end
11
12
  end
12
13
  end
@@ -89,6 +89,7 @@ module Xeroizer
89
89
  has_many :line_items, :complete_on_page => true
90
90
  has_many :payments
91
91
  has_many :credit_notes
92
+ has_many :prepayments
92
93
 
93
94
  validates_presence_of :date, :due_date, :unless => :new_record?
94
95
  validates_inclusion_of :type, :in => INVOICE_TYPES
@@ -151,7 +152,7 @@ module Xeroizer
151
152
  # Calculate sub_total from line_items.
152
153
  def sub_total(always_summary = false)
153
154
  if !@sub_total_is_set && not_summary_or_loaded_record(always_summary)
154
- overall_sum = (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.line_amount }
155
+ overall_sum = (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.line_amount }
155
156
 
156
157
  # If the default amount types are inclusive of 'tax' then remove the tax amount from this sub-total.
157
158
  overall_sum -= total_tax if line_amount_types == 'Inclusive'
@@ -164,7 +165,7 @@ module Xeroizer
164
165
  # Calculate total_tax from line_items.
165
166
  def total_tax(always_summary = false)
166
167
  if !@total_tax_is_set && not_summary_or_loaded_record(always_summary)
167
- (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.tax_amount }
168
+ (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.tax_amount }
168
169
  else
169
170
  attributes[:total_tax]
170
171
  end
@@ -209,6 +210,12 @@ module Xeroizer
209
210
  change_status!('AUTHORISED')
210
211
  end
211
212
 
213
+ # Send an email containing the invoice.
214
+ def email
215
+ email_url = "#{parent.url}/#{CGI.escape(id)}/Email"
216
+ parent.application.http_post(parent.application.client, email_url, "")
217
+ end
218
+
212
219
  protected
213
220
 
214
221
  def change_status!(new_status)
@@ -39,7 +39,7 @@ module Xeroizer
39
39
  return attributes[:line_amount] if summary_only || @line_amount_set
40
40
 
41
41
  if quantity && unit_amount
42
- total = quantity * unit_amount
42
+ total = coerce_numeric(quantity) * coerce_numeric(unit_amount)
43
43
  if discount_rate
44
44
  BigDecimal((total * ((100 - discount_rate) / 100)).to_s).round(2)
45
45
  else
@@ -48,7 +48,14 @@ module Xeroizer
48
48
  end
49
49
  end
50
50
 
51
+ private
52
+
53
+ def coerce_numeric(number)
54
+ return number if number.is_a? Numeric
55
+ BigDecimal(number)
56
+ end
57
+
51
58
  end
52
-
59
+
53
60
  end
54
61
  end
@@ -1,3 +1,4 @@
1
+ require "xeroizer/models/payment_terms"
1
2
  module Xeroizer
2
3
  module Record
3
4
 
@@ -62,6 +63,7 @@ module Xeroizer
62
63
  has_many :addresses
63
64
  has_many :phones
64
65
  has_many :external_links
66
+ has_one :payment_terms, :model_name => 'PaymentTerms'
65
67
 
66
68
  validates :sales_tax_basis, :message => "is not a valid option" do
67
69
  valid = true
@@ -0,0 +1,22 @@
1
+ module Xeroizer
2
+ module Record
3
+
4
+ class PaymentServiceModel < BaseModel
5
+
6
+ set_permissions :read, :write, :update
7
+
8
+ end
9
+
10
+ class PaymentService < Base
11
+
12
+ set_primary_key :payment_service_id
13
+
14
+ guid :payment_service_id
15
+ string :payment_service_name
16
+ string :payment_service_url
17
+ string :payment_service_type
18
+ string :pay_now_text
19
+
20
+ end
21
+ end
22
+ end
@@ -25,6 +25,7 @@ module Xeroizer
25
25
  string :reference
26
26
  decimal :currency_rate
27
27
  decimal :remaining_credit
28
+ decimal :applied_amount
28
29
  boolean :has_attachments
29
30
 
30
31
  belongs_to :contact
@@ -30,15 +30,15 @@ module Xeroizer
30
30
  boolean :is_discounted
31
31
  string :reference
32
32
  string :type
33
- string :currency_rate
33
+ decimal :currency_rate
34
34
  string :currency_code
35
- guid :branding_theme_id
35
+ guid :branding_theme_id
36
36
  string :status
37
37
  string :line_amount_types
38
- string :sub_total
39
- string :total_tax
40
- string :total
41
- date :updated_date_UTC
38
+ decimal :sub_total
39
+ decimal :total_tax
40
+ decimal :total
41
+ datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
42
42
  boolean :has_attachments
43
43
 
44
44
  has_many :line_items
@@ -10,6 +10,7 @@ module Xeroizer
10
10
  UNIT = {
11
11
  'WEEKLY' => 'Weekly',
12
12
  'MONTHLY' => 'Monthly',
13
+ 'YEARLY' => 'Yearly',
13
14
  } unless defined?(UNIT)
14
15
 
15
16
  PAYMENT_TERM = {
@@ -7,6 +7,7 @@ module Xeroizer
7
7
  class ToBankAccount < Base
8
8
  guid :account_id
9
9
  string :code
10
+ string :name
10
11
  end
11
12
  end
12
13
  end
@@ -30,7 +30,7 @@ module Xeroizer
30
30
  def build(attributes, parent)
31
31
  record = new(parent)
32
32
  attributes.each do | key, value |
33
- attr = record.respond_to?("#{key}=") ? key : record.class.fields[key][:internal_name]
33
+ attr = record.respond_to?("#{key}=") || record.class.fields[key].nil? ? key : record.class.fields[key][:internal_name]
34
34
  record.send("#{attr}=", value)
35
35
  end
36
36
  record
@@ -33,6 +33,9 @@ module Xeroizer
33
33
  end
34
34
  end
35
35
  params[:offset] = options[:offset] if options[:offset]
36
+ params[:Status] = options[:status] if options[:status]
37
+ params[:DateFrom] = options[:date_from] if options[:date_from]
38
+ params[:DateTo] = options[:date_to] if options[:date_to]
36
39
  params[:page] = options[:page] if options[:page]
37
40
  params
38
41
  end
@@ -68,7 +68,7 @@ module Xeroizer
68
68
  :type => field_type
69
69
  })
70
70
  define_method internal_field_name do
71
- @attributes[field_name] || value_if_nil
71
+ @attributes[field_name].nil? ? value_if_nil : @attributes[field_name]
72
72
  end
73
73
 
74
74
  unless options[:skip_writer]
@@ -22,7 +22,7 @@ module Xeroizer
22
22
  when :string then element.text
23
23
  when :boolean then (element.text == 'true')
24
24
  when :integer then element.text.to_i
25
- when :decimal then BigDecimal.new(element.text)
25
+ when :decimal then BigDecimal(element.text)
26
26
  when :date then Date.parse(element.text)
27
27
  when :datetime then Time.parse(element.text)
28
28
  when :datetime_utc then ActiveSupport::TimeZone['UTC'].parse(element.text).utc
@@ -98,7 +98,7 @@ module Xeroizer
98
98
  when :decimal
99
99
  real_value = case value
100
100
  when BigDecimal then value.to_s
101
- when String then BigDecimal.new(value).to_s
101
+ when String then BigDecimal(value).to_s
102
102
  else value
103
103
  end
104
104
  b.tag!(field[:api_name], real_value)
@@ -31,7 +31,7 @@ module Xeroizer
31
31
  end
32
32
 
33
33
  def sum(column_name, &block)
34
- sections.first.rows.inject(BigDecimal.new('0')) do | sum, row |
34
+ sections.first.rows.inject(BigDecimal('0')) do | sum, row |
35
35
  sum += row.cell(column_name).value if row.class == Xeroizer::Report::Row && (block.nil? || block.call(row))
36
36
  sum
37
37
  end
@@ -47,7 +47,7 @@ module Xeroizer
47
47
 
48
48
  def parse_value(value)
49
49
  case value
50
- when /^[-]?\d+(\.\d+)?$/ then BigDecimal.new(value)
50
+ when /^[-]?\d+(\.\d+)?$/ then BigDecimal(value)
51
51
  when /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/ then Time.xmlschema(value)
52
52
  else value
53
53
  end
@@ -1,3 +1,3 @@
1
1
  module Xeroizer
2
- VERSION = "2.18.1".freeze
2
+ VERSION = "2.19.0".freeze
3
3
  end
@@ -18,7 +18,7 @@ class AboutFetchingBankTransactions < Test::Unit::TestCase
18
18
  keys = [:line_amount_types, :contact, :date, :status, :line_items,
19
19
  :updated_date_utc, :currency_code, :bank_transaction_id,
20
20
  :bank_account, :type, :reference, :is_reconciled, :currency_rate]
21
- assert_equal(@a_new_bank_transaction.attributes.keys, keys)
21
+ assert_equal(keys, @a_new_bank_transaction.attributes.keys)
22
22
  end
23
23
 
24
24
  it "returns full line item details" do
@@ -38,7 +38,7 @@ class AboutFetchingBankTransactions < Test::Unit::TestCase
38
38
  keys = [:line_amount_types, :contact, :date, :status, :updated_date_utc,
39
39
  :currency_code, :bank_transaction_id, :bank_account, :type, :reference,
40
40
  :is_reconciled]
41
- assert_equal(@the_first_bank_transaction.attributes.keys, keys)
41
+ assert_equal(keys, @the_first_bank_transaction.attributes.keys)
42
42
  end
43
43
 
44
44
  it "returns contact" do
@@ -20,6 +20,16 @@
20
20
  <FinancialYearEndMonth>6</FinancialYearEndMonth>
21
21
  <PeriodLockDate>2008-06-30T00:00:00</PeriodLockDate>
22
22
  <CreatedDateUTC>2011-05-19T05:26:03.95</CreatedDateUTC>
23
+ <PaymentTerms>
24
+ <Bills>
25
+ <Day>4</Day>
26
+ <Type>OFFOLLOWINGMONTH</Type>
27
+ </Bills>
28
+ <Sales>
29
+ <Day>2</Day>
30
+ <Type>OFFOLLOWINGMONTH</Type>
31
+ </Sales>
32
+ </PaymentTerms>
23
33
  </Organisation>
24
34
  </Organisations>
25
35
  </Response>
@@ -0,0 +1,15 @@
1
+ <Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2
+ <Id>42d8b00c-2fdd-4a8a-9b84-82cfe78ff34a</Id>
3
+ <Status>OK</Status>
4
+ <ProviderName>Test Provider Name</ProviderName>
5
+ <DateTimeUTC>2018-06-18T04:13:44.7828584Z</DateTimeUTC>
6
+ <PaymentServices>
7
+ <PaymentService>
8
+ <PaymentServiceID>4d7f4335-6f16-437a-86e3-a856ebc576b8</PaymentServiceID>
9
+ <PaymentServiceName>Custom Service</PaymentServiceName>
10
+ <PaymentServiceType>Custom</PaymentServiceType>
11
+ <PaymentServiceUrl>http://example.com</PaymentServiceUrl>
12
+ <PayNowText>Pay Me</PayNowText>
13
+ </PaymentService>
14
+ </PaymentServices>
15
+ </Response>
@@ -3,12 +3,12 @@ require 'test_helper'
3
3
  class HttpTest < Test::Unit::TestCase
4
4
  include TestHelper
5
5
 
6
- def setup
7
- @headers = {"User-Agent" => "Xeroizer/2.15.5"}
8
- @application = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET, :default_headers => @headers)
9
- end
10
-
11
6
  context "default_headers" do
7
+ setup do
8
+ @headers = { "User-Agent" => "Xeroizer/2.15.5" }
9
+ @application = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET, :default_headers => @headers)
10
+ end
11
+
12
12
  should "recognize default_headers" do
13
13
  Xeroizer::OAuth.any_instance.expects(:get).with("/test", has_entry(@headers)).returns(stub(:plain_body => "", :code => "200"))
14
14
  @application.http_get(@application.client, "http://example.com/test")
@@ -75,4 +75,12 @@ class LineItemTest < Test::Unit::TestCase
75
75
  assert_equal "0.0", line_item.line_amount.to_s,
76
76
  "expected line amount to be zero when unit_amount is zero"
77
77
  end
78
+
79
+ it "coerces decimals when calculating line amount" do
80
+ line_item = LineItem.new(nil)
81
+ line_item.quantity = "1"
82
+ line_item.unit_amount = 50
83
+ assert_equal 50, line_item.line_amount,
84
+ "expected line amount to be calculated from coerced values"
85
+ end
78
86
  end
@@ -35,4 +35,18 @@ class OrganisationTest < Test::Unit::TestCase
35
35
  end
36
36
  end
37
37
 
38
+ context "parse response" do
39
+ it "includes payment_terms" do
40
+ @instance = Xeroizer::Record::OrganisationModel.new(nil, "Organisation")
41
+ some_xml = get_record_xml("organisations")
42
+
43
+ result = @instance.parse_response(some_xml)
44
+ organisation = result.response_items.first
45
+
46
+ assert_equal(organisation.payment_terms.bills.day, "4")
47
+ assert_equal(organisation.payment_terms.bills.type, "OFFOLLOWINGMONTH")
48
+ assert_equal(organisation.payment_terms.sales.day, "2")
49
+ assert_equal(organisation.payment_terms.sales.type, "OFFOLLOWINGMONTH")
50
+ end
51
+ end
38
52
  end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class PaymentServiceTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def setup
7
+ @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET)
8
+ end
9
+
10
+ context "response parsing" do
11
+ it "parses default attributes" do
12
+ @instance = Xeroizer::Record::PaymentServiceModel.new(nil, "PaymentService")
13
+
14
+ some_xml = get_record_xml("payment_service")
15
+
16
+ result = @instance.parse_response(some_xml)
17
+ payment_service = result.response_items.first
18
+
19
+ keys = [:payment_service_id,
20
+ :payment_service_name,
21
+ :payment_service_type,
22
+ :payment_service_url,
23
+ :pay_now_text
24
+ ]
25
+
26
+ assert_equal(payment_service.attributes.keys, keys)
27
+ end
28
+ end
29
+ end
@@ -18,7 +18,7 @@ class RepeatingInvoiceTest < Test::Unit::TestCase
18
18
  repeating_invoice = repeating_invoices.first
19
19
 
20
20
  assert_equal "PowerDirect", repeating_invoice.contact_name
21
- assert_equal BigDecimal.new(90), repeating_invoice.total
21
+ assert_equal BigDecimal(90), repeating_invoice.total
22
22
  assert_equal true, repeating_invoice.accounts_payable?
23
23
 
24
24
  schedule = repeating_invoice.schedule
@@ -104,6 +104,16 @@ class RecordBaseTest < Test::Unit::TestCase
104
104
  end
105
105
  end
106
106
 
107
+ context 'build' do
108
+
109
+ should "raise an undefined method error with useful message" do
110
+ assert_raise_message("undefined method `this_method_does_not_exist=' for #<Xeroizer::Record::Contact >") do
111
+ @client.Contact.build(:this_method_does_not_exist => true)
112
+ end
113
+ end
114
+
115
+ end
116
+
107
117
  context 'saving' do
108
118
  context 'invalid record' do
109
119
  setup do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xeroizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.18.1
4
+ version: 2.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wayne Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-07 00:00:00.000000000 Z
11
+ date: 2019-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rest-client
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: turn
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -297,6 +283,7 @@ files:
297
283
  - lib/xeroizer/models/organisation.rb
298
284
  - lib/xeroizer/models/overpayment.rb
299
285
  - lib/xeroizer/models/payment.rb
286
+ - lib/xeroizer/models/payment_service.rb
300
287
  - lib/xeroizer/models/payment_terms.rb
301
288
  - lib/xeroizer/models/payroll/bank_account.rb
302
289
  - lib/xeroizer/models/payroll/employee.rb
@@ -382,6 +369,7 @@ files:
382
369
  - test/stub_responses/nonce_used
383
370
  - test/stub_responses/organisation.xml
384
371
  - test/stub_responses/organisations.xml
372
+ - test/stub_responses/payment_service.xml
385
373
  - test/stub_responses/payments.xml
386
374
  - test/stub_responses/prepayments.xml
387
375
  - test/stub_responses/rate_limit_exceeded
@@ -611,6 +599,7 @@ files:
611
599
  - test/unit/models/line_item_test.rb
612
600
  - test/unit/models/manual_journal_test.rb
613
601
  - test/unit/models/organisation_test.rb
602
+ - test/unit/models/payment_service_test.rb
614
603
  - test/unit/models/phone_test.rb
615
604
  - test/unit/models/prepayment_test.rb
616
605
  - test/unit/models/repeating_invoice_test.rb
@@ -654,7 +643,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
654
643
  version: '0'
655
644
  requirements: []
656
645
  rubyforge_project:
657
- rubygems_version: 2.6.14
646
+ rubygems_version: 2.5.1
658
647
  signing_key:
659
648
  specification_version: 4
660
649
  summary: Ruby Library for Xero accounting API
@@ -693,6 +682,7 @@ test_files:
693
682
  - test/stub_responses/nonce_used
694
683
  - test/stub_responses/organisation.xml
695
684
  - test/stub_responses/organisations.xml
685
+ - test/stub_responses/payment_service.xml
696
686
  - test/stub_responses/payments.xml
697
687
  - test/stub_responses/prepayments.xml
698
688
  - test/stub_responses/rate_limit_exceeded
@@ -922,6 +912,7 @@ test_files:
922
912
  - test/unit/models/line_item_test.rb
923
913
  - test/unit/models/manual_journal_test.rb
924
914
  - test/unit/models/organisation_test.rb
915
+ - test/unit/models/payment_service_test.rb
925
916
  - test/unit/models/phone_test.rb
926
917
  - test/unit/models/prepayment_test.rb
927
918
  - test/unit/models/repeating_invoice_test.rb