elmas 2.4.2 → 2.5.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 (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