xero_gateway 2.1.0 → 2.3.0

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -11
  3. data/README.md +212 -0
  4. data/Rakefile +0 -1
  5. data/lib/oauth/oauth_consumer.rb +25 -9
  6. data/lib/xero_gateway.rb +5 -1
  7. data/lib/xero_gateway/address.rb +29 -27
  8. data/lib/xero_gateway/bank_transaction.rb +11 -3
  9. data/lib/xero_gateway/base_record.rb +97 -0
  10. data/lib/xero_gateway/contact_group.rb +87 -0
  11. data/lib/xero_gateway/currency.rb +8 -54
  12. data/lib/xero_gateway/dates.rb +4 -0
  13. data/lib/xero_gateway/exceptions.rb +14 -13
  14. data/lib/xero_gateway/gateway.rb +90 -5
  15. data/lib/xero_gateway/http.rb +16 -8
  16. data/lib/xero_gateway/invoice.rb +9 -3
  17. data/lib/xero_gateway/item.rb +27 -0
  18. data/lib/xero_gateway/line_item_calculations.rb +3 -3
  19. data/lib/xero_gateway/manual_journal.rb +5 -2
  20. data/lib/xero_gateway/organisation.rb +22 -73
  21. data/lib/xero_gateway/payment.rb +22 -9
  22. data/lib/xero_gateway/phone.rb +2 -0
  23. data/lib/xero_gateway/report.rb +95 -0
  24. data/lib/xero_gateway/response.rb +12 -7
  25. data/lib/xero_gateway/tax_rate.rb +13 -61
  26. data/lib/xero_gateway/tracking_category.rb +39 -13
  27. data/lib/xero_gateway/version.rb +3 -0
  28. data/test/integration/get_items_test.rb +25 -0
  29. data/test/integration/get_payments_test.rb +54 -0
  30. data/test/test_helper.rb +51 -16
  31. data/test/unit/address_test.rb +34 -0
  32. data/test/unit/bank_transaction_test.rb +7 -0
  33. data/test/unit/contact_group_test.rb +47 -0
  34. data/test/unit/contact_test.rb +35 -16
  35. data/test/unit/credit_note_test.rb +2 -2
  36. data/test/unit/gateway_test.rb +170 -1
  37. data/test/unit/invoice_test.rb +27 -7
  38. data/test/unit/item_test.rb +51 -0
  39. data/test/unit/organisation_test.rb +1 -0
  40. data/test/unit/payment_test.rb +18 -11
  41. data/test/unit/report_test.rb +78 -0
  42. data/xero_gateway.gemspec +29 -13
  43. metadata +176 -89
  44. data/README.textile +0 -357
  45. data/init.rb +0 -1
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df1d3e6fc6dce9cf79cc5e5c8098f635a50a07d4
4
+ data.tar.gz: 17287f49b11ab9698ae48b61196afa9445af8598
5
+ SHA512:
6
+ metadata.gz: fb668a27cc5f55d0e5257387572a8511e80fed505a5b81202ea148ace725ce85c5f2d2d5d45f78fe1cff366d302577ea5440465fd7c1379ced7212351d8aa23a
7
+ data.tar.gz: 81225482469435edbec27331e3b93fcde586e2605142519714f55c27c4ad13090a0b11632d780ca1b5cde78475d66818135c673cb4fe6b3e2d3a2660b7f2a4d4
data/Gemfile CHANGED
@@ -1,12 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- gem 'builder', '>= 2.1.2'
4
- gem 'oauth', '>= 0.3.6'
5
- gem 'activesupport'
6
-
7
- group :test do
8
- gem 'i18n' # For fixing undocumented active_support dependency
9
- gem 'mocha'
10
- gem 'shoulda'
11
- gem 'libxml-ruby'
12
- end
3
+ # Specify your gem's dependencies in omniauth-mashapeid.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,212 @@
1
+ Xero API wrapper [![Build Status](https://travis-ci.org/xero-gateway/xero_gateway.svg?branch=master)](https://travis-ci.org/xero-gateway/xero_gateway) [![Gem Version](https://badge.fury.io/rb/xero_gateway.svg)](https://badge.fury.io/rb/xero_gateway)
2
+ ================
3
+
4
+ # Getting Started
5
+
6
+ This is a Ruby gem for communicating with the Xero API.
7
+ You can find more information about the Xero API at <https://developer.xero.com>.
8
+
9
+ ## Installation
10
+
11
+ Just add the `xero_gateway` gem to your Gemfile, like so:
12
+
13
+ ```ruby
14
+ gem 'xero_gateway'
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ gateway = XeroGateway::Gateway.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
21
+ ```
22
+
23
+ ### Authenticating with OAuth
24
+
25
+ The Xero Gateway uses [OAuth 1.0a](https://oauth.net/core/1.0a/) for authentication. Xero Gateway
26
+ implements OAuth in a very similar manner to the [Twitter gem by John Nunemaker](http://github.com/jnunemaker/twitter)
27
+ , so if you've used that before this will all seem familiar.
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.
35
+
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:
39
+
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
+ ```
45
+
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.
49
+
50
+ (If you're unsure about the Callback URL, specify nothing - it will
51
+ become clear a bit later)
52
+
53
+ 2. **Create a Xero Gateway in your App**
54
+
55
+ ```ruby
56
+ gateway = XeroGateway::Gateway.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
57
+ ```
58
+
59
+ or for Private applications
60
+
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
+ ```
65
+
66
+ 3. **Creating a Request Token**
67
+
68
+ You'll then need to get a Request Token from Xero.
69
+
70
+ ```ruby
71
+ request_token = gateway.request_token
72
+ ```
73
+
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)
77
+
78
+ Next, you need to redirect your user to the authorization url for this
79
+ request token. In Rails, that looks something like this:
80
+
81
+ ```ruby
82
+ redirect_to request_token.authorize_url
83
+ ```
84
+
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.
88
+
89
+ ```ruby
90
+ request_token = request_token(oauth_callback: "http://yourapp.comcom/xero/callback")
91
+ redirect_to request_token.authorize_url
92
+ ```
93
+
94
+ 4. **Retrieving an Access Token**
95
+
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):
101
+
102
+ ```ruby
103
+ gateway.authorize_from_request(request_token.token, request_token.secret, oauth_verifier: params[:oauth_verifier])
104
+ ```
105
+
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)
109
+
110
+ Now you can access Xero API methods:
111
+
112
+ ```ruby
113
+ gateway.get_contacts
114
+ # => #<XeroGateway::Response:0x007fd367181388 ...
115
+ ```
116
+
117
+ ### Storing Access Tokens
118
+
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.
124
+
125
+ ```ruby
126
+ access_token, access_secret = gateway.access_token
127
+ ```
128
+
129
+ You can authorize a `Gateway` instance later on using the
130
+ `authorize_from_access` method:
131
+
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
+ ```
136
+
137
+ ## Examples
138
+
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
141
+ OAuth at work.
142
+
143
+ If you're working with Rails, a controller similar to this might come in
144
+ handy:
145
+
146
+ ```ruby
147
+ class XeroSessionsController < ApplicationController
148
+
149
+ before_action :get_xero_gateway
150
+
151
+ def new
152
+ session[:request_token] = @xero_gateway.request_token.token
153
+ session[:request_secret] = @xero_gateway.request_token.secret
154
+
155
+ redirect_to @xero_gateway.request_token.authorize_url
156
+ end
157
+
158
+ def create
159
+ @xero_gateway.authorize_from_request(session[:request_token], session[:request_secret],
160
+ oauth_verifier: params[:oauth_verifier])
161
+
162
+ session[:xero_auth] = { access_token: @xero_gateway.access_token.token,
163
+ access_secret: @xero_gateway.access_token.secret }
164
+
165
+ session.data.delete(:request_token)
166
+ session.data.delete(:request_secret)
167
+ end
168
+
169
+ def destroy
170
+ session.data.delete(:xero_auth)
171
+ end
172
+
173
+ private
174
+
175
+ def get_xero_gateway
176
+ @xero_gateway = XeroGateway::Gateway.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
177
+ end
178
+
179
+ end
180
+ ```
181
+
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
184
+ refreshing them every 30 minutes ;)
185
+
186
+ ## API Methods
187
+
188
+ You can find a full listing of all implemented methods on [the wiki page](https://github.com/xero-gateway/xero_gateway/wiki/API-Methods).
189
+
190
+ ## Logging
191
+
192
+ You can specify a logger to use (so you can track down those tricky
193
+ exceptions) by using:
194
+
195
+ ```ruby
196
+ gateway.logger = ActiveSupport::BufferedLogger.new("log_file_name.log")
197
+ ```
198
+
199
+ Your logger simply needs to respond to `info`.
200
+
201
+ ## Contributing
202
+
203
+ We welcome contributions, thanks for pitching in! :sparkles:
204
+
205
+ 1. Fork the repo
206
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
207
+ 3. Make sure you have some tests, and they pass! (`bundle exec rake`)
208
+ 4. Commit your changes (`git commit -am 'Added some feature'`)
209
+ 5. Push to the branch (`git push origin my-new-feature`)
210
+ 6. Create new Pull Request
211
+
212
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
- require 'rdoc/task'
4
3
 
5
4
  desc 'Default: run unit tests.'
6
5
  task :default => :test
@@ -1,14 +1,30 @@
1
1
  module OAuth
2
2
  class Consumer
3
-
4
- def http_with_ssl_client_certificates(*args)
5
- @http ||= http_without_ssl_client_certificates(*args).tap do |http|
6
- http.cert = options[:ssl_client_cert]
7
- http.key = options[:ssl_client_key]
3
+
4
+ if RUBY_VERSION >= "2.0.0"
5
+
6
+ # we got Module#prepend, let's use it
7
+ module ClientCertificateExtensions
8
+ def http
9
+ super.tap do |http|
10
+ http.cert = options[:ssl_client_cert]
11
+ http.key = options[:ssl_client_key]
12
+ end
13
+ end
8
14
  end
15
+
16
+ prepend ClientCertificateExtensions
17
+
18
+ else
19
+ def http_with_ssl_client_certificates(*args)
20
+ @http ||= http_without_ssl_client_certificates(*args).tap do |http|
21
+ http.cert = options[:ssl_client_cert]
22
+ http.key = options[:ssl_client_key]
23
+ end
24
+ end
25
+
26
+ alias_method_chain :http, :ssl_client_certificates
9
27
  end
10
-
11
- alias_method_chain :http, :ssl_client_certificates
12
-
28
+
13
29
  end
14
- end
30
+ end
data/lib/xero_gateway.rb CHANGED
@@ -18,10 +18,12 @@ require File.join(File.dirname(__FILE__), 'xero_gateway', 'dates')
18
18
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'money')
19
19
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'line_item_calculations')
20
20
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'response')
21
+ require File.join(File.dirname(__FILE__), 'xero_gateway', 'base_record')
21
22
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'account')
22
23
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'accounts_list')
23
24
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'tracking_category')
24
25
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'contact')
26
+ require File.join(File.dirname(__FILE__), 'xero_gateway', 'contact_group')
25
27
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'line_item')
26
28
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'payment')
27
29
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'invoice')
@@ -30,13 +32,15 @@ require File.join(File.dirname(__FILE__), 'xero_gateway', 'credit_note')
30
32
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'journal_line')
31
33
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'manual_journal')
32
34
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'address')
35
+ require File.join(File.dirname(__FILE__), 'xero_gateway', 'report')
33
36
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'phone')
34
37
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'organisation')
35
38
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'tax_rate')
36
39
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'currency')
40
+ require File.join(File.dirname(__FILE__), 'xero_gateway', 'item')
37
41
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'error')
38
42
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'oauth')
39
43
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'exceptions')
40
44
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'gateway')
41
45
  require File.join(File.dirname(__FILE__), 'xero_gateway', 'private_app')
42
- require File.join(File.dirname(__FILE__), 'xero_gateway', 'partner_app')
46
+ require File.join(File.dirname(__FILE__), 'xero_gateway', 'partner_app')
@@ -1,44 +1,44 @@
1
1
  module XeroGateway
2
2
  class Address
3
-
3
+
4
4
  ADDRESS_TYPE = {
5
5
  'STREET' => 'Street',
6
6
  'POBOX' => 'PO Box'
7
7
  } unless defined?(ADDRESS_TYPE)
8
-
8
+
9
9
  # Any errors that occurred when the #valid? method called.
10
10
  attr_reader :errors
11
-
12
- attr_accessor :address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country
13
-
11
+
12
+ attr_accessor :address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country, :attention_to
13
+
14
14
  def initialize(params = {})
15
15
  @errors ||= []
16
-
16
+
17
17
  params = {
18
18
  :address_type => "POBOX"
19
19
  }.merge(params)
20
-
20
+
21
21
  params.each do |k,v|
22
22
  self.send("#{k}=", v)
23
23
  end
24
24
  end
25
-
25
+
26
26
  # Validate the Address record according to what will be valid by the gateway.
27
27
  #
28
- # Usage:
28
+ # Usage:
29
29
  # address.valid? # Returns true/false
30
- #
30
+ #
31
31
  # Additionally sets address.errors array to an array of field/error.
32
32
  def valid?
33
33
  @errors = []
34
-
34
+
35
35
  if address_type && !ADDRESS_TYPE[address_type]
36
36
  @errors << ['address_type', "must be one of #{ADDRESS_TYPE.keys.join('/')} and is currently #{address_type}"]
37
37
  end
38
-
38
+
39
39
  @errors.size == 0
40
- end
41
-
40
+ end
41
+
42
42
  def to_xml(b = Builder::XmlMarkup.new)
43
43
  b.Address {
44
44
  b.AddressType address_type
@@ -50,44 +50,46 @@ module XeroGateway
50
50
  b.Region region if region
51
51
  b.PostalCode post_code if post_code
52
52
  b.Country country if country
53
+ b.AttentionTo attention_to if attention_to
53
54
  }
54
55
  end
55
-
56
+
56
57
  def self.from_xml(address_element)
57
58
  address = Address.new
58
59
  address_element.children.each do |element|
59
60
  case(element.name)
60
- when "AddressType" then address.address_type = element.text
61
+ when "AddressType" then address.address_type = element.text
61
62
  when "AddressLine1" then address.line_1 = element.text
62
63
  when "AddressLine2" then address.line_2 = element.text
63
64
  when "AddressLine3" then address.line_3 = element.text
64
- when "AddressLine4" then address.line_4 = element.text
65
- when "City" then address.city = element.text
66
- when "Region" then address.region = element.text
67
- when "PostalCode" then address.post_code = element.text
68
- when "Country" then address.country = element.text
65
+ when "AddressLine4" then address.line_4 = element.text
66
+ when "City" then address.city = element.text
67
+ when "Region" then address.region = element.text
68
+ when "PostalCode" then address.post_code = element.text
69
+ when "Country" then address.country = element.text
70
+ when "AttentionTo" then address.attention_to = element.text
69
71
  end
70
72
  end
71
73
  address
72
74
  end
73
-
75
+
74
76
  def self.parse(string)
75
77
  address = Address.new
76
-
78
+
77
79
  parts = string.split("\r\n")
78
-
80
+
79
81
  if(parts.size > 3)
80
82
  parts = [parts.shift, parts.shift, parts.shift, parts.join(", ")]
81
83
  end
82
-
84
+
83
85
  parts.each_with_index do |line, index|
84
86
  address.send("line_#{index+1}=", line)
85
87
  end
86
88
  address
87
89
  end
88
-
90
+
89
91
  def ==(other)
90
- [:address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country].each do |field|
92
+ [:address_type, :line_1, :line_2, :line_3, :line_4, :city, :region, :post_code, :country, :attention_to].each do |field|
91
93
  return false if send(field) != other.send(field)
92
94
  end
93
95
  return true
@@ -25,7 +25,7 @@ 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
28
+ attr_accessor :bank_transaction_id, :type, :date, :reference, :status, :contact, :line_items, :bank_account, :url, :is_reconciled, :updated_at
29
29
 
30
30
  def initialize(params = {})
31
31
  @errors ||= []
@@ -98,6 +98,12 @@ module XeroGateway
98
98
  @line_items_downloaded
99
99
  end
100
100
 
101
+ %w(sub_total tax_total total).each do |line_item_total_type|
102
+ define_method("#{line_item_total_type}=") do |new_total|
103
+ instance_variable_set("@#{line_item_total_type}", new_total) unless line_items_downloaded?
104
+ end
105
+ end
106
+
101
107
  # If line items are not downloaded, then attempt a download now (if this record was found to begin with).
102
108
  def line_items
103
109
  if line_items_downloaded?
@@ -146,6 +152,7 @@ module XeroGateway
146
152
  bank_transaction_element.children.each do |element|
147
153
  case(element.name)
148
154
  when "BankTransactionID" then bank_transaction.bank_transaction_id = element.text
155
+ when "UpdatedDateUTC" then bank_transaction.updated_at = parse_date_time(element.text)
149
156
  when "Type" then bank_transaction.type = element.text
150
157
  # when "CurrencyCode" then invoice.currency_code = element.text
151
158
  when "Contact" then bank_transaction.contact = Contact.from_xml(element)
@@ -154,8 +161,9 @@ module XeroGateway
154
161
  when "Status" then bank_transaction.status = element.text
155
162
  when "Reference" then bank_transaction.reference = element.text
156
163
  when "LineItems" then element.children.each {|line_item| bank_transaction.line_items_downloaded = true; bank_transaction.line_items << LineItem.from_xml(line_item) }
157
- # when "SubTotal" then invoice.sub_total = BigDecimal.new(element.text)
158
- # when "TotalTax" then invoice.total_tax = BigDecimal.new(element.text)
164
+ when "Total" then bank_transaction.total = BigDecimal.new(element.text)
165
+ when "SubTotal" then bank_transaction.sub_total = BigDecimal.new(element.text)
166
+ when "TotalTax" then bank_transaction.total_tax = BigDecimal.new(element.text)
159
167
  # when "Total" then invoice.total = BigDecimal.new(element.text)
160
168
  # when "InvoiceID" then invoice.invoice_id = element.text
161
169
  # when "InvoiceNumber" then invoice.invoice_number = element.text