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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +26 -0
  3. data/README.md +1 -1
  4. data/app/models/concerns/solidus_promotions/conditions/taxon_condition.rb +2 -0
  5. data/app/models/solidus_promotions/benefit.rb +6 -0
  6. data/app/models/solidus_promotions/calculators/distributed_amount.rb +1 -1
  7. data/app/models/solidus_promotions/calculators/flat_rate.rb +1 -1
  8. data/app/models/solidus_promotions/calculators/flexi_rate.rb +1 -1
  9. data/app/models/solidus_promotions/calculators/tiered_flat_rate.rb +1 -1
  10. data/app/models/solidus_promotions/calculators/tiered_percent.rb +1 -1
  11. data/app/models/solidus_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +1 -1
  12. data/app/models/solidus_promotions/conditions/item_total.rb +1 -1
  13. data/app/models/solidus_promotions/conditions/taxon_revenue.rb +50 -0
  14. data/app/models/solidus_promotions/load_promotions.rb +6 -0
  15. data/app/models/solidus_promotions/order_adjuster/discount_order.rb +12 -11
  16. data/app/models/solidus_promotions/order_adjuster.rb +10 -0
  17. data/app/models/solidus_promotions/promotion.rb +1 -0
  18. data/app/models/solidus_promotions/promotion_eligibility_checker.rb +60 -0
  19. data/app/models/solidus_promotions/promotion_handler/coupon.rb +7 -7
  20. data/app/models/solidus_promotions/promotion_handler/page.rb +7 -7
  21. data/config/locales/en.yml +15 -0
  22. data/lib/solidus_promotions/configuration.rb +7 -1
  23. data/lib/solidus_promotions/promotion_migrator.rb +4 -1
  24. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_fields.html.erb +1 -1
  25. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_fields.html.erb +1 -1
  26. data/lib/views/backend/solidus_promotions/admin/condition_fields/_taxon_revenue.html.erb +35 -0
  27. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e2451b1f94d95a3ec8151297d094332656c24101b8db9b2940d8c29fc801f5b
4
- data.tar.gz: '0296e8e6ecd7044da5d0a72e7247b7c177edf520908416d1c27d775a082fcde6'
3
+ metadata.gz: 811d1da52213897e7b0d15eb5edcebfff15c589a2cba817a78cd71102108bda8
4
+ data.tar.gz: 97b589f9439bee201fee75b9a6b290c55e04f8f4f9980108052b8162308fd932
5
5
  SHA512:
6
- metadata.gz: e6cc3fe416a832ed5abf6cfe424c68472c97bb1eda709df891240f72d1a13bebc76d8ebd4c8b8adb75bc03654976631ba6bd6ba924c7b6233e8ac6ac3dc6cbaa
7
- data.tar.gz: f895736cae7093535b52ae333f1c883dc438b77f92002ab930801b042d3bfb401095640b6f10c800c62feecac811f6715c2494e0863bb07dc057ddcfed05a1ad
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 of the parent repository](https://github.com/solidusio/solidus/blob/main/LICENSE.md).
132
+ Copyright (c) 2024 Martin Meyerhoff, Solidus Team, released under the [BSD-3-Clause license](./LICENSE).
@@ -52,6 +52,8 @@ module SolidusPromotions
52
52
  end
53
53
 
54
54
  def load_taxon_ids_with_children
55
+ return [] unless taxon_ids.present?
56
+
55
57
  aggregation_function = if ActiveRecord::Base.connection.adapter_name.downcase.match?(/postgres/)
56
58
  "string_agg(child.id::text, ',')"
57
59
  else
@@ -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[:currency] }
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[:currency] }
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[:currency] }
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[:currency] }
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[:currency] }
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[:currency] }
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[:currency] }
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
- discounts = generate_discounts(benefits, shipment)
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
- discounts = generate_discounts(benefits, rate)
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
- # Try applying this promotion, with no effects
81
- Spree::Config.promotions.order_adjuster_class.new(order, dry_run_promotion: promotion).call
80
+ # Check promotion eligibility
81
+ checker = Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion)
82
82
 
83
- if promotion.eligibility_results.success?
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(promotion)
91
+ set_promotion_eligibility_error(checker.results)
92
92
  end
93
93
  end
94
94
 
95
- def set_promotion_eligibility_error(promotion)
96
- eligibility_error = promotion.eligibility_results.detect { |result| !result.success }
97
- set_error_code(eligibility_error.code, error: eligibility_error.message, errors: promotion.eligibility_results.error_messages)
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
- Spree::Config.promotions.order_adjuster_class.new(order, dry_run_promotion: promotion).call
16
- if promotion.eligibility_results.success?
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
 
@@ -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 ON solidus_promotions_promotion_code_batches.base_code = spree_promotion_code_batches.base_code
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};
@@ -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[:currency]
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[:currency]
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.7.2
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.10
420
+ rubygems_version: 4.0.20
417
421
  specification_version: 4
418
422
  summary: New promotion system for Solidus
419
423
  test_files: []