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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df1d3e6fc6dce9cf79cc5e5c8098f635a50a07d4
4
- data.tar.gz: 17287f49b11ab9698ae48b61196afa9445af8598
3
+ metadata.gz: 7df3847b8dad3d3d5c593510c72696d7a39763b3
4
+ data.tar.gz: 5427008d5f2f9d14dfa3834a7125ad84af3db90c
5
5
  SHA512:
6
- metadata.gz: fb668a27cc5f55d0e5257387572a8511e80fed505a5b81202ea148ace725ce85c5f2d2d5d45f78fe1cff366d302577ea5440465fd7c1379ced7212351d8aa23a
7
- data.tar.gz: 81225482469435edbec27331e3b93fcde586e2605142519714f55c27c4ad13090a0b11632d780ca1b5cde78475d66818135c673cb4fe6b3e2d3a2660b7f2a4d4
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
- 1. **Get a Consumer Key & Secret**
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
- &gt; Add Application.
29
+ ### Authenticating: Public/Partner Applications
35
30
 
36
- If you want to create a private application (that accesses your own Xero
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
- On the right-hand-side of your application's page there's a box titled
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
- (If you're unsure about the Callback URL, specify nothing - it will
51
- become clear a bit later)
37
+ Head to <https://api.xero.com>, log in and then click My Applications &gt; Add Application.
52
38
 
53
- 2. **Create a Xero Gateway in your App**
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
- ```ruby
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
- or for Private applications
43
+ 2. **Create a Xero Gateway in your App**
60
44
 
61
- ```ruby
62
- require 'xero_gateway'
63
- gateway = XeroGateway::PrivateApp.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET, PATH_TO_YOUR_PRIVATE_KEY)
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
- You'll then need to get a Request Token from Xero.
51
+ You'll then need to get a Request Token from Xero.
69
52
 
70
- ```ruby
71
- request_token = gateway.request_token
72
- ```
53
+ ```ruby
54
+ request_token = gateway.request_token
55
+ ```
73
56
 
74
- You should keep this around - you'll need it to exchange for an Access
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
- Next, you need to redirect your user to the authorization url for this
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
- ```ruby
82
- redirect_to request_token.authorize_url
83
- ```
61
+ ```ruby
62
+ redirect_to request_token.authorize_url
63
+ ```
84
64
 
85
- You may also provide a callback parameter, which is the URL within your
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
- ```ruby
90
- request_token = request_token(oauth_callback: "http://yourapp.comcom/xero/callback")
91
- redirect_to request_token.authorize_url
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
- If you've specified a Callback URL when setting up your application or
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
- ```ruby
103
- gateway.authorize_from_request(request_token.token, request_token.secret, oauth_verifier: params[:oauth_verifier])
104
- ```
76
+ ```ruby
77
+ gateway.authorize_from_request(request_token.token, request_token.secret, oauth_verifier: params[:oauth_verifier])
78
+ ```
105
79
 
106
- (If you haven't specified a Callback URL, the user will be presented
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
- Now you can access Xero API methods:
82
+ Now you can access Xero API methods:
111
83
 
112
- ```ruby
113
- gateway.get_contacts
114
- # => #<XeroGateway::Response:0x007fd367181388 ...
115
- ```
84
+ ```ruby
85
+ gateway.get_contacts
86
+ # => #<XeroGateway::Response:0x007fd367181388 ...
87
+ ```
116
88
 
117
- ### Storing Access Tokens
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
- access_token, access_secret = gateway.access_token
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
- gateway = XeroGateway::Gateway.new(XERO_CONSUMER_KEY, XERO_CONSUMER_SECRET)
134
- gateway.authorize_from_access(your_stored_token.access_token, your_stored_token.access_secret)
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 &gt; 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 CONSUMER\_KEY and CONSUMER\_SECRET to
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
@@ -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
+
@@ -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
- attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :line_items, :bank_account, :url, :is_reconciled, :updated_at
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
- # b.CurrencyCode self.currency_code if self.currency_code
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
- # when "CurrencyCode" then invoice.currency_code = element.text
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, "#{attribute}#{child_attribute}", child_attr_definition)
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 then element.text == "true"
75
- when :float then element.text.to_f
76
- when :integer then element.text.to_i
77
- else element.text
78
- end if element.text.present?
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