sage_one 0.0.1
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/.gitignore +14 -0
- data/.travis.yml +11 -0
- data/.yardopts +4 -0
- data/Gemfile +8 -0
- data/LICENSE +19 -0
- data/README.md +141 -0
- data/Rakefile +4 -0
- data/bin/autospec +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/bin/yri +16 -0
- data/lib/faraday/request/oauth2.rb +25 -0
- data/lib/faraday/response/convert_sdata_to_headers.rb +70 -0
- data/lib/faraday/response/raise_sage_one_exception.rb +42 -0
- data/lib/sage_one/client/contacts.rb +14 -0
- data/lib/sage_one/client/sales_invoices.rb +77 -0
- data/lib/sage_one/client.rb +33 -0
- data/lib/sage_one/configuration.rb +81 -0
- data/lib/sage_one/connection.rb +44 -0
- data/lib/sage_one/error.rb +39 -0
- data/lib/sage_one/oauth.rb +49 -0
- data/lib/sage_one/request.rb +72 -0
- data/lib/sage_one/version.rb +3 -0
- data/lib/sage_one.rb +31 -0
- data/sage_one.gemspec +32 -0
- data/spec/faraday/request/oauth2_spec.rb +44 -0
- data/spec/faraday/response/convert_sdata_to_headers_spec.rb +113 -0
- data/spec/faraday/response/raise_sage_one_exception_spec.rb +45 -0
- data/spec/fixtures/contact.json +28 -0
- data/spec/fixtures/invalid_sales_invoice.json +28 -0
- data/spec/fixtures/oauth/invalid_client.json +1 -0
- data/spec/fixtures/oauth/invalid_grant.json +1 -0
- data/spec/fixtures/oauth/oauth_token.json +4 -0
- data/spec/fixtures/sales_invoice.json +43 -0
- data/spec/fixtures/sales_invoices.json +90 -0
- data/spec/sage_one/client/contacts_spec.rb +19 -0
- data/spec/sage_one/client/sales_invoices_spec.rb +53 -0
- data/spec/sage_one/client_spec.rb +41 -0
- data/spec/sage_one/configuration_spec.rb +88 -0
- data/spec/sage_one/connection_spec.rb +36 -0
- data/spec/sage_one/oauth_spec.rb +44 -0
- data/spec/sage_one/request_spec.rb +113 -0
- data/spec/sage_one/version_spec.rb +7 -0
- data/spec/sage_one_spec.rb +38 -0
- data/spec/spec_helper.rb +76 -0
- metadata +301 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Luke Brown, Chris Stainthorpe
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Sage One [][travis] [][gemnasium]
|
2
|
+
Faraday-based Ruby wrapper for the Sage One API
|
3
|
+
|
4
|
+
[travis]: http://travis-ci.org/customersure/sage_one
|
5
|
+
[gemnasium]: https://gemnasium.com/customersure/sage_one
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add `sage_one` gem to your `Gemfile`
|
9
|
+
|
10
|
+
gem 'sage_one'
|
11
|
+
|
12
|
+
## Documentation
|
13
|
+
[http://rdoc.info/gems/sage_one][documentation]
|
14
|
+
|
15
|
+
[documentation]: http://rdoc.info/gems/sage_one
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
### Authentication
|
19
|
+
This documentation assumes you have already obtained a client id and client secret from Sage.
|
20
|
+
|
21
|
+
To make any requests to the Sage One API, you must present an OAuth access token. The basic flow for obtaining a token for a user is as follows:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# The code below is over-simplified. Read Sage's own API docs and the documentation for SageOne::Oauth to get
|
25
|
+
# a better idea of how to implement this in the context of your own app.
|
26
|
+
SageOne.configure do |c|
|
27
|
+
c.client_id = "YOUR_CLIENT_ID_OBTAINED_FROM_SAGE"
|
28
|
+
c.client_secret = "YOUR_CLIENT_SECRET_OBTAINED_FROM_SAGE"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Redirect the current user to SageOne. This will give them the choice to link SageOne with your app.
|
32
|
+
# and subsequently redirect them back to your callback_url with an authorisation_code if they choose to do so.
|
33
|
+
redirect_to SageOne.authorize_url('https://www.example.com/your_callback_url')
|
34
|
+
|
35
|
+
# Then, in the callback URL controller, run get_access_token, i.e.
|
36
|
+
response = SageOne.get_access_token(params[:code], 'https://www.example.com/your_callback_url')
|
37
|
+
User.save_access_token(response.access_token) unless response.access_token.nil?
|
38
|
+
```
|
39
|
+
|
40
|
+
### Standard API requests
|
41
|
+
Once you have an access token, configure the client with it, along with your client id and secret, and you're good to go:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
SageOne.configure do |c|
|
45
|
+
c.client_id = "YOUR_CLIENT_ID_OBTAINED_FROM_SAGE"
|
46
|
+
c.client_secret = "YOUR_CLIENT_SECRET_OBTAINED_FROM_SAGE"
|
47
|
+
c.access_token = current_user.access_token
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get an array of all sales invoices
|
51
|
+
invoices = SageOne.sales_invoices
|
52
|
+
|
53
|
+
# Add search params
|
54
|
+
SageOne.sales_invoices(status: 2, contact: 65489)
|
55
|
+
|
56
|
+
# Dates in search params...
|
57
|
+
# The SageOne API requires that you specify dates as dd/mm/yyyy
|
58
|
+
SageOne.sales_invoices(from_date: '21/11/2011')
|
59
|
+
|
60
|
+
# We simplify this by also allowing you to specify a date-like object (anything that responds to strftime)
|
61
|
+
SageOne.sales_invoices(from_date: 2.weeks.ago) #rails
|
62
|
+
|
63
|
+
# Note that we can't protect you from doing the wrong thing with ambiguous dates..
|
64
|
+
SageOne.sales_invoices(from_date: '05/01/2012') # Hope that you meant 5th January and not 1st May
|
65
|
+
```
|
66
|
+
You can configure the `SageOne` client on the fly. For example, if you'd prefer to configure your client_id and secret in an
|
67
|
+
initializer then set the access_token in a controller:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
SageOne.new(access_token: current_user.access_token).sales_invoices
|
71
|
+
```
|
72
|
+
|
73
|
+
### Pagination
|
74
|
+
You can request any 'page' of results returned from the API by adding `start_index: n` to any API call:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
SageOne.sales_invoices(start_index: 30)
|
78
|
+
SageOne.sales_invoices(start_date: '28/02/2010', start_index: 50)
|
79
|
+
|
80
|
+
```
|
81
|
+
|
82
|
+
You can also turn on 'auto traversal' to have the client recursively get a full result set. Beware of using this on large result sets.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# Recursively request ALL sales invoices, appending them to the body of the request
|
86
|
+
SageOne.new(auto_traversal: true).sales_invoices
|
87
|
+
```
|
88
|
+
|
89
|
+
### Other usage notees
|
90
|
+
- HTTP 1.1 requires that you set a Host: header. Whilst the gem will currently work fine without this, if conformance to the spec is important to you, set this with `request_host=`
|
91
|
+
- You can set a proxy server with `proxy=`
|
92
|
+
- To obtain raw, unprocessed responses back from the API, specify `raw_responses = true`. Read the documentation for more information on this.
|
93
|
+
|
94
|
+
|
95
|
+
## Open Source
|
96
|
+
See the [LICENSE][] file for more information.
|
97
|
+
|
98
|
+
We actively encourage contributions to this gem. Whilst we have provided a robust, tested, documented, full-featured core of an wrapper for this new API, we have to balance our time spent on this gem against [the day job][cs].
|
99
|
+
Therefore, if there are API endpoints we haven't covered yet, please fork us, add tests, documentation and coverage, and submit a pull request.
|
100
|
+
|
101
|
+
### Submitting a Pull Request
|
102
|
+
1. [Fork the repository.][fork]
|
103
|
+
2. [Create a topic branch.][branch]
|
104
|
+
3. Add specs for your unimplemented feature or bug fix.
|
105
|
+
4. Run `bundle exec rake spec`. If your specs pass, return to step 3.
|
106
|
+
5. Implement your feature or bug fix.
|
107
|
+
6. Run `bundle exec rake spec`. If your specs fail, return to step 5.
|
108
|
+
7. Run `open coverage/index.html`. If your changes are not completely covered
|
109
|
+
by your tests, return to step 3.
|
110
|
+
8. Add documentation for your feature or bug fix.
|
111
|
+
9. Run `bundle exec rake doc:yard`. If your changes are not 100% documented, go
|
112
|
+
back to step 8.
|
113
|
+
10. Add, commit, and push your changes.
|
114
|
+
11. [Submit a pull request.][pr]
|
115
|
+
|
116
|
+
[fork]: http://help.github.com/fork-a-repo/
|
117
|
+
[branch]: http://learn.github.com/p/branching.html
|
118
|
+
[pr]: http://help.github.com/send-pull-requests/
|
119
|
+
[cs]: http://www.customersure.com/
|
120
|
+
|
121
|
+
## Supported Ruby Versions
|
122
|
+
TODO
|
123
|
+
|
124
|
+
## Contributors and Inspiration
|
125
|
+
|
126
|
+
[SageOne][sageone] is a product of [The Sage Group][sage].
|
127
|
+
|
128
|
+
The `sage_one` gem was created by [Luke Brown][luke] and [Chris Stainthorpe][chris] whilst at [CustomerSure][cs], but is heavily inspired by the following gems: [Octokit][], [Twitter][], and [instagram-ruby-gem][].
|
129
|
+
|
130
|
+
[sage]: http://www.sage.com/
|
131
|
+
[sageone]: http://www.sageone.com/
|
132
|
+
[luke]: http://www.tsdbrown.com/
|
133
|
+
[chris]: http://www.randomcat.co.uk/
|
134
|
+
[octokit]: https://github.com/pengwynn/octokit/
|
135
|
+
[twitter]: https://github.com/sferik/twitter/
|
136
|
+
[instagram-ruby-gem]: https://github.com/Instagram/instagram-ruby-gem/
|
137
|
+
|
138
|
+
## Copyright
|
139
|
+
Copyright (c) 2012 Chris Stainthorpe, Luke Brown. See [LICENSE][] for details.
|
140
|
+
|
141
|
+
[license]: https://github.com/customersure/sage_one/blob/master/LICENSE
|
data/Rakefile
ADDED
data/bin/autospec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'autospec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sage_one', 'autospec')
|
data/bin/htmldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sage_one', 'htmldiff')
|
data/bin/ldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sage_one', 'ldiff')
|
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sage_one', 'rake')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sage_one', 'rspec')
|
data/bin/yard
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yard' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yard')
|
data/bin/yardoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yardoc' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yardoc')
|
data/bin/yri
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yri' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yri')
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module FaradayMiddleware
|
5
|
+
|
6
|
+
AUTH_HEADER = 'Authorization'.freeze
|
7
|
+
|
8
|
+
# Simple middleware that adds the access token to each request.
|
9
|
+
#
|
10
|
+
# The access token is placed in the "Authorization" HTTP request header.
|
11
|
+
# However, an explicit "Authorization" header for the current request
|
12
|
+
# will not be overriden.
|
13
|
+
# @api private
|
14
|
+
class OAuth2 < Faraday::Middleware
|
15
|
+
def call(env)
|
16
|
+
env[:request_headers][AUTH_HEADER] ||= %(Bearer #{@token}) if @token
|
17
|
+
@app.call env
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(app, token = nil)
|
21
|
+
super(app)
|
22
|
+
@token = token
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware/response_middleware'
|
3
|
+
require 'addressable/uri'
|
4
|
+
|
5
|
+
# @api private
|
6
|
+
module FaradayMiddleware
|
7
|
+
|
8
|
+
# Middleware to strip out Sage's pagination SData from the body and place
|
9
|
+
# it in a custom response header instead (Using familiar 'Link' header syntax).
|
10
|
+
# This just leaves the resources in the body which can then be recursively
|
11
|
+
# collected later by following the links.
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
class ConvertSdataToHeaders < ResponseMiddleware
|
15
|
+
|
16
|
+
SDATA_START_INDEX = "$startIndex".freeze
|
17
|
+
SDATA_TOTAL_RESULTS = "$totalResults".freeze
|
18
|
+
SDATA_ITEMS_PER_PAGE = "$itemsPerPage".freeze
|
19
|
+
SDATA_RESOURCES = "$resources".freeze
|
20
|
+
SDATA_HEADER_NAME = "X-SData-Pagination-Links".freeze
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
@app.call(env).on_complete do
|
24
|
+
@response = env[:response]
|
25
|
+
|
26
|
+
# Only proceed if SData is actually present
|
27
|
+
next unless @response.body && @response.body.kind_of?(Hash) && @response.body[SDATA_TOTAL_RESULTS]
|
28
|
+
|
29
|
+
@url = Addressable::URI::parse(env[:url])
|
30
|
+
@url.query_values ||= {}
|
31
|
+
|
32
|
+
# Add 'next' link, if we're not on the last page
|
33
|
+
add_link(next_start_index, 'next') if next_start_index < @response.body[SDATA_TOTAL_RESULTS]
|
34
|
+
|
35
|
+
# Add 'prev' link if we're not on the first page
|
36
|
+
add_link(prev_start_index, 'prev') if prev_start_index >= 0
|
37
|
+
|
38
|
+
# Special case: If we're halfway through the first page, don't allow negative start indices
|
39
|
+
add_link(0, 'prev') if @response.body[SDATA_START_INDEX] != 0 && prev_start_index < 0
|
40
|
+
|
41
|
+
# Add the page links into the header
|
42
|
+
@response.headers[SDATA_HEADER_NAME] = @links.join(', ') unless @links.empty?
|
43
|
+
|
44
|
+
# Strip out the SData from the body
|
45
|
+
env[:body] = @response.body[SDATA_RESOURCES]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(app)
|
50
|
+
@links = []
|
51
|
+
super app
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def add_link(start_index, rel)
|
57
|
+
@url.query_values = @url.query_values.merge({ SDATA_START_INDEX => start_index })
|
58
|
+
@links << %Q{<#{@url.to_str}>; rel="#{rel}"}
|
59
|
+
end
|
60
|
+
|
61
|
+
def next_start_index
|
62
|
+
@response.body[SDATA_START_INDEX] + @response.body[SDATA_ITEMS_PER_PAGE]
|
63
|
+
end
|
64
|
+
|
65
|
+
def prev_start_index
|
66
|
+
@response.body[SDATA_START_INDEX] - @response.body[SDATA_ITEMS_PER_PAGE]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module FaradayMiddleware
|
5
|
+
|
6
|
+
# Checks the status of the API request and raises
|
7
|
+
# relevant exceptions when detected.
|
8
|
+
# @see SageOne::Error SageOne::Error for possible errors to rescue from.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class RaiseSageOneException < Faraday::Middleware
|
12
|
+
def call(env)
|
13
|
+
@app.call(env).on_complete do |response|
|
14
|
+
case response[:status].to_i
|
15
|
+
when 400
|
16
|
+
raise SageOne::BadRequest, error_message(response)
|
17
|
+
when 401
|
18
|
+
raise SageOne::Unauthorized, error_message(response)
|
19
|
+
when 403
|
20
|
+
raise SageOne::Forbidden, error_message(response)
|
21
|
+
when 404
|
22
|
+
raise SageOne::NotFound, error_message(response)
|
23
|
+
when 409
|
24
|
+
raise SageOne::Conflict, error_message(response)
|
25
|
+
when 422
|
26
|
+
raise SageOne::UnprocessableEntity, error_message(response)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def error_message(response)
|
34
|
+
JSON.unparse({
|
35
|
+
method: response[:method],
|
36
|
+
url: response[:url].to_s,
|
37
|
+
status: response[:status],
|
38
|
+
body: (response[:body].nil? ? "" : response[:body])
|
39
|
+
})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SageOne
|
2
|
+
class Client
|
3
|
+
module Contacts
|
4
|
+
# Get a contact record by ID
|
5
|
+
# @param [Integer] id Contact ID
|
6
|
+
# @return [Hashie::Mash] Contact record
|
7
|
+
# @example Get a contact:
|
8
|
+
# SageOne.contact(12345)
|
9
|
+
def contact(id)
|
10
|
+
get("contacts/#{id}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module SageOne
|
2
|
+
class Client
|
3
|
+
module SalesInvoices
|
4
|
+
|
5
|
+
# List sales invoices
|
6
|
+
# @param options [Hash] A customizable set of options.
|
7
|
+
# @option options [Integer] :contact Use this to filter by contact id
|
8
|
+
# @option options [Integer] :status Invoice payment status.
|
9
|
+
# @option options [String] :search Filter by contact_name or reference (not case sensitive)
|
10
|
+
# @option options [String, #strftime] :from_date Either a string formatted as 'dd/mm/yyyy' or an object which responds to strftime
|
11
|
+
# @option options [String, #strftime] :to_date Either a string formatted as 'dd/mm/yyyy' or an object which responds to strftime
|
12
|
+
# @option options [Integer] :start_index The start index in pagination to begin from.
|
13
|
+
# @return [Array<Invoice>] A list of all sales_invoices. Each invoice is a Hashie.
|
14
|
+
# @example Get all sales invoices
|
15
|
+
# SageOne.sales_invoices
|
16
|
+
def sales_invoices(options={})
|
17
|
+
get("sales_invoices", options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a sales invoice
|
21
|
+
# @param options [Hash] A customizable set of options. Note that you don't have to wrap these,
|
22
|
+
# i.e. { sales_invoice: {options} }, just pass in the options hash
|
23
|
+
# @option options [String, #strftime] :date The invoice date either as a dd/mm/yyyy-formatted string or any object that responds to strftime
|
24
|
+
# @option options [Integer] :contact_id The ID of the contact associated with the sales invoice. This must be a customer (contact_type 1).
|
25
|
+
# @option options [String] :contact_name The name of the contact associated with the invoice. This should be the contact[name_and_company_name] from the ID of the specified contact.
|
26
|
+
# @option options [String] :main_address The address where the invoice is to be sent.
|
27
|
+
# @option options [Integer] :carriage_tax_code_id The ID of the tax_rate if sales_invoice[carriage] is supplied.
|
28
|
+
# @option options [Array<Hash>] :line_items_attributes An array of line items. Each Hash requires:
|
29
|
+
# * <b>:unit_price</b> (<tt>Float</tt>) The unit cost of the line item.
|
30
|
+
# * <b>:quantity</b> (<tt>Float</tt>) The number of units on the specified line item.
|
31
|
+
# * <b>:description</b> (<tt>String</tt>) The description of the specified line item, maximum 60 characters.
|
32
|
+
# * <b>:tax_code_id</b> (<tt>Integer</tt>) The ID of the tax_rate for the item line.
|
33
|
+
# * <b>:ledger_account_id</b> (<tt>Integer</tt>) The ID of the income_type.
|
34
|
+
# @return [Invoice] A Hashie of the created invoice
|
35
|
+
# @example Create a sales invoice:
|
36
|
+
# SageOne.create_sales_invoice({
|
37
|
+
# date: Time.now
|
38
|
+
# contact_id: 654
|
39
|
+
# contact_name: "Dave Regis"
|
40
|
+
# main_address: "Regis Enterprises, PO Box 123"
|
41
|
+
# carriage_tax_code_id: 5
|
42
|
+
# line_items_attributes: { unit_price: 12.34, quantity: 1.0, description: "Salmon steak", tax_code_id: 1, ledger_account_id: 987 }
|
43
|
+
# })
|
44
|
+
def create_sales_invoice(options)
|
45
|
+
post('sales_invoices', sales_invoice: options)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Retrieve a sales invoice
|
49
|
+
# @param id [Integer] The id of the invoice you want to retrieve.
|
50
|
+
# @return [Invoice] A Hashie of the requested invoice
|
51
|
+
# @example Load an invoice
|
52
|
+
# invoice = SageOne.sales_invoice(8754)
|
53
|
+
def sales_invoice(id)
|
54
|
+
get "sales_invoices/#{id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Update a sales invoice
|
58
|
+
# @param id [Integer] The id of the invoice you want to update.
|
59
|
+
# @param (see #create_sales_invoice)
|
60
|
+
# @option (see #create_sales_invoice)
|
61
|
+
# @return [Invoice] A Hashie of the updated invoice
|
62
|
+
def update_sales_invoice(id, options)
|
63
|
+
put("sales_invoices/#{id}", sales_invoice: options)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Delete a sales invoice
|
67
|
+
# @param id [Integer] The id of the invoice you want to delete.
|
68
|
+
# @return [Invoice] A Hashie of the deleted invoice
|
69
|
+
# @example Delete an invoice
|
70
|
+
# invoice = SageOne.delete_sales_invoice!(12)
|
71
|
+
def delete_sales_invoice!(id)
|
72
|
+
delete("sales_invoices/#{id}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'sage_one/connection'
|
2
|
+
require 'sage_one/request'
|
3
|
+
|
4
|
+
require 'sage_one/oauth'
|
5
|
+
|
6
|
+
require 'sage_one/client/sales_invoices'
|
7
|
+
require 'sage_one/client/contacts'
|
8
|
+
|
9
|
+
module SageOne
|
10
|
+
class Client
|
11
|
+
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
|
12
|
+
|
13
|
+
# Creates an instance of Client configured with
|
14
|
+
# the current SageOne::Configuration options.
|
15
|
+
# Pass in a hash of any valid options to override
|
16
|
+
# them for this instance.
|
17
|
+
#
|
18
|
+
# @see SageOne::Configuration::VALID_OPTIONS_KEYS
|
19
|
+
# SageOne::Configuration::VALID_OPTIONS_KEYS
|
20
|
+
def initialize(options={})
|
21
|
+
options = SageOne.options.merge(options)
|
22
|
+
Configuration::VALID_OPTIONS_KEYS.each do |key|
|
23
|
+
send("#{key}=", options[key])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
include SageOne::Connection
|
28
|
+
include SageOne::Request
|
29
|
+
include SageOne::OAuth
|
30
|
+
include SageOne::Client::SalesInvoices
|
31
|
+
include SageOne::Client::Contacts
|
32
|
+
end
|
33
|
+
end
|