billomat 0.2.0 → 0.3.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.
@@ -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,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Billomat
4
+ # The +billomat+ gem configuration.
4
5
  class Configuration
5
6
  attr_accessor :api_key, :subdomain, :timeout, :app_id, :app_secret
6
7
  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,29 +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
52
  resp = response
37
53
 
38
- raise GatewayError, resp.body if resp.code > 299
39
54
  return nil if resp.body.empty?
40
55
 
41
56
  JSON.parse(resp.body)
57
+ rescue RestClient::Exception => e
58
+ raise GatewayError, e
42
59
  end
43
60
 
61
+ # Executes the API call and return the response.
62
+ #
63
+ # @return [RestClient::Response] the API response
44
64
  def response
45
65
  RestClient::Request.execute(
46
- method: method,
47
- url: url,
48
- timeout: timeout,
49
- headers: headers,
50
- payload: body.to_json
66
+ method: method,
67
+ url: url,
68
+ timeout: timeout,
69
+ headers: headers,
70
+ payload: body.to_json
51
71
  )
52
72
  end
53
73
 
54
- # @return [String] The complete URL for the request
74
+ # @return [String] the complete URL for the request
55
75
  def url
56
76
  "https://#{config.subdomain}.billomat.net/api#{path}"
57
77
  end
@@ -61,18 +81,18 @@ module Billomat
61
81
  config.timeout || 5
62
82
  end
63
83
 
64
- # @return [Hash] The headers for the request.
84
+ # @return [Hash] the headers for the request
65
85
  def headers
66
86
  {
67
- 'Accept' => 'application/json',
68
- 'Content-Type' => 'application/json',
87
+ 'Accept' => 'application/json',
88
+ 'Content-Type' => 'application/json',
69
89
  'X-BillomatApiKey' => config.api_key,
70
- 'X-AppId' => config.app_id,
71
- 'X-AppSecret' => config.app_secret
90
+ 'X-AppId' => config.app_id,
91
+ 'X-AppSecret' => config.app_secret
72
92
  }.reject { |_, val| val.nil? }
73
93
  end
74
94
 
75
- # @return [Billomat::Configuration] The global gem configuration
95
+ # @return [Billomat::Configuration] the global gem configuration
76
96
  #
77
97
  # :reek:UtilityFunction because it's a shorthand
78
98
  def config
@@ -4,50 +4,49 @@ require 'ostruct'
4
4
 
5
5
  module Billomat
6
6
  module Models
7
- ##
8
7
  # This class is the base for all other models (resources).
9
8
  # It handles the communication with the gateway to talk to the API.
10
9
  class Base
11
10
  attr_accessor :data
12
11
 
13
- ##
14
- # Tries to find the resource for the given id
12
+ # Tries to find the resource for the given id.
15
13
  #
16
- # @param [String] id The resource id
17
- # @return [Billomat::Models::Base, nil] The found resource or nil
14
+ # @param id [String] the resource id
15
+ # @return [Billomat::Models::Base, nil] the found resource or nil
18
16
  def self.find(id)
19
17
  return nil if id.nil?
18
+
20
19
  resp = Billomat::Gateway.new(:get, "#{base_path}/#{id}").run
21
20
  new(resp[resource_name])
22
21
  end
23
22
 
24
- ##
25
- # Allows to query for a record
23
+ # Allows to query for a record.
26
24
  #
27
- # @param [Hash] hash The query parameters
28
- # @return [Array<Billomat::Models::Base>] The found records
25
+ # @param hash [Hash] the query parameters
26
+ # @return [Array<Billomat::Models::Base>] the found records
29
27
  def self.where(hash = {})
30
28
  Billomat::Search.new(self, hash).run
31
29
  end
32
30
 
33
31
  ##
34
- # Initializes a new model
32
+ # Initializes a new model.
35
33
  #
36
- # @param [Hash] data The attributes of the object
37
- # @return [Billomat::Models::Base] The record as an object
34
+ # @param data [Hash] the attributes of the object
35
+ # @return [Billomat::Models::Base] the record as an object
38
36
  def initialize(data = {})
39
37
  @data = OpenStruct.new(data)
40
38
  end
41
39
 
42
- ##
43
40
  # Persists the current object in the API.
44
41
  # When record is new it calls create, otherwise it saves the object.
45
42
  #
46
43
  # @return [TrueClass]
47
44
  def save
48
45
  return create if id.nil?
46
+
49
47
  update
50
48
  end
49
+ alias save! save
51
50
 
52
51
  # @return [TrueClass]
53
52
  def create
@@ -59,6 +58,7 @@ module Billomat
59
58
 
60
59
  true
61
60
  end
61
+ alias create! create
62
62
 
63
63
  # @return [TrueClass]
64
64
  def update
@@ -68,6 +68,7 @@ module Billomat
68
68
 
69
69
  true
70
70
  end
71
+ alias update! update
71
72
 
72
73
  # @return [TrueClass]
73
74
  def delete
@@ -76,52 +77,50 @@ module Billomat
76
77
 
77
78
  true
78
79
  end
80
+ alias delete! delete
79
81
 
80
- # @return [String, nil] The object's ID
82
+ # @return [String, nil] the object's ID
81
83
  def id
82
84
  @data['id'] || nil
83
85
  end
84
86
 
85
- ##
86
- # Wraps the data so the API accepts the request
87
+ # Wraps the data so the API accepts the request.
87
88
  #
88
89
  # @example
89
90
  # some_invoice.wrapped_data
