xeroizer-float 2.15.3.13 → 2.15.3.14
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/LICENSE.txt +22 -1
- data/README.md +29 -2
- data/VERSION +1 -1
- data/lib/xeroizer/exceptions.rb +17 -2
- data/lib/xeroizer/http.rb +19 -4
- data/lib/xeroizer/models/bank_transaction.rb +11 -0
- data/lib/xeroizer/models/credit_note.rb +9 -0
- data/lib/xeroizer/models/invoice.rb +13 -1
- data/lib/xeroizer/models/manual_journal.rb +1 -1
- data/lib/xeroizer/oauth.rb +24 -5
- data/lib/xeroizer/record/base.rb +25 -1
- data/lib/xeroizer/record/base_model.rb +79 -9
- data/lib/xeroizer/record/model_definition_helper.rb +2 -0
- data/lib/xeroizer/record/xml_helper.rb +3 -2
- data/lib/xeroizer/response.rb +14 -0
- data/test/acceptance/acceptance_test.rb +1 -1
- data/test/acceptance/bulk_operations_test.rb +48 -0
- data/test/stub_responses/bank_transactions.xml +52 -0
- data/test/stub_responses/credit_notes.xml +3 -2
- data/test/stub_responses/invoices.xml +3 -2
- data/test/stub_responses/records/bank_transaction-8644472b-ea4e-4e5a-9987-f79250ece35e.xml +65 -0
- data/test/stub_responses/records/bank_transaction-ee4c4d5a-437d-4369-b152-848bd932f431.xml +72 -0
- data/test/stub_responses/refresh_responses.rb +3 -3
- data/test/unit/models/bank_transaction_test.rb +28 -0
- data/test/unit/models/credit_note_test.rb +9 -0
- data/test/unit/models/invoice_test.rb +7 -0
- metadata +22 -2
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2013 Wayne Robinson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -18,3 +18,24 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
-----------------------------------------------------------------------
|
23
|
+
|
24
|
+
Part of the HTTP communication and OAuth authentication library were
|
25
|
+
provided by the https://github.com/tlconnor/xero_gateway gem created by
|
26
|
+
Tim Connor (github.com/tlconnor) and Nik Wakelin (github.com/nikz).
|
27
|
+
The license for this Gem is included below:
|
28
|
+
|
29
|
+
Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
|
30
|
+
|
31
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
32
|
+
purpose with or without fee is hereby granted, provided that the above
|
33
|
+
copyright notice and this permission notice appear in all copies.
|
34
|
+
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
36
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
37
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
38
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
39
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
40
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
41
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Xeroizer API Library ![Project status](http://stillmaintained.com/waynerobinson/
|
|
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
8
|
**Contributors**: See Contributors section below
|
9
|
-
**Copyright**: 2007-
|
9
|
+
**Copyright**: 2007-2013
|
10
10
|
**License**: MIT License
|
11
11
|
|
12
12
|
Introduction
|
@@ -435,9 +435,30 @@ contact.save
|
|
435
435
|
Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
|
436
436
|
minimum validation requirements for each of the record types.
|
437
437
|
|
438
|
+
### Bulk Creates & Updates
|
439
|
+
|
440
|
+
Xero has a hard daily limit on the number of API requests you can make (currently 1,000 requests
|
441
|
+
per account per day). To save on requests, you can batch creates and updates into a single PUT or
|
442
|
+
POST call, like so:
|
443
|
+
|
444
|
+
```ruby
|
445
|
+
contact1 = xero.Contact.create(some_attributes)
|
446
|
+
xero.Contact.batch_save do
|
447
|
+
contact1.email_address = "foo@bar.com"
|
448
|
+
contact2 = xero.Contact.build(some_other_attributes)
|
449
|
+
contact3 = xero.Contact.build(some_more_attributes)
|
450
|
+
end
|
451
|
+
```
|
452
|
+
|
453
|
+
`batch_save` will issue one PUT request for every 2,000 unsaved records built within its block, and one
|
454
|
+
POST request for evert 2,000 existing records that have been altered within its block. If any of the
|
455
|
+
unsaved records aren't valid, it'll return `false` before sending anything across the wire;
|
456
|
+
otherwise, it returns `true`. `batch_save` takes one optional argument: the number of records to
|
457
|
+
create/update per request. (Defaults to 2,000.)
|
458
|
+
|
438
459
|
### Errors
|
439
460
|
|
440
|
-
If a record doesn't match
|
461
|
+
If a record doesn't match its internal validation requirements, the `#save` method will return
|
441
462
|
`false` and the `#errors` attribute will be populated with what went wrong.
|
442
463
|
|
443
464
|
For example:
|
@@ -527,3 +548,9 @@ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
|
|
527
548
|
YOUR_OAUTH_CONSUMER_SECRET,
|
528
549
|
:rate_limit_sleep => 2)
|
529
550
|
```
|
551
|
+
|
552
|
+
|
553
|
+
### Contributors
|
554
|
+
Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
|
555
|
+
and Nik Wakelin and portions of the networking and authentication code are based completely off
|
556
|
+
this project. Copyright for these components remains held in the name of Tim Connor.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.15.3.
|
1
|
+
2.15.3.14
|
data/lib/xeroizer/exceptions.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
|
+
# Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
1
15
|
module Xeroizer
|
2
16
|
class ApiException < StandardError
|
3
17
|
|
4
|
-
attr_reader :type, :message, :xml, :request_body
|
18
|
+
attr_reader :type, :message, :xml, :parsed_xml, :request_body
|
5
19
|
|
6
|
-
def initialize(type, message, xml, request_body)
|
20
|
+
def initialize(type, message, xml, parsed_xml, request_body)
|
7
21
|
@type = type
|
8
22
|
@message = message
|
9
23
|
@xml = xml
|
24
|
+
@parsed_xml = parsed_xml
|
10
25
|
@request_body = request_body
|
11
26
|
end
|
12
27
|
|
data/lib/xeroizer/http.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
1
15
|
module Xeroizer
|
2
16
|
module Http
|
3
17
|
|
@@ -69,7 +83,7 @@ module Xeroizer
|
|
69
83
|
|
70
84
|
begin
|
71
85
|
attempts += 1
|
72
|
-
logger.info("\n== [#{Time.now.to_s}] XeroGateway Request: #{uri.request_uri} ") if self.logger
|
86
|
+
logger.info("\n== [#{Time.now.to_s}] XeroGateway Request: #{method.to_s.upcase} #{uri.request_uri} ") if self.logger
|
73
87
|
|
74
88
|
response = case method
|
75
89
|
when :get then client.get(uri.request_uri, headers)
|
@@ -133,16 +147,17 @@ module Xeroizer
|
|
133
147
|
|
134
148
|
# XeroGenericApplication API Exceptions *claim* to be UTF-16 encoded, but fail REXML/Iconv parsing...
|
135
149
|
# So let's ignore that :)
|
136
|
-
|
150
|
+
raw_response.gsub! '<?xml version="1.0" encoding="utf-16"?>', ''
|
137
151
|
|
138
152
|
# doc = REXML::Document.new(raw_response, :ignore_whitespace_nodes => :all)
|
139
153
|
doc = Nokogiri::XML(raw_response)
|
140
154
|
|
141
155
|
if doc && doc.root && doc.root.name == "ApiException"
|
142
156
|
|
143
|
-
raise ApiException.new(doc.root.xpath("Type").text,
|
144
|
-
doc.root.xpath("Message").text,
|
157
|
+
raise ApiException.new(doc.root.xpath("Type").text,
|
158
|
+
doc.root.xpath("Message").text,
|
145
159
|
raw_response,
|
160
|
+
doc,
|
146
161
|
request_body)
|
147
162
|
|
148
163
|
else
|
@@ -34,6 +34,9 @@ module Xeroizer
|
|
34
34
|
boolean :is_reconciled
|
35
35
|
string :status
|
36
36
|
|
37
|
+
string :currency_code
|
38
|
+
decimal :currency_rate, :calculated => true
|
39
|
+
|
37
40
|
alias_method :reconciled?, :is_reconciled
|
38
41
|
|
39
42
|
belongs_to :contact, :model_name => 'Contact'
|
@@ -55,6 +58,14 @@ module Xeroizer
|
|
55
58
|
self.line_items.size > 0
|
56
59
|
end
|
57
60
|
|
61
|
+
def currency_rate
|
62
|
+
if attributes[:currency_rate]
|
63
|
+
BigDecimal.new(attributes[:currency_rate])
|
64
|
+
else
|
65
|
+
BigDecimal.new('1.0')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
58
69
|
def sub_total=(value); raise SettingTotalDirectlyNotSupported.new(:sub_total); end
|
59
70
|
def total_tax=(value); raise SettingTotalDirectlyNotSupported.new(:total_tax); end
|
60
71
|
def total=(value); raise SettingTotalDirectlyNotSupported.new(:total); end
|
@@ -57,6 +57,7 @@ module Xeroizer
|
|
57
57
|
decimal :total, :calculated => true
|
58
58
|
datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
|
59
59
|
string :currency_code
|
60
|
+
decimal :currency_rate, :calculated => true
|
60
61
|
datetime :fully_paid_on_date
|
61
62
|
boolean :sent_to_contact
|
62
63
|
|
@@ -81,6 +82,14 @@ module Xeroizer
|
|
81
82
|
def contact_id
|
82
83
|
attributes[:contact] && attributes[:contact][:contact_id]
|
83
84
|
end
|
85
|
+
|
86
|
+
def currency_rate
|
87
|
+
if attributes[:currency_rate]
|
88
|
+
BigDecimal.new(attributes[:currency_rate])
|
89
|
+
else
|
90
|
+
BigDecimal.new('1.0')
|
91
|
+
end
|
92
|
+
end
|
84
93
|
|
85
94
|
# Swallow assignment of attributes that should only be calculated automatically.
|
86
95
|
def sub_total=(value); raise SettingTotalDirectlyNotSupported.new(:sub_total); end
|
@@ -2,7 +2,10 @@ module Xeroizer
|
|
2
2
|
module Record
|
3
3
|
|
4
4
|
class InvoiceModel < BaseModel
|
5
|
-
|
5
|
+
# To create a new invoice, use the folowing
|
6
|
+
# $xero_client.Invoice.build(type: 'ACCREC', ..., contact: {name: 'Foo Bar'},...)
|
7
|
+
# Note that we are not making an api request to xero just to get the contact
|
8
|
+
|
6
9
|
set_permissions :read, :write, :update
|
7
10
|
|
8
11
|
public
|
@@ -62,6 +65,7 @@ module Xeroizer
|
|
62
65
|
decimal :amount_credited
|
63
66
|
datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
|
64
67
|
string :currency_code
|
68
|
+
decimal :currency_rate, :calculated => true
|
65
69
|
datetime :fully_paid_on_date
|
66
70
|
boolean :sent_to_contact
|
67
71
|
|
@@ -106,6 +110,14 @@ module Xeroizer
|
|
106
110
|
def accounts_receivable?
|
107
111
|
type == 'ACCREC'
|
108
112
|
end
|
113
|
+
|
114
|
+
def currency_rate
|
115
|
+
if attributes[:currency_rate]
|
116
|
+
BigDecimal.new(attributes[:currency_rate])
|
117
|
+
else
|
118
|
+
BigDecimal.new('1.0')
|
119
|
+
end
|
120
|
+
end
|
109
121
|
|
110
122
|
def sub_total=(sub_total)
|
111
123
|
@sub_total_is_set = true
|
data/lib/xeroizer/oauth.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
1
15
|
module Xeroizer
|
2
16
|
|
3
17
|
# Shamelessly taken from the XeroGateway library by Tim Connor which is shamelessly
|
@@ -50,6 +64,7 @@ module Xeroizer
|
|
50
64
|
# @option options [String] :request_token_path base URL path for getting a RequestToken (default: "/oauth/RequestToken")
|
51
65
|
# @option options [String] :signature_method method usd to sign requests (default: OAuth library default)
|
52
66
|
# @option options [String] :site base site for API requests (default: "https://api.xero.com")
|
67
|
+
# @option options [IO] :http_debug_output filehandle to write HTTP traffic to
|
53
68
|
# @option options [OpenSSL:X509::Certificate] :ssl_client_cert client-side SSL certificate to use for requests (used for PartnerApplication mode)
|
54
69
|
# @option options [OpenSSL::PKey::RSA] :ssl_client_key client-side SSL private key to use for requests (used for PartnerApplication mode)
|
55
70
|
def initialize(ctoken, csecret, options = {})
|
@@ -104,9 +119,9 @@ module Xeroizer
|
|
104
119
|
})
|
105
120
|
update_attributes_from_token(access_token)
|
106
121
|
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
122
|
+
|
123
|
+
private
|
124
|
+
|
110
125
|
# Create an OAuth consumer with the SSL client key if specified in @consumer_options when
|
111
126
|
# this instance was created.
|
112
127
|
def create_consumer
|
@@ -116,8 +131,13 @@ module Xeroizer
|
|
116
131
|
consumer.http.key = @consumer_options[:ssl_client_key]
|
117
132
|
end
|
118
133
|
consumer
|
134
|
+
|
135
|
+
if @consumer_options[:http_debug_output]
|
136
|
+
consumer.http.set_debug_output(@consumer_options[:http_debug_output])
|
137
|
+
end
|
138
|
+
consumer
|
119
139
|
end
|
120
|
-
|
140
|
+
|
121
141
|
# Update instance variables with those from the AccessToken.
|
122
142
|
def update_attributes_from_token(access_token)
|
123
143
|
@expires_at = Time.now + access_token.params[:oauth_expires_in].to_i
|
@@ -125,6 +145,5 @@ module Xeroizer
|
|
125
145
|
@session_handle = access_token.params[:oauth_session_handle]
|
126
146
|
@atoken, @asecret = access_token.token, access_token.secret
|
127
147
|
end
|
128
|
-
|
129
148
|
end
|
130
149
|
end
|
data/lib/xeroizer/record/base.rb
CHANGED
@@ -51,11 +51,13 @@ module Xeroizer
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def []=(attribute, value)
|
54
|
+
parent.mark_dirty(self) if parent
|
54
55
|
self.send("#{attribute}=".to_sym, value)
|
55
56
|
end
|
56
57
|
|
57
58
|
def attributes=(new_attributes)
|
58
59
|
return unless new_attributes.is_a?(Hash)
|
60
|
+
parent.mark_dirty(self) if parent
|
59
61
|
new_attributes.each do | key, value |
|
60
62
|
self.send("#{key}=".to_sym, value)
|
61
63
|
end
|
@@ -84,6 +86,7 @@ module Xeroizer
|
|
84
86
|
record = self.parent.find(self.id)
|
85
87
|
@attributes = record.attributes if record
|
86
88
|
@complete_record_downloaded = true
|
89
|
+
parent.mark_clean(self)
|
87
90
|
self
|
88
91
|
end
|
89
92
|
|
@@ -94,9 +97,30 @@ module Xeroizer
|
|
94
97
|
else
|
95
98
|
update
|
96
99
|
end
|
100
|
+
saved!
|
101
|
+
end
|
102
|
+
|
103
|
+
def saved!
|
97
104
|
@complete_record_downloaded = true
|
105
|
+
parent.mark_clean(self)
|
98
106
|
true
|
99
107
|
end
|
108
|
+
|
109
|
+
def to_json(*args)
|
110
|
+
to_h.to_json(*args)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Deprecated
|
114
|
+
def as_json(options = {})
|
115
|
+
to_h.to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_h
|
119
|
+
attrs = self.attributes.reject {|k, v| k == :parent }.map do |k, v|
|
120
|
+
[k, v.kind_of?(Array) ? v.map(&:to_h) : (v.respond_to?(:to_h) ? v.to_h : v)]
|
121
|
+
end
|
122
|
+
Hash[attrs]
|
123
|
+
end
|
100
124
|
|
101
125
|
def inspect
|
102
126
|
attribute_string = self.attributes.collect do |attr, value|
|
@@ -112,7 +136,7 @@ module Xeroizer
|
|
112
136
|
request = to_xml
|
113
137
|
log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}"
|
114
138
|
|
115
|
-
response = parent.
|
139
|
+
response = parent.http_post(request)
|
116
140
|
log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}"
|
117
141
|
|
118
142
|
parse_save_response(response)
|
@@ -18,7 +18,9 @@ module Xeroizer
|
|
18
18
|
class_inheritable_attributes :xml_root_name
|
19
19
|
class_inheritable_attributes :optional_xml_root_name
|
20
20
|
class_inheritable_attributes :xml_node_name
|
21
|
-
|
21
|
+
|
22
|
+
DEFAULT_RECORDS_PER_BATCH_SAVE = 2000
|
23
|
+
|
22
24
|
include BaseModelHttpProxy
|
23
25
|
|
24
26
|
attr_reader :application
|
@@ -89,7 +91,22 @@ module Xeroizer
|
|
89
91
|
|
90
92
|
# Build a record with attributes set to the value of attributes.
|
91
93
|
def build(attributes = {})
|
92
|
-
model_class.build(attributes, self)
|
94
|
+
model_class.build(attributes, self).tap do |resource|
|
95
|
+
mark_dirty(resource)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def mark_dirty(resource)
|
100
|
+
if @allow_batch_operations
|
101
|
+
@objects[model_class] ||= {}
|
102
|
+
@objects[model_class][resource.object_id] ||= resource
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def mark_clean(resource)
|
107
|
+
if @objects and @objects[model_class]
|
108
|
+
@objects[model_class].delete(resource.object_id)
|
109
|
+
end
|
93
110
|
end
|
94
111
|
|
95
112
|
# Create (build and save) a record with attributes set to the value of attributes.
|
@@ -122,7 +139,37 @@ module Xeroizer
|
|
122
139
|
result.complete_record_downloaded = true if result
|
123
140
|
result
|
124
141
|
end
|
125
|
-
|
142
|
+
|
143
|
+
def batch_save(chunk_size = DEFAULT_RECORDS_PER_BATCH_SAVE)
|
144
|
+
@objects = {}
|
145
|
+
@allow_batch_operations = true
|
146
|
+
|
147
|
+
yield
|
148
|
+
|
149
|
+
if @objects[model_class]
|
150
|
+
objects = @objects[model_class].values.compact
|
151
|
+
return false unless objects.all?(&:valid?)
|
152
|
+
actions = objects.group_by {|o| o.new_record? ? :http_put : :http_post }
|
153
|
+
actions.each_pair do |http_method, records|
|
154
|
+
records.each_slice(chunk_size) do |some_records|
|
155
|
+
request = to_bulk_xml(some_records)
|
156
|
+
response = parse_response(self.send(http_method, request, {:summarizeErrors => false}))
|
157
|
+
response.response_items.each_with_index do |record, i|
|
158
|
+
if record and record.is_a?(model_class)
|
159
|
+
some_records[i].attributes = record.attributes
|
160
|
+
some_records[i].errors = record.errors
|
161
|
+
some_records[i].saved!
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
@objects = {}
|
169
|
+
@allow_batch_operations = false
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
126
173
|
def parse_response(response_xml, options = {})
|
127
174
|
Response.parse(response_xml, options) do | response, elements, response_model_name |
|
128
175
|
if model_name == response_model_name
|
@@ -131,17 +178,40 @@ module Xeroizer
|
|
131
178
|
end
|
132
179
|
end
|
133
180
|
end
|
134
|
-
|
181
|
+
|
135
182
|
protected
|
136
|
-
|
183
|
+
|
137
184
|
# Parse the records part of the XML response and builds model instances as necessary.
|
138
185
|
def parse_records(response, elements)
|
139
186
|
elements.each do | element |
|
140
|
-
|
187
|
+
new_record = model_class.build_from_node(element, self)
|
188
|
+
if element.attribute('status').try(:value) == 'ERROR'
|
189
|
+
new_record.errors = []
|
190
|
+
element.xpath('.//ValidationError').each do |err|
|
191
|
+
new_record.errors << err.text.gsub(/^\s+/, '').gsub(/\s+$/, '')
|
192
|
+
end
|
193
|
+
end
|
194
|
+
response.response_items << new_record
|
141
195
|
end
|
142
196
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
197
|
+
|
198
|
+
def to_bulk_xml(records, builder = Builder::XmlMarkup.new(:indent => 2))
|
199
|
+
tag = (self.class.optional_xml_root_name || model_name).pluralize
|
200
|
+
builder.tag!(tag) do
|
201
|
+
records.each {|r| r.to_xml(builder) }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Parse the response from a create/update request.
|
206
|
+
def parse_save_response(response_xml)
|
207
|
+
response = parse_response(response_xml)
|
208
|
+
record = response.response_items.first if response.response_items.is_a?(Array)
|
209
|
+
if record && record.is_a?(self.class)
|
210
|
+
@attributes = record.attributes
|
211
|
+
end
|
212
|
+
self
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
146
216
|
end
|
147
217
|
end
|
@@ -73,6 +73,7 @@ module Xeroizer
|
|
73
73
|
|
74
74
|
unless options[:skip_writer]
|
75
75
|
define_method "#{internal_field_name}=".to_sym do | value |
|
76
|
+
parent.mark_dirty(self) if parent
|
76
77
|
@attributes[field_name] = value
|
77
78
|
end
|
78
79
|
end
|
@@ -89,6 +90,7 @@ module Xeroizer
|
|
89
90
|
|
90
91
|
# Sets the value of the Xero primary key for this record if it exists.
|
91
92
|
def id=(new_id)
|
93
|
+
parent.mark_dirty(self) if parent
|
92
94
|
self[self.class.primary_key_name] = new_id
|
93
95
|
end
|
94
96
|
|
@@ -47,7 +47,8 @@ module Xeroizer
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
|
+
parent.mark_clean(record)
|
51
52
|
record
|
52
53
|
end
|
53
54
|
|
@@ -87,7 +88,7 @@ module Xeroizer
|
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
|
-
# Format
|
91
|
+
# Format an attribute for use in the XML passed to Xero.
|
91
92
|
def xml_value_from_field(b, field, value)
|
92
93
|
case field[:type]
|
93
94
|
when :guid then b.tag!(field[:api_name], value)
|
data/lib/xeroizer/response.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# Copyright (c) 2008 Tim Connor <tlconnor@gmail.com>
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
1
15
|
module Xeroizer
|
2
16
|
class Response
|
3
17
|
|
@@ -48,6 +48,6 @@ module AcceptanceTest
|
|
48
48
|
assert_not_nil ENV["CONSUMER_SECRET"], "No CONSUMER_SECRET environment variable specified."
|
49
49
|
assert_not_nil ENV["KEY_FILE"], "No KEY_FILE environment variable specified."
|
50
50
|
assert File.exists?(ENV["KEY_FILE"]), "The file <#{ENV["KEY_FILE"]}> does not exist."
|
51
|
-
OAuthCredentials.new ENV["CONSUMER_KEY"], ENV["CONSUMER_SECRET"], ENV["KEY_FILE"]
|
51
|
+
Xeroizer::OAuthCredentials.new ENV["CONSUMER_KEY"], ENV["CONSUMER_SECRET"], ENV["KEY_FILE"]
|
52
52
|
end
|
53
53
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "acceptance_test"
|
3
|
+
|
4
|
+
class BulkOperationsTest < Test::Unit::TestCase
|
5
|
+
include AcceptanceTest
|
6
|
+
|
7
|
+
def random_name
|
8
|
+
"test-person-#{rand 1000000000}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
@client = Xeroizer::PrivateApplication.new(@consumer_key, @consumer_secret, @key_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
can "create multiple invoices at once" do
|
17
|
+
c1, c2 = nil, nil
|
18
|
+
assert_true(
|
19
|
+
@client.Contact.batch_save do
|
20
|
+
c1 = @client.Contact.build(name: random_name)
|
21
|
+
c2 = @client.Contact.build(name: random_name)
|
22
|
+
end
|
23
|
+
)
|
24
|
+
[c1, c2].each {|c| assert_false c.new_record? }
|
25
|
+
end
|
26
|
+
|
27
|
+
can "create and update new records in bulk" do
|
28
|
+
c1, c2 = nil, nil
|
29
|
+
assert_true(
|
30
|
+
@client.Contact.batch_save do
|
31
|
+
c1 = @client.Contact.create(name: random_name)
|
32
|
+
c1.email_address = "foo@bar.com"
|
33
|
+
c2 = @client.Contact.build(name: random_name)
|
34
|
+
end
|
35
|
+
)
|
36
|
+
[c1, c2].each {|c| assert_false c.new_record? }
|
37
|
+
c1.download_complete_record!
|
38
|
+
assert_equal c1.email_address, "foo@bar.com"
|
39
|
+
end
|
40
|
+
|
41
|
+
can "return false from #batch_save if validation fails" do
|
42
|
+
assert_false(
|
43
|
+
@client.Contact.batch_save do
|
44
|
+
@client.Contact.build(email_address: "guy-with-no-name@example.com")
|
45
|
+
end
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
2
|
+
<Id>8ce97e9e-31d2-457a-a75d-5e359ffac103</Id>
|
3
|
+
<Status>OK</Status>
|
4
|
+
<ProviderName>Xero API Previewer</ProviderName>
|
5
|
+
<DateTimeUTC>2013-05-20T11:54:50.9949578Z</DateTimeUTC>
|
6
|
+
<BankTransactions>
|
7
|
+
<BankTransaction>
|
8
|
+
<Contact>
|
9
|
+
<ContactID>a852a44c-3d8f-4c4b-a628-3a2c2121b9b1</ContactID>
|
10
|
+
<Name>Bank West</Name>
|
11
|
+
</Contact>
|
12
|
+
<Date>2013-05-16T00:00:00</Date>
|
13
|
+
<Status>AUTHORISED</Status>
|
14
|
+
<LineAmountTypes>Inclusive</LineAmountTypes>
|
15
|
+
<SubTotal>1000.00</SubTotal>
|
16
|
+
<TotalTax>0.00</TotalTax>
|
17
|
+
<Total>1000.00</Total>
|
18
|
+
<UpdatedDateUTC>2013-05-16T15:45:07.563</UpdatedDateUTC>
|
19
|
+
<CurrencyCode>GBP</CurrencyCode>
|
20
|
+
<BankTransactionID>ee4c4d5a-437d-4369-b152-848bd932f431</BankTransactionID>
|
21
|
+
<BankAccount>
|
22
|
+
<AccountID>bd9e85e0-0478-433d-ae9f-0b3c4f04bfe4</AccountID>
|
23
|
+
<Code>090</Code>
|
24
|
+
<Name>Business Bank Account</Name>
|
25
|
+
</BankAccount>
|
26
|
+
<Type>SPEND</Type>
|
27
|
+
<IsReconciled>false</IsReconciled>
|
28
|
+
</BankTransaction>
|
29
|
+
<BankTransaction>
|
30
|
+
<Contact>
|
31
|
+
<ContactID>d69ed9cd-f98a-4604-aced-54a1611554b2</ContactID>
|
32
|
+
<Name>Foo</Name>
|
33
|
+
</Contact>
|
34
|
+
<Date>2013-05-20T00:00:00</Date>
|
35
|
+
<Status>AUTHORISED</Status>
|
36
|
+
<LineAmountTypes>Inclusive</LineAmountTypes>
|
37
|
+
<SubTotal>123.00</SubTotal>
|
38
|
+
<TotalTax>0.00</TotalTax>
|
39
|
+
<Total>123.00</Total>
|
40
|
+
<UpdatedDateUTC>2013-05-20T11:39:41.34</UpdatedDateUTC>
|
41
|
+
<CurrencyCode>USD</CurrencyCode>
|
42
|
+
<BankTransactionID>8644472b-ea4e-4e5a-9987-f79250ece35e</BankTransactionID>
|
43
|
+
<BankAccount>
|
44
|
+
<AccountID>66c28ab3-05c2-4db7-9066-4357b8099fd8</AccountID>
|
45
|
+
<Name>FooBar</Name>
|
46
|
+
</BankAccount>
|
47
|
+
<Type>SPEND</Type>
|
48
|
+
<IsReconciled>false</IsReconciled>
|
49
|
+
<CurrencyRate>1.519520</CurrencyRate>
|
50
|
+
</BankTransaction>
|
51
|
+
</BankTransactions>
|
52
|
+
</Response>
|
@@ -17,7 +17,8 @@
|
|
17
17
|
<TotalTax>19.90</TotalTax>
|
18
18
|
<Total>218.90</Total>
|
19
19
|
<UpdatedDateUTC>2008-09-16T09:40:09.467</UpdatedDateUTC>
|
20
|
-
<CurrencyCode>
|
20
|
+
<CurrencyCode>USD</CurrencyCode>
|
21
|
+
<CurrencyRate>1.5619</CurrencyRate>
|
21
22
|
<FullyPaidOnDate>2011-03-07T00:00:00</FullyPaidOnDate>
|
22
23
|
<Type>ACCPAYCREDIT</Type>
|
23
24
|
<CreditNoteID>7df8949c-b71f-40c0-bbcf-39f2f450f286</CreditNoteID>
|
@@ -166,4 +167,4 @@
|
|
166
167
|
<SentToContact>false</SentToContact>
|
167
168
|
</CreditNote>
|
168
169
|
</CreditNotes>
|
169
|
-
</Response>
|
170
|
+
</Response>
|
@@ -17,7 +17,8 @@
|
|
17
17
|
<TotalTax>13.50</TotalTax>
|
18
18
|
<Total>148.50</Total>
|
19
19
|
<UpdatedDateUTC>2008-09-16T10:28:51.5</UpdatedDateUTC>
|
20
|
-
<CurrencyCode>
|
20
|
+
<CurrencyCode>USD</CurrencyCode>
|
21
|
+
<CurrencyRate>1.516990</CurrencyRate>
|
21
22
|
<FullyPaidOnDate>2011-02-24T00:00:00</FullyPaidOnDate>
|
22
23
|
<Type>ACCPAY</Type>
|
23
24
|
<InvoiceID>0032f627-3156-4d30-9b1c-4d3b994dc921</InvoiceID>
|
@@ -1896,4 +1897,4 @@
|
|
1896
1897
|
<SentToContact>false</SentToContact>
|
1897
1898
|
</Invoice>
|
1898
1899
|
</Invoices>
|
1899
|
-
</Response>
|
1900
|
+
</Response>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
2
|
+
<Id>3465b325-f509-4323-9d1c-31b112aaae06</Id>
|
3
|
+
<Status>OK</Status>
|
4
|
+
<ProviderName>Xero API Previewer</ProviderName>
|
5
|
+
<DateTimeUTC>2013-05-20T11:57:54.000904Z</DateTimeUTC>
|
6
|
+
<BankTransactions>
|
7
|
+
<BankTransaction>
|
8
|
+
<Contact>
|
9
|
+
<ContactID>d69ed9cd-f98a-4604-aced-54a1611554b2</ContactID>
|
10
|
+
<ContactStatus>ACTIVE</ContactStatus>
|
11
|
+
<Name>Foo</Name>
|
12
|
+
<Addresses>
|
13
|
+
<Address>
|
14
|
+
<AddressType>POBOX</AddressType>
|
15
|
+
</Address>
|
16
|
+
<Address>
|
17
|
+
<AddressType>STREET</AddressType>
|
18
|
+
</Address>
|
19
|
+
</Addresses>
|
20
|
+
<Phones>
|
21
|
+
<Phone>
|
22
|
+
<PhoneType>DDI</PhoneType>
|
23
|
+
</Phone>
|
24
|
+
<Phone>
|
25
|
+
<PhoneType>DEFAULT</PhoneType>
|
26
|
+
</Phone>
|
27
|
+
<Phone>
|
28
|
+
<PhoneType>FAX</PhoneType>
|
29
|
+
</Phone>
|
30
|
+
<Phone>
|
31
|
+
<PhoneType>MOBILE</PhoneType>
|
32
|
+
</Phone>
|
33
|
+
</Phones>
|
34
|
+
<UpdatedDateUTC>2013-05-20T11:39:41.34</UpdatedDateUTC>
|
35
|
+
<DefaultCurrency>USD</DefaultCurrency>
|
36
|
+
</Contact>
|
37
|
+
<Date>2013-05-20T00:00:00</Date>
|
38
|
+
<Status>AUTHORISED</Status>
|
39
|
+
<LineAmountTypes>Inclusive</LineAmountTypes>
|
40
|
+
<LineItems>
|
41
|
+
<LineItem>
|
42
|
+
<UnitAmount>123.00</UnitAmount>
|
43
|
+
<TaxType>NONE</TaxType>
|
44
|
+
<TaxAmount>0.00</TaxAmount>
|
45
|
+
<LineAmount>123.00</LineAmount>
|
46
|
+
<AccountCode>320</AccountCode>
|
47
|
+
<Quantity>1.0000</Quantity>
|
48
|
+
</LineItem>
|
49
|
+
</LineItems>
|
50
|
+
<SubTotal>123.00</SubTotal>
|
51
|
+
<TotalTax>0.00</TotalTax>
|
52
|
+
<Total>123.00</Total>
|
53
|
+
<UpdatedDateUTC>2013-05-20T11:39:41.34</UpdatedDateUTC>
|
54
|
+
<CurrencyCode>USD</CurrencyCode>
|
55
|
+
<BankTransactionID>8644472b-ea4e-4e5a-9987-f79250ece35e</BankTransactionID>
|
56
|
+
<BankAccount>
|
57
|
+
<AccountID>66c28ab3-05c2-4db7-9066-4357b8099fd8</AccountID>
|
58
|
+
<Name>FooBar</Name>
|
59
|
+
</BankAccount>
|
60
|
+
<Type>SPEND</Type>
|
61
|
+
<IsReconciled>false</IsReconciled>
|
62
|
+
<CurrencyRate>1.519520</CurrencyRate>
|
63
|
+
</BankTransaction>
|
64
|
+
</BankTransactions>
|
65
|
+
</Response>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
2
|
+
<Id>3f444db0-9293-4b4a-bd2c-b2b59ee51442</Id>
|
3
|
+
<Status>OK</Status>
|
4
|
+
<ProviderName>Xero API Previewer</ProviderName>
|
5
|
+
<DateTimeUTC>2013-05-20T11:56:54.1897372Z</DateTimeUTC>
|
6
|
+
<BankTransactions>
|
7
|
+
<BankTransaction>
|
8
|
+
<Contact>
|
9
|
+
<ContactID>a852a44c-3d8f-4c4b-a628-3a2c2121b9b1</ContactID>
|
10
|
+
<ContactStatus>ACTIVE</ContactStatus>
|
11
|
+
<Name>Bank West</Name>
|
12
|
+
<Addresses>
|
13
|
+
<Address>
|
14
|
+
<AddressType>POBOX</AddressType>
|
15
|
+
<AddressLine1>Procurement Services</AddressLine1>
|
16
|
+
<AddressLine2>GPO 1234</AddressLine2>
|
17
|
+
<City>Pinehaven</City>
|
18
|
+
<PostalCode>PI98 7HV</PostalCode>
|
19
|
+
</Address>
|
20
|
+
<Address>
|
21
|
+
<AddressType>STREET</AddressType>
|
22
|
+
</Address>
|
23
|
+
</Addresses>
|
24
|
+
<Phones>
|
25
|
+
<Phone>
|
26
|
+
<PhoneType>MOBILE</PhoneType>
|
27
|
+
</Phone>
|
28
|
+
<Phone>
|
29
|
+
<PhoneType>DEFAULT</PhoneType>
|
30
|
+
<PhoneNumber>2023456</PhoneNumber>
|
31
|
+
<PhoneAreaCode>02</PhoneAreaCode>
|
32
|
+
</Phone>
|
33
|
+
<Phone>
|
34
|
+
<PhoneType>FAX</PhoneType>
|
35
|
+
</Phone>
|
36
|
+
<Phone>
|
37
|
+
<PhoneType>DDI</PhoneType>
|
38
|
+
</Phone>
|
39
|
+
</Phones>
|
40
|
+
<UpdatedDateUTC>2013-05-12T04:24:12.347</UpdatedDateUTC>
|
41
|
+
</Contact>
|
42
|
+
<Date>2013-05-16T00:00:00</Date>
|
43
|
+
<Status>AUTHORISED</Status>
|
44
|
+
<LineAmountTypes>Inclusive</LineAmountTypes>
|
45
|
+
<LineItems>
|
46
|
+
<LineItem>
|
47
|
+
<Description>Loan Repayment</Description>
|
48
|
+
<UnitAmount>1000.00</UnitAmount>
|
49
|
+
<TaxType>NONE</TaxType>
|
50
|
+
<TaxAmount>0.00</TaxAmount>
|
51
|
+
<LineAmount>1000.00</LineAmount>
|
52
|
+
<AccountCode>900</AccountCode>
|
53
|
+
<Quantity>1.0000</Quantity>
|
54
|
+
</LineItem>
|
55
|
+
</LineItems>
|
56
|
+
<SubTotal>1000.00</SubTotal>
|
57
|
+
<TotalTax>0.00</TotalTax>
|
58
|
+
<Total>1000.00</Total>
|
59
|
+
<UpdatedDateUTC>2013-05-16T15:45:07.563</UpdatedDateUTC>
|
60
|
+
<CurrencyCode>GBP</CurrencyCode>
|
61
|
+
<BankTransactionID>ee4c4d5a-437d-4369-b152-848bd932f431</BankTransactionID>
|
62
|
+
<BankAccount>
|
63
|
+
<AccountID>bd9e85e0-0478-433d-ae9f-0b3c4f04bfe4</AccountID>
|
64
|
+
<Code>090</Code>
|
65
|
+
<Name>Business Bank Account</Name>
|
66
|
+
</BankAccount>
|
67
|
+
<Type>SPEND</Type>
|
68
|
+
<IsReconciled>false</IsReconciled>
|
69
|
+
<CurrencyRate>1.000000</CurrencyRate>
|
70
|
+
</BankTransaction>
|
71
|
+
</BankTransactions>
|
72
|
+
</Response>
|
@@ -7,7 +7,7 @@ base_path = File.expand_path(File.dirname(__FILE__))
|
|
7
7
|
if ARGV[3]
|
8
8
|
models = [ARGV[3]]
|
9
9
|
else
|
10
|
-
models = %w(Account BrandingTheme Contact CreditNote Currency Employee Invoice Item ManualJournal Organisation Payment TaxRate TrackingCategory)
|
10
|
+
models = %w(Account BankTransaction BrandingTheme Contact CreditNote Currency Employee Invoice Item ManualJournal Organisation Payment TaxRate TrackingCategory)
|
11
11
|
end
|
12
12
|
|
13
13
|
models.each do | model_name |
|
@@ -17,7 +17,7 @@ models.each do | model_name |
|
|
17
17
|
records = model.all
|
18
18
|
File.open("#{base_path}/#{model_name.underscore.pluralize}.xml", "w") { | fp | fp.write model.response.response_xml }
|
19
19
|
|
20
|
-
if %w(Contact CreditNote Invoice ManualJournal).include?(model_name)
|
20
|
+
if %w(BankTransaction Contact CreditNote Invoice ManualJournal).include?(model_name)
|
21
21
|
records.each do | summary_record |
|
22
22
|
record = model.find(summary_record.id)
|
23
23
|
File.open("#{base_path}/records/#{model_name.underscore}-#{record.id}.xml", "w") { | fp | fp.write model.response.response_xml }
|
@@ -26,4 +26,4 @@ models.each do | model_name |
|
|
26
26
|
record = model.find(records.first.id)
|
27
27
|
File.open("#{base_path}/#{model_name.underscore.singularize}.xml", "w") { | fp | fp.write model.response.response_xml }
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class BankTransactionTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
4
5
|
include Xeroizer::Record
|
5
6
|
|
6
7
|
def setup
|
7
8
|
fake_parent = Class.new do
|
8
9
|
attr_accessor :application
|
10
|
+
def mark_dirty(*args)
|
11
|
+
end
|
9
12
|
end.new
|
10
13
|
|
11
14
|
the_line_items = [
|
@@ -15,6 +18,9 @@ class BankTransactionTest < Test::Unit::TestCase
|
|
15
18
|
|
16
19
|
@the_bank_transaction = BankTransaction.new fake_parent
|
17
20
|
@the_bank_transaction.line_items = the_line_items
|
21
|
+
|
22
|
+
@client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET)
|
23
|
+
mock_api('BankTransactions')
|
18
24
|
end
|
19
25
|
|
20
26
|
context "given a bank_transaction with line_amount_types set to \"Exclusive\"" do
|
@@ -44,4 +50,26 @@ class BankTransactionTest < Test::Unit::TestCase
|
|
44
50
|
assert_equal "1.0", @the_bank_transaction.sub_total.to_s
|
45
51
|
end
|
46
52
|
end
|
53
|
+
|
54
|
+
context "bank transaction totals" do
|
55
|
+
|
56
|
+
should "large-scale testing from API XML" do
|
57
|
+
bank_transactions = @client.BankTransaction.all
|
58
|
+
bank_transactions.each do | bank_transaction |
|
59
|
+
assert(!!bank_transaction.attributes[:currency_code], "Doesn't have currency code in attributes")
|
60
|
+
assert(!!bank_transaction.currency_code, "Doesn't have currency code in model")
|
61
|
+
assert(!!bank_transaction.currency_rate, "Doesn't have currency rate in model")
|
62
|
+
|
63
|
+
|
64
|
+
assert_equal(bank_transaction.attributes[:currency_code], bank_transaction.currency_code)
|
65
|
+
|
66
|
+
if bank_transaction.attributes[:currency_rate]
|
67
|
+
assert_equal(bank_transaction.attributes[:currency_rate], bank_transaction.currency_rate)
|
68
|
+
else
|
69
|
+
assert_equal(1.0, bank_transaction.currency_rate)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
47
75
|
end
|
@@ -26,6 +26,15 @@ class CreditNoteTest < Test::Unit::TestCase
|
|
26
26
|
should "large-scale testing from API XML" do
|
27
27
|
credit_notes = @client.CreditNote.all
|
28
28
|
credit_notes.each do | credit_note |
|
29
|
+
assert_equal(credit_note.attributes[:currency_code], credit_note.currency_code)
|
30
|
+
|
31
|
+
if credit_note.attributes[:currency_rate]
|
32
|
+
assert_equal(credit_note.attributes[:currency_rate], credit_note.currency_rate)
|
33
|
+
else
|
34
|
+
assert_equal(1.0, credit_note.currency_rate)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
29
38
|
assert_equal(credit_note.attributes[:sub_total], credit_note.sub_total)
|
30
39
|
assert_equal(credit_note.attributes[:total_tax], credit_note.total_tax)
|
31
40
|
assert_equal(credit_note.attributes[:total], credit_note.total)
|
@@ -44,6 +44,13 @@ class InvoiceTest < Test::Unit::TestCase
|
|
44
44
|
should "large-scale testing from API XML" do
|
45
45
|
invoices = @client.Invoice.all
|
46
46
|
invoices.each do | invoice |
|
47
|
+
assert_equal(invoice.attributes[:currency_code], invoice.currency_code)
|
48
|
+
if invoice.attributes[:currency_rate]
|
49
|
+
assert_equal(invoice.attributes[:currency_rate], invoice.currency_rate)
|
50
|
+
else
|
51
|
+
assert_equal(1.0, invoice.currency_rate)
|
52
|
+
end
|
53
|
+
|
47
54
|
assert_equal(invoice.attributes[:sub_total], invoice.sub_total)
|
48
55
|
assert_equal(invoice.attributes[:total_tax], invoice.total_tax)
|
49
56
|
assert_equal(invoice.attributes[:total], invoice.total)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xeroizer-float
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.15.3.
|
4
|
+
version: 2.15.3.14
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-05-20 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: builder
|
@@ -60,6 +60,22 @@ dependencies:
|
|
60
60
|
- - ! '>='
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: tzinfo
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
63
79
|
- !ruby/object:Gem::Dependency
|
64
80
|
name: nokogiri
|
65
81
|
requirement: !ruby/object:Gem::Requirement
|
@@ -278,8 +294,10 @@ files:
|
|
278
294
|
- test/acceptance/about_fetching_bank_transactions_test.rb
|
279
295
|
- test/acceptance/acceptance_test.rb
|
280
296
|
- test/acceptance/bank_transaction_reference_data.rb
|
297
|
+
- test/acceptance/bulk_operations_test.rb
|
281
298
|
- test/stub_responses/accounts.xml
|
282
299
|
- test/stub_responses/api_exception.xml
|
300
|
+
- test/stub_responses/bank_transactions.xml
|
283
301
|
- test/stub_responses/bogus_oauth_error
|
284
302
|
- test/stub_responses/branding_themes.xml
|
285
303
|
- test/stub_responses/contact.xml
|
@@ -304,6 +322,8 @@ files:
|
|
304
322
|
- test/stub_responses/organisations.xml
|
305
323
|
- test/stub_responses/payments.xml
|
306
324
|
- test/stub_responses/rate_limit_exceeded
|
325
|
+
- test/stub_responses/records/bank_transaction-8644472b-ea4e-4e5a-9987-f79250ece35e.xml
|
326
|
+
- test/stub_responses/records/bank_transaction-ee4c4d5a-437d-4369-b152-848bd932f431.xml
|
307
327
|
- test/stub_responses/records/contact-043892a1-aef1-4c18-88d8-b8ccb6d31466.xml
|
308
328
|
- test/stub_responses/records/contact-09664078-efe2-4a88-89a5-67eac9b0047b.xml
|
309
329
|
- test/stub_responses/records/contact-0a4cf37b-a1a8-4753-9ee2-f9207f63a8ff.xml
|