elmas 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -0
  3. data/Gemfile +3 -1
  4. data/Guardfile +6 -4
  5. data/README.md +15 -4
  6. data/Rakefile +3 -1
  7. data/elmas.gemspec +17 -15
  8. data/lib/elmas.rb +11 -2
  9. data/lib/elmas/api.rb +2 -0
  10. data/lib/elmas/client.rb +2 -0
  11. data/lib/elmas/config.rb +28 -21
  12. data/lib/elmas/exception.rb +2 -0
  13. data/lib/elmas/oauth.rb +40 -4
  14. data/lib/elmas/parser.rb +2 -0
  15. data/lib/elmas/request.rb +2 -0
  16. data/lib/elmas/resource.rb +7 -5
  17. data/lib/elmas/resources/account.rb +27 -24
  18. data/lib/elmas/resources/aging_receivables_list.rb +8 -6
  19. data/lib/elmas/resources/bank_account.rb +13 -19
  20. data/lib/elmas/resources/bank_entry.rb +6 -4
  21. data/lib/elmas/resources/bank_entry_line.rb +6 -4
  22. data/lib/elmas/resources/base_entry_line.rb +10 -6
  23. data/lib/elmas/resources/cash_entry.rb +22 -0
  24. data/lib/elmas/resources/cash_entry_line.rb +20 -0
  25. data/lib/elmas/resources/contact.rb +10 -8
  26. data/lib/elmas/resources/costcenter.rb +4 -2
  27. data/lib/elmas/resources/costunit.rb +4 -2
  28. data/lib/elmas/resources/division.rb +26 -0
  29. data/lib/elmas/resources/document.rb +7 -5
  30. data/lib/elmas/resources/document_attachment.rb +3 -1
  31. data/lib/elmas/resources/general_journal_entry.rb +4 -2
  32. data/lib/elmas/resources/general_journal_entry_line.rb +2 -0
  33. data/lib/elmas/resources/gl_account.rb +11 -9
  34. data/lib/elmas/resources/item.rb +21 -18
  35. data/lib/elmas/resources/item_group.rb +4 -2
  36. data/lib/elmas/resources/journal.rb +6 -4
  37. data/lib/elmas/resources/layout.rb +6 -4
  38. data/lib/elmas/resources/mailbox.rb +6 -4
  39. data/lib/elmas/resources/payment_condition.rb +9 -7
  40. data/lib/elmas/resources/printed_sales_invoice.rb +12 -10
  41. data/lib/elmas/resources/project.rb +11 -19
  42. data/lib/elmas/resources/purchase_entry.rb +5 -6
  43. data/lib/elmas/resources/purchase_entry_line.rb +2 -0
  44. data/lib/elmas/resources/receivables_list.rb +4 -5
  45. data/lib/elmas/resources/sales_entry.rb +5 -7
  46. data/lib/elmas/resources/sales_entry_line.rb +2 -0
  47. data/lib/elmas/resources/sales_invoice.rb +4 -5
  48. data/lib/elmas/resources/sales_invoice_line.rb +4 -5
  49. data/lib/elmas/resources/sales_item_prices.rb +4 -5
  50. data/lib/elmas/resources/sales_order.rb +4 -5
  51. data/lib/elmas/resources/sales_order_line.rb +4 -5
  52. data/lib/elmas/resources/shared_sales_attributes.rb +6 -11
  53. data/lib/elmas/resources/time_transaction.rb +8 -12
  54. data/lib/elmas/resources/transaction.rb +4 -2
  55. data/lib/elmas/resources/transaction_line.rb +4 -2
  56. data/lib/elmas/resources/vat_code.rb +5 -8
  57. data/lib/elmas/response.rb +5 -2
  58. data/lib/elmas/result_set.rb +4 -2
  59. data/lib/elmas/sanitizer.rb +7 -5
  60. data/lib/elmas/uri.rb +2 -0
  61. data/lib/elmas/utils.rb +2 -0
  62. data/lib/elmas/version.rb +4 -2
  63. metadata +29 -27
  64. data/lib/elmas/log.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90fb105de4c3f2aa77e6f7b3626dd5094321830e
4
- data.tar.gz: 17e8a3283657ecf5b2719149513de9006397f654
3
+ metadata.gz: c3beb2738aa3a94532e1e4f032a229f87e94aa08
4
+ data.tar.gz: 37ecf10dae1c0baf831cd221f15c900959d7137b
5
5
  SHA512:
