solidus_promotions 4.7.2 → 4.8.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/LICENSE +26 -0
- data/README.md +1 -1
- data/app/models/concerns/solidus_promotions/conditions/taxon_condition.rb +2 -0
- data/app/models/solidus_promotions/benefit.rb +6 -0
- data/app/models/solidus_promotions/calculators/distributed_amount.rb +1 -1
- data/app/models/solidus_promotions/calculators/flat_rate.rb +1 -1
- data/app/models/solidus_promotions/calculators/flexi_rate.rb +1 -1
- data/app/models/solidus_promotions/calculators/tiered_flat_rate.rb +1 -1
- data/app/models/solidus_promotions/calculators/tiered_percent.rb +1 -1
- data/app/models/solidus_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +1 -1
- data/app/models/solidus_promotions/conditions/item_total.rb +1 -1
- data/app/models/solidus_promotions/conditions/taxon_revenue.rb +50 -0
- data/app/models/solidus_promotions/load_promotions.rb +6 -0
- data/app/models/solidus_promotions/order_adjuster/discount_order.rb +12 -11
- data/app/models/solidus_promotions/order_adjuster.rb +10 -0
- data/app/models/solidus_promotions/promotion.rb +1 -0
- data/app/models/solidus_promotions/promotion_eligibility_checker.rb +60 -0
- data/app/models/solidus_promotions/promotion_handler/coupon.rb +7 -7
- data/app/models/solidus_promotions/promotion_handler/page.rb +7 -7
- data/config/locales/en.yml +15 -0
- data/lib/solidus_promotions/configuration.rb +7 -1
- data/lib/solidus_promotions/promotion_migrator.rb +4 -1
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_fields.html.erb +1 -1
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_fields.html.erb +1 -1
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_taxon_revenue.html.erb +35 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 811d1da52213897e7b0d15eb5edcebfff15c589a2cba817a78cd71102108bda8
|
|
4
|
+
data.tar.gz: 97b589f9439bee201fee75b9a6b290c55e04f8f4f9980108052b8162308fd932
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba882e0b5172335780662f5d13acd8d12e569318d7998b0ba05d057353d8ada749b916d9520cc180e0978fd6bdf9a557c55d2cb39589ab01848dae40d3b88427
|
|
7
|
+
data.tar.gz: e4277bbede3bcb99ea59d265a9e86f0bb75dce9f09cfb17079231144919a8b5d2e8435d372a5d4eaf9027785ec57c9e7638879f4dc3797498c2bb4b04651360a
|
data/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Copyright (c) 2024-2026, Martin Meyerhoff and other Solidus contributors
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
* Neither the name Solidus nor the names of its contributors may be used to
|
|
13
|
+
endorse or promote products derived from this software without specific
|
|
14
|
+
prior written permission.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
|
@@ -129,4 +129,4 @@ SolidusDevSupport::TestingSupport::Factories.load_for(SolidusPromotions::Engine)
|
|
|
129
129
|
|
|
130
130
|
## License
|
|
131
131
|
|
|
132
|
-
Copyright (c) 2024 Martin Meyerhoff, Solidus Team, released under the [license
|
|
132
|
+
Copyright (c) 2024 Martin Meyerhoff, Solidus Team, released under the [BSD-3-Clause license](./LICENSE).
|
|
@@ -270,6 +270,12 @@ module SolidusPromotions
|
|
|
270
270
|
# @param dry_run [Boolean] whether to collect detailed eligibility information
|
|
271
271
|
# @return [Boolean] true when all applicable conditions are eligible
|
|
272
272
|
def eligible_by_applicable_conditions?(promotable, dry_run: false)
|
|
273
|
+
if dry_run
|
|
274
|
+
Spree.deprecator.warn <<~MSG
|
|
275
|
+
Passing `dry_run` to `#eligible_by_applicable_conditions` is deprecated. If you want to check promotion
|
|
276
|
+
eligibility, use the `SolidusPromotions::PromotionEligibilityChecker` service object instead.
|
|
277
|
+
MSG
|
|
278
|
+
end
|
|
273
279
|
conditions.map do |condition|
|
|
274
280
|
next unless condition.applicable?(promotable)
|
|
275
281
|
eligible = condition.eligible?(promotable)
|
|
@@ -19,7 +19,7 @@ module SolidusPromotions
|
|
|
19
19
|
include PromotionCalculator
|
|
20
20
|
|
|
21
21
|
preference :amount, :decimal, default: Spree::ZERO
|
|
22
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
22
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
23
23
|
|
|
24
24
|
# Computes the weighted discount amount for a specific line item.
|
|
25
25
|
#
|
|
@@ -12,7 +12,7 @@ module SolidusPromotions
|
|
|
12
12
|
include PromotionCalculator
|
|
13
13
|
|
|
14
14
|
preference :amount, :decimal, default: Spree::ZERO
|
|
15
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
15
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
16
16
|
|
|
17
17
|
# Computes the discount amount for an item.
|
|
18
18
|
#
|
|
@@ -25,7 +25,7 @@ module SolidusPromotions
|
|
|
25
25
|
preference :first_item, :decimal, default: Spree::ZERO
|
|
26
26
|
preference :additional_item, :decimal, default: Spree::ZERO
|
|
27
27
|
preference :max_items, :integer, default: 0
|
|
28
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
28
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
29
29
|
|
|
30
30
|
# Computes the discount amount for a line item based on its quantity.
|
|
31
31
|
#
|
|
@@ -40,7 +40,7 @@ module SolidusPromotions
|
|
|
40
40
|
|
|
41
41
|
preference :base_amount, :decimal, default: Spree::ZERO
|
|
42
42
|
preference :tiers, :hash, default: {10 => 10}
|
|
43
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
43
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
44
44
|
|
|
45
45
|
before_validation :transform_preferred_tiers
|
|
46
46
|
|
|
@@ -45,7 +45,7 @@ module SolidusPromotions
|
|
|
45
45
|
|
|
46
46
|
preference :base_percent, :decimal, default: Spree::ZERO
|
|
47
47
|
preference :tiers, :hash, default: {50 => 5}
|
|
48
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
48
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
49
49
|
|
|
50
50
|
before_validation :transform_preferred_tiers
|
|
51
51
|
|
|
@@ -46,7 +46,7 @@ module SolidusPromotions
|
|
|
46
46
|
|
|
47
47
|
preference :base_percent, :decimal, default: Spree::ZERO
|
|
48
48
|
preference :tiers, :hash, default: {10 => 5}
|
|
49
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
49
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
50
50
|
|
|
51
51
|
before_validation :transform_preferred_tiers
|
|
52
52
|
|
|
@@ -12,7 +12,7 @@ module SolidusPromotions
|
|
|
12
12
|
include OrderLevelCondition
|
|
13
13
|
|
|
14
14
|
preference :amount, :decimal, default: 100.00
|
|
15
|
-
preference :currency, :string, default: -> { Spree::Config
|
|
15
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
16
16
|
preference :operator, :string, default: "gt"
|
|
17
17
|
|
|
18
18
|
# The list of allowed operators names mapped to their symbols.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class TaxonRevenue < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
include TaxonCondition
|
|
8
|
+
|
|
9
|
+
preference :operator, :string, default: "gte"
|
|
10
|
+
preference :amount, :decimal, default: 0
|
|
11
|
+
preference :currency, :string, default: -> { Spree::Config.currency }
|
|
12
|
+
preference :match_policy, :string, default: "include"
|
|
13
|
+
|
|
14
|
+
OPERATORS = {"gte" => :>=, "gt" => :>, "lt" => :<, "lte" => :<=}.freeze
|
|
15
|
+
MATCH_POLICIES = ["include", "exclude"].freeze
|
|
16
|
+
|
|
17
|
+
validates :preferred_match_policy, inclusion: {in: MATCH_POLICIES}
|
|
18
|
+
|
|
19
|
+
def self.operator_options
|
|
20
|
+
OPERATORS.map do |name, _method|
|
|
21
|
+
[I18n.t(name, scope: [:solidus_promotions, :operators]), name]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.match_policy_options
|
|
26
|
+
MATCH_POLICIES.map do |name|
|
|
27
|
+
[I18n.t(name, scope: %i[solidus_promotions conditions taxon_revenue match_policies]), name]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def order_eligible?(order, _options = {})
|
|
32
|
+
matching = order.line_items.select { |line_item| taxon_match?(line_item) }
|
|
33
|
+
|
|
34
|
+
matching.sum(&:discounted_amount).public_send(OPERATORS.fetch(preferred_operator), preferred_amount)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def taxon_match?(line_item)
|
|
40
|
+
line_item_taxon_ids = line_item.variant.product.classifications.map(&:taxon_id)
|
|
41
|
+
|
|
42
|
+
in_selected_taxons = taxon_ids_with_children.any? do |taxon_and_descendant_ids|
|
|
43
|
+
(line_item_taxon_ids & taxon_and_descendant_ids).any?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
(preferred_match_policy == "exclude") ? !in_selected_taxons : in_selected_taxons
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
module SolidusPromotions
|
|
4
4
|
class LoadPromotions
|
|
5
5
|
def initialize(order:, dry_run_promotion: nil)
|
|
6
|
+
if dry_run_promotion
|
|
7
|
+
Spree.deprecator.warn <<~MSG
|
|
8
|
+
Passing `dry_run_promotion` to `SolidusPromotions::LoadPromotions` is deprecated.
|
|
9
|
+
Use `Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion).call` instead.
|
|
10
|
+
MSG
|
|
11
|
+
end
|
|
6
12
|
@order = order
|
|
7
13
|
@dry_run_promotion = dry_run_promotion
|
|
8
14
|
end
|
|
@@ -6,6 +6,12 @@ module SolidusPromotions
|
|
|
6
6
|
attr_reader :order, :promotions, :dry_run
|
|
7
7
|
|
|
8
8
|
def initialize(order, promotions, dry_run: false)
|
|
9
|
+
if dry_run
|
|
10
|
+
Spree.deprecator.warn <<~MSG
|
|
11
|
+
Passing `dry_run` to `SolidusPromotions::OrderAdjuster::DiscountOrder` is deprecated.
|
|
12
|
+
Use `Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion).call` instead.
|
|
13
|
+
MSG
|
|
14
|
+
end
|
|
9
15
|
@order = order
|
|
10
16
|
@promotions = promotions
|
|
11
17
|
@dry_run = dry_run
|
|
@@ -46,27 +52,20 @@ module SolidusPromotions
|
|
|
46
52
|
def adjust_line_items(benefits)
|
|
47
53
|
order.discountable_line_items.filter_map do |line_item|
|
|
48
54
|
next unless line_item.variant.product.promotionable?
|
|
49
|
-
|
|
50
|
-
discounts = generate_discounts(benefits, line_item)
|
|
51
|
-
chosen_discounts = SolidusPromotions.config.discount_chooser_class.new(discounts).call
|
|
52
|
-
(line_item.current_lane_discounts - chosen_discounts).each(&:mark_for_destruction)
|
|
55
|
+
generate_discounts(benefits, line_item)
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
def adjust_shipments(benefits)
|
|
57
60
|
order.shipments.map do |shipment|
|
|
58
|
-
|
|
59
|
-
chosen_discounts = SolidusPromotions.config.discount_chooser_class.new(discounts).call
|
|
60
|
-
(shipment.current_lane_discounts - chosen_discounts).each(&:mark_for_destruction)
|
|
61
|
+
generate_discounts(benefits, shipment)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def adjust_shipping_rates(benefits)
|
|
65
66
|
order.shipments.flat_map(&:shipping_rates).filter_map do |rate|
|
|
66
67
|
next unless rate.cost
|
|
67
|
-
|
|
68
|
-
chosen_discounts = SolidusPromotions.config.discount_chooser_class.new(discounts).call
|
|
69
|
-
(rate.current_lane_discounts - chosen_discounts).each(&:mark_for_destruction)
|
|
68
|
+
generate_discounts(benefits, rate)
|
|
70
69
|
end
|
|
71
70
|
end
|
|
72
71
|
|
|
@@ -78,9 +77,11 @@ module SolidusPromotions
|
|
|
78
77
|
|
|
79
78
|
def generate_discounts(possible_benefits, item)
|
|
80
79
|
eligible_benefits = eligible_benefits_for_promotable(possible_benefits, item)
|
|
81
|
-
eligible_benefits.filter_map do |benefit|
|
|
80
|
+
discounts = eligible_benefits.filter_map do |benefit|
|
|
82
81
|
benefit.can_discount?(item) && benefit.discount(item)
|
|
83
82
|
end
|
|
83
|
+
chosen_discounts = SolidusPromotions.config.discount_chooser_class.new(discounts).call
|
|
84
|
+
(item.current_lane_discounts - chosen_discounts).each(&:mark_for_destruction)
|
|
84
85
|
end
|
|
85
86
|
end
|
|
86
87
|
end
|
|
@@ -5,6 +5,12 @@ module SolidusPromotions
|
|
|
5
5
|
attr_reader :order, :promotions, :dry_run
|
|
6
6
|
|
|
7
7
|
def initialize(order, dry_run_promotion: nil)
|
|
8
|
+
if dry_run_promotion
|
|
9
|
+
Spree.deprecator.warn <<~MSG
|
|
10
|
+
Passing `dry_run_promotion` to `SolidusPromotions::OrderAdjuster` is deprecated.
|
|
11
|
+
Use `Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion).call` instead.
|
|
12
|
+
MSG
|
|
13
|
+
end
|
|
8
14
|
@order = order
|
|
9
15
|
@dry_run = !!dry_run_promotion
|
|
10
16
|
@promotions = SolidusPromotions::LoadPromotions.new(
|
|
@@ -16,6 +22,10 @@ module SolidusPromotions
|
|
|
16
22
|
def call(persist: true) # rubocop:disable Lint/UnusedMethodArgument
|
|
17
23
|
return order unless SolidusPromotions::Promotion.order_activatable?(order)
|
|
18
24
|
|
|
25
|
+
ActiveRecord::Associations::Preloader.new(records: order.line_items + order.shipments, associations: :adjustments).call
|
|
26
|
+
# DiscountOrder dereferences `line_item.variant.product` for every line item.
|
|
27
|
+
ActiveRecord::Associations::Preloader.new(records: order.line_items, associations: {variant: :product}).call
|
|
28
|
+
|
|
19
29
|
SetDiscountsToZero.call(order)
|
|
20
30
|
|
|
21
31
|
DiscountOrder.new(order, promotions, dry_run: dry_run).call
|
|
@@ -152,6 +152,7 @@ module SolidusPromotions
|
|
|
152
152
|
def eligibility_results
|
|
153
153
|
@eligibility_results ||= SolidusPromotions::EligibilityResults.new(self)
|
|
154
154
|
end
|
|
155
|
+
deprecate eligibility_results: "Please use the `SolidusPromotions::PromotionEligibilityChecker` service object instead", deprecator: Spree.deprecator
|
|
155
156
|
|
|
156
157
|
private
|
|
157
158
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
class PromotionEligibilityChecker
|
|
5
|
+
attr_reader :results
|
|
6
|
+
|
|
7
|
+
def initialize(order:, promotion:)
|
|
8
|
+
@order = order
|
|
9
|
+
@promotion = promotion
|
|
10
|
+
@results = SolidusPromotions::EligibilityResults.new(promotion)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
SolidusPromotions::PromotionLane.set(current_lane: promotion.lane) do
|
|
15
|
+
promotion.benefits.any? do |benefit|
|
|
16
|
+
# We're running this first and storing the result so the following
|
|
17
|
+
# block does not short-circuit on ineligible items, and we get all errors.
|
|
18
|
+
order_eligible = applicable_conditions_eligible?(order, benefit)
|
|
19
|
+
(
|
|
20
|
+
order.line_items.any? do |line_item|
|
|
21
|
+
check_item(line_item, benefit)
|
|
22
|
+
end || order.shipments.any? do |shipment|
|
|
23
|
+
check_item(shipment, benefit)
|
|
24
|
+
end
|
|
25
|
+
) && order_eligible
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :order, :promotion
|
|
33
|
+
|
|
34
|
+
def check_item(item, benefit)
|
|
35
|
+
benefit.can_discount?(item) &&
|
|
36
|
+
applicable_conditions_eligible?(item, benefit)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def applicable_conditions_eligible?(item, benefit)
|
|
40
|
+
benefit.conditions.map do |condition|
|
|
41
|
+
next unless condition.applicable?(item)
|
|
42
|
+
eligible = !!condition.eligible?(item)
|
|
43
|
+
|
|
44
|
+
if condition.eligibility_errors.details[:base].first
|
|
45
|
+
code = condition.eligibility_errors.details[:base].first[:error_code]
|
|
46
|
+
message = condition.eligibility_errors.full_messages.first
|
|
47
|
+
end
|
|
48
|
+
results.add(
|
|
49
|
+
item: item,
|
|
50
|
+
condition: condition,
|
|
51
|
+
success: eligible,
|
|
52
|
+
code: eligible ? nil : (code || :coupon_code_unknown_error),
|
|
53
|
+
message: eligible ? nil : (message || I18n.t(:coupon_code_unknown_error, scope: [:solidus_promotions, :eligibility_errors]))
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
eligible
|
|
57
|
+
end.compact.all?
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -77,10 +77,10 @@ module SolidusPromotions
|
|
|
77
77
|
return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded? || promotion_code.usage_limit_exceeded?
|
|
78
78
|
return promotion_applied if promotion_exists_on_order?(order, promotion)
|
|
79
79
|
|
|
80
|
-
#
|
|
81
|
-
Spree::Config.promotions.
|
|
80
|
+
# Check promotion eligibility
|
|
81
|
+
checker = Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion)
|
|
82
82
|
|
|
83
|
-
if
|
|
83
|
+
if checker.call
|
|
84
84
|
order.solidus_order_promotions.create!(
|
|
85
85
|
promotion: promotion,
|
|
86
86
|
promotion_code: promotion_code
|
|
@@ -88,13 +88,13 @@ module SolidusPromotions
|
|
|
88
88
|
order.recalculate
|
|
89
89
|
set_success_code :coupon_code_applied
|
|
90
90
|
else
|
|
91
|
-
set_promotion_eligibility_error(
|
|
91
|
+
set_promotion_eligibility_error(checker.results)
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
def set_promotion_eligibility_error(
|
|
96
|
-
eligibility_error =
|
|
97
|
-
set_error_code(eligibility_error.code, error: eligibility_error.message, errors:
|
|
95
|
+
def set_promotion_eligibility_error(results)
|
|
96
|
+
eligibility_error = results.detect { |result| !result.success }
|
|
97
|
+
set_error_code(eligibility_error.code, error: eligibility_error.message, errors: results.error_messages)
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def promotion_usage_limit_exceeded
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
module SolidusPromotions
|
|
4
4
|
module PromotionHandler
|
|
5
5
|
class Page
|
|
6
|
-
attr_reader :order, :path
|
|
6
|
+
attr_reader :order, :path, :checker
|
|
7
7
|
|
|
8
8
|
def initialize(order, path)
|
|
9
9
|
@order = order
|
|
10
10
|
@path = path.delete_prefix("/")
|
|
11
|
+
@checker = Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion)
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
delegate :results, to: :checker
|
|
15
|
+
|
|
13
16
|
def activate
|
|
14
|
-
if promotion
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
order.solidus_promotions << promotion
|
|
18
|
-
order.recalculate
|
|
19
|
-
end
|
|
17
|
+
if promotion && checker.call
|
|
18
|
+
order.solidus_promotions << promotion
|
|
19
|
+
order.recalculate
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
data/config/locales/en.yml
CHANGED
|
@@ -76,6 +76,10 @@ en:
|
|
|
76
76
|
match_policies:
|
|
77
77
|
include: Line item's product has one of the chosen taxons
|
|
78
78
|
exclude: Line item's product does not have one of the chosen taxons
|
|
79
|
+
taxon_revenue:
|
|
80
|
+
match_policies:
|
|
81
|
+
include: Revenue from selected taxons
|
|
82
|
+
exclude: Revenue excluding selected taxons
|
|
79
83
|
item_total_condition:
|
|
80
84
|
operators:
|
|
81
85
|
gt: greater than
|
|
@@ -175,6 +179,11 @@ en:
|
|
|
175
179
|
add: Add
|
|
176
180
|
destroy: Delete
|
|
177
181
|
update: Update
|
|
182
|
+
operators:
|
|
183
|
+
gt: greater than
|
|
184
|
+
gte: greater than or equal to
|
|
185
|
+
lt: less than
|
|
186
|
+
lte: less than or equal to
|
|
178
187
|
admin:
|
|
179
188
|
promotions:
|
|
180
189
|
benefits:
|
|
@@ -247,6 +256,7 @@ en:
|
|
|
247
256
|
solidus_promotions/conditions/price_product: Price matches specified product(s)
|
|
248
257
|
solidus_promotions/conditions/price_taxon: Price matches specified taxon(s)
|
|
249
258
|
solidus_promotions/conditions/price_option_value: Price matches specified option value(s)
|
|
259
|
+
solidus_promotions/conditions/taxon_revenue: Taxon Revenue
|
|
250
260
|
solidus_promotions/promotion_category:
|
|
251
261
|
one: Promotion Category
|
|
252
262
|
other: Promotion Categories
|
|
@@ -324,6 +334,11 @@ en:
|
|
|
324
334
|
description: Available only to logged in users
|
|
325
335
|
solidus_promotions/conditions/user_role:
|
|
326
336
|
description: Order includes User with specified Role(s)
|
|
337
|
+
solidus_promotions/conditions/taxon_revenue:
|
|
338
|
+
description: Controls eligibility by matching on revenue from or excluding selected taxons.
|
|
339
|
+
preferred_operator: Operator
|
|
340
|
+
preferred_amount: Amount
|
|
341
|
+
preferred_match_policy: Match Policy
|
|
327
342
|
solidus_promotions/calculators/tiered_flat_rate:
|
|
328
343
|
description: Flat Rate in tiers based on item amount
|
|
329
344
|
preferred_base_amount: Base Amount
|
|
@@ -8,6 +8,11 @@ module SolidusPromotions
|
|
|
8
8
|
|
|
9
9
|
class_name_attribute :order_adjuster_class, default: "SolidusPromotions::OrderAdjuster"
|
|
10
10
|
|
|
11
|
+
# Service object that checks whether a promotion is eligible for an order.
|
|
12
|
+
# @!attribute [rw] eligibility_checker_class
|
|
13
|
+
# @return [String] Defaults to "SolidusPromotions::PromotionEligibilityChecker".
|
|
14
|
+
class_name_attribute :eligibility_checker_class, default: "SolidusPromotions::PromotionEligibilityChecker"
|
|
15
|
+
|
|
11
16
|
class_name_attribute :coupon_code_handler_class, default: "SolidusPromotions::PromotionHandler::Coupon"
|
|
12
17
|
|
|
13
18
|
# The class used to normalize coupon codes before saving or lookup.
|
|
@@ -86,7 +91,8 @@ module SolidusPromotions
|
|
|
86
91
|
"SolidusPromotions::Conditions::ShippingMethod",
|
|
87
92
|
"SolidusPromotions::Conditions::PriceProduct",
|
|
88
93
|
"SolidusPromotions::Conditions::PriceTaxon",
|
|
89
|
-
"SolidusPromotions::Conditions::PriceOptionValue"
|
|
94
|
+
"SolidusPromotions::Conditions::PriceOptionValue",
|
|
95
|
+
"SolidusPromotions::Conditions::TaxonRevenue"
|
|
90
96
|
]
|
|
91
97
|
|
|
92
98
|
add_class_set :benefits, default: [
|
|
@@ -61,7 +61,10 @@ module SolidusPromotions
|
|
|
61
61
|
SELECT solidus_promotions_promotions.id AS promotion_id, solidus_promotions_promotion_code_batches.id AS promotion_code_batch_id, value, spree_promotion_codes.created_at, spree_promotion_codes.updated_at
|
|
62
62
|
FROM spree_promotion_codes
|
|
63
63
|
LEFT OUTER JOIN spree_promotion_code_batches ON spree_promotion_code_batches.id = spree_promotion_codes.promotion_code_batch_id
|
|
64
|
-
LEFT OUTER JOIN solidus_promotions_promotion_code_batches
|
|
64
|
+
LEFT OUTER JOIN solidus_promotions_promotion_code_batches
|
|
65
|
+
ON solidus_promotions_promotion_code_batches.base_code = spree_promotion_code_batches.base_code
|
|
66
|
+
AND solidus_promotions_promotion_code_batches.promotion_id = #{new_promotion.id}
|
|
67
|
+
AND solidus_promotions_promotion_code_batches.created_at = spree_promotion_code_batches.created_at
|
|
65
68
|
INNER JOIN spree_promotions ON spree_promotion_codes.promotion_id = spree_promotions.id
|
|
66
69
|
INNER JOIN solidus_promotions_promotions ON spree_promotions.id = solidus_promotions_promotions.original_promotion_id
|
|
67
70
|
WHERE spree_promotion_codes.promotion_id = #{new_promotion.original_promotion_id};
|
data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_fields.html.erb
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"#{prefix}[calculator_attributes][preferred_currency]",
|
|
15
15
|
options_for_select(
|
|
16
16
|
Spree::Config.available_currencies,
|
|
17
|
-
calculator.preferred_currency || Spree::Config
|
|
17
|
+
calculator.preferred_currency || Spree::Config.currency
|
|
18
18
|
),
|
|
19
19
|
{ is: "select-two", class: 'fullwidth' }
|
|
20
20
|
) %>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"#{prefix}[calculator_attributes][preferred_currency]",
|
|
15
15
|
options_for_select(
|
|
16
16
|
Spree::Config.available_currencies,
|
|
17
|
-
calculator.preferred_currency || Spree::Config
|
|
17
|
+
calculator.preferred_currency || Spree::Config.currency
|
|
18
18
|
),
|
|
19
19
|
{ is: "select-two", class: 'fullwidth' }
|
|
20
20
|
) %>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<p>
|
|
2
|
+
<%= condition.class.human_attribute_name(:description) %>
|
|
3
|
+
</p>
|
|
4
|
+
<%= fields_for param_prefix, condition do |form| %>
|
|
5
|
+
<div class="field">
|
|
6
|
+
<%= form.label :preferred_match_policy %>
|
|
7
|
+
<%= form.select :preferred_match_policy,
|
|
8
|
+
SolidusPromotions::Conditions::TaxonRevenue.match_policy_options,
|
|
9
|
+
{}, class: "custom-select fullwidth" %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="col-6">
|
|
14
|
+
<div class="field">
|
|
15
|
+
<%= form.label :preferred_operator %>
|
|
16
|
+
<%= form.select :preferred_operator, options_for_select(condition.class.operator_options, condition.preferred_operator), {}, {class: 'custom-select select_item_total fullwidth'} %>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="col-6">
|
|
21
|
+
<div class="field">
|
|
22
|
+
<%= fields_for param_prefix, condition do |f| %>
|
|
23
|
+
<%= f.label :preferred_amount %>
|
|
24
|
+
<%= render "solidus_promotions/admin/shared/number_with_currency", f: f, amount_attr: :preferred_amount, currency_attr: :preferred_currency %>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="field taxons_condition_taxons">
|
|
31
|
+
<%= form.label :taxon_ids_string, t('solidus_promotions.taxon_condition.choose_taxons') %>
|
|
32
|
+
<%= form.hidden_field :taxon_ids_string, value: condition.taxon_ids.join(","), is: "taxon-picker", class: "fullwidth" %>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<% end %>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solidus_promotions
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Martin Meyerhoff
|
|
@@ -127,6 +127,7 @@ extensions: []
|
|
|
127
127
|
extra_rdoc_files: []
|
|
128
128
|
files:
|
|
129
129
|
- ".github/stale.yml"
|
|
130
|
+
- LICENSE
|
|
130
131
|
- MIGRATING.md
|
|
131
132
|
- README.md
|
|
132
133
|
- Rakefile
|
|
@@ -204,6 +205,7 @@ files:
|
|
|
204
205
|
- app/models/solidus_promotions/conditions/shipping_method.rb
|
|
205
206
|
- app/models/solidus_promotions/conditions/store.rb
|
|
206
207
|
- app/models/solidus_promotions/conditions/taxon.rb
|
|
208
|
+
- app/models/solidus_promotions/conditions/taxon_revenue.rb
|
|
207
209
|
- app/models/solidus_promotions/conditions/user.rb
|
|
208
210
|
- app/models/solidus_promotions/conditions/user_logged_in.rb
|
|
209
211
|
- app/models/solidus_promotions/conditions/user_role.rb
|
|
@@ -228,6 +230,7 @@ files:
|
|
|
228
230
|
- app/models/solidus_promotions/promotion_code.rb
|
|
229
231
|
- app/models/solidus_promotions/promotion_code/batch_builder.rb
|
|
230
232
|
- app/models/solidus_promotions/promotion_code_batch.rb
|
|
233
|
+
- app/models/solidus_promotions/promotion_eligibility_checker.rb
|
|
231
234
|
- app/models/solidus_promotions/promotion_finder.rb
|
|
232
235
|
- app/models/solidus_promotions/promotion_handler/coupon.rb
|
|
233
236
|
- app/models/solidus_promotions/promotion_handler/page.rb
|
|
@@ -357,6 +360,7 @@ files:
|
|
|
357
360
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_shipping_method.html.erb
|
|
358
361
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_store.html.erb
|
|
359
362
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_taxon.html.erb
|
|
363
|
+
- lib/views/backend/solidus_promotions/admin/condition_fields/_taxon_revenue.html.erb
|
|
360
364
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_user.html.erb
|
|
361
365
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_user_logged_in.html.erb
|
|
362
366
|
- lib/views/backend/solidus_promotions/admin/condition_fields/_user_role.html.erb
|
|
@@ -413,7 +417,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
413
417
|
- !ruby/object:Gem::Version
|
|
414
418
|
version: '0'
|
|
415
419
|
requirements: []
|
|
416
|
-
rubygems_version: 4.0.
|
|
420
|
+
rubygems_version: 4.0.20
|
|
417
421
|
specification_version: 4
|
|
418
422
|
summary: New promotion system for Solidus
|
|
419
423
|
test_files: []
|