90
91
  # #=> { "invoice" => { "id" => "12345" } }
91
92
  #
92
- # @return [Hash] The wrapped data
93
+ # @return [Hash] the wrapped data
93
94
  def wrapped_data
94
95
  { self.class.resource_name => @data.to_h }
95
96
  end
96
97
 
97
- ##
98
- # Returns the object with the right JSON structure
98
+ # Returns the object with the right JSON structure.
99
99
  #
100
- # @return [Hash] The objects data
100
+ # @return [Hash] the objects data
101
101
  def as_json(_options = nil)
102
102
  @data.to_h
103
103
  end
104
104
 
105
- ##
106
- # All values in the @data hash can be accessed like a 'normal' method
105
+ # All values in the @data hash can be accessed like a 'normal' method.
107
106
  #
108
107
  # @example
109
108
  # invoice = Billomat::Models::Invoice.new(invoice_number: '123')
110
109
  # invoice.invoice_number
111
110
  # #=> '123'
112
111
  def method_missing(method, *args, &block)
113
- return @data[method] if @data.to_h.keys.include?(method)
112
+ return @data[method] if @data.to_h.key?(method)
113
+
114
114
  super
115
115
  end
116
116
 
117
- ##
118
- # Necessary for method_missing
117
+ # Necessary for method_missing.
119
118
  #
120
119
  # @param [Symbol] method The method name
121
120
  # @param [TrueClass, FalseClass] include_privat
122
121
  # @return [TrueClass, FalseClass]
123
122
  def respond_to_missing?(method, include_privat = false)
124
- @data.to_h.keys.include?(method.to_s) || super
123
+ @data.to_h.key?(method.to_s) || super
125
124
  end
126
125
  end
127
126
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the client resource
5
+ # Representation of the client resource.
7
6
  class Client < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/clients'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'client'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the client resource
5
+ # Representation of the client resource.
7
6
  class Contact < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/contacts'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'contact'
16
15
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Billomat
4
+ module Models
5
+ # Representation of the credit note resource
6
+ class CreditNote < Base
7
+ # @return [String] the resource's base path
8
+ def self.base_path
9
+ '/credit-notes'
10
+ end
11
+
12
+ # @return [String] the resource's name
13
+ def self.resource_name
14
+ 'credit-note'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Billomat
4
+ module Models
5
+ # Representation of the credit note item resource
6
+ class CreditNoteItem < Base
7
+ # @return [String] the resource's base path
8
+ def self.base_path
9
+ '/credit-note-items'
10
+ end
11
+
12
+ # @return [String] the resource's name
13
+ def self.resource_name
14
+ 'credit-note-item'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,33 +2,29 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
5
  # Representation of the invoice resource
7
6
  class Invoice < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/invoices'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'invoice'
16
15
  end
17
16
 
18
- ##
19
- # Completes the invoice by calling the Complete action
17
+ # Completes the invoice by calling the Complete action.
20
18
  def complete!
21
19
  Billomat::Actions::Complete.new(id).call
22
20
  end
23
21
 
24
- ##
25
- # Cancels the invoice by calling the Cancel action
22
+ # Cancels the invoice by calling the Cancel action.
26
23
  def cancel!
27
24
  Billomat::Actions::Cancel.new(id).call
28
25
  end
29
26
 
30
- ##
31
- # Sends the invoice as an email to the given recipient
27
+ # Sends the invoice as an email to the given recipient.
32
28
  #
33
29
  # @param [String] recipient The email address of the recipient
34
30
  def send_email(recipient)
@@ -37,8 +33,7 @@ module Billomat
37
33
  Billomat::Actions::Email.new(id, email_params).call
38
34
  end
39
35
 
40
- ##
41
- # Allows to download the invoice as an PDF
36
+ # Allows to download the invoice as an PDF.
42
37
  def to_pdf
43
38
  Billomat::Actions::Pdf.new(id).call
44
39
  end
@@ -3,14 +3,14 @@
3
3
  module Billomat
4
4
  module Models
5
5
  ##
6
- # Representation of the invoice item resource
6
+ # Representation of the invoice item resource.
7
7
  class InvoiceItem < Base
8
- # @return [String] The resource's base path
8
+ # @return [String] the resource's base path
9
9
  def self.base_path
10
10
  '/invoice-items'
11
11
  end
12
12
 
13
- # @return [String] The resource's name
13
+ # @return [String] the resource's name
14
14
  def self.resource_name
15
15
  'invoice-item'
16
16
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the invoice payment resource
5
+ # Representation of the invoice payment resource.
7
6
  class InvoicePayment < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/invoice-payments'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'invoice-payment'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the tag resource
5
+ # Representation of the tag resource.
7
6
  class Tag < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/tags'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'tag'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the template resource
5
+ # Representation of the template resource.
7
6
  class Template < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/templates'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'template'
16
15
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'billomat/models/base'
4
4
  require 'billomat/models/client'
5
+ require 'billomat/models/credit_note'
6
+ require 'billomat/models/credit_note_item'
5
7
  require 'billomat/models/invoice'
6
8
  require 'billomat/models/invoice_item'
7
9
  require 'billomat/models/invoice_payment'
@@ -10,7 +12,6 @@ require 'billomat/models/tag'
10
12
  require 'billomat/models/template'
11
13
 
12
14
  module Billomat
13
- ##
14
- # Models represent a resource in the API, e.g. invoices, clients
15
+ # Models represent a resource in the API, e.g. invoices, clients.
15
16
  module Models; end
16
17
  end