xero_gateway 2.1.0 → 2.3.0
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 +7 -0
- data/Gemfile +3 -11
- data/README.md +212 -0
- data/Rakefile +0 -1
- data/lib/oauth/oauth_consumer.rb +25 -9
- data/lib/xero_gateway.rb +5 -1
- data/lib/xero_gateway/address.rb +29 -27
- data/lib/xero_gateway/bank_transaction.rb +11 -3
- data/lib/xero_gateway/base_record.rb +97 -0
- data/lib/xero_gateway/contact_group.rb +87 -0
- data/lib/xero_gateway/currency.rb +8 -54
- data/lib/xero_gateway/dates.rb +4 -0
- data/lib/xero_gateway/exceptions.rb +14 -13
- data/lib/xero_gateway/gateway.rb +90 -5
- data/lib/xero_gateway/http.rb +16 -8
- data/lib/xero_gateway/invoice.rb +9 -3
- data/lib/xero_gateway/item.rb +27 -0
- data/lib/xero_gateway/line_item_calculations.rb +3 -3
- data/lib/xero_gateway/manual_journal.rb +5 -2
- data/lib/xero_gateway/organisation.rb +22 -73
- data/lib/xero_gateway/payment.rb +22 -9
- data/lib/xero_gateway/phone.rb +2 -0
- data/lib/xero_gateway/report.rb +95 -0
- data/lib/xero_gateway/response.rb +12 -7
- data/lib/xero_gateway/tax_rate.rb +13 -61
- data/lib/xero_gateway/tracking_category.rb +39 -13
- data/lib/xero_gateway/version.rb +3 -0
- data/test/integration/get_items_test.rb +25 -0
- data/test/integration/get_payments_test.rb +54 -0
- data/test/test_helper.rb +51 -16
- data/test/unit/address_test.rb +34 -0
- data/test/unit/bank_transaction_test.rb +7 -0
- data/test/unit/contact_group_test.rb +47 -0
- data/test/unit/contact_test.rb +35 -16
- data/test/unit/credit_note_test.rb +2 -2
- data/test/unit/gateway_test.rb +170 -1
- data/test/unit/invoice_test.rb +27 -7
- data/test/unit/item_test.rb +51 -0
- data/test/unit/organisation_test.rb +1 -0
- data/test/unit/payment_test.rb +18 -11
- data/test/unit/report_test.rb +78 -0
- data/xero_gateway.gemspec +29 -13
- metadata +176 -89
- data/README.textile +0 -357
- data/init.rb +0 -1
@@ -1,63 +1,15 @@
|
|
1
1
|
module XeroGateway
|
2
|
-
class TaxRate
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
"EffectiveRate" => :float
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_accessor *ATTRS.keys.map(&:underscore)
|
19
|
-
|
20
|
-
def initialize(params = {})
|
21
|
-
params.each do |k,v|
|
22
|
-
self.send("#{k}=", v)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def ==(other)
|
27
|
-
ATTRS.keys.map(&:underscore).each do |field|
|
28
|
-
return false if send(field) != other.send(field)
|
29
|
-
end
|
30
|
-
return true
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_xml
|
34
|
-
b = Builder::XmlMarkup.new
|
35
|
-
|
36
|
-
b.TaxRate do
|
37
|
-
ATTRS.keys.each do |attr|
|
38
|
-
eval("b.#{attr} '#{self.send(attr.underscore.to_sym)}'")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.from_xml(tax_rate_element)
|
44
|
-
TaxRate.new.tap do |tax_rate|
|
45
|
-
tax_rate_element.children.each do |element|
|
46
|
-
|
47
|
-
attribute = element.name
|
48
|
-
underscored_attribute = element.name.underscore
|
49
|
-
|
50
|
-
next if !ATTRS.keys.include?(attribute)
|
51
|
-
|
52
|
-
case (ATTRS[attribute])
|
53
|
-
when :boolean then tax_rate.send("#{underscored_attribute}=", (element.text == "true"))
|
54
|
-
when :float then tax_rate.send("#{underscored_attribute}=", element.text.to_f)
|
55
|
-
else tax_rate.send("#{underscored_attribute}=", element.text)
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
2
|
+
class TaxRate < BaseRecord
|
3
|
+
attributes({
|
4
|
+
"Name" => :string,
|
5
|
+
"TaxType" => :string,
|
6
|
+
"CanApplyToAssets" => :boolean,
|
7
|
+
"CanApplyToEquity" => :boolean,
|
8
|
+
"CanApplyToExpenses" => :boolean,
|
9
|
+
"CanApplyToLiabilities" => :boolean,
|
10
|
+
"CanApplyToRevenue" => :boolean,
|
11
|
+
"DisplayTaxRate" => :float,
|
12
|
+
"EffectiveRate" => :float
|
13
|
+
})
|
62
14
|
end
|
63
|
-
end
|
15
|
+
end
|
@@ -18,19 +18,43 @@
|
|
18
18
|
#
|
19
19
|
module XeroGateway
|
20
20
|
class TrackingCategory
|
21
|
-
attr_accessor :tracking_category_id, :name, :options
|
22
|
-
|
21
|
+
attr_accessor :tracking_category_id, :name, :status, :options
|
22
|
+
attr_accessor :all_options
|
23
|
+
|
24
|
+
class Option
|
25
|
+
attr_accessor :tracking_option_id, :name, :status
|
26
|
+
|
27
|
+
def initialize(params = {})
|
28
|
+
params.each do |k,v|
|
29
|
+
self.send("#{k}=", v)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.from_xml(option_element)
|
34
|
+
option = Option.new
|
35
|
+
option_element.children.each do |element|
|
36
|
+
case(element.name)
|
37
|
+
when "TrackingOptionID" then option.tracking_option_id = element.text
|
38
|
+
when "Name" then option.name = element.text
|
39
|
+
when "Status" then option.status = element.text
|
40
|
+
end
|
41
|
+
end
|
42
|
+
option
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
23
46
|
def initialize(params = {})
|
24
47
|
@options = []
|
48
|
+
@all_options = []
|
25
49
|
params.each do |k,v|
|
26
50
|
self.send("#{k}=", v)
|
27
51
|
end
|
28
52
|
end
|
29
|
-
|
53
|
+
|
30
54
|
def option
|
31
55
|
options[0] if options.size == 1
|
32
56
|
end
|
33
|
-
|
57
|
+
|
34
58
|
def to_xml(b = Builder::XmlMarkup.new)
|
35
59
|
b.TrackingCategory {
|
36
60
|
b.TrackingCategoryID tracking_category_id unless tracking_category_id.nil?
|
@@ -45,43 +69,45 @@ module XeroGateway
|
|
45
69
|
else
|
46
70
|
b.Option {
|
47
71
|
b.Name self.options.to_s
|
48
|
-
}
|
72
|
+
}
|
49
73
|
end
|
50
74
|
}
|
51
75
|
}
|
52
76
|
end
|
53
|
-
|
77
|
+
|
54
78
|
# When a tracking category is serialized as part of an invoice it may only have a single
|
55
79
|
# option, and the Options tag is omitted
|
56
80
|
def to_xml_for_invoice_messages(b = Builder::XmlMarkup.new)
|
57
81
|
b.TrackingCategory {
|
58
82
|
b.TrackingCategoryID self.tracking_category_id unless tracking_category_id.nil?
|
59
83
|
b.Name self.name
|
60
|
-
b.Option self.options.is_a?(Array) ? self.options.first : self.options.to_s
|
61
|
-
}
|
84
|
+
b.Option self.options.is_a?(Array) ? self.options.first : self.options.to_s
|
85
|
+
}
|
62
86
|
end
|
63
|
-
|
87
|
+
|
64
88
|
def self.from_xml(tracking_category_element)
|
65
89
|
tracking_category = TrackingCategory.new
|
66
90
|
tracking_category_element.children.each do |element|
|
67
91
|
case(element.name)
|
68
92
|
when "TrackingCategoryID" then tracking_category.tracking_category_id = element.text
|
69
93
|
when "Name" then tracking_category.name = element.text
|
94
|
+
when "Status" then tracking_category.status = element.text
|
70
95
|
when "Options" then
|
71
96
|
element.children.each do |option_child|
|
72
97
|
tracking_category.options << option_child.children.detect {|c| c.name == "Name"}.text
|
98
|
+
tracking_category.all_options << Option.from_xml(option_child)
|
73
99
|
end
|
74
100
|
when "Option" then tracking_category.options << element.text
|
75
101
|
end
|
76
102
|
end
|
77
|
-
tracking_category
|
78
|
-
end
|
79
|
-
|
103
|
+
tracking_category
|
104
|
+
end
|
105
|
+
|
80
106
|
def ==(other)
|
81
107
|
[:tracking_category_id, :name, :options].each do |field|
|
82
108
|
return false if send(field) != other.send(field)
|
83
109
|
end
|
84
110
|
return true
|
85
|
-
end
|
111
|
+
end
|
86
112
|
end
|
87
113
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class GetItemsTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
|
8
|
+
|
9
|
+
if STUB_XERO_CALLS
|
10
|
+
@gateway.xero_url = "DUMMY_URL"
|
11
|
+
|
12
|
+
@gateway.stubs(:http_get).with {|client, url, params| url =~ /Items$/ }.returns(get_file_as_string("items.xml"))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_items
|
17
|
+
result = @gateway.get_items
|
18
|
+
assert result.success?
|
19
|
+
assert !result.response_xml.nil?
|
20
|
+
|
21
|
+
assert result.items.size > 0
|
22
|
+
assert_equal XeroGateway::Item, result.items.first.class
|
23
|
+
assert_equal "An Untracked Item", result.items.first.name
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class GetPaymentsTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
|
8
|
+
|
9
|
+
if STUB_XERO_CALLS
|
10
|
+
@gateway.xero_url = "DUMMY_URL"
|
11
|
+
|
12
|
+
@gateway.stubs(:http_get).with {|client, url, params| url =~ /Payments(\/[0-9a-z\-]+)?$/i }.returns(get_file_as_string("payments.xml"))
|
13
|
+
@gateway.stubs(:http_put).with {|client, url, body, params| url =~ /Payments$/ }.returns(get_file_as_string("create_payments.xml"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_get_payments
|
18
|
+
# Make sure there is a bank transaction in Xero to retrieve
|
19
|
+
payment = @gateway.create_payment(create_test_payment).payments
|
20
|
+
|
21
|
+
result = @gateway.get_payments
|
22
|
+
assert result.success?
|
23
|
+
assert !result.request_params.nil?
|
24
|
+
assert !result.response_xml.nil?
|
25
|
+
assert result.payments.collect {|i| i.reference}.include?(payment.first.reference)
|
26
|
+
assert result.payments.collect {|i| i.payment_id}.include?(payment.first.payment_id)
|
27
|
+
assert_kind_of(XeroGateway::Payment, payment.first)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_get_payments_modified_since_date
|
31
|
+
# Create a test payment
|
32
|
+
@gateway.create_payment(create_test_payment)
|
33
|
+
|
34
|
+
# Check that it is returned
|
35
|
+
result = @gateway.get_payments(:modified_since => Date.today - 1)
|
36
|
+
assert result.success?
|
37
|
+
assert !result.request_params.nil?
|
38
|
+
assert !result.response_xml.nil?
|
39
|
+
assert result.request_params.keys.include?(:ModifiedAfter) # make sure the flag was sent
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_get_payments_find_by_id
|
43
|
+
# Create a test payment
|
44
|
+
@gateway.create_payment(create_test_payment)
|
45
|
+
|
46
|
+
# Check that it is returned
|
47
|
+
result = @gateway.get_payments(:payment_id => create_test_payment.payment_id)
|
48
|
+
assert result.success?
|
49
|
+
assert !result.request_params.nil?
|
50
|
+
assert !result.response_xml.nil?
|
51
|
+
assert result.request_params.keys.include?(:PaymentID) # make sure the flag was sent
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/setup'
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
|
-
require 'mocha'
|
4
|
+
require 'mocha/setup'
|
5
5
|
require 'shoulda'
|
6
6
|
|
7
7
|
require 'libxml'
|
8
8
|
|
9
|
-
require File.dirname(__FILE__) + '/../lib/xero_gateway.rb'
|
9
|
+
require File.dirname(__FILE__) + '/../lib/xero_gateway.rb'
|
10
10
|
|
11
11
|
module TestHelper
|
12
12
|
# The integration tests can be run against the Xero test environment. You mush have a company set up in the test
|
13
13
|
# environment, and you must have set up a customer key for that account.
|
14
14
|
#
|
15
15
|
# You can then run the tests against the test environment using the commands (linux or mac):
|
16
|
-
# export STUB_XERO_CALLS=false
|
16
|
+
# export STUB_XERO_CALLS=false
|
17
17
|
# rake test
|
18
18
|
# (this probably won't work under OAuth?)
|
19
19
|
#
|
20
|
-
|
20
|
+
|
21
21
|
STUB_XERO_CALLS = ENV["STUB_XERO_CALLS"].nil? ? true : (ENV["STUB_XERO_CALLS"] == "true") unless defined? STUB_XERO_CALLS
|
22
|
-
|
22
|
+
|
23
23
|
CONSUMER_KEY = ENV["CONSUMER_KEY"] || "fake_key" unless defined?(CONSUMER_KEY)
|
24
24
|
CONSUMER_SECRET = ENV["CONSUMER_SECRET"] || "fake_secret" unless defined?(CONSUMER_SECRET)
|
25
|
-
|
25
|
+
|
26
26
|
# Helper constant for checking regex
|
27
27
|
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)
|
28
28
|
|
29
|
-
|
29
|
+
|
30
30
|
def dummy_invoice(with_line_items = true)
|
31
31
|
invoice = XeroGateway::Invoice.new({
|
32
32
|
:invoice_type => "ACCREC",
|
@@ -50,7 +50,7 @@ module TestHelper
|
|
50
50
|
end
|
51
51
|
invoice
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
def dummy_credit_note(with_line_items = true)
|
55
55
|
credit_note = XeroGateway::CreditNote.new({
|
56
56
|
:type => "ACCRECCREDIT",
|
@@ -73,7 +73,7 @@ module TestHelper
|
|
73
73
|
end
|
74
74
|
credit_note
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
def dummy_contact
|
78
78
|
unique_id = Time.now.to_f
|
79
79
|
contact = XeroGateway::Contact.new(:name => STUB_XERO_CALLS ? "CONTACT NAME" : "THE NAME OF THE CONTACT #{unique_id}")
|
@@ -90,15 +90,19 @@ module TestHelper
|
|
90
90
|
|
91
91
|
contact
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
def get_file_as_string(filename)
|
95
95
|
data = ''
|
96
|
-
f = File.open(File.dirname(__FILE__) + "/stub_responses/" + filename, "r")
|
96
|
+
f = File.open(File.dirname(__FILE__) + "/stub_responses/" + filename, "r")
|
97
97
|
f.each_line do |line|
|
98
98
|
data += line
|
99
99
|
end
|
100
100
|
f.close
|
101
|
-
|
101
|
+
data
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_file(filename)
|
105
|
+
data = File.read( File.dirname(__FILE__) + "/stub_responses/" + filename)
|
102
106
|
end
|
103
107
|
|
104
108
|
def create_test_bank_transaction(params={}, contact_params={}, line_item_params={})
|
@@ -117,6 +121,37 @@ module TestHelper
|
|
117
121
|
bank_transaction
|
118
122
|
end
|
119
123
|
|
124
|
+
def create_test_payment(params={}, contact_params={})
|
125
|
+
params = {
|
126
|
+
:payment_id => 'a99a9aaa-9999-99a9-9aa9-aaaaaa9a9999',
|
127
|
+
:payment_type => 'ACCRECPAYMENT',
|
128
|
+
:updated_at => Time.now,
|
129
|
+
:reference => '12345',
|
130
|
+
:account_id => 'o99o9ooo-9999-99o9-9oo9-oooooo9o9999',
|
131
|
+
:amount => 1000.0,
|
132
|
+
:currency_rate => 1.0,
|
133
|
+
:date => Date.today.to_time,
|
134
|
+
:invoice_id => 'i99i9iii-9999-99i9-9ii9-iiiiii9i9999',
|
135
|
+
:invoice_number => 'INV-0001',
|
136
|
+
:reconciled => true,
|
137
|
+
}.merge(params)
|
138
|
+
XeroGateway::Payment.new(params)
|
139
|
+
end
|
140
|
+
|
141
|
+
def create_test_report_bank_statement(params={}, body_params={})
|
142
|
+
params = {
|
143
|
+
:report_id => 'BankStatement',
|
144
|
+
:report_name => 'BankStatement',
|
145
|
+
:report_type => 'BankStatement',
|
146
|
+
:report_titles => [],
|
147
|
+
:report_date => Date.today,
|
148
|
+
:updated_at => Time.now-3.days,
|
149
|
+
:column_names => [],
|
150
|
+
:body => []
|
151
|
+
}.merge(params)
|
152
|
+
XeroGateway::Report.new(params)
|
153
|
+
end
|
154
|
+
|
120
155
|
def add_test_line_items(bank_transaction, line_item_params={})
|
121
156
|
if line_item_params
|
122
157
|
line_item_params = [line_item_params].flatten # always use an array, even if only a single hash passed in
|
@@ -177,7 +212,7 @@ module TestHelper
|
|
177
212
|
end
|
178
213
|
|
179
214
|
def create_test_manual_journal(params={}, journal_line_params={})
|
180
|
-
params = {
|
215
|
+
params = {
|
181
216
|
:date => Date.today,
|
182
217
|
:narration => 'test narration',
|
183
218
|
:status => 'POSTED'
|
@@ -202,9 +237,9 @@ module TestHelper
|
|
202
237
|
journal_line_params[1] = {
|
203
238
|
:description => "SECOND LINE",
|
204
239
|
:account_code => "200",
|
205
|
-
:line_amount => BigDecimal.new("-100"),
|
240
|
+
:line_amount => BigDecimal.new("-100"),
|
206
241
|
:tracking => XeroGateway::TrackingCategory.new(:name => "blah2", :options => "hello2")
|
207
|
-
}.merge(params_line_1)
|
242
|
+
}.merge(params_line_1)
|
208
243
|
|
209
244
|
# Create line_items from line_item_params
|
210
245
|
journal_line_params.each do |journal_line|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../test_helper.rb')
|
2
|
+
|
3
|
+
class AddressTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
test "build and parse XML" do
|
6
|
+
address = create_test_address
|
7
|
+
|
8
|
+
# Generate the XML message
|
9
|
+
address_xml = address.to_xml
|
10
|
+
|
11
|
+
# Parse the XML message and retrieve the address element
|
12
|
+
address_element = REXML::XPath.first(REXML::Document.new(address_xml), "/Address")
|
13
|
+
|
14
|
+
# Build a new contact from the XML
|
15
|
+
result_address = XeroGateway::Address.from_xml(address_element)
|
16
|
+
|
17
|
+
# Check the contact details
|
18
|
+
assert_equal address, result_address
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def create_test_address
|
24
|
+
XeroGateway::Address.new(
|
25
|
+
line_1: "25 Taranaki St",
|
26
|
+
line_2: "Te Aro",
|
27
|
+
city: "Wellington",
|
28
|
+
post_code: "6011",
|
29
|
+
country: "New Zealand",
|
30
|
+
attention_to: "Hashigo Zake"
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -97,8 +97,15 @@ class BankTransactionTest < Test::Unit::TestCase
|
|
97
97
|
|
98
98
|
assert_xml_field bank_transaction_element, 'Url', :value => 'http://example.com\?with=params&and=more'
|
99
99
|
|
100
|
+
# test total without downloading each line items
|
101
|
+
total_elem = REXML::Element.new('Total')
|
102
|
+
total_elem.text = '1000'
|
103
|
+
bank_transaction_element.add_element(total_elem)
|
104
|
+
|
105
|
+
XeroGateway::BankTransaction.any_instance.stubs(:line_items_downloaded?).returns(false)
|
100
106
|
parsed_bank_transaction = XeroGateway::BankTransaction.from_xml(bank_transaction_element)
|
101
107
|
assert_equal 'http://example.com?with=params&and=more', parsed_bank_transaction.url
|
108
|
+
assert_equal BigDecimal.new('1000'), parsed_bank_transaction.total
|
102
109
|
end
|
103
110
|
|
104
111
|
should "ignore missing contact" do
|