xeroizer-float 2.15.3.13 → 2.15.3.14
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.
- 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 
|
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
|