billomat 0.1.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +30 -0
  3. data/.github/workflows/documentation.yml +38 -0
  4. data/.github/workflows/test.yml +59 -0
  5. data/.gitignore +5 -0
  6. data/.rubocop.yml +53 -0
  7. data/.simplecov +3 -0
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +48 -0
  10. data/Dockerfile +29 -0
  11. data/Envfile +6 -0
  12. data/Gemfile +3 -1
  13. data/Makefile +135 -0
  14. data/README.md +42 -17
  15. data/Rakefile +74 -3
  16. data/billomat.gemspec +28 -22
  17. data/config/docker/.bash_profile +3 -0
  18. data/config/docker/.bashrc +48 -0
  19. data/config/docker/.inputrc +17 -0
  20. data/doc/assets/project.svg +68 -0
  21. data/docker-compose.yml +9 -0
  22. data/lib/billomat/actions/cancel.rb +3 -5
  23. data/lib/billomat/actions/complete.rb +8 -12
  24. data/lib/billomat/actions/email.rb +6 -10
  25. data/lib/billomat/actions/pdf.rb +4 -10
  26. data/lib/billomat/actions/uncancel.rb +3 -6
  27. data/lib/billomat/actions.rb +0 -1
  28. data/lib/billomat/configuration.rb +2 -1
  29. data/lib/billomat/gateway.rb +52 -24
  30. data/lib/billomat/models/base.rb +26 -27
  31. data/lib/billomat/models/client.rb +3 -4
  32. data/lib/billomat/models/contact.rb +18 -0
  33. data/lib/billomat/models/credit_note.rb +18 -0
  34. data/lib/billomat/models/credit_note_item.rb +18 -0
  35. data/lib/billomat/models/invoice.rb +6 -11
  36. data/lib/billomat/models/invoice_item.rb +3 -3
  37. data/lib/billomat/models/invoice_payment.rb +3 -4
  38. data/lib/billomat/models/tag.rb +18 -0
  39. data/lib/billomat/models/template.rb +18 -0
  40. data/lib/billomat/models.rb +6 -2
  41. data/lib/billomat/search.rb +12 -14
  42. data/lib/billomat/version.rb +1 -1
  43. data/lib/billomat.rb +15 -18
  44. metadata +117 -20
  45. data/.travis.yml +0 -18
  46. data/doc/assets/logo.png +0 -0
  47. data/doc/assets/project.png +0 -0
  48. data/doc/assets/project.xcf +0 -0
data/billomat.gemspec CHANGED
@@ -1,40 +1,46 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "billomat/version"
5
+ require 'billomat/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "billomat"
8
+ spec.name = 'billomat'
8
9
  spec.version = Billomat::VERSION
9
10
  spec.licenses = ['MIT']
10
- spec.authors = ["Henning Vogt"]
11
- spec.email = ["henning.vogt@hausgold.de"]
11
+ spec.authors = ['Henning Vogt']
12
+ spec.email = ['henning.vogt@hausgold.de']
12
13
 
13
- spec.summary = %q{Wrapper for the Billomat API}
14
- spec.homepage = "https://github.com/hausgold/billomat"
14
+ spec.summary = 'Wrapper for the Billomat API'
15
+ spec.homepage = 'https://github.com/hausgold/billomat'
15
16
 
16
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
- # to allow pushing to a single host or delete this section to allow pushing to any host.
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the
18
+ # 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing
20
+ # to any host.
18
21
  if spec.respond_to?(:metadata)
19
- spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
23
  else
21
- raise "RubyGems 2.0 or newer is required to protect against " \
22
- "public gem pushes."
24
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
25
+ 'public gem pushes.'
23
26
  end
24
27
 
25
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
29
  f.match(%r{^(test|spec|features)/})
27
30
  end
28
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
- spec.require_paths = ["lib"]
30
-
31
- spec.required_ruby_version = '>= 2.0'
32
+ spec.require_paths = ['lib']
32
33
 
33
34
  spec.add_dependency 'rest-client', '~> 2.0', '>= 2.0.2'
34
35
 
