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.
Files changed (50) hide show
  1. data/.gitignore +14 -0
  2. data/.travis.yml +11 -0
  3. data/.yardopts +4 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +19 -0
  6. data/README.md +141 -0
  7. data/Rakefile +4 -0
  8. data/bin/autospec +16 -0
  9. data/bin/htmldiff +16 -0
  10. data/bin/ldiff +16 -0
  11. data/bin/rake +16 -0
  12. data/bin/rspec +16 -0
  13. data/bin/yard +16 -0
  14. data/bin/yardoc +16 -0
  15. data/bin/yri +16 -0
  16. data/lib/faraday/request/oauth2.rb +25 -0
  17. data/lib/faraday/response/convert_sdata_to_headers.rb +70 -0
  18. data/lib/faraday/response/raise_sage_one_exception.rb +42 -0
  19. data/lib/sage_one/client/contacts.rb +14 -0
  20. data/lib/sage_one/client/sales_invoices.rb +77 -0
  21. data/lib/sage_one/client.rb +33 -0
  22. data/lib/sage_one/configuration.rb +81 -0
  23. data/lib/sage_one/connection.rb +44 -0
  24. data/lib/sage_one/error.rb +39 -0
  25. data/lib/sage_one/oauth.rb +49 -0
  26. data/lib/sage_one/request.rb +72 -0
  27. data/lib/sage_one/version.rb +3 -0
  28. data/lib/sage_one.rb +31 -0
  29. data/sage_one.gemspec +32 -0
  30. data/spec/faraday/request/oauth2_spec.rb +44 -0
  31. data/spec/faraday/response/convert_sdata_to_headers_spec.rb +113 -0
  32. data/spec/faraday/response/raise_sage_one_exception_spec.rb +45 -0
  33. data/spec/fixtures/contact.json +28 -0
  34. data/spec/fixtures/invalid_sales_invoice.json +28 -0
  35. data/spec/fixtures/oauth/invalid_client.json +1 -0
  36. data/spec/fixtures/oauth/invalid_grant.json +1 -0
  37. data/spec/fixtures/oauth/oauth_token.json +4 -0
  38. data/spec/fixtures/sales_invoice.json +43 -0
  39. data/spec/fixtures/sales_invoices.json +90 -0
  40. data/spec/sage_one/client/contacts_spec.rb +19 -0
  41. data/spec/sage_one/client/sales_invoices_spec.rb +53 -0
  42. data/spec/sage_one/client_spec.rb +41 -0
  43. data/spec/sage_one/configuration_spec.rb +88 -0
  44. data/spec/sage_one/connection_spec.rb +36 -0
  45. data/spec/sage_one/oauth_spec.rb +44 -0
  46. data/spec/sage_one/request_spec.rb +113 -0
  47. data/spec/sage_one/version_spec.rb +7 -0
  48. data/spec/sage_one_spec.rb +38 -0
  49. data/spec/spec_helper.rb +76 -0
  50. metadata +301 -0
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ log/*
5
+ .rvmrc
6
+ .DS_Store
7
+ coverage
8
+ *.sublime
9
+ .yardoc
10
+ _yardoc
11
+ spec/reports
12
+ rdoc
13
+ doc/*
14
+ start_irb
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ - ruby-head
8
+ - jruby-head
9
+ before_install:
10
+ - gem update --system
11
+ - gem --version
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --no-private
2
+ lib/**/*.rb
3
+ -
4
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ platforms :jruby do
4
+ gem "jruby-openssl", "~> 0.7"
5
+ end
6
+
7
+ # Specify your gem's dependencies in sage_one.gemspec
8
+ gemspec
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 [![Build Status](https://secure.travis-ci.org/customersure/sage_one.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/7b1e86c3d9e3583a684d326a97ba06d0.png)][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
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ task :default => :spec
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