xero_gateway 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +87 -82
- data/examples/partner_app.rb +0 -4
- data/examples/private_app.rb +18 -0
- data/lib/xero_gateway/account.rb +29 -12
- data/lib/xero_gateway/bank_transaction.rb +6 -11
- data/lib/xero_gateway/base_record.rb +26 -7
- data/lib/xero_gateway/contact.rb +40 -38
- data/lib/xero_gateway/contact_group.rb +12 -10
- data/lib/xero_gateway/credit_note.rb +42 -50
- data/lib/xero_gateway/gateway.rb +10 -7
- data/lib/xero_gateway/invoice.rb +53 -54
- data/lib/xero_gateway/line_item_calculations.rb +20 -22
- data/lib/xero_gateway/manual_journal.rb +2 -1
- data/lib/xero_gateway/organisation.rb +8 -1
- data/lib/xero_gateway/partner_app.rb +14 -20
- data/lib/xero_gateway/tax_rate.rb +1 -0
- data/lib/xero_gateway/version.rb +1 -1
- data/test/integration/get_invoices_test.rb +31 -22
- data/test/integration/get_organisation_test.rb +8 -7
- data/test/integration/get_tax_rates_test.rb +8 -8
- data/test/test_helper.rb +1 -1
- data/test/unit/account_test.rb +10 -6
- data/test/unit/bank_transaction_test.rb +11 -2
- data/test/unit/contact_test.rb +62 -0
- data/test/unit/credit_note_test.rb +51 -63
- data/test/unit/gateway_test.rb +16 -4
- data/test/unit/invoice_test.rb +91 -55
- data/test/unit/manual_journal_test.rb +7 -7
- data/test/unit/organisation_test.rb +52 -7
- data/test/unit/tax_rate_test.rb +8 -7
- data/xero_gateway.gemspec +7 -2
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7df3847b8dad3d3d5c593510c72696d7a39763b3
|
4
|
+
data.tar.gz: 5427008d5f2f9d14dfa3834a7125ad84af3db90c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fd76a237a99430e26b722736025353481263717be0cc0428e1c50180c6106c11b856f9aae2ea5af5452ebaf519f534f4d8e9638ac59a51b2c1cb8322ff66c73
|
7
|
+
data.tar.gz: 874d4febc34ac826a6df223246c453ae8c74bdf35ecbdffd84d3d6a2ebd94f8d48d9358f8015a5a60118635558303c30ace9868e5732e996c83c48ac5d995ebc
|
data/README.md
CHANGED
@@ -26,120 +26,126 @@ The Xero Gateway uses [OAuth 1.0a](https://oauth.net/core/1.0a/) for authenticat
|
|
26
26
|
implements OAuth in a very similar manner to the [Twitter gem by John Nunemaker](http://github.com/jnunemaker/twitter)
|
27
27
|
, so if you've used that before this will all seem familiar.
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
First off, you'll need to get a Consumer Key/Secret pair for your
|
32
|
-
application from Xero.\
|
33
|
-
Head to <http://api.xero.com>, log in and then click My Applications
|
34
|
-
> Add Application.
|
29
|
+
### Authenticating: Public/Partner Applications
|
35
30
|
|
36
|
-
|
37
|
-
account rather than your users), you'll need to generate an RSA keypair
|
38
|
-
and an X509 certificate. This can be done with OpenSSL as below:
|
31
|
+
Public (or Partner, if you've been through the process to be approved) are traditional three-legged OAuth apps that can be used to access many different Xero accounts.
|
39
32
|
|
40
|
-
|
41
|
-
openssl genrsa –out privatekey.pem 1024
|
42
|
-
openssl req –newkey rsa:1024 –x509 –key privatekey.pem –out publickey.cer –days 365
|
43
|
-
openssl pkcs12 –export –out public_privatekey.pfx –inkey privatekey.pem –in publickey.cer
|
44
|
-
```
|
33
|
+
1. **Get a Consumer Key & Secret**
|
45
34
|
|
46
|
-
|
47
|
-
"OAuth Credentials". Use the Key and Secret from this box in order to
|
48
|
-
set up a new Gateway instance.
|
35
|
+
First off, you'll need to get a Consumer Key/Secret pair for your application from Xero.
|
49
36
|
|
50
|
-
|
51
|
-
become clear a bit later)
|
37
|
+
Head to <https://api.xero.com>, log in and then click My Applications > Add Application.
|
52
38
|
|
53
|
-
|
39
|
+
Part of the process for this will ask you for an "OAuth Redirect URL". This is where customers will be redirected once they complete logging in with Xero.
|
54
40
|
|
55
|
-
|
56
|
-
gateway = XeroGateway::Gateway.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
|
57
|
-
```
|
41
|
+
On the right-hand-side of your application's page there's a box titled "OAuth Credentials". Use the Key and Secret from this box in order to set up a new Gateway instance.
|
58
42
|
|
59
|
-
|
43
|
+
2. **Create a Xero Gateway in your App**
|
60
44
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
```
|
45
|
+
```ruby
|
46
|
+
gateway = XeroGateway::Gateway.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
|
47
|
+
```
|
65
48
|
|
66
49
|
3. **Creating a Request Token**
|
67
50
|
|
68
|
-
|
51
|
+
You'll then need to get a Request Token from Xero.
|
69
52
|
|
70
|
-
|
71
|
-
|
72
|
-
|
53
|
+
```ruby
|
54
|
+
request_token = gateway.request_token
|
55
|
+
```
|
73
56
|
|
74
|
-
|
75
|
-
Token later. (If you're using Rails, this means storing it in the
|
76
|
-
session or something similar)
|
57
|
+
You should keep this around - you'll need it to exchange for an Access Token later. (If you're using Rails, this means storing it in the session or something similar)
|
77
58
|
|
78
|
-
|
79
|
-
request token. In Rails, that looks something like this:
|
59
|
+
Next, you need to redirect your user to the authorization url for this request token. In Rails, that looks something like this:
|
80
60
|
|
81
|
-
|
82
|
-
|
83
|
-
|
61
|
+
```ruby
|
62
|
+
redirect_to request_token.authorize_url
|
63
|
+
```
|
84
64
|
|
85
|
-
|
86
|
-
app the user will be redirected to. See next section for more
|
87
|
-
information on what parameters Xero sends with this request.
|
65
|
+
You may also provide a callback parameter, which is the URL within your app the user will be redirected to. You need to ensure that the domain and port match the callback URL you specified in the Xero Developer Center!
|
88
66
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
67
|
+
```ruby
|
68
|
+
request_token = request_token(oauth_callback: "https://yourapp.com/xero/callback")
|
69
|
+
redirect_to request_token.authorize_url
|
70
|
+
```
|
93
71
|
|
94
72
|
4. **Retrieving an Access Token**
|
95
73
|
|
96
|
-
|
97
|
-
provided an oauth\_callback parameter on your request token, your user
|
98
|
-
will be redirected to that URL with an OAuth Verifier as a GET
|
99
|
-
parameter. You can then exchange your Request Token for an Access Token
|
100
|
-
like this (assuming Rails, once again):
|
74
|
+
If you've specified a Callback URL when setting up your application or provided an oauth\_callback parameter on your request token, your user will be redirected to that URL with an OAuth Verifier as a GET parameter. You can then exchange your Request Token for an Access Token like this (assuming Rails, once again):
|
101
75
|
|
102
|
-
|
103
|
-
|
104
|
-
|
76
|
+
```ruby
|
77
|
+
gateway.authorize_from_request(request_token.token, request_token.secret, oauth_verifier: params[:oauth_verifier])
|
78
|
+
```
|
105
79
|
|
106
|
-
|
107
|
-
with a numeric verifier which they must copy+paste into your
|
108
|
-
application; see examples/oauth.rb for an example)
|
80
|
+
(If you haven't specified a Callback URL, the user will be presented with a numeric verifier which they must copy+paste into your application; see examples/oauth.rb for an example)
|
109
81
|
|
110
|
-
|
82
|
+
Now you can access Xero API methods:
|
111
83
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
84
|
+
```ruby
|
85
|
+
gateway.get_contacts
|
86
|
+
# => #<XeroGateway::Response:0x007fd367181388 ...
|
87
|
+
```
|
116
88
|
|
117
|
-
|
89
|
+
#### Storing Access Tokens
|
118
90
|
|
119
|
-
You can also store the Access Token/Secret pair so that you can access
|
120
|
-
the API without user intervention. Currently, these access tokens are
|
121
|
-
only valid for 30 minutes, and will raise a
|
122
|
-
`XeroGateway::OAuth::TokenExpired` exception if you attempt to access the
|
123
|
-
API beyond the token's expiry time.
|
91
|
+
You can also store the Access Token/Secret pair so that you can access
|
92
|
+
the API without user intervention. Currently, these access tokens are
|
93
|
+
only valid for 30 minutes, and will raise a
|
94
|
+
`XeroGateway::OAuth::TokenExpired` exception if you attempt to access the
|
95
|
+
API beyond the token's expiry time.
|
124
96
|
|
125
|
-
```ruby
|
126
|
-
|
127
|
-
```
|
97
|
+
```ruby
|
98
|
+
access_token, access_secret = gateway.access_token
|
99
|
+
```
|
128
100
|
|
129
|
-
You can authorize a `Gateway` instance later on using the
|
130
|
-
`authorize_from_access` method:
|
101
|
+
You can authorize a `Gateway` instance later on using the
|
102
|
+
`authorize_from_access` method:
|
131
103
|
|
132
|
-
```ruby
|
133
|
-
|
134
|
-
|
135
|
-
```
|
104
|
+
```ruby
|
105
|
+
gateway = XeroGateway::Gateway.new(XERO_CONSUMER_KEY, XERO_CONSUMER_SECRET)
|
106
|
+
gateway.authorize_from_access(your_stored_token.access_token, your_stored_token.access_secret)
|
107
|
+
```
|
108
|
+
|
109
|
+
### Authenticating: Private Applications
|
110
|
+
|
111
|
+
Private applications are used to access a single Xero account.
|
112
|
+
|
113
|
+
1. **Get a Consumer Key & Secret**
|
114
|
+
|
115
|
+
Head to <https://api.xero.com>, log in and then click My Applications > Add Application.
|
116
|
+
|
117
|
+
You'll need to generate an RSA keypair and an X509 certificate. This can be done with OpenSSL as below:
|
118
|
+
|
119
|
+
```bash
|
120
|
+
openssl genrsa -out privatekey.pem
|
121
|
+
openssl req -newkey rsa:1024 -x509 -days 365 -in privatekey.pem -out publickey.cer
|
122
|
+
```
|
123
|
+
|
124
|
+
You can then copy `publickey.cer` and paste it into the certificate box (`cat publickey.cer | pbcopy` on a Mac :apple:)
|
125
|
+
|
126
|
+
Make sure you keep `privatekey.pem` about, as you'll need it to connect to Xero in your app.
|
127
|
+
|
128
|
+
2. **Create a Xero Gateway in your App**
|
129
|
+
|
130
|
+
It's as easy as:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
require 'xero_gateway'
|
134
|
+
gateway = XeroGateway::PrivateApp.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET, PATH_TO_YOUR_PRIVATE_KEY)
|
135
|
+
|
136
|
+
pp gateway.get_contacts
|
137
|
+
```
|
138
|
+
|
139
|
+
Note that for private apps, your consumer key and secret do double duty as your access token and secret pair :)
|
136
140
|
|
137
141
|
## Examples
|
138
142
|
|
139
|
-
Open examples/oauth.rb and change
|
140
|
-
the values for a Test OAuth Application in order to see an example of
|
143
|
+
Open `examples/oauth.rb` and change `CONSUMER_KEY` and `CONSUMER_SECRET` to
|
144
|
+
the values for a Test OAuth Public Application in order to see an example of
|
141
145
|
OAuth at work.
|
142
146
|
|
147
|
+
There's also `examples/private_app.rb` if you'd like to see an example private app.
|
148
|
+
|
143
149
|
If you're working with Rails, a controller similar to this might come in
|
144
150
|
handy:
|
145
151
|
|
@@ -179,8 +185,7 @@ handy:
|
|
179
185
|
end
|
180
186
|
```
|
181
187
|
|
182
|
-
Note that I'm just storing the Access Token + Secret in the session here
|
183
|
-
- you could equally store them in the database if you felt like
|
188
|
+
Note that I'm just storing the Access Token + Secret in the session here - you could equally store them in the database if you felt like
|
184
189
|
refreshing them every 30 minutes ;)
|
185
190
|
|
186
191
|
## API Methods
|
data/examples/partner_app.rb
CHANGED
@@ -6,13 +6,9 @@ require File.dirname(__FILE__) + '/../lib/xero_gateway.rb'
|
|
6
6
|
XERO_CONSUMER_KEY = "YOUR CONSUMER KEY"
|
7
7
|
XERO_CONSUMER_SECRET = "YOUR CONSUMER SERET"
|
8
8
|
|
9
|
-
ENTRUST_CERT_NAME = "YOUR_ENTRUST_CERT.pem"
|
10
|
-
ENTRUST_KEY_NAME = "YOUR_ENTRUST_KEY.pem"
|
11
9
|
PRIVATE_KEY = "YOUR_PRIVATE_KEY.pem"
|
12
10
|
|
13
11
|
gateway = XeroGateway::PartnerApp.new(XERO_CONSUMER_KEY, XERO_CONSUMER_SECRET,
|
14
|
-
:ssl_client_cert => File.join(File.dirname(__FILE__), ENTRUST_CERT_NAME)
|
15
|
-
:ssl_client_key => File.join(File.dirname(__FILE__), ENTRUST_KEY_NAME),
|
16
12
|
:private_key_file => File.join(File.dirname(__FILE__), PRIVATE_KEY))
|
17
13
|
|
18
14
|
# authorize in browser
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
require_relative '../lib/xero_gateway.rb'
|
5
|
+
|
6
|
+
XERO_CONSUMER_KEY = "YOUR CONSUMER KEY"
|
7
|
+
XERO_CONSUMER_SECRET = "YOUR CONSUMER SECRET"
|
8
|
+
PATH_TO_PRIVATE_KEY = "/path/to/privatekey.pem"
|
9
|
+
|
10
|
+
gateway = XeroGateway::PrivateApp.new(
|
11
|
+
XERO_CONSUMER_KEY,
|
12
|
+
XERO_CONSUMER_SECRET,
|
13
|
+
PATH_TO_PRIVATE_KEY)
|
14
|
+
|
15
|
+
# Example API Call
|
16
|
+
pp gateway.get_contacts.contacts
|
17
|
+
|
18
|
+
|
data/lib/xero_gateway/account.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module XeroGateway
|
2
2
|
class Account
|
3
|
-
|
3
|
+
|
4
4
|
TYPE = {
|
5
5
|
'CURRENT' => '',
|
6
6
|
'FIXED' => '',
|
@@ -17,14 +17,27 @@ module XeroGateway
|
|
17
17
|
'REVENUE' => '',
|
18
18
|
'SALES' => ''
|
19
19
|
} unless defined?(TYPE)
|
20
|
-
|
20
|
+
|
21
|
+
ACCOUNT_CLASS = {
|
22
|
+
'ASSET' => '',
|
23
|
+
'EQUITY' => '',
|
24
|
+
'EXPENSE' => '',
|
25
|
+
'LIABILITY' => '',
|
26
|
+
'REVENUE' => '',
|
27
|
+
} unless defined?(ACCOUNT_CLASS)
|
28
|
+
|
29
|
+
STATUSES = {
|
30
|
+
'ACTIVE' => '',
|
31
|
+
'ARCHIVED' => '',
|
32
|
+
} unless defined?(STATUSES)
|
33
|
+
|
21
34
|
TAX_TYPE = {
|
22
35
|
'NONE' => 'No GST',
|
23
36
|
'EXEMPTINPUT' => 'VAT on expenses exempt from VAT (UK only)',
|
24
37
|
'INPUT' => 'GST on expenses',
|
25
38
|
'SRINPUT' => 'VAT on expenses',
|
26
39
|
'ZERORATEDINPUT' => 'Expense purchased from overseas (UK only)',
|
27
|
-
'RRINPUT' => 'Reduced rate VAT on expenses (UK Only)',
|
40
|
+
'RRINPUT' => 'Reduced rate VAT on expenses (UK Only)',
|
28
41
|
'EXEMPTOUTPUT' => 'VAT on sales exempt from VAT (UK only)',
|
29
42
|
'ECZROUTPUT' => 'EC Zero-rated output',
|
30
43
|
'OUTPUT' => 'OUTPUT (old rate)',
|
@@ -34,28 +47,30 @@ module XeroGateway
|
|
34
47
|
'RROUTPUT' => 'Reduced rate VAT on sales (UK Only)',
|
35
48
|
'ZERORATED' => 'Zero-rated supplies/sales from overseas (NZ Only)'
|
36
49
|
} unless defined?(TAX_TYPE)
|
37
|
-
|
38
|
-
attr_accessor :account_id, :code, :name, :type, :tax_type, :description, :system_account, :enable_payments_to_account, :currency_code
|
39
|
-
|
50
|
+
|
51
|
+
attr_accessor :account_id, :code, :name, :type, :status, :account_class, :tax_type, :description, :system_account, :enable_payments_to_account, :currency_code
|
52
|
+
|
40
53
|
def initialize(params = {})
|
41
54
|
params.each do |k,v|
|
42
55
|
self.send("#{k}=", v)
|
43
56
|
end
|
44
57
|
end
|
45
|
-
|
58
|
+
|
46
59
|
def ==(other)
|
47
|
-
[:account_id, :code, :name, :type, :tax_type, :description, :system_account, :enable_payments_to_account].each do |field|
|
60
|
+
[:account_id, :code, :name, :type, :status, :account_class, :tax_type, :description, :system_account, :enable_payments_to_account].each do |field|
|
48
61
|
return false if send(field) != other.send(field)
|
49
62
|
end
|
50
63
|
return true
|
51
64
|
end
|
52
|
-
|
65
|
+
|
53
66
|
def to_xml(b = Builder::XmlMarkup.new, options={})
|
54
67
|
b.tag!(options[:name] ? options[:name] : 'Account') {
|
55
68
|
b.AccountID self.account_id
|
56
69
|
b.Code self.code
|
57
70
|
b.Name self.name
|
58
71
|
b.Type self.type
|
72
|
+
b.Status self.status
|
73
|
+
b.Class self.account_class
|
59
74
|
b.TaxType self.tax_type
|
60
75
|
b.Description self.description
|
61
76
|
b.SystemAccount self.system_account unless self.system_account.nil?
|
@@ -63,7 +78,7 @@ module XeroGateway
|
|
63
78
|
b.CurrencyCode currency_code if currency_code
|
64
79
|
}
|
65
80
|
end
|
66
|
-
|
81
|
+
|
67
82
|
def self.from_xml(account_element)
|
68
83
|
account = Account.new
|
69
84
|
account_element.children.each do |element|
|
@@ -72,15 +87,17 @@ module XeroGateway
|
|
72
87
|
when "Code" then account.code = element.text
|
73
88
|
when "Name" then account.name = element.text
|
74
89
|
when "Type" then account.type = element.text
|
90
|
+
when "Status" then account.status = element.text
|
91
|
+
when "Class" then account.account_class = element.text
|
75
92
|
when "TaxType" then account.tax_type = element.text
|
76
93
|
when "Description" then account.description = element.text
|
77
94
|
when "SystemAccount" then account.system_account = element.text
|
78
95
|
when "EnablePaymentsToAccount" then account.enable_payments_to_account = (element.text == 'true')
|
79
96
|
when "CurrencyCode" then account.currency_code = element.text
|
80
97
|
end
|
81
|
-
end
|
98
|
+
end
|
82
99
|
account
|
83
100
|
end
|
84
|
-
|
101
|
+
|
85
102
|
end
|
86
103
|
end
|
@@ -25,7 +25,8 @@ module XeroGateway
|
|
25
25
|
attr_accessor :line_items_downloaded
|
26
26
|
|
27
27
|
# accessible fields
|
28
|
-
|
28
|
+
attr_writer :line_items
|
29
|
+
attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :bank_account, :url, :is_reconciled, :updated_at, :currency_code, :currency_rate
|
29
30
|
|
30
31
|
def initialize(params = {})
|
31
32
|
@errors ||= []
|
@@ -131,7 +132,8 @@ module XeroGateway
|
|
131
132
|
b.BankTransaction {
|
132
133
|
b.BankTransactionID bank_transaction_id if bank_transaction_id
|
133
134
|
b.Type type
|
134
|
-
|
135
|
+
b.CurrencyCode currency_code if currency_code
|
136
|
+
b.CurrencyRate currency_rate if currency_rate
|
135
137
|
contact.to_xml(b) if contact
|
136
138
|
bank_account.to_xml(b, :name => 'BankAccount') if bank_account
|
137
139
|
b.Date BankTransaction.format_date(date || Date.today)
|
@@ -154,7 +156,8 @@ module XeroGateway
|
|
154
156
|
when "BankTransactionID" then bank_transaction.bank_transaction_id = element.text
|
155
157
|
when "UpdatedDateUTC" then bank_transaction.updated_at = parse_date_time(element.text)
|
156
158
|
when "Type" then bank_transaction.type = element.text
|
157
|
-
|
159
|
+
when "CurrencyCode" then bank_transaction.currency_code = element.text
|
160
|
+
when "CurrencyRate" then bank_transaction.currency_rate = BigDecimal.new(element.text)
|
158
161
|
when "Contact" then bank_transaction.contact = Contact.from_xml(element)
|
159
162
|
when "BankAccount" then bank_transaction.bank_account = Account.from_xml(element)
|
160
163
|
when "Date" then bank_transaction.date = parse_date(element.text)
|
@@ -164,14 +167,6 @@ module XeroGateway
|
|
164
167
|
when "Total" then bank_transaction.total = BigDecimal.new(element.text)
|
165
168
|
when "SubTotal" then bank_transaction.sub_total = BigDecimal.new(element.text)
|
166
169
|
when "TotalTax" then bank_transaction.total_tax = BigDecimal.new(element.text)
|
167
|
-
# when "Total" then invoice.total = BigDecimal.new(element.text)
|
168
|
-
# when "InvoiceID" then invoice.invoice_id = element.text
|
169
|
-
# when "InvoiceNumber" then invoice.invoice_number = element.text
|
170
|
-
# when "Payments" then element.children.each { | payment | invoice.payments << Payment.from_xml(payment) }
|
171
|
-
# when "AmountDue" then invoice.amount_due = BigDecimal.new(element.text)
|
172
|
-
# when "AmountPaid" then invoice.amount_paid = BigDecimal.new(element.text)
|
173
|
-
# when "AmountCredited" then invoice.amount_credited = BigDecimal.new(element.text)
|
174
|
-
# when "SentToContact" then invoice.sent_to_contact = (element.text.strip.downcase == "true")
|
175
170
|
when "IsReconciled" then bank_transaction.is_reconciled = (element.text.strip.downcase == "true")
|
176
171
|
when "Url" then bank_transaction.url = element.text
|
177
172
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module XeroGateway
|
2
2
|
class BaseRecord
|
3
|
+
|
4
|
+
class UnsupportedAttributeType < StandardError; end
|
5
|
+
|
3
6
|
class_attribute :element_name
|
4
7
|
class_attribute :attribute_definitions
|
5
8
|
|
@@ -59,27 +62,35 @@ module XeroGateway
|
|
59
62
|
if Hash === attr_definition
|
60
63
|
element.children.each do |child|
|
61
64
|
next unless child.respond_to?(:name)
|
62
|
-
|
65
|
+
|
63
66
|
child_attribute = child.name
|
64
67
|
child_attr_definition = attr_definition[child_attribute]
|
68
|
+
child_attr_name = "#{attribute}#{child_attribute}" # SalesDetails/UnitPrice => SalesDetailsUnitPrice
|
69
|
+
|
65
70
|
next unless child_attr_definition
|
66
71
|
|
67
|
-
from_xml_attributes(child,
|
72
|
+
from_xml_attributes(child, child_attr_name, child_attr_definition)
|
68
73
|
end
|
69
74
|
|
70
75
|
return
|
71
76
|
end
|
72
77
|
|
73
78
|
value = case attr_definition
|
74
|
-
when :boolean
|
75
|
-
when :float
|
76
|
-
when :integer
|
77
|
-
|
78
|
-
|
79
|
+
when :boolean then element.text == "true"
|
80
|
+
when :float then element.text.to_f
|
81
|
+
when :integer then element.text.to_i
|
82
|
+
when Array then array_from_xml(element, attr_definition)
|
83
|
+
else element.text
|
84
|
+
end if element.text.present? || element.children.present?
|
79
85
|
|
80
86
|
send("#{attribute.underscore}=", value)
|
81
87
|
end
|
82
88
|
|
89
|
+
def array_from_xml(element, attr_definition)
|
90
|
+
definition_klass = attr_definition.first
|
91
|
+
element.children.map { |child_el| definition_klass.from_xml(child_el) }
|
92
|
+
end
|
93
|
+
|
83
94
|
def to_xml_attributes(builder = Builder::XmlMarkup.new, path = nil, attr_definitions = self.class.attribute_definitions)
|
84
95
|
attr_definitions.each do |attr, value|
|
85
96
|
case value
|
@@ -87,6 +98,14 @@ module XeroGateway
|
|
87
98
|
builder.__send__(attr) do
|
88
99
|
to_xml_attributes(builder, "#{path}#{attr}", value)
|
89
100
|
end
|
101
|
+
when Array
|
102
|
+
raise UnsupportedAttributeType.new("#{value} instances don't respond to #to_xml") unless value.first.method_defined?(:to_xml)
|
103
|
+
value = send("#{path}#{attr}".underscore) || []
|
104
|
+
builder.__send__(attr) do |array_wrapper|
|
105
|
+
value.map do |k|
|
106
|
+
k.to_xml(array_wrapper)
|
107
|
+
end
|
108
|
+
end
|
90
109
|
else
|
91
110
|
builder.__send__(attr, send("#{path}#{attr}".underscore))
|
92
111
|
end
|