erp_integration 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b73b17b72afec2d03e59213c35db31b1260f304907a9686af94a6bc0123b19c2
4
- data.tar.gz: e2f42a3b2c313c6aa91efdcd5663b3c070a04221416799f0b4ee8753fc29df43
3
+ metadata.gz: 5657cdcc644e079869eeb3189a451e97b558cb0c64f93b123280301acd0886b1
4
+ data.tar.gz: 5a279cf9b1c38ebba6939861e6f15bd5d4ad66acc03364068d5c5ffe46dfa3be
5
5
  SHA512:
6
- metadata.gz: e951c654c3293e5e169b212c5b0dc5ecc1b742750155520edb68302b2452e7f5637caa2e36ec75e37363e76626efe667186bd45e58e38cfe0c08c92709cd9f9a
7
- data.tar.gz: ff1d1e69793589517db55c30ad56be8e7a484d77ef5cba846d00805aad46e3123d90343b38854a6b2323eb7607835fe9ab4a6d491d769d6d78a66b2657e1c4c0
6
+ metadata.gz: 2a3203a5b4277d6656f955aa015b01ab9c180cf628510eceeff4f6b6cab878a349a403c495342a0d050cd5c23c25fb0bda5bbe945b658e64fe53261c7fd78e38
7
+ data.tar.gz: f9014829733dd877118093fc7375517ba73982a87c1ad84f1497c183e514900372297c127551de9591801e1404f65ef21439bf0ef8aa3778fb3c27fb650eca6c
data/.reek.yml CHANGED
@@ -49,7 +49,7 @@ detectors:
49
49
  # Acceptable code smell as we don't know the full extend of our configuration
50
50
  # interface for the gem yet.
51
51
  - ErpIntegration::Configuration
52
- - ErpIntegration::Resource#initialize
52
+ - ErpIntegration::Resource
53
53
  MissingSafeMethod:
54
54
  enabled: true
55
55
  exclude: []
data/README.md CHANGED
@@ -32,6 +32,29 @@ ErpIntegration.configure do |config|
32
32
  end
