xero_gateway-n8vision 2.0.20
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 +12 -0
- data/LICENSE +14 -0
- data/README.textile +357 -0
- data/Rakefile +14 -0
- data/examples/oauth.rb +25 -0
- data/examples/partner_app.rb +36 -0
- data/init.rb +1 -0
- data/lib/oauth/oauth_consumer.rb +14 -0
- data/lib/xero_gateway.rb +41 -0
- data/lib/xero_gateway/account.rb +86 -0
- data/lib/xero_gateway/accounts_list.rb +73 -0
- data/lib/xero_gateway/address.rb +96 -0
- data/lib/xero_gateway/bank_transaction.rb +175 -0
- data/lib/xero_gateway/ca-certificates.crt +2560 -0
- data/lib/xero_gateway/contact.rb +203 -0
- data/lib/xero_gateway/credit_note.rb +220 -0
- data/lib/xero_gateway/currency.rb +56 -0
- data/lib/xero_gateway/dates.rb +25 -0
- data/lib/xero_gateway/error.rb +18 -0
- data/lib/xero_gateway/exceptions.rb +51 -0
- data/lib/xero_gateway/gateway.rb +698 -0
- data/lib/xero_gateway/http.rb +135 -0
- data/lib/xero_gateway/http_encoding_helper.rb +49 -0
- data/lib/xero_gateway/invoice.rb +238 -0
- data/lib/xero_gateway/journal_line.rb +102 -0
- data/lib/xero_gateway/line_item.rb +125 -0
- data/lib/xero_gateway/line_item_calculations.rb +51 -0
- data/lib/xero_gateway/manual_journal.rb +163 -0
- data/lib/xero_gateway/money.rb +16 -0
- data/lib/xero_gateway/oauth.rb +92 -0
- data/lib/xero_gateway/organisation.rb +75 -0
- data/lib/xero_gateway/partner_app.rb +30 -0
- data/lib/xero_gateway/payment.rb +43 -0
- data/lib/xero_gateway/phone.rb +77 -0
- data/lib/xero_gateway/private_app.rb +17 -0
- data/lib/xero_gateway/response.rb +43 -0
- data/lib/xero_gateway/tax_rate.rb +63 -0
- data/lib/xero_gateway/tracking_category.rb +87 -0
- data/test/integration/accounts_list_test.rb +109 -0
- data/test/integration/create_bank_transaction_test.rb +38 -0
- data/test/integration/create_contact_test.rb +66 -0
- data/test/integration/create_credit_note_test.rb +49 -0
- data/test/integration/create_invoice_test.rb +49 -0
- data/test/integration/create_manual_journal_test.rb +35 -0
- data/test/integration/get_accounts_test.rb +23 -0
- data/test/integration/get_bank_transaction_test.rb +51 -0
- data/test/integration/get_bank_transactions_test.rb +88 -0
- data/test/integration/get_contact_test.rb +28 -0
- data/test/integration/get_contacts_test.rb +40 -0
- data/test/integration/get_credit_note_test.rb +48 -0
- data/test/integration/get_credit_notes_test.rb +90 -0
- data/test/integration/get_currencies_test.rb +25 -0
- data/test/integration/get_invoice_test.rb +48 -0
- data/test/integration/get_invoices_test.rb +92 -0
- data/test/integration/get_manual_journal_test.rb +50 -0
- data/test/integration/get_manual_journals_test.rb +88 -0
- data/test/integration/get_organisation_test.rb +24 -0
- data/test/integration/get_tax_rates_test.rb +25 -0
- data/test/integration/get_tracking_categories_test.rb +27 -0
- data/test/integration/update_bank_transaction_test.rb +31 -0
- data/test/integration/update_contact_test.rb +31 -0
- data/test/integration/update_invoice_test.rb +31 -0
- data/test/integration/update_manual_journal_test.rb +31 -0
- data/test/test_helper.rb +217 -0
- data/test/unit/account_test.rb +47 -0
- data/test/unit/bank_transaction_test.rb +126 -0
- data/test/unit/contact_test.rb +97 -0
- data/test/unit/credit_note_test.rb +284 -0
- data/test/unit/currency_test.rb +31 -0
- data/test/unit/gateway_test.rb +119 -0
- data/test/unit/invoice_test.rb +326 -0
- data/test/unit/manual_journal_test.rb +93 -0
- data/test/unit/oauth_test.rb +116 -0
- data/test/unit/organisation_test.rb +38 -0
- data/test/unit/tax_rate_test.rb +38 -0
- data/test/unit/tracking_category_test.rb +52 -0
- data/xero_gateway-n8vision.gemspec +15 -0
- metadata +178 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Organisation
|
3
|
+
|
4
|
+
unless defined? ATTRS
|
5
|
+
ATTRS = {
|
6
|
+
"Name" => :string, # Display name of organisation shown in Xero
|
7
|
+
"LegalName" => :string, # Organisation name shown on Reports
|
8
|
+
"PaysTax" => :boolean, # Boolean to describe if organisation is registered with a local tax authority i.e. true, false
|
9
|
+
"Version" => :string, # See Version Types
|
10
|
+
"BaseCurrency" => :string, # Default currency for organisation. See Currency types
|
11
|
+
"OrganisationType" => :string, # UNDOCUMENTED parameter, only returned for "real" (i.e non-demo) companies
|
12
|
+
"OrganisationStatus" => :string, # UNDOCUMENTED parameter
|
13
|
+
"IsDemoCompany" => :boolean, # UNDOCUMENTED parameter
|
14
|
+
"APIKey" => :string, # UNDOCUMENTED paramater, returned if organisations are linked via Xero Network
|
15
|
+
"CountryCode" => :string, # UNDOCUMENTED parameter
|
16
|
+
"TaxNumber" => :string,
|
17
|
+
"FinancialYearEndDay" => :string,
|
18
|
+
"FinancialYearEndMonth" => :string,
|
19
|
+
"PeriodLockDate" => :string,
|
20
|
+
"CreatedDateUTC" => :string
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor *ATTRS.keys.map(&:underscore)
|
25
|
+
|
26
|
+
def initialize(params = {})
|
27
|
+
params.each do |k,v|
|
28
|
+
self.send("#{k}=", v)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
ATTRS.keys.map(&:underscore).each do |field|
|
34
|
+
return false if send(field) != other.send(field)
|
35
|
+
end
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_xml
|
40
|
+
b = Builder::XmlMarkup.new
|
41
|
+
|
42
|
+
b.Organisation do
|
43
|
+
ATTRS.keys.each do |attr|
|
44
|
+
eval("b.#{attr} '#{self.send(attr.underscore.to_sym)}'")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.from_xml(organisation_element)
|
50
|
+
Organisation.new.tap do |org|
|
51
|
+
organisation_element.children.each do |element|
|
52
|
+
|
53
|
+
attribute = element.name
|
54
|
+
underscored_attribute = element.name.underscore
|
55
|
+
|
56
|
+
if ATTRS.keys.include?(attribute)
|
57
|
+
|
58
|
+
case (ATTRS[attribute])
|
59
|
+
when :boolean then org.send("#{underscored_attribute}=", (element.text == "true"))
|
60
|
+
when :float then org.send("#{underscored_attribute}=", element.text.to_f)
|
61
|
+
else org.send("#{underscored_attribute}=", element.text)
|
62
|
+
end
|
63
|
+
|
64
|
+
else
|
65
|
+
|
66
|
+
warn "Ignoring unknown attribute: #{attribute}"
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class PartnerApp < Gateway
|
3
|
+
|
4
|
+
class CertificateRequired < StandardError; end
|
5
|
+
|
6
|
+
NO_SSL_CLIENT_CERT_MESSAGE = "You need to provide a client ssl certificate and key pair (these are the ones you got from Entrust and should not be password protected) as :ssl_client_cert and :ssl_client_key (should be .crt or .pem files)"
|
7
|
+
NO_PRIVATE_KEY_ERROR_MESSAGE = "You need to provide your private key (corresponds to the public key you uploaded at api.xero.com) as :private_key_file (should be .crt or .pem files)"
|
8
|
+
|
9
|
+
def_delegators :client, :session_handle, :renew_access_token
|
10
|
+
|
11
|
+
def initialize(consumer_key, consumer_secret, options = {})
|
12
|
+
|
13
|
+
raise CertificateRequired.new(NO_SSL_CLIENT_CERT_MESSAGE) unless options[:ssl_client_cert]
|
14
|
+
raise CertificateRequired.new(NO_SSL_CLIENT_CERT_MESSAGE) unless options[:ssl_client_key]
|
15
|
+
raise CertificateRequired.new(NO_PRIVATE_KEY_ERROR_MESSAGE) unless options[:private_key_file]
|
16
|
+
|
17
|
+
options.merge!(
|
18
|
+
:site => "https://api-partner.network.xero.com",
|
19
|
+
:authorize_url => 'https://api.xero.com/oauth/Authorize',
|
20
|
+
:signature_method => 'RSA-SHA1',
|
21
|
+
:ssl_client_cert => OpenSSL::X509::Certificate.new(File.read(options[:ssl_client_cert])),
|
22
|
+
:ssl_client_key => OpenSSL::PKey::RSA.new(File.read(options[:ssl_client_key])),
|
23
|
+
:private_key_file => options[:private_key_file]
|
24
|
+
)
|
25
|
+
|
26
|
+
@xero_url = options[:xero_url] || "https://api-partner.xero.com/api.xro/2.0"
|
27
|
+
@client = OAuth.new(consumer_key, consumer_secret, options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Payment
|
3
|
+
include Money
|
4
|
+
include Dates
|
5
|
+
|
6
|
+
# Any errors that occurred when the #valid? method called.
|
7
|
+
attr_reader :errors
|
8
|
+
|
9
|
+
# All accessible fields
|
10
|
+
attr_accessor :payment_id, :date, :amount, :reference, :currency_rate
|
11
|
+
|
12
|
+
def initialize(params = {})
|
13
|
+
@errors ||= []
|
14
|
+
|
15
|
+
params.each do |k,v|
|
16
|
+
self.send("#{k}=", v)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_xml(payment_element)
|
21
|
+
payment = Payment.new
|
22
|
+
payment_element.children.each do | element |
|
23
|
+
case element.name
|
24
|
+
when 'PaymentID' then payment.payment_id = element.text
|
25
|
+
when 'Date' then payment.date = parse_date_time(element.text)
|
26
|
+
when 'Amount' then payment.amount = BigDecimal.new(element.text)
|
27
|
+
when 'Reference' then payment.reference = element.text
|
28
|
+
when 'CurrencyRate' then payment.currency_rate = BigDecimal.new(element.text)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
payment
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
[:payment_id, :date, :amount].each do |field|
|
36
|
+
return false if send(field) != other.send(field)
|
37
|
+
end
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Phone
|
3
|
+
|
4
|
+
PHONE_TYPE = {
|
5
|
+
'DEFAULT' => 'Default',
|
6
|
+
'DDI' => 'Direct Dial-In',
|
7
|
+
'MOBILE' => 'Mobile',
|
8
|
+
'FAX' => 'Fax'
|
9
|
+
} unless defined?(PHONE_TYPE)
|
10
|
+
|
11
|
+
# Any errors that occurred when the #valid? method called.
|
12
|
+
attr_reader :errors
|
13
|
+
|
14
|
+
attr_accessor :phone_type, :number, :area_code, :country_code
|
15
|
+
|
16
|
+
def initialize(params = {})
|
17
|
+
@errors ||= []
|
18
|
+
|
19
|
+
params = {
|
20
|
+
:phone_type => "DEFAULT"
|
21
|
+
}.merge(params)
|
22
|
+
|
23
|
+
params.each do |k,v|
|
24
|
+
self.send("#{k}=", v)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Validate the Phone record according to what will be valid by the gateway.
|
29
|
+
#
|
30
|
+
# Usage:
|
31
|
+
# phone.valid? # Returns true/false
|
32
|
+
#
|
33
|
+
# Additionally sets phone.errors array to an array of field/error.
|
34
|
+
def valid?
|
35
|
+
@errors = []
|
36
|
+
|
37
|
+
unless number
|
38
|
+
@errors << ['number', "can't be blank"]
|
39
|
+
end
|
40
|
+
|
41
|
+
if phone_type && !PHONE_TYPE[phone_type]
|
42
|
+
@errors << ['phone_type', "must be one of #{PHONE_TYPE.keys.join('/')}"]
|
43
|
+
end
|
44
|
+
|
45
|
+
@errors.size == 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
49
|
+
b.Phone {
|
50
|
+
b.PhoneType phone_type
|
51
|
+
b.PhoneNumber number
|
52
|
+
b.PhoneAreaCode area_code if area_code
|
53
|
+
b.PhoneCountryCode country_code if country_code
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.from_xml(phone_element)
|
58
|
+
phone = Phone.new
|
59
|
+
phone_element.children.each do |element|
|
60
|
+
case(element.name)
|
61
|
+
when "PhoneType" then phone.phone_type = element.text
|
62
|
+
when "PhoneNumber" then phone.number = element.text
|
63
|
+
when "PhoneAreaCode" then phone.area_code = element.text
|
64
|
+
when "PhoneCountryCode" then phone.country_code = element.text
|
65
|
+
end
|
66
|
+
end
|
67
|
+
phone
|
68
|
+
end
|
69
|
+
|
70
|
+
def ==(other)
|
71
|
+
[:phone_type, :number, :area_code, :country_code].each do |field|
|
72
|
+
return false if send(field) != other.send(field)
|
73
|
+
end
|
74
|
+
return true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class PrivateApp < Gateway
|
3
|
+
#
|
4
|
+
# The consumer key and secret here correspond to those provided
|
5
|
+
# to you by Xero inside the API Previewer.
|
6
|
+
def initialize(consumer_key, consumer_secret, path_to_private_key, options = {})
|
7
|
+
options.merge!(
|
8
|
+
:signature_method => 'RSA-SHA1',
|
9
|
+
:private_key_file => path_to_private_key
|
10
|
+
)
|
11
|
+
|
12
|
+
@xero_url = options[:xero_url] || "https://api.xero.com/api.xro/2.0"
|
13
|
+
@client = OAuth.new(consumer_key, consumer_secret, options)
|
14
|
+
@client.authorize_from_access(consumer_key, consumer_secret)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class Response
|
3
|
+
attr_accessor :response_id, :status, :errors, :provider, :date_time, :response_item, :request_params, :request_xml, :response_xml
|
4
|
+
|
5
|
+
def array_wrapped_response_item
|
6
|
+
Array(response_item)
|
7
|
+
end
|
8
|
+
|
9
|
+
alias_method :invoice, :response_item
|
10
|
+
alias_method :credit_note, :response_item
|
11
|
+
alias_method :bank_transaction, :response_item
|
12
|
+
alias_method :manual_journal, :response_item
|
13
|
+
alias_method :contact, :response_item
|
14
|
+
alias_method :organisation, :response_item
|
15
|
+
alias_method :invoices, :array_wrapped_response_item
|
16
|
+
alias_method :credit_notes, :array_wrapped_response_item
|
17
|
+
alias_method :bank_transactions, :array_wrapped_response_item
|
18
|
+
alias_method :manual_journals, :array_wrapped_response_item
|
19
|
+
alias_method :contacts, :array_wrapped_response_item
|
20
|
+
alias_method :accounts, :array_wrapped_response_item
|
21
|
+
alias_method :tracking_categories, :array_wrapped_response_item
|
22
|
+
alias_method :tax_rates, :array_wrapped_response_item
|
23
|
+
alias_method :currencies, :array_wrapped_response_item
|
24
|
+
|
25
|
+
def initialize(params = {})
|
26
|
+
params.each do |k,v|
|
27
|
+
self.send("#{k}=", v)
|
28
|
+
end
|
29
|
+
|
30
|
+
@errors ||= []
|
31
|
+
@response_item ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def success?
|
36
|
+
status == "OK"
|
37
|
+
end
|
38
|
+
|
39
|
+
def error
|
40
|
+
errors.blank? ? nil : errors[0]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
class TaxRate
|
3
|
+
|
4
|
+
unless defined? ATTRS
|
5
|
+
ATTRS = {
|
6
|
+
"Name" => :string,
|
7
|
+
"TaxType" => :string,
|
8
|
+
"CanApplyToAssets" => :boolean,
|
9
|
+
"CanApplyToEquity" => :boolean,
|
10
|
+
"CanApplyToExpenses" => :boolean,
|
11
|
+
"CanApplyToLiabilities" => :boolean,
|
12
|
+
"CanApplyToRevenue" => :boolean,
|
13
|
+
"DisplayTaxRate" => :float,
|
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
|
+
raise "Unknown attribute: #{attribute}" unless 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
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Tacking categories look like:
|
2
|
+
#
|
3
|
+
# <TrackingCategory>
|
4
|
+
# <Name>Region</Name>
|
5
|
+
# <Status>ACTIVE</Status>
|
6
|
+
# <TrackingCategoryID>e4a95e64-ebaa-401e-81cf-b625c7532d01</TrackingCategoryID>
|
7
|
+
# <Options>
|
8
|
+
# <Option>
|
9
|
+
# <TrackingOptionID>ea7f7b6a-0d22-4d5c-9317-54542a10215e</TrackingOptionID>
|
10
|
+
# <Name>North</Name>
|
11
|
+
# </Option>
|
12
|
+
# <Option>
|
13
|
+
# <TrackingOptionID>8e8b8d7b-fa75-4b24-b429-8cbc1a21af23</TrackingOptionID>
|
14
|
+
# <Name>South</Name>
|
15
|
+
# </Option>
|
16
|
+
# </Options>
|
17
|
+
# </TrackingCategory>
|
18
|
+
#
|
19
|
+
module XeroGateway
|
20
|
+
class TrackingCategory
|
21
|
+
attr_accessor :tracking_category_id, :name, :options
|
22
|
+
|
23
|
+
def initialize(params = {})
|
24
|
+
@options = []
|
25
|
+
params.each do |k,v|
|
26
|
+
self.send("#{k}=", v)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def option
|
31
|
+
options[0] if options.size == 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
35
|
+
b.TrackingCategory {
|
36
|
+
b.TrackingCategoryID tracking_category_id unless tracking_category_id.nil?
|
37
|
+
b.Name self.name
|
38
|
+
b.Options {
|
39
|
+
if self.options.is_a?(Array)
|
40
|
+
self.options.each do |option|
|
41
|
+
b.Option {
|
42
|
+
b.Name option
|
43
|
+
}
|
44
|
+
end
|
45
|
+
else
|
46
|
+
b.Option {
|
47
|
+
b.Name self.options.to_s
|
48
|
+
}
|
49
|
+
end
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# When a tracking category is serialized as part of an invoice it may only have a single
|
55
|
+
# option, and the Options tag is omitted
|
56
|
+
def to_xml_for_invoice_messages(b = Builder::XmlMarkup.new)
|
57
|
+
b.TrackingCategory {
|
58
|
+
b.TrackingCategoryID self.tracking_category_id unless tracking_category_id.nil?
|
59
|
+
b.Name self.name
|
60
|
+
b.Option self.options.is_a?(Array) ? self.options.first : self.options.to_s
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.from_xml(tracking_category_element)
|
65
|
+
tracking_category = TrackingCategory.new
|
66
|
+
tracking_category_element.children.each do |element|
|
67
|
+
case(element.name)
|
68
|
+
when "TrackingCategoryID" then tracking_category.tracking_category_id = element.text
|
69
|
+
when "Name" then tracking_category.name = element.text
|
70
|
+
when "Options" then
|
71
|
+
element.children.each do |option_child|
|
72
|
+
tracking_category.options << option_child.children.detect {|c| c.name == "Name"}.text
|
73
|
+
end
|
74
|
+
when "Option" then tracking_category.options << element.text
|
75
|
+
end
|
76
|
+
end
|
77
|
+
tracking_category
|
78
|
+
end
|
79
|
+
|
80
|
+
def ==(other)
|
81
|
+
[:tracking_category_id, :name, :options].each do |field|
|
82
|
+
return false if send(field) != other.send(field)
|
83
|
+
end
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class AccountsListTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = XeroGateway::Gateway.new(CONSUMER_KEY, CONSUMER_SECRET)
|
8
|
+
|
9
|
+
# Always stub out calls for this integration test as we need to be able to control the data.
|
10
|
+
@gateway.xero_url = "DUMMY_URL"
|
11
|
+
|
12
|
+
@gateway.stubs(:http_get).with {|client, url, params| url =~ /Accounts$/ }.returns(get_file_as_string("accounts.xml"))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_get_accounts_list
|
16
|
+
accounts_list = @gateway.get_accounts_list
|
17
|
+
assert_not_equal(0, accounts_list.accounts.size)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Make sure that the list is loaded when finding things.
|
21
|
+
def test_raise_error_on_not_loaded
|
22
|
+
accounts_list = @gateway.get_accounts_list(false)
|
23
|
+
assert_equal(false, accounts_list.loaded?)
|
24
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list[200] }
|
25
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_by_code(200) }
|
26
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_all_by_type('EXPENSE') }
|
27
|
+
assert_raise(XeroGateway::AccountsListNotLoadedError) { accounts_list.find_all_by_tax_type('OUTPUT') }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Test simple lookup by account code (from cache).
|
31
|
+
def test_simple_lookup_by_account_code
|
32
|
+
accounts_list = @gateway.get_accounts_list
|
33
|
+
assert_equal(true, accounts_list.loaded?)
|
34
|
+
|
35
|
+
# Load data in the stubbed response.
|
36
|
+
expected_accounts = accounts_as_array
|
37
|
+
|
38
|
+
# Make sure that every single expected account exists in the cached lookup hash.
|
39
|
+
expected_accounts.each do | expected_account |
|
40
|
+
found_account = accounts_list.find_by_code(expected_account.code)
|
41
|
+
assert_kind_of(XeroGateway::Account, found_account)
|
42
|
+
assert(expected_account == found_account, "Found account does not match expected account.")
|
43
|
+
|
44
|
+
found_account_shortcut = accounts_list[expected_account.code]
|
45
|
+
assert_kind_of(XeroGateway::Account, found_account_shortcut)
|
46
|
+
assert(expected_account == found_account_shortcut, "Found account does not match expected account (shortcut).")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Test finding accounts by their account type (from cache).
|
51
|
+
def test_lookup_by_account_type
|
52
|
+
accounts_list = @gateway.get_accounts_list
|
53
|
+
assert_equal(true, accounts_list.loaded?)
|
54
|
+
|
55
|
+
# Load data in the stubbed response.
|
56
|
+
expected_accounts = accounts_as_array
|
57
|
+
|
58
|
+
# Get all the unique account types present in the expected accounts data along with their counts.
|
59
|
+
unique_types = expected_accounts.inject({}) do | list, account |
|
60
|
+
list[account.type] = 0 if list[account.type].nil?
|
61
|
+
list[account.type] += 1
|
62
|
+
list
|
63
|
+
end
|
64
|
+
|
65
|
+
assert_not_equal(0, unique_types)
|
66
|
+
unique_types.each do | account_type, count |
|
67
|
+
found_accounts = accounts_list.find_all_by_type(account_type)
|
68
|
+
assert_equal(count, found_accounts.size)
|
69
|
+
found_accounts.each do | found_account |
|
70
|
+
assert_kind_of(XeroGateway::Account, found_account)
|
71
|
+
assert_equal(account_type, found_account.type)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Test finding accounts by their tax type (from cache).
|
77
|
+
def test_lookup_by_tax_type
|
78
|
+
accounts_list = @gateway.get_accounts_list
|
79
|
+
assert_equal(true, accounts_list.loaded?)
|
80
|
+
|
81
|
+
# Load data in the stubbed response.
|
82
|
+
expected_accounts = accounts_as_array
|
83
|
+
|
84
|
+
# Get all the unique tax types present in the expected accounts data along with their counts.
|
85
|
+
unique_types = expected_accounts.inject({}) do | list, account |
|
86
|
+
list[account.tax_type] = 0 if list[account.tax_type].nil?
|
87
|
+
list[account.tax_type] += 1
|
88
|
+
list
|
89
|
+
end
|
90
|
+
|
91
|
+
assert_not_equal(0, unique_types)
|
92
|
+
unique_types.each do | tax_type, count |
|
93
|
+
found_accounts = accounts_list.find_all_by_tax_type(tax_type)
|
94
|
+
assert_equal(count, found_accounts.size)
|
95
|
+
found_accounts.each do | found_account |
|
96
|
+
assert_kind_of(XeroGateway::Account, found_account)
|
97
|
+
assert_equal(tax_type, found_account.tax_type)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def accounts_as_array
|
105
|
+
response = @gateway.__send__(:parse_response, get_file_as_string("accounts.xml"))
|
106
|
+
response.accounts
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|