xeroizer 2.17.1 → 2.18.1
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.
- checksums.yaml +4 -4
- data/README.md +60 -3
- data/lib/xeroizer.rb +1 -0
- data/lib/xeroizer/exceptions.rb +2 -0
- data/lib/xeroizer/generic_application.rb +1 -0
- data/lib/xeroizer/http.rb +2 -2
- data/lib/xeroizer/models/contact.rb +2 -2
- data/lib/xeroizer/models/credit_note.rb +1 -0
- data/lib/xeroizer/models/currency.rb +14 -2
- data/lib/xeroizer/models/invoice.rb +3 -0
- data/lib/xeroizer/models/item.rb +2 -1
- data/lib/xeroizer/models/item_purchase_details.rb +1 -1
- data/lib/xeroizer/models/online_invoice.rb +37 -0
- data/lib/xeroizer/record/base.rb +10 -1
- data/lib/xeroizer/record/base_model_http_proxy.rb +33 -17
- data/lib/xeroizer/version.rb +1 -1
- data/test/acceptance/about_creating_bank_transactions_test.rb +12 -2
- data/test/acceptance/about_online_invoice_test.rb +29 -0
- data/test/test_helper.rb +1 -1
- data/test/unit/http_tsl_12_upgrade_test.rb +31 -0
- data/test/unit/models/contact_test.rb +18 -9
- data/test/unit/models/line_item_test.rb +11 -17
- data/test/unit/oauth_test.rb +2 -2
- data/test/unit/record/base_test.rb +58 -0
- data/test/unit/record/parse_params_test.rb +59 -0
- metadata +22 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea62d6d6a33c98a88703fe9f1c6b675fcfa6731e
|
4
|
+
data.tar.gz: cf3504a3fafc2b1a8d65e274b03373a7dd466d75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74c217092e28ee42009e40461d99e4b542a09531cba94cfe22f07544dfc5cf7fe35c10d95691a91ffbc152738d67a07470c043b061aa963688b970095db0011e
|
7
|
+
data.tar.gz: 35af4e22733df6241464e95b2a33b7020fbe5590df8e84292ceabdc3f2b0f830174f69d59693af9e09c77cd318c4132767ac2dbd542f7c8c09e9219a45f72cc3
|
data/README.md
CHANGED
@@ -225,7 +225,7 @@ access_secret = client.access_token.secret
|
|
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).
|
228
|
-
> **`#authorization_expires_at`**: How long this organisation has authorised you to access their data (usually
|
228
|
+
> **`#authorization_expires_at`**: How long this organisation has authorised you to access their data (usually 10 years into the future).
|
229
229
|
|
230
230
|
#### AccessToken Renewal
|
231
231
|
|
@@ -269,7 +269,7 @@ Retrieves list of all records with matching options.
|
|
269
269
|
**Note:** Some records (Invoice, CreditNote) only return summary information for the contact and no line items
|
270
270
|
when returning them this list operation. This library takes care of automatically retrieving the
|
271
271
|
contact and line items from Xero on first access however, this first access has a large performance penalty
|
272
|
-
and will count as an extra query towards your
|
272
|
+
and will count as an extra query towards your 5,000/day and 60/minute request per organisation limit.
|
273
273
|
|
274
274
|
Valid options are:
|
275
275
|
|
@@ -460,7 +460,7 @@ minimum validation requirements for each of the record types.
|
|
460
460
|
|
461
461
|
### Bulk Creates & Updates
|
462
462
|
|
463
|
-
Xero has a hard daily limit on the number of API requests you can make (currently
|
463
|
+
Xero has a hard daily limit on the number of API requests you can make (currently 5,000 requests
|
464
464
|
per account per day). To save on requests, you can batch creates and updates into a single PUT or
|
465
465
|
POST call, like so:
|
466
466
|
|
@@ -512,6 +512,43 @@ contact.errors_for(:name) # will contain ["can't be blank"]
|
|
512
512
|
If something goes really wrong and the particular validation isn't handled by the internal
|
513
513
|
validators then the library may raise a `Xeroizer::ApiException`.
|
514
514
|
|
515
|
+
Example Use Cases
|
516
|
+
-------
|
517
|
+
|
518
|
+
Creating & Paying an invoice:
|
519
|
+
|
520
|
+
```ruby
|
521
|
+
contact = xero.Contact.first
|
522
|
+
|
523
|
+
# Build the Invoice, add a LineItem and save it
|
524
|
+
invoice = xero.Invoice.build(:type => "ACCREC", :contact => contact, :date => DateTime.new(2017,10,19), :due_date => DateTime.new(2017,11,19))
|
525
|
+
|
526
|
+
invoice.add_line_item(:description => 'test', :unit_amount => '200.00', :quantity => '1', :account_code => '200')
|
527
|
+
|
528
|
+
invoice.save
|
529
|
+
|
530
|
+
# An invoice created without a status will default to 'DRAFT'
|
531
|
+
invoice.approved?
|
532
|
+
|
533
|
+
# Payments can only be created against 'AUTHROISED' invoices
|
534
|
+
invoice.approve!
|
535
|
+
|
536
|
+
# Find the first bank account
|
537
|
+
bank_account = xero.Account.first(:where => {:type => 'BANK'})
|
538
|
+
|
539
|
+
# Create & save the payment
|
540
|
+
payment = xero.Payment.build(:invoice => invoice, :account => bank_account, :amount => '220.00')
|
541
|
+
payment.save
|
542
|
+
|
543
|
+
# Reload the invoice from the Xero API
|
544
|
+
invoice = xero.Invoice.find(invoice.id)
|
545
|
+
|
546
|
+
# Invoice status is now "PAID" & Payment details have been returned as well
|
547
|
+
invoice.status
|
548
|
+
invoice.payments.first
|
549
|
+
invoice.payments.first.date
|
550
|
+
```
|
551
|
+
|
515
552
|
Reports
|
516
553
|
-------
|
517
554
|
|
@@ -646,6 +683,26 @@ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
|
|
646
683
|
|
647
684
|
This option adds the unitdp=4 query string parameter to all requests for models with line items - invoices, credit notes, bank transactions and receipts.
|
648
685
|
|
686
|
+
Tests
|
687
|
+
-----
|
688
|
+
|
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).
|
690
|
+
|
691
|
+
Once you have created your Private App, set these environment variables:
|
692
|
+
```
|
693
|
+
EXPORT CONSUMER_KEY="your private app's consumer key"
|
694
|
+
EXPORT CONSUMER_SECRET="your private app's consumer secret"
|
695
|
+
EXPORT PRIVATE_KEY_PATH="the path to your private app's private key"
|
696
|
+
```
|
697
|
+
|
698
|
+
PRIVATE_KEY_PATH is the path to the private key for your Private App (you uploaded the Public Key when you created the Private App)
|
699
|
+
|
700
|
+
Then run the tests
|
701
|
+
```
|
702
|
+
rake test
|
703
|
+
```
|
704
|
+
|
705
|
+
|
649
706
|
### Contributors
|
650
707
|
Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
|
651
708
|
and Nik Wakelin and portions of the networking and authentication code are based completely off
|
data/lib/xeroizer.rb
CHANGED
@@ -44,6 +44,7 @@ require 'xeroizer/models/employee'
|
|
44
44
|
require 'xeroizer/models/expense_claim'
|
45
45
|
require 'xeroizer/models/invoice'
|
46
46
|
require 'xeroizer/models/invoice_reminder'
|
47
|
+
require 'xeroizer/models/online_invoice'
|
47
48
|
require 'xeroizer/models/item'
|
48
49
|
require 'xeroizer/models/item_purchase_details'
|
49
50
|
require 'xeroizer/models/item_sales_details'
|
data/lib/xeroizer/exceptions.rb
CHANGED
data/lib/xeroizer/http.rb
CHANGED
@@ -223,9 +223,9 @@ module Xeroizer
|
|
223
223
|
|
224
224
|
# unitdp query string parameter to be added to request params
|
225
225
|
# when the application option has been set and the model has line items
|
226
|
-
#
|
226
|
+
# https://developer.xero.com/documentation/api-guides/rounding-in-xero#unitamount
|
227
227
|
def unitdp_param(request_url)
|
228
|
-
models = [/Invoices/, /CreditNotes/, /BankTransactions/, /Receipts/]
|
228
|
+
models = [/Invoices/, /CreditNotes/, /BankTransactions/, /Receipts/, /Items/, /Overpayments/, /Prepayments/]
|
229
229
|
self.unitdp == 4 && models.any?{ |m| request_url =~ m } ? {:unitdp => 4} : {}
|
230
230
|
end
|
231
231
|
|
@@ -53,7 +53,7 @@ module Xeroizer
|
|
53
53
|
|
54
54
|
has_many :addresses, :list_complete => true
|
55
55
|
has_many :phones, :list_complete => true
|
56
|
-
has_many :contact_groups
|
56
|
+
has_many :contact_groups, :list_complete => true
|
57
57
|
has_many :contact_persons, :internal_name => :contact_people
|
58
58
|
|
59
59
|
has_many :sales_tracking_categories, :model_name => 'ContactSalesTrackingCategory'
|
@@ -63,7 +63,7 @@ module Xeroizer
|
|
63
63
|
has_one :batch_payments, :model_name => 'BatchPayments', :list_complete => true
|
64
64
|
has_one :payment_terms, :model_name => 'PaymentTerms', :list_complete => true
|
65
65
|
|
66
|
-
validates_presence_of :name, :unless => Proc.new { | contact | contact.contact_id.present?}
|
66
|
+
validates_presence_of :name, :unless => Proc.new { | contact | contact.contact_id.present? || contact.contact_number.present? }
|
67
67
|
validates_inclusion_of :contact_status, :in => CONTACT_STATUS.keys, :allow_blanks => true
|
68
68
|
validates_associated :addresses, allow_blanks: true
|
69
69
|
|
@@ -3,14 +3,26 @@ module Xeroizer
|
|
3
3
|
|
4
4
|
class CurrencyModel < BaseModel
|
5
5
|
|
6
|
-
set_permissions :read
|
6
|
+
set_permissions :read, :write
|
7
|
+
|
8
|
+
# Currencies can only be created (added), no update or delete is possible
|
9
|
+
def create_method
|
10
|
+
:http_put
|
11
|
+
end
|
7
12
|
|
8
13
|
end
|
9
14
|
|
10
15
|
class Currency < Base
|
16
|
+
|
17
|
+
# Currency does not have an ID
|
18
|
+
# This method overrides the base model to always treat a Currency as new (so it can be saved)
|
19
|
+
# Attempting to update a currency will result in a validation error.
|
20
|
+
def new_record?
|
21
|
+
true
|
22
|
+
end
|
11
23
|
|
12
24
|
string :code
|
13
|
-
string :description
|
25
|
+
string :description # read only
|
14
26
|
|
15
27
|
end
|
16
28
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "xeroizer/models/attachment"
|
2
|
+
require "xeroizer/models/online_invoice"
|
2
3
|
|
3
4
|
module Xeroizer
|
4
5
|
module Record
|
@@ -14,6 +15,7 @@ module Xeroizer
|
|
14
15
|
set_permissions :read, :write, :update
|
15
16
|
|
16
17
|
include AttachmentModel::Extensions
|
18
|
+
include OnlineInvoiceModel::Extensions
|
17
19
|
|
18
20
|
public
|
19
21
|
|
@@ -51,6 +53,7 @@ module Xeroizer
|
|
51
53
|
INVOICE_STATUSES = INVOICE_STATUS.keys.sort
|
52
54
|
|
53
55
|
include Attachment::Extensions
|
56
|
+
include OnlineInvoice::Extensions
|
54
57
|
|
55
58
|
set_primary_key :invoice_id
|
56
59
|
set_possible_primary_keys :invoice_id, :invoice_number
|
data/lib/xeroizer/models/item.rb
CHANGED
@@ -18,7 +18,6 @@ module Xeroizer
|
|
18
18
|
string :purchase_description
|
19
19
|
string :name
|
20
20
|
|
21
|
-
decimal :unit_price
|
22
21
|
decimal :total_cost_pool # read only
|
23
22
|
decimal :quantity_on_hand # read only
|
24
23
|
|
@@ -27,6 +26,8 @@ module Xeroizer
|
|
27
26
|
boolean :is_tracked_as_inventory # read only, infered from inventory_asset_account_code, cogs_account_code, is_sold and is_purchased
|
28
27
|
string :inventory_asset_account_code
|
29
28
|
|
29
|
+
datetime_utc :updated_date_utc, api_name: 'UpdatedDateUTC'
|
30
|
+
|
30
31
|
belongs_to :purchase_details, :model_name => 'ItemPurchaseDetails'
|
31
32
|
belongs_to :sales_details, :model_name => 'ItemSalesDetails'
|
32
33
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Xeroizer
|
2
|
+
module Record
|
3
|
+
|
4
|
+
class OnlineInvoiceModel < BaseModel
|
5
|
+
|
6
|
+
module Extensions
|
7
|
+
def online_invoice(id)
|
8
|
+
application.OnlineInvoice.online_invoice(url, id)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
set_permissions :read
|
13
|
+
|
14
|
+
def online_invoice(url, id)
|
15
|
+
response_xml = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}/OnlineInvoice")
|
16
|
+
|
17
|
+
response = parse_response(response_xml)
|
18
|
+
|
19
|
+
response.response_items.first
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class OnlineInvoice < Base
|
25
|
+
|
26
|
+
module Extensions
|
27
|
+
def online_invoice
|
28
|
+
parent.online_invoice(id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
string :online_invoice_url
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/xeroizer/record/base.rb
CHANGED
@@ -105,12 +105,21 @@ module Xeroizer
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def save
|
108
|
-
|
108
|
+
save!
|
109
|
+
true
|
110
|
+
rescue XeroizerError => e
|
111
|
+
log "[ERROR SAVING] (#{__FILE__}:#{__LINE__}) - #{e.message}"
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
def save!
|
116
|
+
raise RecordInvalid unless valid?
|
109
117
|
if new_record?
|
110
118
|
create
|
111
119
|
else
|
112
120
|
update
|
113
121
|
end
|
122
|
+
|
114
123
|
saved!
|
115
124
|
end
|
116
125
|
|
@@ -1,28 +1,34 @@
|
|
1
|
-
require 'xeroizer/application_http_proxy'
|
1
|
+
require 'xeroizer/application_http_proxy'
|
2
2
|
|
3
3
|
module Xeroizer
|
4
4
|
module Record
|
5
5
|
module BaseModelHttpProxy
|
6
|
-
|
6
|
+
|
7
7
|
def self.included(base)
|
8
8
|
base.send :include, Xeroizer::ApplicationHttpProxy
|
9
9
|
base.send :include, InstanceMethods
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
module InstanceMethods
|
13
|
-
|
13
|
+
|
14
14
|
protected
|
15
|
-
|
15
|
+
|
16
16
|
# Parse parameters for GET requests.
|
17
17
|
def parse_params(options)
|
18
18
|
params = {}
|
19
19
|
params[:ModifiedAfter] = options[:modified_since] if options[:modified_since]
|
20
20
|
params[:includeArchived] = options[:include_archived] if options[:include_archived]
|
21
21
|
params[:order] = options[:order] if options[:order]
|
22
|
+
params[:createdByMyApp] = options[:createdByMyApp] if options[:createdByMyApp]
|
23
|
+
|
24
|
+
params[:IDs] = filterize(options[:IDs]) if options[:IDs]
|
25
|
+
params[:InvoiceNumbers] = filterize(options[:InvoiceNumbers]) if options[:InvoiceNumbers]
|
26
|
+
params[:ContactIDs] = filterize(options[:ContactIDs]) if options[:ContactIDs]
|
27
|
+
params[:Statuses] = filterize(options[:Statuses]) if options[:Statuses]
|
22
28
|
|
23
29
|
if options[:where]
|
24
30
|
params[:where] = case options[:where]
|
25
|
-
when String then options[:where]
|
31
|
+
when String then options[:where]
|
26
32
|
when Hash then parse_where_hash(options[:where])
|
27
33
|
end
|
28
34
|
end
|
@@ -30,7 +36,7 @@ module Xeroizer
|
|
30
36
|
params[:page] = options[:page] if options[:page]
|
31
37
|
params
|
32
38
|
end
|
33
|
-
|
39
|
+
|
34
40
|
# Parse the :where part of the options for GET parameters and construct a valid
|
35
41
|
# .Net version of the criteria to pass to Xero.
|
36
42
|
#
|
@@ -46,14 +52,14 @@ module Xeroizer
|
|
46
52
|
(attribute_name, expression) = extract_expression_from_attribute_name(key)
|
47
53
|
(_, field) = model_class.fields.find { | k, v | v[:internal_name] == attribute_name }
|
48
54
|
if field
|
49
|
-
conditions << where_condition_part(field, expression, value)
|
55
|
+
conditions << where_condition_part(field, expression, value)
|
50
56
|
else
|
51
57
|
raise InvalidAttributeInWhere.new(model_name, attribute_name)
|
52
58
|
end
|
53
59
|
end
|
54
60
|
conditions.map { | (attr, expression, value) | "#{attr}#{expression}#{value}"}.join('&&')
|
55
61
|
end
|
56
|
-
|
62
|
+
|
57
63
|
# Extract the attribute name and expression from the attribute.
|
58
64
|
#
|
59
65
|
# @return [Array] containing [actual_attribute_name, expression]
|
@@ -64,37 +70,37 @@ module Xeroizer
|
|
64
70
|
key.to_s.gsub(/(_is_not|\<\>)$/, '').to_sym,
|
65
71
|
'<>'
|
66
72
|
]
|
67
|
-
|
73
|
+
|
68
74
|
when /(_is_greater_than|\>)$/
|
69
75
|
[
|
70
76
|
key.to_s.gsub(/(_is_greater_than|\>)$/, '').to_sym,
|
71
77
|
'>'
|
72
78
|
]
|
73
|
-
|
79
|
+
|
74
80
|
when /(_is_greater_than_or_equal_to|\>\=)$/
|
75
81
|
[
|
76
82
|
key.to_s.gsub(/(_is_greater_than_or_equal_to|\>\=)$/, '').to_sym,
|
77
83
|
'>='
|
78
84
|
]
|
79
|
-
|
85
|
+
|
80
86
|
when /(_is_less_than|\<)$/
|
81
87
|
[
|
82
88
|
key.to_s.gsub(/(_is_less_than|\<)$/, '').to_sym,
|
83
89
|
'<'
|
84
90
|
]
|
85
|
-
|
91
|
+
|
86
92
|
when /(_is_less_than_or_equal_to|\<\=)$/
|
87
93
|
[
|
88
94
|
key.to_s.gsub(/(_is_less_than_or_equal_to|\<\=)$/, '').to_sym,
|
89
95
|
'<='
|
90
96
|
]
|
91
|
-
|
97
|
+
|
92
98
|
else
|
93
99
|
[key, '==']
|
94
|
-
|
100
|
+
|
95
101
|
end
|
96
102
|
end
|
97
|
-
|
103
|
+
|
98
104
|
# Creates a condition part array containing the:
|
99
105
|
# * Field's API name
|
100
106
|
# * Expression
|
@@ -114,8 +120,18 @@ module Xeroizer
|
|
114
120
|
end
|
115
121
|
end
|
116
122
|
|
123
|
+
private
|
124
|
+
|
125
|
+
# Filtering params expect a comma separated list of strings
|
126
|
+
def filterize(values)
|
127
|
+
case values
|
128
|
+
when String then values
|
129
|
+
when Array then values.join(',')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
117
133
|
end
|
118
|
-
|
134
|
+
|
119
135
|
end
|
120
136
|
end
|
121
137
|
end
|
data/lib/xeroizer/version.rb
CHANGED
@@ -149,14 +149,24 @@ class AboutCreatingBankTransactions < Test::Unit::TestCase
|
|
149
149
|
}]
|
150
150
|
end
|
151
151
|
|
152
|
-
it "fails with ApiException when you try and create a new bank account with missing account type" do
|
152
|
+
it "fails with ApiException when you try and create a new bank account with missing account type with save! method" do
|
153
153
|
new_account = client.Account.build(
|
154
154
|
:name => "Example bank account",
|
155
155
|
:code => "ACC-001"
|
156
156
|
)
|
157
157
|
|
158
158
|
assert_raise Xeroizer::ApiException do
|
159
|
-
new_account.save
|
159
|
+
new_account.save!
|
160
160
|
end
|
161
161
|
end
|
162
|
+
|
163
|
+
it "returns false when you try and create a new bank account with a missing account type with save method" do
|
164
|
+
new_account = client.Account.build(
|
165
|
+
:name => "Example bank account",
|
166
|
+
:code => "ACC-001"
|
167
|
+
)
|
168
|
+
|
169
|
+
assert new_account.save == false, "Account save method expected to return false"
|
170
|
+
|
171
|
+
end
|
162
172
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "acceptance_test"
|
3
|
+
|
4
|
+
class AboutGetOnlineInvoiceUrl < Test::Unit::TestCase
|
5
|
+
include AcceptanceTest
|
6
|
+
|
7
|
+
let :client do
|
8
|
+
Xeroizer::PrivateApplication.new(@consumer_key, @consumer_secret, @key_file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
@invoice = client.Invoice.all(:where => 'Type=="ACCREC"').first
|
14
|
+
@invoice_acc_pay = client.Invoice.all(:where => 'Type=="ACCPAY"').first
|
15
|
+
end
|
16
|
+
|
17
|
+
can "Request OnlineInvoice of an AccRec invoice" do
|
18
|
+
@online_invoice = @invoice.online_invoice
|
19
|
+
|
20
|
+
assert @online_invoice.online_invoice_url, "online_invoice_url not found"
|
21
|
+
assert @online_invoice.online_invoice_url.start_with?('https://in.xero.com/'), "online_invoice_url returned in unexpected format"
|
22
|
+
end
|
23
|
+
|
24
|
+
can "Not request OnlineInvoice of an AccPay invoice" do
|
25
|
+
assert_raise do
|
26
|
+
@online_invoice_acc_pay = @invoice_acc_pay.online_invoice
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -26,7 +26,7 @@ module TestHelper
|
|
26
26
|
|
27
27
|
CONSUMER_KEY = ENV["CONSUMER_KEY"] || "fake_key" unless defined?(CONSUMER_KEY)
|
28
28
|
CONSUMER_SECRET = ENV["CONSUMER_SECRET"] || "fake_secret" unless defined?(CONSUMER_SECRET)
|
29
|
-
|
29
|
+
PRIVATE_KEY_PATH = ENV["PRIVATE_KEY_PATH"] || "fake_key" unless defined?(PRIVATE_KEY_PATH)
|
30
30
|
|
31
31
|
# Helper constant for checking regex
|
32
32
|
GUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/ unless defined?(GUID_REGEX)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class HttpTest < Test::Unit::TestCase
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@application = Xeroizer::PrivateApplication.new(CONSUMER_KEY, CONSUMER_SECRET, PRIVATE_KEY_PATH, {:xero_url => 'https://api-tls.xero.com/', :site => 'https://api-tls.xero.com/'})
|
9
|
+
@applicationExternal = Xeroizer::PrivateApplication.new(CONSUMER_KEY, CONSUMER_SECRET, PRIVATE_KEY_PATH, {:xero_url => 'https://www.howsmyssl.com', :site => 'https://www.howsmyssl.com'})
|
10
|
+
end
|
11
|
+
|
12
|
+
context "Connect to Xero TLS 1.2 Test endpoint" do
|
13
|
+
should "receive a 301 response (endpoint redirects on success)" do
|
14
|
+
begin
|
15
|
+
@application.http_get(@application.client, "https://api-tls.xero.com/")
|
16
|
+
rescue Xeroizer::Http::BadResponse => e
|
17
|
+
assert_equal "Unknown response code: 301", e.message, "Unexpected Response Code (Xero): Check TLS 1.2 is set"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Connect to external TLS 1.2 Test endpoint" do
|
24
|
+
should "respond with tls version 1.2" do
|
25
|
+
response = @applicationExternal.http_get(@applicationExternal.client, "https://www.howsmyssl.com/a/check")
|
26
|
+
jsonResponse = JSON.parse(response)
|
27
|
+
assert_equal "TLS 1.2", jsonResponse['tls_version'], "Unexpected Response (External): Check TLS 1.2 is set"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -21,6 +21,24 @@ class ContactTest < Test::Unit::TestCase
|
|
21
21
|
assert_equal(0, contact.errors.size)
|
22
22
|
end
|
23
23
|
|
24
|
+
should "be able to have no name if it has a contact_id" do
|
25
|
+
contact = @client.Contact.build
|
26
|
+
|
27
|
+
assert_equal(false, contact.valid?)
|
28
|
+
contact.contact_id = "1-2-3"
|
29
|
+
assert_equal(true, contact.valid?)
|
30
|
+
assert_equal(0, contact.errors.size)
|
31
|
+
end
|
32
|
+
|
33
|
+
should "be able to have no name if it has a contact_number" do
|
34
|
+
contact = @client.Contact.build
|
35
|
+
|
36
|
+
assert_equal(false, contact.valid?)
|
37
|
+
contact.contact_number = "abc123"
|
38
|
+
assert_equal(true, contact.valid?)
|
39
|
+
assert_equal(0, contact.errors.size)
|
40
|
+
end
|
41
|
+
|
24
42
|
should "not allow invalid addresses" do
|
25
43
|
contact = @client.Contact.build(name: "SOMETHING")
|
26
44
|
address = contact.add_address(:type => "INVALID_TYPE")
|
@@ -66,15 +84,6 @@ class ContactTest < Test::Unit::TestCase
|
|
66
84
|
assert_equal(contact.attributes.keys, keys)
|
67
85
|
end
|
68
86
|
|
69
|
-
should "be able to have no name if has a contact_id" do
|
70
|
-
contact = @client.Contact.build
|
71
|
-
|
72
|
-
assert_equal(false, contact.valid?)
|
73
|
-
contact.contact_id = "1-2-3"
|
74
|
-
assert_equal(true, contact.valid?)
|
75
|
-
assert_equal(0, contact.errors.size)
|
76
|
-
end
|
77
|
-
|
78
87
|
it "parses extra attributes when present" do
|
79
88
|
@instance = Xeroizer::Record::ContactModel.new(nil, "Contact")
|
80
89
|
some_xml = get_record_xml("contact_with_details")
|
@@ -24,11 +24,8 @@ class LineItemTest < Test::Unit::TestCase
|
|
24
24
|
|
25
25
|
line_item.quantity = 1
|
26
26
|
line_item.unit_amount = BigDecimal("1337.00")
|
27
|
-
line_item.tax_amount = BigDecimal("0.15")
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
assert_equal expected.to_s, line_item.line_amount.to_s,
|
28
|
+
assert_equal "1337.0", line_item.line_amount.to_s,
|
32
29
|
"expected line_amount to equal unit_amount times quantity"
|
33
30
|
end
|
34
31
|
|
@@ -36,12 +33,9 @@ class LineItemTest < Test::Unit::TestCase
|
|
36
33
|
line_item = LineItem.new(nil)
|
37
34
|
line_item.quantity = 1
|
38
35
|
line_item.unit_amount = BigDecimal("1337.00")
|
39
|
-
line_item.
|
40
|
-
line_item.discount_rate = BigDecimal("10.0")
|
36
|
+
line_item.discount_rate = BigDecimal("12.34")
|
41
37
|
|
42
|
-
|
43
|
-
expected = BigDecimal(((line_item.quantity * line_item.unit_amount) * discount).to_s).round(2)
|
44
|
-
assert_equal expected.to_s, line_item.line_amount.to_s,
|
38
|
+
assert_equal "1172.01", line_item.line_amount.to_s,
|
45
39
|
"expected line_amount to equal unit_amount times quantity minus the discount"
|
46
40
|
end
|
47
41
|
|
@@ -50,20 +44,19 @@ class LineItemTest < Test::Unit::TestCase
|
|
50
44
|
|
51
45
|
line_item.quantity = nil
|
52
46
|
line_item.unit_amount = BigDecimal("1.00")
|
53
|
-
line_item.tax_amount = BigDecimal("0.15")
|
54
47
|
|
55
|
-
assert_equal "0.0", line_item.line_amount.to_s,
|
48
|
+
assert_equal "0.0", line_item.line_amount.to_s,
|
49
|
+
"expected line amount to be zero when quantity is nil"
|
56
50
|
|
57
51
|
line_item.quantity = 0
|
58
|
-
assert_equal "0.0", line_item.line_amount.to_s,
|
52
|
+
assert_equal "0.0", line_item.line_amount.to_s,
|
53
|
+
"expected line amount to be zero when quantity is zero"
|
59
54
|
end
|
60
55
|
|
61
56
|
it "is not possible to set unit_amount to zero" do
|
62
57
|
line_item = LineItem.new(nil)
|
63
58
|
|
64
|
-
line_item.quantity = 1
|
65
59
|
line_item.unit_amount = nil
|
66
|
-
line_item.tax_amount = BigDecimal("0.15")
|
67
60
|
|
68
61
|
assert_equal 0.0, line_item.unit_amount,
|
69
62
|
"Expected setting unit_amount to nil to be ignored, i.e., it should remain zero"
|
@@ -74,11 +67,12 @@ class LineItemTest < Test::Unit::TestCase
|
|
74
67
|
|
75
68
|
line_item.quantity = 1
|
76
69
|
line_item.unit_amount = nil
|
77
|
-
line_item.tax_amount = BigDecimal("0.15")
|
78
70
|
|
79
|
-
assert_equal "0.0", line_item.line_amount.to_s,
|
71
|
+
assert_equal "0.0", line_item.line_amount.to_s,
|
72
|
+
"expected line amount to be zero when unit_amount is nil"
|
80
73
|
|
81
74
|
line_item.unit_amount = BigDecimal("0.00")
|
82
|
-
assert_equal "0.0", line_item.line_amount.to_s,
|
75
|
+
assert_equal "0.0", line_item.line_amount.to_s,
|
76
|
+
"expected line amount to be zero when unit_amount is zero"
|
83
77
|
end
|
84
78
|
end
|
data/test/unit/oauth_test.rb
CHANGED
@@ -99,7 +99,7 @@ class OAuthTest < Test::Unit::TestCase
|
|
99
99
|
|
100
100
|
assert_raises Xeroizer::ApiException do
|
101
101
|
contact = @client.Contact.build(:name => 'Test Contact')
|
102
|
-
contact.save
|
102
|
+
contact.save!
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -109,7 +109,7 @@ class OAuthTest < Test::Unit::TestCase
|
|
109
109
|
|
110
110
|
assert_raises Xeroizer::UnparseableResponse do
|
111
111
|
contact = @client.Contact.build(:name => 'Test Contact')
|
112
|
-
contact.save
|
112
|
+
contact.save!
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -103,4 +103,62 @@ class RecordBaseTest < Test::Unit::TestCase
|
|
103
103
|
an_example_instance.save
|
104
104
|
end
|
105
105
|
end
|
106
|
+
|
107
|
+
context 'saving' do
|
108
|
+
context 'invalid record' do
|
109
|
+
setup do
|
110
|
+
@contact.stubs(:valid?).returns(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
must 'raise an exception saving with #save!' do
|
114
|
+
assert_raise(Xeroizer::RecordInvalid) do
|
115
|
+
@contact.save!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
must 'return false saving with #save' do
|
120
|
+
assert_equal(false, @contact.save)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'api error received' do
|
125
|
+
setup do
|
126
|
+
response = get_file_as_string('api_exception.xml')
|
127
|
+
doc = Nokogiri::XML(response)
|
128
|
+
exception = Xeroizer::ApiException.new(doc.root.xpath("Type").text,
|
129
|
+
doc.root.xpath("Message").text,
|
130
|
+
response,
|
131
|
+
doc,
|
132
|
+
'<FakeRequest />')
|
133
|
+
|
134
|
+
@contact.stubs(:valid?).returns(true)
|
135
|
+
@contact.stubs(:create).raises(exception)
|
136
|
+
@contact.stubs(:update).raises(exception)
|
137
|
+
end
|
138
|
+
|
139
|
+
must 'raise an exception creating records with #save!' do
|
140
|
+
@contact.stubs(:new_record?).returns(true)
|
141
|
+
assert_raise(Xeroizer::ApiException) do
|
142
|
+
@contact.save!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
must 'raise an exception updating records with #save!' do
|
147
|
+
@contact.stubs(:new_record?).returns(false)
|
148
|
+
assert_raise(Xeroizer::ApiException) do
|
149
|
+
@contact.save!
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
must 'return false creating records with #save' do
|
154
|
+
@contact.stubs(:new_record?).returns(true)
|
155
|
+
assert_equal(false, @contact.save)
|
156
|
+
end
|
157
|
+
|
158
|
+
must 'return false updating records with #save' do
|
159
|
+
@contact.stubs(:new_record?).returns(false)
|
160
|
+
assert_equal(false, @contact.save)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
106
164
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Xeroizer
|
4
|
+
module Record
|
5
|
+
|
6
|
+
class ParseParamTestModel < BaseModel
|
7
|
+
end
|
8
|
+
|
9
|
+
class ParseParamTest < Base
|
10
|
+
|
11
|
+
set_primary_key :primary_key_id
|
12
|
+
|
13
|
+
guid :primary_key_id
|
14
|
+
string :string1
|
15
|
+
boolean :boolean1
|
16
|
+
integer :integer1
|
17
|
+
decimal :decimal1
|
18
|
+
date :date1
|
19
|
+
datetime :datetime1
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ParseParamsTest < Test::Unit::TestCase
|
27
|
+
include TestHelper
|
28
|
+
|
29
|
+
def setup
|
30
|
+
@client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET)
|
31
|
+
@model = Xeroizer::Record::ParseParamTestModel.new(@client, "ParseParamTest")
|
32
|
+
end
|
33
|
+
|
34
|
+
context "should return valid and filtered params" do
|
35
|
+
should "filter unsupported keys" do
|
36
|
+
params = @model.send(:parse_params, {
|
37
|
+
:should_be_filtered_out => 'should be filtered',
|
38
|
+
:modified_since => Date.parse("2010-01-05"),
|
39
|
+
:include_archived => true,
|
40
|
+
:order => :order,
|
41
|
+
:where => 'where string',
|
42
|
+
:IDs => ['29ed7958-0466-486d-bf57-3fd966ea37d7', 'd561892a-9023-498c-a28d-c626ed3940d8'],
|
43
|
+
:InvoiceNumbers => 'INV-0034,INV-0035,INV-0036,INV-0037',
|
44
|
+
:ContactIDs => 'b919a496-1a1c-4fc6-b6ef-8c561e0dd8c2,8289cca4-90a9-466b-95f3-f0adf351b2ac',
|
45
|
+
:Statuses => ['DRAFT', nil, 'SUBMITTED'],
|
46
|
+
:offset => 100,
|
47
|
+
:page => 2
|
48
|
+
})
|
49
|
+
|
50
|
+
params.assert_valid_keys(:ModifiedAfter, :includeArchived, :order, :where,
|
51
|
+
:IDs, :InvoiceNumbers, :ContactIDs, :Statuses,
|
52
|
+
:offset, :page)
|
53
|
+
assert_equal(params[:IDs], '29ed7958-0466-486d-bf57-3fd966ea37d7,d561892a-9023-498c-a28d-c626ed3940d8')
|
54
|
+
assert_equal(params[:InvoiceNumbers], 'INV-0034,INV-0035,INV-0036,INV-0037')
|
55
|
+
assert_equal(params[:ContactIDs], 'b919a496-1a1c-4fc6-b6ef-8c561e0dd8c2,8289cca4-90a9-466b-95f3-f0adf351b2ac')
|
56
|
+
assert_equal(params[:Statuses], 'DRAFT,,SUBMITTED')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
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.
|
4
|
+
version: 2.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wayne Robinson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -234,8 +234,11 @@ dependencies:
|
|
234
234
|
- - ">="
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: '0'
|
237
|
-
description: Ruby library for the Xero accounting
|
238
|
-
|
237
|
+
description: Ruby library for the Xero accounting API. Originally developed by Wayne
|
238
|
+
Robinson, now maintained by the Xero API Team & Xero/Ruby developer community.
|
239
|
+
email:
|
240
|
+
- wayne.robinson@gmail.com
|
241
|
+
- api@xero.com
|
239
242
|
executables: []
|
240
243
|
extensions: []
|
241
244
|
extra_rdoc_files: []
|
@@ -289,6 +292,7 @@ files:
|
|
289
292
|
- lib/xeroizer/models/line_item_sum.rb
|
290
293
|
- lib/xeroizer/models/manual_journal.rb
|
291
294
|
- lib/xeroizer/models/manual_journal_line.rb
|
295
|
+
- lib/xeroizer/models/online_invoice.rb
|
292
296
|
- lib/xeroizer/models/option.rb
|
293
297
|
- lib/xeroizer/models/organisation.rb
|
294
298
|
- lib/xeroizer/models/overpayment.rb
|
@@ -347,6 +351,7 @@ files:
|
|
347
351
|
- test/acceptance/about_creating_bank_transactions_test.rb
|
348
352
|
- test/acceptance/about_creating_prepayment_test.rb
|
349
353
|
- test/acceptance/about_fetching_bank_transactions_test.rb
|
354
|
+
- test/acceptance/about_online_invoice_test.rb
|
350
355
|
- test/acceptance/acceptance_test.rb
|
351
356
|
- test/acceptance/bank_transaction_reference_data.rb
|
352
357
|
- test/acceptance/bank_transfer_test.rb
|
@@ -591,6 +596,7 @@ files:
|
|
591
596
|
- test/test_helper.rb
|
592
597
|
- test/unit/generic_application_test.rb
|
593
598
|
- test/unit/http_test.rb
|
599
|
+
- test/unit/http_tsl_12_upgrade_test.rb
|
594
600
|
- test/unit/models/address_test.rb
|
595
601
|
- test/unit/models/bank_transaction_model_parsing_test.rb
|
596
602
|
- test/unit/models/bank_transaction_test.rb
|
@@ -616,6 +622,7 @@ files:
|
|
616
622
|
- test/unit/record/base_test.rb
|
617
623
|
- test/unit/record/block_validator_test.rb
|
618
624
|
- test/unit/record/model_definition_test.rb
|
625
|
+
- test/unit/record/parse_params_test.rb
|
619
626
|
- test/unit/record/parse_where_hash_test.rb
|
620
627
|
- test/unit/record/record_association_test.rb
|
621
628
|
- test/unit/record/validators_test.rb
|
@@ -625,7 +632,12 @@ files:
|
|
625
632
|
homepage: http://github.com/waynerobinson/xeroizer
|
626
633
|
licenses:
|
627
634
|
- MIT
|
628
|
-
metadata:
|
635
|
+
metadata:
|
636
|
+
bug_tracker_uri: https://github.com/waynerobinson/xeroizer/issues
|
637
|
+
changelog_uri: https://github.com/waynerobinson/xeroizer/releases
|
638
|
+
source_code_uri: https://github.com/waynerobinson/xeroizer
|
639
|
+
documentation_uri: https://developer.xero.com/documentation/
|
640
|
+
mailing_list_uri: https://developer.xero.com/subscribe-to-the-xero-api-developer-mailing-list
|
629
641
|
post_install_message:
|
630
642
|
rdoc_options: []
|
631
643
|
require_paths:
|
@@ -642,14 +654,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
642
654
|
version: '0'
|
643
655
|
requirements: []
|
644
656
|
rubyforge_project:
|
645
|
-
rubygems_version: 2.6.
|
657
|
+
rubygems_version: 2.6.14
|
646
658
|
signing_key:
|
647
659
|
specification_version: 4
|
648
|
-
summary: Xero
|
660
|
+
summary: Ruby Library for Xero accounting API
|
649
661
|
test_files:
|
650
662
|
- test/acceptance/about_creating_bank_transactions_test.rb
|
651
663
|
- test/acceptance/about_creating_prepayment_test.rb
|
652
664
|
- test/acceptance/about_fetching_bank_transactions_test.rb
|
665
|
+
- test/acceptance/about_online_invoice_test.rb
|
653
666
|
- test/acceptance/acceptance_test.rb
|
654
667
|
- test/acceptance/bank_transaction_reference_data.rb
|
655
668
|
- test/acceptance/bank_transfer_test.rb
|
@@ -894,6 +907,7 @@ test_files:
|
|
894
907
|
- test/test_helper.rb
|
895
908
|
- test/unit/generic_application_test.rb
|
896
909
|
- test/unit/http_test.rb
|
910
|
+
- test/unit/http_tsl_12_upgrade_test.rb
|
897
911
|
- test/unit/models/address_test.rb
|
898
912
|
- test/unit/models/bank_transaction_model_parsing_test.rb
|
899
913
|
- test/unit/models/bank_transaction_test.rb
|
@@ -919,6 +933,7 @@ test_files:
|
|
919
933
|
- test/unit/record/base_test.rb
|
920
934
|
- test/unit/record/block_validator_test.rb
|
921
935
|
- test/unit/record/model_definition_test.rb
|
936
|
+
- test/unit/record/parse_params_test.rb
|
922
937
|
- test/unit/record/parse_where_hash_test.rb
|
923
938
|
- test/unit/record/record_association_test.rb
|
924
939
|
- test/unit/record/validators_test.rb
|