xero_gateway 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|