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 +4 -4
- data/.reek.yml +1 -1
- data/README.md +23 -0
- data/bin/prerelease +2 -2
- data/erp_integration.gemspec +1 -1
- data/lib/erp_integration/configuration.rb +18 -0
- data/lib/erp_integration/fulfil/api_resource.rb +68 -0
- data/lib/erp_integration/{clients/fulfil_client.rb → fulfil/client.rb} +6 -6
- data/lib/erp_integration/fulfil/collection.rb +26 -0
- data/lib/erp_integration/fulfil/query.rb +39 -0
- data/lib/erp_integration/fulfil/query_methods.rb +90 -0
- data/lib/erp_integration/fulfil/resources/order.rb +29 -0
- data/lib/erp_integration/fulfil/resources/purchase_order.rb +13 -0
- data/lib/erp_integration/fulfil/resources/purchase_order_line.rb +13 -0
- data/lib/erp_integration/fulfil/where_clause.rb +77 -0
- data/lib/erp_integration/order.rb +2 -9
- data/lib/erp_integration/purchase_order.rb +15 -0
- data/lib/erp_integration/purchase_order_line.rb +12 -0
- data/lib/erp_integration/resource.rb +27 -6
- data/lib/erp_integration/version.rb +3 -1
- data/lib/erp_integration.rb +9 -5
- metadata +19 -10
- data/lib/erp_integration/orders/fulfil_order.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5657cdcc644e079869eeb3189a451e97b558cb0c64f93b123280301acd0886b1
|
4
|
+
data.tar.gz: 5a279cf9b1c38ebba6939861e6f15bd5d4ad66acc03364068d5c5ffe46dfa3be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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 "
|
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
|
data/erp_integration.gemspec
CHANGED
@@ -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.
|
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
|
7
|
-
|
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,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`
|
5
|
-
#
|
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__),
|
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
|
-
#
|
66
|
-
#
|
67
|
-
|
68
|
-
|
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.
|
data/lib/erp_integration.rb
CHANGED
@@ -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/
|
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.
|
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-
|
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.
|
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.
|
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/
|
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
|