35
- spec.add_development_dependency "bundler", "~> 1.15"
36
- spec.add_development_dependency "rake", "~> 10.0"
37
- spec.add_development_dependency "rspec", "~> 3.0"
38
- spec.add_development_dependency "simplecov", "~> 0.15"
39
- spec.add_development_dependency "pry", "~> 0.11"
36
+ spec.add_development_dependency 'bundler', '>= 1.15', '< 3'
37
+ spec.add_development_dependency 'pry', '~> 0.11'
38
+ spec.add_development_dependency 'railties', '>= 4.2.0', '< 6.1'
39
+ spec.add_development_dependency 'rake', '~> 10.0'
40
+ spec.add_development_dependency 'rspec', '~> 3.0'
41
+ spec.add_development_dependency 'rubocop', '~> 0.63.1'
42
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.31'
43
+ spec.add_development_dependency 'simplecov', '< 0.18'
44
+ spec.add_development_dependency 'yard', '~> 0.9.18'
45
+ spec.add_development_dependency 'yard-activesupport-concern', '~> 0.0.1'
40
46
  end
@@ -0,0 +1,3 @@
1
+ if [ -f ~/.bashrc ]; then
2
+ . ~/.bashrc
3
+ fi
@@ -0,0 +1,48 @@
1
+ # ~/.bashrc: executed by bash(1) for non-login shells.
2
+ # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
3
+ # for examples
4
+
5
+ _GEM_PATHS=$(ls -d1 ${HOME}/.gem/ruby/*/bin 2>/dev/null | paste -sd ':')
6
+ _APP_PATHS=$(ls -d1 /app/vendor/bundle/ruby/*/bin 2>/dev/null | paste -sd ':')
7
+
8
+ export PATH="${_GEM_PATHS}:${_APP_PATHS}:${PATH}"
9
+ export PATH="/app/node_modules/.bin:${HOME}/.bin:/app/bin:${PATH}"
10
+ export MAKE_ENV=baremetal
11
+
12
+ # Disable the autostart of all supervisord units
13
+ sudo sed -i 's/autostart=.*/autostart=false/g' /etc/supervisor/conf.d/*
14
+
15
+ # Start the supervisord (empty, no units)
16
+ sudo supervisord >/dev/null 2>&1 &
17
+
18
+ # Wait for supervisord
19
+ while ! supervisorctl status >/dev/null 2>&1; do sleep 1; done
20
+
21
+ # Boot the mDNS stack
22
+ echo '# Start the mDNS stack'
23
+ sudo supervisorctl start dbus avahi
24
+ echo
25
+
26
+ function watch-make-test()
27
+ {
28
+ while [ 1 ]; do
29
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
30
+ make test
31
+ done
32
+ }
33
+
34
+ function watch-make()
35
+ {
36
+ while [ 1 ]; do
37
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
38
+ make $@
39
+ done
40
+ }
41
+
42
+ function watch-run()
43
+ {
44
+ while [ 1 ]; do
45
+ inotifywait --quiet -r `pwd` -e close_write --format '%e -> %w%f'
46
+ bash -c "$@"
47
+ done
48
+ }
@@ -0,0 +1,17 @@
1
+ # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
2
+ "\e[1;5C": forward-word
3
+ "\e[1;5D": backward-word
4
+ "\e[5C": forward-word
5
+ "\e[5D": backward-word
6
+ "\e\e[C": forward-word
7
+ "\e\e[D": backward-word
8
+
9
+ # handle common Home/End escape codes
10
+ "\e[1~": beginning-of-line
11
+ "\e[4~": end-of-line
12
+ "\e[7~": beginning-of-line
13
+ "\e[8~": end-of-line
14
+ "\eOH": beginning-of-line
15
+ "\eOF": end-of-line
16
+ "\e[H": beginning-of-line
17
+ "\e[F": end-of-line
@@ -0,0 +1,68 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6
+ xmlns:svg="http://www.w3.org/2000/svg"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ version="1.1"
9
+ id="Ebene_1"
10
+ x="0px"
11
+ y="0px"
12
+ viewBox="0 0 800 200"
13
+ xml:space="preserve"
14
+ width="800"
15
+ height="200"><metadata
16
+ id="metadata33"><rdf:RDF><cc:Work
17
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
18
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
19
+ id="defs31" />
20
+ <style
21
+ type="text/css"
22
+ id="style2">
23
+ .st0{fill-rule:evenodd;clip-rule:evenodd;fill:#E73E11;}
24
+ .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0371B9;}
25
+ .st2{fill:#132E48;}
26
+ .st3{font-family:'OpenSans-Bold';}
27
+ .st4{font-size:29.5168px;}
28
+ .st5{fill-rule:evenodd;clip-rule:evenodd;fill:none;}
29
+ .st6{opacity:0.5;fill:#132E48;}
30
+ .st7{font-family:'OpenSans';}
31
+ .st8{font-size:12px;}
32
+ </style>
33
+ <g
34
+ transform="translate(0,1.53584)"
35
+ id="g828"><g
36
+ transform="translate(35.93985,35.66416)"
37
+ id="g8">
38
+ <path
39
+ style="clip-rule:evenodd;fill:#e73e11;fill-rule:evenodd"
40
+ id="path4"
41
+ d="m -0.1,124.4 c 0,0 33.7,-123.2 66.7,-123.2 12.8,0 26.9,21.9 38.8,47.2 -23.6,27.9 -66.6,59.7 -94,76 -7.1,0 -11.5,0 -11.5,0 z"
42
+ class="st0" />
43
+ <path
44
+ style="clip-rule:evenodd;fill:#0371b9;fill-rule:evenodd"
45
+ id="path6"
46
+ d="m 88.1,101.8 c 13.5,-10.4 18.4,-16.2 27.1,-25.4 10,25.7 16.7,48 16.7,48 0,0 -41.4,0 -78,0 14.6,-7.9 18.7,-10.7 34.2,-22.6 z"
47
+ class="st1" />
48
+ </g><text
49
+ y="106.40316"
50
+ x="192.43155"
51
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.51733398px;font-family:'Open Sans', sans-serif;-inkscape-font-specification:'OpenSans-Bold, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#132e48"
52
+ id="text10"
53
+ class="st2 st3 st4">Billomat</text>
54
+ <rect
55
+ style="clip-rule:evenodd;fill:none;fill-rule:evenodd"
56
+ id="rect12"
57
+ height="24"
58
+ width="314.5"
59
+ class="st5"
60
+ y="118.06416"
61
+ x="194.23985" /><text
62
+ y="127.22146"
63
+ x="194.21715"
64
+ style="font-size:12px;font-family:'Open Sans', sans-serif;opacity:0.5;fill:#132e48;-inkscape-font-specification:'Open Sans, sans-serif, Normal';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;"
65
+ id="text14"
66
+ class="st6 st7 st8">API wrapper for billomat.com</text>
67
+ </g>
68
+ </svg>
@@ -0,0 +1,9 @@
1
+ version: "3"
2
+ services:
3
+ test:
4
+ build: .
5
+ env_file: Envfile
6
+ network_mode: bridge
7
+ working_dir: /app
8
+ volumes:
9
+ - .:/app
@@ -2,17 +2,15 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This actions cancels an invoice
7
6
  class Cancel
8
- # @param [String] invoice_id The invoice ID
7
+ # @param invoice_id [String] the invoice ID
9
8
  # @return [Billomat::Actions::Cancel]
10
9
  def initialize(invoice_id)
11
10
  @invoice_id = invoice_id
12
11
  end
13
12
 
14
- ##
15
- # Calls the gateway
13
+ # Calls the gateway.
16
14
  #
17
15
  # @return [TrueClass]
18
16
  def call
@@ -21,7 +19,7 @@ module Billomat
21
19
  true
22
20
  end
23
21
 
24
- # @return [String] The cancel path with the invoice_id
22
+ # @return [String] the cancel path with the invoice_id
25
23
  def path
26
24
  "/invoices/#{@invoice_id}/cancel"
27
25
  end
@@ -2,14 +2,12 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
- # Completes an invoice by calling the /complete path on a resource
5
+ # Completes an invoice by calling the /complete path on a resource.
7
6
  class Complete
8
- ##
9
- # Returns a Complete object
7
+ # Returns a Complete object.
10
8
  #
11
- # @param [String] invoice_id The ID of the invoice
12
- # @param [Hash] opts The options for this request
9
+ # @param invoice_id [String] the ID of the invoice
10
+ # @param opts [Hash] the options for this request
13
11
  # @return [Billomat::Actions::Complete]
14
12
  #
15
13
  # @example
@@ -19,8 +17,7 @@ module Billomat
19
17
  @opts = opts
20
18
  end
21
19
 
22
- ##
23
- # Calls the gateway
20
+ # Calls the gateway.
24
21
  #
25
22
  # @return [TrueClass]
26
23
  def call
@@ -29,15 +26,14 @@ module Billomat
29
26
  true
30
27
  end
31
28
 
32
- ##
33
- # The given options have to be wrapped
29
+ # The given options have to be wrapped.
34
30
  #
35
- # @return [Hash] The payload for the complete request
31
+ # @return [Hash] the payload for the complete request
36
32
  def wrapped_data
37
33
  { complete: @opts }
38
34
  end
39
35
 
40
- # @return [String] The complete path with the invoice_id
36
+ # @return [String] the complete path with the invoice_id
41
37
  def path
42
38
  "/invoices/#{@invoice_id}/complete"
43
39
  end
@@ -2,25 +2,22 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This actions sends an invoice email.
7
6
  # Recipients must be passed like this:
8
- # { recipients: { to: 'bob@example.org' } }
7
+ # { recipients: { to: 'bob@example.org' } }
9
8
  #
10
9
  # @example
11
10
  # Billomat::Actions::Email.new('1235', { recipiens: { to: 'a@b.org' } })
12
11
  class Email
13
- # @param [String] invoice_id The invoice ID
14
- # @param [Hash] opts The options for this action
15
- #
12
+ # @param invoice_id [String] the invoice ID
13
+ # @param opts [Hash] the options for this action
16
14
  # @return [Billomat::Actions::Email]
17
15
  def initialize(invoice_id, opts = {})
18
16
  @invoice_id = invoice_id
19
17
  @opts = opts
20
18
  end
21
19
 
22
- ##
23
- # Calls the gateway
20
+ # Calls the gateway.
24
21
  #
25
22
  # @return [TrueClass]
26
23
  def call
@@ -29,15 +26,14 @@ module Billomat
29
26
  true
30
27
  end
31
28
 
32
- ##
33
29
  # Wraps the options
34
30
  #
35
- # @return [Hash] The wrapped email options
31
+ # @return [Hash] the wrapped email options
36
32
  def wrapped_data
37
33
  { email: @opts }
38
34
  end
39
35
 
40
- # @return [String] The path for the email action
36
+ # @return [String] the path for the email action
41
37
  def path
42
38
  "/invoices/#{@invoice_id}/email"
43
39
  end
@@ -2,21 +2,18 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This class allows to download the invoice as a pdf.
7
6
  # The PDF comes in a base64 encoded string in the response body.
8
7
  class Pdf
9
- # @param [String] invoice_id The invoice ID
10
- # @param [Hash] opts The options for this action
11
- #
8
+ # @param invoice_id [String] the invoice ID
9
+ # @param opts [Hash] the options for this action
12
10
  # @return [Billomat::Actions::Pdf]
13
11
  def initialize(invoice_id, opts = {})
14
12
  @invoice_id = invoice_id
15
13
  @opts = opts
16
14
  end
17
15
 
18
- ##
19
- # Calls the gateway
16
+ # Calls the gateway.
20
17
  #
21
18
  # @return [TrueClass]
22
19
  def call
@@ -24,10 +21,7 @@ module Billomat
24
21
  resp['pdf']
25
22
  end
26
23
 
27
- ##
28
- # Wraps the options
29
- #
30
- # @return [Hash] The wrapped email options
24
+ # @return [String] the path for the PDF action
31
25
  def path
32
26
  "/invoices/#{@invoice_id}/pdf"
33
27
  end
@@ -2,21 +2,18 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This actions uncancels an canceld invoice.
7
6
  #
8
7
  # @example
9
8
  # Billomat::Actions::Uncancel.new('1235')
10
9
  class Uncancel
11
- # @param [String] invoice_id The invoice ID
12
- #
10
+ # @param invoice_id [String] the invoice ID
13
11
  # @return [Billomat::Actions::Uncancel]
14
12
  def initialize(invoice_id)
15
13
  @invoice_id = invoice_id
16
14
  end
17
15
 
18
- ##
19
- # Calls the gateway
16
+ # Calls the gateway.
20
17
  #
21
18
  # @return [TrueClass]
22
19
  def call
@@ -25,7 +22,7 @@ module Billomat
25
22
  true
26
23
  end
27
24
 
28
- # @return [String] The path for the uncancel action
25
+ # @return [String] the path for the uncancel action
29
26
  def path
30
27
  "/invoices/#{@invoice_id}/uncancel"
31
28
  end
@@ -7,7 +7,6 @@ require 'billomat/actions/cancel'
7
7
  require 'billomat/actions/uncancel'
8
8
 
9
9
  module Billomat
10
- ##
11
10
  # Actions are API calls that do not directly represent a resource.
12
11
  # They are mostly non-RESTful actions that are called on a resources.
13
12
  module Actions; end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Billomat
4
+ # The +billomat+ gem configuration.
4
5
  class Configuration
5
- attr_accessor :api_key, :subdomain, :timeout
6
+ attr_accessor :api_key, :subdomain, :timeout, :app_id, :app_secret
6
7
  end
7
8
  end
@@ -4,16 +4,32 @@ require 'rest-client'
4
4
  require 'json'
5
5
 
6
6
  module Billomat
7
- ##
8
- # Raised if something goes wrong during an API call
9
- class GatewayError < StandardError; end
7
+ # Raised if something goes wrong during an API call.
8
+ class GatewayError < StandardError
9
+ attr_reader :original_exception
10
10
 
11
- ##
12
- # This class can be used by the gem to communicate with the API
11
+ # Create a new GatewayError from a RestClient exception.
12
+ #
13
+ # @param original_exception [RestClient::Exception] the original exception
14
+ def initialize(original_exception)
15
+ # Extract the error from the response if it is present. If no error
16
+ # could be extracted, use the whole body
17
+ body = original_exception.response&.body
18
+ response_error = JSON.parse(body).dig('errors', 'error') || body if body
19
+
20
+ # Use the default message and append our detailed error
21
+ message = original_exception.default_message
22
+ message += " ('#{response_error}')" if response_error
23
+
24
+ super(message)
25
+ @original_exception = original_exception
26
+ end
27
+ end
28
+
29
+ # This class can be used by the gem to communicate with the API.
13
30
  class Gateway
14
31
  attr_reader :method, :path, :body
15
32
 
16
- ##
17
33
  # Creates a new Gateway
18
34
  #
19
35
  # @param [Symbol] method The HTTP verb
@@ -29,25 +45,33 @@ module Billomat
29
45
  @body = body
30
46
  end
31
47
 
32
- ##
33
- # Executes the API call
34
- # @return [Hash] The response body
48
+ # Executes the API call and parse the response.
49
+ #
50
+ # @return [Hash] the response body
35
51
  def run
36
- resp = RestClient::Request.execute(
37
- method: method,
38
- url: url,
39
- timeout: timeout,
40
- headers: headers,
41
- payload: body.to_json
42
- )
52
+ resp = response
43
53
 
44
- raise GatewayError, resp.body if resp.code > 299
45
54
  return nil if resp.body.empty?
46
55
 
47
56
  JSON.parse(resp.body)
57
+ rescue RestClient::Exception => e
58
+ raise GatewayError, e
59
+ end
60
+
61
+ # Executes the API call and return the response.
62
+ #
63
+ # @return [RestClient::Response] the API response
64
+ def response
65
+ RestClient::Request.execute(
66
+ method: method,
67
+ url: url,
68
+ timeout: timeout,
69
+ headers: headers,
70
+ payload: body.to_json
71
+ )
48
72
  end
49
73
 
50
- # @return [String] The complete URL for the request
74
+ # @return [String] the complete URL for the request
51
75
  def url
52
76
  "https://#{config.subdomain}.billomat.net/api#{path}"
53
77
  end
@@ -57,16 +81,20 @@ module Billomat
57
81
  config.timeout || 5
58
82
  end
59
83
 
60
- # @return [Hash] The headers for the request.
84
+ # @return [Hash] the headers for the request
61
85
  def headers
62
86
  {
63
- 'Accept' => 'application/json',
64
- 'Content-Type' => 'application/json',
65
- 'X-BillomatApiKey' => config.api_key
66
- }
87
+ 'Accept' => 'application/json',
88
+ 'Content-Type' => 'application/json',
89
+ 'X-BillomatApiKey' => config.api_key,
90
+ 'X-AppId' => config.app_id,
91
+ 'X-AppSecret' => config.app_secret
92
+ }.reject { |_, val| val.nil? }
67
93
  end
68
94
 
69
- # @return [Billomat::Configuration] The global gem configuration
95
+ # @return [Billomat::Configuration] the global gem configuration
96
+ #
97
+ # :reek:UtilityFunction because it's a shorthand
70
98
  def config
71
99
  Billomat.configuration
72
100
  end