xero_gateway 2.0.2
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/CHANGELOG.textile +51 -0
- data/LICENSE +14 -0
- data/README.textile +289 -0
- data/Rakefile +14 -0
- data/examples/oauth.rb +25 -0
- data/init.rb +1 -0
- data/lib/xero_gateway/account.rb +78 -0
- data/lib/xero_gateway/accounts_list.rb +77 -0
- data/lib/xero_gateway/address.rb +97 -0
- data/lib/xero_gateway/ca-certificates.crt +2560 -0
- data/lib/xero_gateway/contact.rb +206 -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 +41 -0
- data/lib/xero_gateway/gateway.rb +363 -0
- data/lib/xero_gateway/http.rb +128 -0
- data/lib/xero_gateway/http_encoding_helper.rb +49 -0
- data/lib/xero_gateway/invoice.rb +278 -0
- data/lib/xero_gateway/line_item.rb +123 -0
- data/lib/xero_gateway/money.rb +16 -0
- data/lib/xero_gateway/oauth.rb +56 -0
- data/lib/xero_gateway/organisation.rb +61 -0
- data/lib/xero_gateway/payment.rb +40 -0
- data/lib/xero_gateway/phone.rb +77 -0
- data/lib/xero_gateway/private_app.rb +17 -0
- data/lib/xero_gateway/response.rb +37 -0
- data/lib/xero_gateway/tax_rate.rb +63 -0
- data/lib/xero_gateway/tracking_category.rb +62 -0
- data/lib/xero_gateway.rb +33 -0
- data/test/integration/accounts_list_test.rb +109 -0
- data/test/integration/create_contact_test.rb +66 -0
- data/test/integration/create_invoice_test.rb +49 -0
- data/test/integration/get_accounts_test.rb +23 -0
- data/test/integration/get_contact_test.rb +28 -0
- data/test/integration/get_contacts_test.rb +40 -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 +90 -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 +26 -0
- data/test/integration/update_contact_test.rb +31 -0
- data/test/stub_responses/accounts.xml +1 -0
- data/test/stub_responses/api_exception.xml +153 -0
- data/test/stub_responses/contact.xml +1 -0
- data/test/stub_responses/contacts.xml +2189 -0
- data/test/stub_responses/create_invoice.xml +64 -0
- data/test/stub_responses/currencies.xml +16 -0
- data/test/stub_responses/invalid_api_key_error.xml +1 -0
- data/test/stub_responses/invalid_consumer_key +1 -0
- data/test/stub_responses/invalid_request_token +1 -0
- data/test/stub_responses/invoice.xml +1 -0
- data/test/stub_responses/invoice_not_found_error.xml +1 -0
- data/test/stub_responses/invoices.xml +1 -0
- data/test/stub_responses/organisation.xml +14 -0
- data/test/stub_responses/tax_rates.xml +52 -0
- data/test/stub_responses/token_expired +1 -0
- data/test/stub_responses/tracking_categories.xml +1 -0
- data/test/stub_responses/unknown_error.xml +1 -0
- data/test/test_helper.rb +81 -0
- data/test/unit/account_test.rb +34 -0
- data/test/unit/contact_test.rb +97 -0
- data/test/unit/currency_test.rb +31 -0
- data/test/unit/gateway_test.rb +79 -0
- data/test/unit/invoice_test.rb +302 -0
- data/test/unit/oauth_test.rb +110 -0
- data/test/unit/organisation_test.rb +34 -0
- data/test/unit/tax_rate_test.rb +38 -0
- data/test/unit/tracking_category_test.rb +30 -0
- data/test/xsd/README +2 -0
- data/test/xsd/create_contact.xsd +61 -0
- data/test/xsd/create_invoice.xsd +107 -0
- data/xero_gateway.gemspec +87 -0
- metadata +172 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'account')
|
2
|
+
|
3
|
+
module XeroGateway
|
4
|
+
class LineItem
|
5
|
+
include Money
|
6
|
+
|
7
|
+
TAX_TYPE = Account::TAX_TYPE unless defined?(TAX_TYPE)
|
8
|
+
|
9
|
+
# Any errors that occurred when the #valid? method called.
|
10
|
+
attr_reader :errors
|
11
|
+
|
12
|
+
# All accessible fields
|
13
|
+
attr_accessor :line_item_id, :description, :quantity, :unit_amount, :tax_type, :tax_amount, :account_code, :tracking
|
14
|
+
|
15
|
+
def initialize(params = {})
|
16
|
+
@errors ||= []
|
17
|
+
@tracking ||= []
|
18
|
+
@quantity = 1
|
19
|
+
@unit_amount = BigDecimal.new('0')
|
20
|
+
|
21
|
+
params.each do |k,v|
|
22
|
+
self.send("#{k}=", v)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validate the LineItem record according to what will be valid by the gateway.
|
27
|
+
#
|
28
|
+
# Usage:
|
29
|
+
# line_item.valid? # Returns true/false
|
30
|
+
#
|
31
|
+
# Additionally sets line_item.errors array to an array of field/error.
|
32
|
+
def valid?
|
33
|
+
@errors = []
|
34
|
+
|
35
|
+
if !line_item_id.nil? && line_item_id !~ GUID_REGEX
|
36
|
+
@errors << ['line_item_id', 'must be blank or a valid Xero GUID']
|
37
|
+
end
|
38
|
+
|
39
|
+
unless description
|
40
|
+
@errors << ['description', "can't be blank"]
|
41
|
+
end
|
42
|
+
|
43
|
+
if tax_type && !TAX_TYPE[tax_type]
|
44
|
+
@errors << ['tax_type', "must be one of #{TAX_TYPE.keys.join('/')}"]
|
45
|
+
end
|
46
|
+
|
47
|
+
@errors.size == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_tracking?
|
51
|
+
return false if tracking.nil?
|
52
|
+
|
53
|
+
if tracking.is_a?(Array)
|
54
|
+
return tracking.any?
|
55
|
+
else
|
56
|
+
return tracking.is_a?(TrackingCategory)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Deprecated (but API for setter remains).
|
61
|
+
#
|
62
|
+
# As line_amount must equal quantity * unit_amount for the API call to pass, this is now
|
63
|
+
# automatically calculated in the line_amount method.
|
64
|
+
def line_amount=(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Calculate the line_amount as quantity * unit_amount as this value must be correct
|
68
|
+
# for the API call to succeed.
|
69
|
+
def line_amount
|
70
|
+
quantity * unit_amount
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_xml(b = Builder::XmlMarkup.new)
|
74
|
+
b.LineItem {
|
75
|
+
b.Description description
|
76
|
+
b.Quantity quantity if quantity
|
77
|
+
b.UnitAmount LineItem.format_money(unit_amount)
|
78
|
+
b.TaxType tax_type if tax_type
|
79
|
+
b.TaxAmount tax_amount if tax_amount
|
80
|
+
b.LineAmount line_amount if line_amount
|
81
|
+
b.AccountCode account_code if account_code
|
82
|
+
if has_tracking?
|
83
|
+
b.Tracking {
|
84
|
+
# Due to strange retardness in the Xero API, the XML structure for a tracking category within
|
85
|
+
# an invoice is different to a standalone tracking category.
|
86
|
+
# This means rather than going category.to_xml we need to call the special category.to_xml_for_invoice_messages
|
87
|
+
(tracking.is_a?(TrackingCategory) ? [tracking] : tracking).each do |category|
|
88
|
+
category.to_xml_for_invoice_messages(b)
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.from_xml(line_item_element)
|
96
|
+
line_item = LineItem.new
|
97
|
+
line_item_element.children.each do |element|
|
98
|
+
case(element.name)
|
99
|
+
when "LineItemID" then line_item.line_item_id = element.text
|
100
|
+
when "Description" then line_item.description = element.text
|
101
|
+
when "Quantity" then line_item.quantity = element.text.to_i
|
102
|
+
when "UnitAmount" then line_item.unit_amount = BigDecimal.new(element.text)
|
103
|
+
when "TaxType" then line_item.tax_type = element.text
|
104
|
+
when "TaxAmount" then line_item.tax_amount = BigDecimal.new(element.text)
|
105
|
+
when "LineAmount" then line_item.line_amount = BigDecimal.new(element.text)
|
106
|
+
when "AccountCode" then line_item.account_code = element.text
|
107
|
+
when "Tracking" then
|
108
|
+
element.children.each do | tracking_element |
|
109
|
+
line_item.tracking << TrackingCategory.from_xml(tracking_element)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
line_item
|
114
|
+
end
|
115
|
+
|
116
|
+
def ==(other)
|
117
|
+
[:description, :quantity, :unit_amount, :tax_type, :tax_amount, :line_amount, :account_code].each do |field|
|
118
|
+
return false if send(field) != other.send(field)
|
119
|
+
end
|
120
|
+
return true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
module Money
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def format_money(amount)
|
9
|
+
if amount.class == BigDecimal
|
10
|
+
return amount.to_s("F")
|
11
|
+
end
|
12
|
+
return amount
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module XeroGateway
|
2
|
+
|
3
|
+
# Shamelessly based on the Twitter Gem's OAuth implementation by John Nunemaker
|
4
|
+
# Thanks!
|
5
|
+
#
|
6
|
+
# http://twitter.rubyforge.org/
|
7
|
+
# http://github.com/jnunemaker/twitter/
|
8
|
+
|
9
|
+
class OAuth
|
10
|
+
|
11
|
+
class TokenExpired < StandardError; end
|
12
|
+
class TokenInvalid < StandardError; end
|
13
|
+
|
14
|
+
unless defined? XERO_CONSUMER_OPTIONS
|
15
|
+
XERO_CONSUMER_OPTIONS = {
|
16
|
+
:site => "https://api.xero.com",
|
17
|
+
:request_token_path => "/oauth/RequestToken",
|
18
|
+
:access_token_path => "/oauth/AccessToken",
|
19
|
+
:authorize_path => "/oauth/Authorize"
|
20
|
+
}.freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
extend Forwardable
|
24
|
+
def_delegators :access_token, :get, :post, :put, :delete
|
25
|
+
|
26
|
+
attr_reader :ctoken, :csecret, :consumer_options
|
27
|
+
|
28
|
+
def initialize(ctoken, csecret, options = {})
|
29
|
+
@ctoken, @csecret = ctoken, csecret
|
30
|
+
@consumer_options = XERO_CONSUMER_OPTIONS.merge(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def consumer
|
34
|
+
@consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, consumer_options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def request_token(params = {})
|
38
|
+
@request_token ||= consumer.get_request_token(params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def authorize_from_request(rtoken, rsecret, params = {})
|
42
|
+
request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
|
43
|
+
access_token = request_token.get_access_token(params)
|
44
|
+
@atoken, @asecret = access_token.token, access_token.secret
|
45
|
+
end
|
46
|
+
|
47
|
+
def access_token
|
48
|
+
@access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
|
49
|
+
end
|
50
|
+
|
51
|
+
def authorize_from_access(atoken, asecret)
|
52
|
+
@atoken, @asecret = atoken, asecret
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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
|
+
"APIKey" => :string # UNDOCUMENTED paramater, returned if organisations are linked via Xero Network
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor *ATTRS.keys.map(&:underscore)
|
17
|
+
|
18
|
+
def initialize(params = {})
|
19
|
+
params.each do |k,v|
|
20
|
+
self.send("#{k}=", v)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
ATTRS.keys.map(&:underscore).each do |field|
|
26
|
+
return false if send(field) != other.send(field)
|
27
|
+
end
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_xml
|
32
|
+
b = Builder::XmlMarkup.new
|
33
|
+
|
34
|
+
b.Organisation do
|
35
|
+
ATTRS.keys.each do |attr|
|
36
|
+
eval("b.#{attr} '#{self.send(attr.underscore.to_sym)}'")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_xml(organisation_element)
|
42
|
+
Organisation.new.tap do |org|
|
43
|
+
organisation_element.children.each do |element|
|
44
|
+
|
45
|
+
attribute = element.name
|
46
|
+
underscored_attribute = element.name.underscore
|
47
|
+
|
48
|
+
raise "Unknown attribute: #{attribute}" unless ATTRS.keys.include?(attribute)
|
49
|
+
|
50
|
+
case (ATTRS[attribute])
|
51
|
+
when :boolean then org.send("#{underscored_attribute}=", (element.text == "true"))
|
52
|
+
when :float then org.send("#{underscored_attribute}=", element.text.to_f)
|
53
|
+
else org.send("#{underscored_attribute}=", element.text)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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 :date, :amount
|
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 'Date' then payment.date = parse_date_time(element.text)
|
25
|
+
when 'Amount' then payment.amount = BigDecimal.new(element.text)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
payment
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
[:date, :amount].each do |field|
|
33
|
+
return false if send(field) != other.send(field)
|
34
|
+
end
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
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,37 @@
|
|
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 :contact, :response_item
|
11
|
+
alias_method :organisation, :response_item
|
12
|
+
alias_method :invoices, :array_wrapped_response_item
|
13
|
+
alias_method :contacts, :array_wrapped_response_item
|
14
|
+
alias_method :accounts, :array_wrapped_response_item
|
15
|
+
alias_method :tracking_categories, :array_wrapped_response_item
|
16
|
+
alias_method :tax_rates, :array_wrapped_response_item
|
17
|
+
alias_method :currencies, :array_wrapped_response_item
|
18
|
+
|
19
|
+
def initialize(params = {})
|
20
|
+
params.each do |k,v|
|
21
|
+
self.send("#{k}=", v)
|
22
|
+
end
|
23
|
+
|
24
|
+
@errors ||= []
|
25
|
+
@response_item ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def success?
|
30
|
+
status == "OK"
|
31
|
+
end
|
32
|
+
|
33
|
+
def error
|
34
|
+
errors.blank? ? nil : errors[0]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
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
|