33
33
  ```
34
34
 
35
+ ### Querying third-party vendors
36
+
37
+ After configuring the gem, one can easily query all the available ERP resources from the connected third-parties.
38
+
39
+ ```ruby
40
+ $ ErpIntegration::Order.where(reference: 'MT1000SKX')
41
+ => #<ErpIntegration::Fulfil::Collection @items=[<ErpIntegration::Order @id=100 />] />
42
+ ```
43
+
44
+ By default, only the `id` will be added to the found ERP resources. However, one can use the `select` method to include more fields.
45
+
46
+ ```ruby
47
+ $ ErpIntegration::Order.select(:id, :reference).where(reference: 'MT1000SKX')
48
+ => #<ErpIntegration::Fulfil::Collection @items=[<ErpIntegration::Order @id=100 @reference=MT1000SKX />] />
49
+ ```
50
+
51
+ There are also other type of `where` queries available:
52
+ - `where_like` for case sensitive queries.
53
+ - `where_ilike` for case insensitive queries.
54
+ - `where_not` for non-equality queries.
55
+ - `where_in` for inclusion queries.
56
+ - `where_not_in` for exclusion queries.
57
+
35
58
  ## Development
36
59
 
37
60
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/prerelease CHANGED
@@ -9,7 +9,7 @@ bundle
9
9
  rake
10
10
 
11
11
  # Update the gem version.
12
- printf "module ErpIntegration\n VERSION = \"$VERSION\".freeze\nend\n" > ./lib/erp_integration/version.rb
12
+ printf "# frozen_string_literal: true\n\nmodule ErpIntegration\n VERSION = '$VERSION'\nend\n" > ./lib/erp_integration/version.rb
13
13
  git add lib/erp_integration/version.rb
14
14
  git checkout -b "release_v.$VERSION"
15
15
  git commit -m "[RELEASE] Bump version for v$VERSION"
@@ -17,4 +17,4 @@ git push origin "release_v.$VERSION"
17
17
 
18
18
  # Tag the new gem version.
19
19
  git tag v$VERSION
20
- git push --tags
20
+ git push --tags
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency 'activesupport', '>= 0.12'
33
33
 
34
34
  # Faraday is a HTTP/REST API client library to interact with third-party API vendors
35
- spec.add_dependency 'faraday', '>= 0.17.3', '< 1.8.0'
35
+ spec.add_dependency 'faraday', '>= 0.17.3', '< 1.9.0'
36
36
  spec.add_dependency 'faraday_middleware', '~> 0.14.0'
37
37
 
38
38
  # Development dependencies
@@ -27,6 +27,16 @@ module ErpIntegration
27
27
  # @return [Symbol] The configured adapter for the orders
28
28
  attr_writer :order_adapter
29
29
 
30
+ # Allows configuring an adapter for the `PurchaseOrder` resource. When none is
31
+ # configured, it will default to Fulfil.
32
+ # @return [Symbol] The configured adapter for the purchase orders.
33
+ attr_writer :purchase_order_adapter
34
+
35
+ # Allows configuring an adapter for the `PurchaseOrderLine` resource. When
36
+ # none is configured, it will default to Fulfil.
37
+ # @return [Symbol] The configured adapter for the purchase order lines.
38
+ attr_writer :purchase_order_line_adapter
39
+
30
40
  def initialize(**options)
31
41
  options.each_pair do |key, value|
32
42
  public_send("#{key}=", value) if respond_to?("#{key}=")
@@ -36,6 +46,14 @@ module ErpIntegration
36
46
  def order_adapter
37
47
  @order_adapter || :fulfil
38
48
  end
49
+
50
+ def purchase_order_adapter
51
+ @purchase_order_adapter || :fulfil
52
+ end
53
+
54
+ def purchase_order_line_adapter
55
+ @purchase_order_line_adapter || :fulfil
56
+ end
39
57
  end
40
58
 
41
59
  # Returns ERP Integration's configuration.
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'collection'
4
+ require_relative 'query_methods'
5
+
6
+ module ErpIntegration
7
+ module Fulfil
8
+ class ApiResource
9
+ include QueryMethods
10
+
11
+ cattr_accessor :resource_klass, instance_accessor: false
12
+
13
+ delegate :client, :model_name, :resource_klass, to: 'self.class'
14
+ delegate_missing_to :all
15
+
16
+ def initialize(resource_klass)
17
+ self.class.resource_klass = resource_klass
18
+ end
19
+
20
+ # The `where` and `includes` methods lazyly build a search/read query
21
+ # for Fulfil. By calling `all`, the prepared search/read query will actually
22
+ # be executed and the results will be fetched.
23
+ # @return [ErpIntegration::Fulfil::Collection] An enumerable collection object with all API results.
24
+ def all
25
+ return @results if defined?(@results)
26
+
27
+ @results = Collection.new(
28
+ client.put(
29
+ "model/#{model_name}/search_read",
30
+ Query.new(selected_fields, where_clauses)
31
+ ),
32
+ resource_klass: resource_klass
33
+ )
34
+ end
35
+
36
+ # The `client` exposes the `ErpIntegration::Fulfil::Client` to the class.
37
+ # @return [ErpIntegration::Fulfil::Client] The HTTP client for Fulfil.
38
+ def self.client
39
+ Client.new(
40
+ api_key: config.fulfil_api_key,
41
+ merchant_id: config.fulfil_merchant_id
42
+ )
43
+ end
44
+
45
+ # The `config` exposes the gem's configuration to the `ApiResource`.
46
+ # @return [ErpIntegration::Configuration] The gem's configuration object.
47
+ def self.config
48
+ ErpIntegration.config
49
+ end
50
+
51
+ # Fulfil doesn't use logical naming conventions. However, we do need
52
+ # the name of a model to build the resource URI.
53
+ #
54
+ # By manually setting the model name, we allow the `Fulfil::Connection`
55
+ # module to connect to the correct API endpoint.
56
+ #
57
+ # @param name [String] The logical path name in Fulfil.
58
+ # @return [String] The model name
59
+ def self.model_name=(name)
60
+ instance_variable_set(:@model_name, name)
61
+ end
62
+
63
+ def self.model_name
64
+ instance_variable_get(:@model_name)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,12 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday_middleware'
4
-
5
3
  module ErpIntegration
6
- module Clients
7
- # The `FulfilClient` is a simple HTTP client configured to be used for API
8
- # requests to the Fulfil endpoints.
9
- class FulfilClient
4
+ module Fulfil
5
+ class Client
10
6
  attr_reader :api_key, :merchant_id
11
7
  attr_writer :connection, :faraday_adapter
12
8
 
@@ -49,6 +45,10 @@ module ErpIntegration
49
45
 
50
46
  %i[delete get patch put post].each do |action_name|
51
47
  define_method(action_name) do |path, options = {}|
48
+ if api_key.nil? || merchant_id.nil?
49
+ raise ErpIntegration::Error, 'The Fulfil API key and/or Merchant ID are missing.'
50
+ end
51
+
52
52
  connection.public_send(action_name, "api/#{version}/#{path}", options).body
53
53
  end
54
54
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ module Fulfil
5
+ # The `Collection` class provides an enumerable interface for the `ApiResource`
6
+ # to consume. It turns all the given items into the passed resource class.
7
+ #
8
+ # @example
9
+ # $ Collection.new([...], resource_klass: ErpIntegration::Order)
10
+ # # => <Collection @items=[<ErpIntegration::Order ... />, <ErpIntegration::Order ... />]
11
+ class Collection
12
+ include Enumerable
13
+ attr_reader :items
14
+
15
+ def initialize(items, resource_klass:)
16
+ @items = items.map { |item| resource_klass.new(item) }
17
+ end
18
+
19
+ # The `each` method turns the `Collection` instance into an enumerable object.
20
+ # For more information, see https://ruby-doc.org/core-3.0.2/Enumerable.html
21
+ def each(&block)
22
+ @items.each(&block)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ module Fulfil
5
+ # The `Query` class transforms where clauses and included fields into a
6
+ # queryable object that Fulfil understands.
7
+ #
8
+ # @example
9
+ # $ Query.new([:id, 'product.id'], [['id', '=', '100']]).to_json
10
+ # # => {"fields":["id","product.id"],"filters":[["id","=","100"]]}
11
+ #
12
+ # The instance of the `Query` class is meant to be passed to `Faraday` or
13
+ # a third-party vendor client (e.g. `Fulfil::Client`) that uses `Faraday`.
14
+ #
15
+ # `Faraday` will call the `.to_json` method before sending the data to Fulfil.
16
+ # So, there is no need to explicitly call `.to_json` on the `Query` instance.
17
+ #
18
+ # @example
19
+ # Faraday.post("/api-endpoint", Query.new([:id, 'product.id'], [WhereClause.new(key: :id, value: 100)]))
20
+ class Query
21
+ attr_reader :fields, :filters
22
+ DEFAULT_FIELDS = %w[id].freeze
23
+
24
+ # @param fields [Array<String|Symbol>] A list of fields for Fulfil.
25
+ # @param filters [Array<WhereClause>] A list of where clauses for Fulfil.
26
+ def initialize(fields, filters)
27
+ @fields = (fields || DEFAULT_FIELDS).map(&:to_s).uniq
28
+ @filters = (filters || []).map(&:to_filter).uniq
29
+ end
30
+
31
+ def to_json(*_object)
32
+ {
33
+ fields: @fields,
34
+ filters: @filters
35
+ }.to_json
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'query'
4
+ require_relative 'where_clause'
5
+
6
+ module ErpIntegration
7
+ module Fulfil
8
+ module QueryMethods
9
+ attr_accessor :selected_fields, :where_clauses
10
+
11
+ # Fulfil only returns an ID by default when one would query their API. However,
12
+ # the ID is seldom the only value one will need. The `select` method allows
13
+ # selecting multiple fields at once.
14
+ #
15
+ # @example
16
+ # $ ErpIntegration::Order.select(:id, :name, 'product.name')
17
+ # # => <ErpIntegration::Fulfil::Resources::Order @selected_fields=[:id, :name, "product.name"] />
18
+ #
19
+ # When one calls the `all` method, it will fetch all the resources from Fulfil
20
+ # and it will return all the given fields (if available).
21
+ #
22
+ # @example
23
+ # $ ErpIntegration::Order.select(:id, :name, 'product.name').all
24
+ # # => <ErpIntegration::Fulfil::Collection @items=[...] />
25
+ #
26
+ # Both a list of Strings, Symbols or a combination of these can be passed
27
+ # to the `select` method.
28
+ def select(*fields)
29
+ clone.select!(*fields)
30
+ end
31
+
32
+ def select!(*fields)
33
+ self.selected_fields = (selected_fields || []).concat(fields)
34
+ self
35
+ end
36
+
37
+ # Fulfil has a very powerful query syntax. However, that query syntax is rather
38
+ # complicated and it's really specific to Fulfil.
39
+ #
40
+ # The `where` method introduces a well-known interface (e.g. ActiveRecord::Relation)
41
+ # to query resources in Fulfil.
42
+ #
43
+ # @example
44
+ # $ ErpIntegration::Order.where(id: 100).all
45
+ # # => <ErpIntegration::Fulfil::Collection @items=[<ErpIntegration::Order @id=100 />] />
46
+ #
47
+ # If one adds the `comparison_operator` key to the arguments, it will use
48
+ # that comparison operator to build the query to Fulfil.
49
+ #
50
+ # All the other `where_` methods use this technique to provide logic for all
51
+ # the different comparison operators.
52
+ def where(*args)
53
+ clone.where!(*args)
54
+ end
55
+
56
+ def where!(args)
57
+ comparison_operator = args.delete(:comparison_operator)
58
+
59
+ args.each_pair do |key, value|
60
+ self.where_clauses =
61
+ (where_clauses || []) << WhereClause.new(
62
+ key: key, value: value, comparison_operator: comparison_operator
63
+ )
64
+ end
65
+
66
+ self
67
+ end
68
+
69
+ def where_ilike(args)
70
+ where(args.merge(comparison_operator: 'ilike'))
71
+ end
72
+
73
+ def where_in(args)
74
+ where(args.merge(comparison_operator: 'in'))
75
+ end
76
+
77
+ def where_like(args)
78
+ where(args.merge(comparison_operator: 'like'))
79
+ end
80
+
81
+ def where_not(args)
82
+ where(args.merge(comparison_operator: '!='))
83
+ end
84
+
85
+ def where_not_in(args)
86
+ where(args.merge(comparison_operator: 'not in'))
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../api_resource'
4
+
5
+ module ErpIntegration
6
+ module Fulfil
7
+ module Resources
8
+ class Order < ApiResource
9
+ self.model_name = 'sale.sale'
10
+
11
+ # Allows cancelling the entire sales order in Fulfil.
12
+ # @param id [Integer|String] The ID of the to be cancelled order.
13
+ # @return [boolean] Whether the sales order was cancelled successfully or not.
14
+ def cancel(id)
15
+ client.put("model/sale.sale/#{id}/cancel")
16
+ true
17
+
18
+ # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
19
+ # be cancelled. If a sales order isn't cancellable by design, no exception should be raised.
20
+ #
21
+ # See the Fulfil's documentation for more information:
22
+ # https://developers.fulfil.io/rest_api/model/sale.sale/#cancel-a-sales-order
23
+ rescue ErpIntegration::HttpError::BadRequest
24
+ false
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../api_resource'
4
+
5
+ module ErpIntegration
6
+ module Fulfil
7
+ module Resources
8
+ class PurchaseOrder < ApiResource
9
+ self.model_name = 'purchase.purchase'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../api_resource'
4
+
5
+ module ErpIntegration
6
+ module Fulfil
7
+ module Resources
8
+ class PurchaseOrderLine < ApiResource
9
+ self.model_name = 'purchase.line'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ module Fulfil
5
+ # The `WhereClause` model encapsulates the logic for the filter options for Fulfil.
6
+ # It transforms the attributes passed to any of the where lookup methods into
7
+ # a format that Fulfil can actually understand.
8
+ #
9
+ # For more information, see their documentation.
10
+ # https://developers.fulfil.io/guides/searching-filtering/#field-filter-expressions
11
+ #
12
+ # @example
13
+ # WhereClause.new({ key: :id, value: 100, comparison_operator: 'ilike' })
14
+ # # => <WhereClause @key="id" value="100" comparison_operator="ilike" />
15
+ #
16
+ # @example
17
+ # $ WhereClause.new({ key: :id, value: 100 })
18
+ # # => <WhereClause @key="id" value="100" comparison_operator="=" />
19
+ class WhereClause
20
+ COMPARISON_OPERATORS = [
21
+ '=', '!=', '<', '<=', '=>', '>', 'like', 'ilike', 'in', 'not in'
22
+ ].freeze
23
+
24
+ DEFAULT_COMPARISON_OPERATOR = '='
25
+
26
+ def initialize(key:, value:, comparison_operator: DEFAULT_COMPARISON_OPERATOR)
27
+ @comparison_operator = verify_comparison_operator(comparison_operator)
28
+ @key = key.to_s
29
+ @value = value.to_s
30
+ end
31
+
32
+ # Transforms the `WhereClause` into a filter object for Fulfil.
33
+ # @return [Array<String>] The formatted filter for Fulfil.
34
+ def to_filter
35
+ [@key, @comparison_operator, @value]
36
+ end
37
+
38
+ # The `===` allows comparing different WhereClause objects. Under the hood,
39
+ # this is used by `.uniq` to determine whether objects are equal to each other.
40
+ #
41
+ # Note: `.uniq` also depends on `.hash` on the `WhereClause` instance. See
42
+ # the `.hash` method to see how the two objects are compared.
43
+ #
44
+ # @example
45
+ # $ WhereClause.new(id: 100) == WhereClause.new(id: 100)
46
+ # # => true
47
+ #
48
+ # $ WhereClause.new(id: 100) == WhereClause.new(id: 101)
49
+ # # => false
50
+ #
51
+ # $ WhereClause.new(id: 100) == WhereClause.new(name: "PT100")
52
+ # # => false
53
+ #
54
+ # @param other [WhereClause] Another `WhereClause` instance.
55
+ # @return [Boolean] Whether or not the `WhereClause`s are equal.
56
+ def ==(other)
57
+ to_filter == other.to_filter
58
+ end
59
+ alias eql? ==
60
+
61
+ # The `.hash` allows comparing two `WhereClause` instances for uniqueness.
62
+ # See https://rubyapi.org/2.3/o/object#method-i-hash
63
+ # @return [Fixnum] A Fixnum that identifies the `WhereClause`.
64
+ def hash
65
+ to_filter.hash
66
+ end
67
+
68
+ private
69
+
70
+ def verify_comparison_operator(comparison_operator)
71
+ return comparison_operator if COMPARISON_OPERATORS.include?(comparison_operator)
72
+
73
+ DEFAULT_COMPARISON_OPERATOR
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,16 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ErpIntegration
4
- # The `Order` class exposes available ERP Order operations
5
- # assigning them to the proper adapter
4
+ # The `ErpIntegration::Order` exposes an uniformed API for interaction with
5
+ # third-party ERP vendors.
6
6
  class Order < Resource
7
7
  attr_accessor :id, :number
8
-
9
- # Allows cancelling the entire sales order.
10
- # @param id [Integer|String] The ID of the to be cancelled order.
11
- # @return [boolean] Whether the sales order was cancelled successfully or not.
12
- def self.cancel(id)
13
- adapter.new.cancel(id)
14
- end
15
8
  end
16
9
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ # The `ErpIntegration::PurchaseOrder` exposes an uniformed API for interaction with
5
+ # third-party ERP vendors.
6
+ class PurchaseOrder < Resource
7
+ attr_accessor :id, :lines, :number, :party, :purchase_date, :warehouse,
8
+ :approval_status, :attachments, :company, :create_date, :create_uid,
9
+ :currency, :customer, :delivery_address, :description, :invoice_address,
10
+ :invoice_method, :invoice_state, :invoices, :metadata, :payment_term,
11
+ :purchase_person, :reference, :shipment_method, :shipments, :state,
12
+ :tax_amount, :total_amount, :total_quantity, :untaxed_amount, :write_date,
13
+ :write_uid
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ # The `ErpIntegration::PurchaseOrderLine` exposes an uniformed API for
5
+ # interaction with third-party ERP vendors.
6
+ class PurchaseOrderLine < Resource
7
+ attr_accessor :id, :amount, :delivery_date, :product, :quantity, :unit_price,
8
+ :attachments, :create_date, :create_uid, :description, :invoice_lines,
9
+ :metadata, :note, :purchase, :quantity_canceled, :quantity_invoiced,
10
+ :quantity_received, :sequence, :taxes, :type, :unit, :write_date, :write_uid
11
+ end
12
+ end
@@ -46,8 +46,8 @@ module ErpIntegration
46
46
  def adapter
47
47
  return @adapter if defined?(@adapter)
48
48
 
49
- require_relative File.join(File.dirname(__FILE__), '..', "#{adapter_path}.rb")
50
- @adapter = adapter_klass.constantize
49
+ require_relative File.join(File.dirname(__FILE__), "#{adapter_path}.rb")
50
+ @adapter = adapter_klass.constantize.new(self)
51
51
  end
52
52
 
53
53
  # Dynamically exposes the adapter class to the resource.
@@ -56,16 +56,37 @@ module ErpIntegration
56
56
  "ErpIntegration::#{adapter_path.classify}"
57
57
  end
58
58
 
59
+ # Provides a relative path to the adapter for the resource.
60
+ # @return [String] the path to the adapter
61
+ def adapter_path
62
+ "#{adapter_type}/resources/#{resource_name}"
63
+ end
64
+
59
65
  # Retrieves the adapter type for the resource from the global configuration.
60
66
  # @return [String] the adapter type for the resource
61
67
  def adapter_type
62
68
  ErpIntegration.config.send("#{resource_name}_adapter").to_s
63
69
  end
64
70
 
65
- # Provides a relative path to the adapter for the resource.
66
- # @return [String] the path to the adapter
67
- def adapter_path
68
- "#{resource_name.pluralize}/#{adapter_type}_#{resource_name}"
71
+ # Due to `method_missing` we're able to delegate all the work automatically
72
+ # to the adapter. However, if the method doesn't exist on the adapter, the
73
+ # `NoMethodError` is still being raised.
74
+ # @param method_name [Symbol] The name of the missing method.
75
+ # @param args [Array] The list of arguments for the missing method.
76
+ # @param block [Proc] Any block given to the missing method.
77
+ def method_missing(method_name, *args, &block)
78
+ if adapter.respond_to?(method_name)
79
+ adapter.send(method_name, *args, &block)
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ # The `respond_to_missing?` complements the `method_missing` method.
86
+ # @param method_name [Symbol] The name of the missing method.
87
+ # @param include_private [Boolean] Whether private methods should be checked too (defaults to false).
88
+ def respond_to_missing?(method_name, include_private = false)
89
+ adapter.respond_to?(method_name) || super
69
90
  end
70
91
 
71
92
  # Derives the name of the resource from the class name.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ErpIntegration
2
- VERSION = "0.2.0".freeze
4
+ VERSION = '0.3.0'
3
5
  end
@@ -2,7 +2,10 @@
2
2
 
3
3
  require 'active_support'
4
4
  require 'active_support/core_ext/string/inflections'
5
+ require 'active_support/core_ext/module/delegation' # Allows using `delegate`
6
+ require 'active_support/core_ext/module/attribute_accessors' # Allows using `delegate_missing_to`
5
7
  require 'faraday'
8
+ require 'faraday_middleware'
6
9
  require 'json'
7
10
 
8
11
  require_relative 'erp_integration/version'
@@ -12,13 +15,14 @@ require_relative 'erp_integration/configuration'
12
15
  # Middleware
13
16
  require_relative 'erp_integration/middleware/error_handling'
14
17
 
15
- # Resources
16
- require_relative 'erp_integration/resource'
17
- require_relative 'erp_integration/order'
18
-
19
18
  # HTTP clients
20
- require_relative 'erp_integration/clients/fulfil_client'
19
+ require_relative 'erp_integration/fulfil/client'
21
20
 
22
21
  # The `ErpIntegration` integrates Mejuri with third-party ERP vendors.
23
22
  module ErpIntegration
23
+ # Resources
24
+ autoload :Resource, 'erp_integration/resource'
25
+ autoload :Order, 'erp_integration/order'
26
+ autoload :PurchaseOrder, 'erp_integration/purchase_order'
27
+ autoload :PurchaseOrderLine, 'erp_integration/purchase_order_line'
24
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erp_integration
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Vermaas
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-23 00:00:00.000000000 Z
11
+ date: 2021-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 0.17.3
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: 1.8.0
36
+ version: 1.9.0
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 0.17.3
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: 1.8.0
46
+ version: 1.9.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: faraday_middleware
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -218,7 +218,7 @@ dependencies:
218
218
  - - '='
219
219
  - !ruby/object:Gem::Version
220
220
  version: 1.19.2
221
- description:
221
+ description:
222
222
  email:
223
223
  - stefan@knowndecimal.com
224
224
  executables: []
@@ -253,12 +253,21 @@ files:
253
253
  - bin/setup
254
254
  - erp_integration.gemspec
255
255
  - lib/erp_integration.rb
256
- - lib/erp_integration/clients/fulfil_client.rb
257
256
  - lib/erp_integration/configuration.rb
258
257
  - lib/erp_integration/errors.rb
258
+ - lib/erp_integration/fulfil/api_resource.rb
259
+ - lib/erp_integration/fulfil/client.rb
260
+ - lib/erp_integration/fulfil/collection.rb
261
+ - lib/erp_integration/fulfil/query.rb
262
+ - lib/erp_integration/fulfil/query_methods.rb
263
+ - lib/erp_integration/fulfil/resources/order.rb
264
+ - lib/erp_integration/fulfil/resources/purchase_order.rb
265
+ - lib/erp_integration/fulfil/resources/purchase_order_line.rb
266
+ - lib/erp_integration/fulfil/where_clause.rb
259
267
  - lib/erp_integration/middleware/error_handling.rb
260
268
  - lib/erp_integration/order.rb
261
- - lib/erp_integration/orders/fulfil_order.rb
269
+ - lib/erp_integration/purchase_order.rb
270
+ - lib/erp_integration/purchase_order_line.rb
262
271
  - lib/erp_integration/resource.rb
263
272
  - lib/erp_integration/version.rb
264
273
  homepage: https://www.github.com/mejuri-inc/erp-integration
@@ -269,7 +278,7 @@ metadata:
269
278
  homepage_uri: https://www.github.com/mejuri-inc/erp-integration
270
279
  source_code_uri: https://www.github.com/mejuri-inc/erp-integration
271
280
  changelog_uri: https://www.github.com/mejuri-inc/erp-integration/blob/main/CHANGELOG.md
272
- post_install_message:
281
+ post_install_message:
273
282
  rdoc_options: []
274
283
  require_paths:
275
284
  - lib
@@ -285,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
285
294
  version: '0'
286
295
  requirements: []
287
296
  rubygems_version: 3.2.22
288
- signing_key:
297
+ signing_key:
289
298
  specification_version: 4
290
299
  summary: Connects Mejuri with third-party ERP vendors
291
300
  test_files: []
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ErpIntegration
4
- module Orders
5
- # The `FulfilOrder` handles all interaction with sales orders in Fulfil.
6
- class FulfilOrder
7
- extend Forwardable
8
- def_delegator 'ErpIntegration', :config
9
-
10
- # Allows cancelling the entire sales order in Fulfil.
11
- # @param id [Integer|String] The ID of the to be cancelled order.
12
- # @return [boolean] Whether the sales order was cancelled successfully or not.
13
- def cancel(id)
14
- client.put("model/sale.sale/#{id}/cancel")
15
- true
16
-
17
- # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
18
- # be cancelled. If a sales order isn't cancellable by design, no exception should be raised.
19
- #
20
- # See the Fulfil's documentation for more information:
21
- # https://developers.fulfil.io/rest_api/model/sale.sale/#cancel-a-sales-order
22
- rescue ErpIntegration::HttpError::BadRequest
23
- false
24
- end
25
-
26
- private
27
-
28
- def client
29
- @client ||= Clients::FulfilClient.new(
30
- api_key: config.fulfil_api_key,
31
- merchant_id: config.fulfil_merchant_id
32
- )
33
- end
34
- end
35
- end
36
- end