6
- metadata.gz: 47063de22815164f663e759576a0ed8555d07ce3f886f7292fa75d5ee1cb248233cc7b8815a4bde0f26acc1c55cba74b987740abcee1cb58d33ad3ea983552df
7
- data.tar.gz: 827676a146a9ad6d76f3d8484a6c4d299e7f548079c955a8588987b29a4441c2117f2e509a10cf38a26af4cd387ebb0956b2cd052b997c074d7eecfdf850047f
6
+ metadata.gz: cb404ddad916430568cc25e593d52b06b0e8e35c4b211c78a8587e866c12e8de69b9ff5bee4f2a73659e49c274159cc034c12083592ab7cf146567be1ba3af1c
7
+ data.tar.gz: 299d31e087f5f2c7847e99cf81cde26e47db46c589216bed196177e75e6e884b69b53a4ea36435897b59d4a69fd3ef0935e523c8d0906cc72580670c1a531f36
@@ -1,6 +1,15 @@
1
1
  Documentation:
2
2
  Enabled: false
3
3
 
4
+ Lint/UriEscapeUnescape:
5
+ Enabled: false
6
+
7
+ NAming/FileName:
8
+ Enabled: false
9
+
10
+ Style/MethodMissing:
11
+ Enabled: false
12
+
4
13
  StringLiterals:
5
14
  EnforcedStyle: double_quotes
6
15
 
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in elmas.gemspec
4
6
  gemspec
data/Guardfile CHANGED
@@ -1,11 +1,13 @@
1
- guard :rspec, cmd: 'rspec' do
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: "rspec" do
2
4
  watch(%r{^spec/.+_spec\.rb$})
3
5
  watch(%r{^lib/(.+)\.rb$}) { "spec" }
4
- watch('spec/spec_helper.rb') { "spec" }
6
+ watch("spec/spec_helper.rb") { "spec" }
5
7
  end
6
8
 
7
- guard :rubocop, all_on_start: false, cli: ['--format', 'clang', '--rails'] do
9
+ guard :rubocop, all_on_start: false, cli: ["--format", "clang", "--rails"] do
8
10
  watch(%r{^spec/.+_spec\.rb$})
9
11
  watch(%r{^lib/(.+)\.rb$})
10
- watch('spec/spec_helper.rb')
12
+ watch("spec/spec_helper.rb")
11
13
  end
data/README.md CHANGED
@@ -18,6 +18,8 @@ Elmas means diamond, but in this case it's an API wrapper for [Exact Online](htt
18
18
  * [mipmip](https://github.com/mipmip)
19
19
  * [Bramjetten](https://github.com/Bramjetten)
20
20
  * [LaurensN](https://github.com/LaurensN)
21
+ * [jdlombardozzi](https://github.com/jdlombardozzi)
22
+ * [michielverkoijen](https://github.com/michielverkoijen)
21
23
 
22
24
  Thanks for helping! If you want to contribute read through this readme how to!
23
25
 
@@ -75,11 +77,8 @@ So combining all of this results in
75
77
  Elmas.configure do |config|
76
78
  config.client_id = ENV['CLIENT_ID']
77
79
  config.client_secret = ENV['CLIENT_SECRET']
78
- end
79
- Elmas.configure do |config|
80
+ config.redirect_uri = ENV['REDIRECT_URI']
80
81
  config.access_token = Elmas.authorize(ENV['EXACT_USER_NAME'], ENV['EXACT_PASSWORD']).access_token
81
- end
82
- Elmas.configure do |config|
83
82
  config.division = Elmas.authorize_division
84
83
  end
85
84
  ```
@@ -98,6 +97,18 @@ unless Elmas.authorized?
98
97
  end
99
98
  ```
100
99
 
100
+ ### Logger
101
+
102
+ The default logger is STDOUT. A custom logger can be be configured.
103
+ ```ruby
104
+ dir = File.dirname("./tmp/errors.log")
105
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
106
+
107
+ Elmas.configure do |config|
108
+ config.logger = ::Logger.new("./tmp/errors.log", "daily")
109
+ end
110
+ ```
111
+
101
112
  ## Accessing the API
102
113
 
103
114
  We can retrieve data from the API using the following syntax.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
 
3
5
  task :rubocop do
@@ -8,4 +10,4 @@ task :rspec do
8
10
  sh "rspec"
9
11
  end
10
12
 
11
- task default: [:rubocop, :rspec]
13
+ task default: %i[rubocop rspec]
@@ -1,37 +1,39 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'elmas/version'
5
+ require "elmas/version"
5
6
 
7
+ # rubocop:disable Metrics/BlockLength
6
8
  Gem::Specification.new do |spec|
7
9
  spec.name = "elmas"
8
10
  spec.authors = ["Marthyn"]
9
11
  spec.email = ["MarthynOlthof@hoppinger.nl"]
10
12
 
11
- spec.summary = %q{API wrapper for Exact Online}
13
+ spec.summary = "API wrapper for Exact Online"
12
14
  spec.homepage = "https://github.com/exactonline/exactonline-api-ruby-client"
13
- spec.licenses = %w(MIT)
15
+ spec.licenses = %w[MIT]
14
16
 
15
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
16
- spec.require_paths = %w(lib)
18
+ spec.require_paths = %w[lib]
17
19
  spec.version = Elmas::Version.to_s
18
20
 
21
+ spec.add_dependency "activeresource"
22
+ spec.add_dependency "activesupport"
19
23
  spec.add_dependency "faraday", [">= 0.12.1"]
20
24
  spec.add_dependency "mechanize", ">= 2.7.5"
21
- spec.add_dependency "activesupport"
22
- spec.add_dependency "activeresource"
23
25
 
24
26
  spec.add_development_dependency "bundler"
27
+ spec.add_development_dependency "dotenv"
28
+ spec.add_development_dependency "guard-rspec"
29
+ spec.add_development_dependency "guard-rubocop"
30
+ spec.add_development_dependency "listen"
31
+ spec.add_development_dependency "mutant-rspec"
25
32
  spec.add_development_dependency "rake"
26
33
  spec.add_development_dependency "rspec"
34
+ spec.add_development_dependency "ruby_dep"
35
+ spec.add_development_dependency "rubycritic"
27
36
  spec.add_development_dependency "simplecov"
28
37
  spec.add_development_dependency "simplecov-rcov"
29
38
  spec.add_development_dependency "webmock"
30
- spec.add_development_dependency "rubycritic"
31
- spec.add_development_dependency "guard-rspec"
32
- spec.add_development_dependency "guard-rubocop"
33
- spec.add_development_dependency "listen"
34
- spec.add_development_dependency "ruby_dep"
35
- spec.add_development_dependency "mutant-rspec"
36
- spec.add_development_dependency "dotenv"
37
39
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "elmas/version"
2
4
  require "elmas/api"
3
5
  require "elmas/config"
4
6
  require "elmas/response"
5
7
  require "elmas/client"
6
- require "elmas/log"
7
8
  require "elmas/resource"
8
9
  require "elmas/result_set"
9
10
  require "elmas/sanitizer"
@@ -44,10 +45,10 @@ require "elmas/resources/vat_code"
44
45
  require "elmas/resources/general_journal_entry"
45
46
  require "elmas/resources/general_journal_entry_line"
46
47
  require "elmas/resources/payment_condition"
48
+ require "elmas/resources/division"
47
49
 
48
50
  module Elmas
49
51
  extend Config
50
- extend Log
51
52
 
52
53
  def self.client(options = {})
53
54
  Elmas::Client.new(options)
@@ -63,4 +64,12 @@ module Elmas
63
64
  def self.respond_to?(method, include_all = false)
64
65
  client.respond_to?(method, include_all) || super
65
66
  end
67
+
68
+ def self.info(msg)
69
+ logger.info(msg)
70
+ end
71
+
72
+ def self.error(msg)
73
+ logger.error(msg)
74
+ end
66
75
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path("../request", __FILE__)
2
4
  require File.expand_path("../config", __FILE__)
3
5
  require File.expand_path("../oauth", __FILE__)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "faraday"
2
4
 
3
5
  module Elmas
@@ -1,29 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "faraday"
2
4
  require "active_resource/threadsafe_attributes"
5
+ require "logger"
3
6
 
4
7
  module Elmas
5
8
  module Config
6
9
  include ThreadsafeAttributes
7
10
  # An array of valid keys in the options hash
8
- VALID_OPTIONS_KEYS = [
9
- :access_token,
10
- :adapter,
11
- :client_id,
12
- :client_secret,
13
- :connection_options,
14
- :redirect_uri,
15
- :response_format,
16
- :user_agent,
17
- :endpoint,
18
- :division,
19
- :base_url,
20
- :refresh_token
11
+ VALID_OPTIONS_KEYS = %i[
12
+ access_token
13
+ adapter
14
+ client_id
15
+ client_secret
16
+ connection_options
17
+ redirect_uri
18
+ response_format
19
+ user_agent
20
+ endpoint
21
+ division
22
+ base_url
23
+ refresh_token
24
+ logger
21
25
  ].freeze
22
26
 
23
27
  # By default, don't set a user access token
24
- DEFAULT_ACCESS_TOKEN = "".freeze
28
+ DEFAULT_ACCESS_TOKEN = ""
25
29
 
26
- DEFAULT_REFRESH_TOKEN = "".freeze
30
+ DEFAULT_REFRESH_TOKEN = ""
27
31
 
28
32
  # The adapter that will be used to connect if none is set
29
33
  #
@@ -31,27 +35,27 @@ module Elmas
31
35
  DEFAULT_ADAPTER = Faraday.default_adapter
32
36
 
33
37
  # By default, client id should be set in .env
34
- DEFAULT_CLIENT_ID = "".freeze
38
+ DEFAULT_CLIENT_ID = ""
35
39
 
36
40
  # By default, client secret should be set in .env
37
- DEFAULT_CLIENT_SECRET = "".freeze
41
+ DEFAULT_CLIENT_SECRET = ""
38
42
 
39
43
  # By default, don't set any connection options
40
44
  DEFAULT_CONNECTION_OPTIONS = {}.freeze
41
45
 
42
- DEFAULT_BASE_URL = "https://start.exactonline.nl".freeze
46
+ DEFAULT_BASE_URL = "https://start.exactonline.nl"
43
47
 
44
48
  # The endpoint that will be used to connect if none is set
45
- DEFAULT_ENDPOINT = "api/v1".freeze
49
+ DEFAULT_ENDPOINT = "api/v1"
46
50
 
47
51
  # the division code you want to connect with
48
- DEFAULT_DIVISION = "".freeze
52
+ DEFAULT_DIVISION = ""
49
53
 
50
54
  # The response format appended to the path and sent in the 'Accept' header if none is set
51
55
  #
52
56
  DEFAULT_FORMAT = :json
53
57
 
54
- DEFAULT_REDIRECT_URI = "https://www.getpostman.com/oauth2/callback".freeze
58
+ DEFAULT_REDIRECT_URI = "https://www.getpostman.com/oauth2/callback"
55
59
 
56
60
  # By default, don't set user agent
57
61
  DEFAULT_USER_AGENT = nil
@@ -59,6 +63,8 @@ module Elmas
59
63
  # An array of valid request/response formats
60
64
  VALID_FORMATS = [:json].freeze
61
65
 
66
+ DEFAULT_LOGGER = ::Logger.new(STDOUT)
67
+
62
68
  # @private
63
69
  threadsafe_attribute(*VALID_OPTIONS_KEYS)
64
70
 
@@ -93,6 +99,7 @@ module Elmas
93
99
  self.response_format = DEFAULT_FORMAT
94
100
  self.user_agent = DEFAULT_USER_AGENT
95
101
  self.refresh_token = DEFAULT_REFRESH_TOKEN
102
+ self.logger = DEFAULT_LOGGER
96
103
  end
97
104
  end
98
105
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Elmas
2
4
  class BadRequestException < StandardError
3
5
  def initialize(response, parsed)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "mechanize"
2
4
  require "uri"
3
5
  require "json"
@@ -7,6 +9,7 @@ require File.expand_path("../response", __FILE__)
7
9
 
8
10
  # from https://developers.exactonline.com/#Example retrieve access token.html
9
11
  module Elmas
12
+ # rubocop:disable Metrics/ModuleLength
10
13
  module OAuth
11
14
  def authorize(user_name, password, options = {})
12
15
  agent = Mechanize.new
@@ -18,6 +21,15 @@ module Elmas
18
21
  OauthResponse.new(get_access_token(code))
19
22
  end
20
23
 
24
+ def refresh_authorization
25
+ OauthResponse.new(get_refresh_token(refresh_token)).tap do |response|
26
+ Elmas.configure do |config|
27
+ config.access_token = response.access_token
28
+ config.refresh_token = response.refresh_token
29
+ end
30
+ end
31
+ end
32
+
21
33
  def authorized?
22
34
  # Do a test call, return false if 401 or any error code
23
35
  get("/Current/Me", no_division: true)
@@ -32,13 +44,10 @@ module Elmas
32
44
 
33
45
  def auto_authorize
34
46
  Elmas.configure do |config|
47
+ config.redirect_uri = ENV["REDIRECT_URI"]
35
48
  config.client_id = ENV["CLIENT_ID"]
36
49
  config.client_secret = ENV["CLIENT_SECRET"]
37
- end
38
- Elmas.configure do |config|
39
50
  config.access_token = Elmas.authorize(ENV["EXACT_USER_NAME"], ENV["EXACT_PASSWORD"]).access_token
40
- end
41
- Elmas.configure do |config|
42
51
  config.division = Elmas.authorize_division
43
52
  end
44
53
  end
@@ -67,6 +76,23 @@ module Elmas
67
76
  end
68
77
  end
69
78
 
79
+ # Return an access token from authorization via refresh token
80
+ def get_refresh_token(refresh_token)
81
+ conn = Faraday.new(url: config[:base_url]) do |faraday|
82
+ faraday.request :url_encoded
83
+ faraday.adapter Faraday.default_adapter
84
+ end
85
+
86
+ params = refresh_access_token_params(refresh_token)
87
+
88
+ conn.post do |req|
89
+ req.url "/api/oauth2/token"
90
+ req.body = params
91
+ req.headers["Accept"] = "application/json"
92
+ req.headers["Content-Type"] = "application/x-www-form-urlencoded"
93
+ end
94
+ end
95
+
70
96
  private
71
97
 
72
98
  def login(agent, user_name, password, options)
@@ -81,6 +107,7 @@ module Elmas
81
107
 
82
108
  def allow_access(agent)
83
109
  return if agent.page.uri.to_s.include?("getpostman")
110
+ return if agent.page.uri.to_s.include?(redirect_uri)
84
111
  form = agent.page.form_with(id: "PublicOAuth2Form")
85
112
  button = form.button_with(id: "AllowButton")
86
113
  agent.submit(form, button)
@@ -101,6 +128,15 @@ module Elmas
101
128
  redirect_uri: redirect_uri
102
129
  }
103
130
  end
131
+
132
+ def refresh_access_token_params(code)
133
+ {
134
+ client_id: client_id,
135
+ client_secret: client_secret,
136
+ grant_type: "refresh_token",
137
+ refresh_token: code
138
+ }
139
+ end
104
140
  end
105
141
  end
106
142
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Elmas
2
4
  class Parser
3
5
  attr_accessor :parsed_json
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Elmas
2
4
  # Defines HTTP request methods
3
5
  module Request
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path("../utils", __FILE__)
2
4
  require File.expand_path("../exception", __FILE__)
3
5
  require File.expand_path("../uri", __FILE__)
@@ -24,8 +26,8 @@ module Elmas
24
26
  def find_all(options = {})
25
27
  @order_by = options[:order_by]
26
28
  @select = options[:select]
27
- response = get(uri([:order, :select]))
28
- response.results if response
29
+ response = get(uri(%i[order select]))
30
+ response&.results
29
31
  end
30
32
 
31
33
  # Pass filters in an array, for example 'filters: [:id, :name]'
@@ -33,14 +35,14 @@ module Elmas
33
35
  @filters = options[:filters]
34
36
  @order_by = options[:order_by]
35
37
  @select = options[:select]
36
- response = get(uri([:order, :select, :filters]))
37
- response.results if response
38
+ response = get(uri(%i[order select filters]))
39
+ response&.results
38
40
  end
39
41
 
40
42
  def find
41
43
  return nil unless id?
42
44
  response = get(uri([:id]))
43
- response.results.first if response
45
+ response&.results&.first
44
46
  end
45
47
 
46
48
  # Normally use the url method (which applies the filters) but sometimes you only want to use the base path or other paths