solidus_friendly_promotions 1.0.0.pre → 1.0.0.rc.2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/MIGRATING.md +1 -1
  3. data/app/controllers/solidus_friendly_promotions/admin/promotions_controller.rb +1 -1
  4. data/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb +20 -0
  5. data/app/decorators/models/solidus_friendly_promotions/order_decorator.rb +11 -0
  6. data/app/javascript/solidus_friendly_promotions.js +5 -2
  7. data/app/models/concerns/solidus_friendly_promotions/actions/order_level_action.rb +15 -0
  8. data/app/models/concerns/solidus_friendly_promotions/calculators/promotion_calculator.rb +11 -0
  9. data/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb +1 -9
  10. data/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb +50 -0
  11. data/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb +3 -9
  12. data/app/models/solidus_friendly_promotions/calculators/flat_rate.rb +2 -0
  13. data/app/models/solidus_friendly_promotions/calculators/flexi_rate.rb +2 -0
  14. data/app/models/solidus_friendly_promotions/calculators/percent.rb +2 -0
  15. data/app/models/solidus_friendly_promotions/calculators/tiered_flat_rate.rb +2 -0
  16. data/app/models/solidus_friendly_promotions/calculators/tiered_percent.rb +2 -0
  17. data/app/models/solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +36 -0
  18. data/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/discount_order.rb +19 -1
  19. data/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/persist_discounted_order.rb +0 -1
  20. data/app/models/solidus_friendly_promotions/friendly_promotion_adjuster.rb +7 -0
  21. data/app/models/solidus_friendly_promotions/promotion.rb +7 -1
  22. data/app/models/solidus_friendly_promotions/promotion_handler/null.rb +20 -0
  23. data/app/models/solidus_friendly_promotions/promotion_rule.rb +1 -1
  24. data/app/models/solidus_friendly_promotions/rules/minimum_quantity.rb +1 -7
  25. data/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_calculator_fields.erb +1 -0
  26. data/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb +19 -0
  27. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_flat_rate/_fields.html.erb +1 -1
  28. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/_fields.html.erb +1 -1
  29. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_fields.html.erb +34 -0
  30. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_tier_fields.html.erb +27 -0
  31. data/app/views/solidus_friendly_promotions/admin/promotions/_table.html.erb +56 -0
  32. data/app/views/solidus_friendly_promotions/admin/promotions/_table_filter.html.erb +69 -0
  33. data/app/views/solidus_friendly_promotions/admin/promotions/edit.html.erb +13 -11
  34. data/app/views/solidus_friendly_promotions/admin/promotions/index.html.erb +2 -103
  35. data/config/locales/en.yml +28 -1
  36. data/db/migrate/20231104135812_add_managed_by_order_action_to_line_items.rb +5 -0
  37. data/lib/generators/solidus_friendly_promotions/install/templates/initializer.rb +18 -5
  38. data/lib/solidus_friendly_promotions/version.rb +1 -1
  39. data/lib/solidus_friendly_promotions.rb +1 -0
  40. data/solidus_friendly_promotions.gemspec +1 -0
  41. metadata +27 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '042911d4cf39c2bddcce9c5826f8c8f790e2ca8396cc32d339c7f5c2dea0a834'
4
- data.tar.gz: cf2a4ee16b1d40a6d89771de93b37b145bfce413aab7114a0660cdf0c5798d27
3
+ metadata.gz: 84ea9571f3d2a4a2dfe9e789c0f3887a0aca2d9559a68004806cd67c395fc959
4
+ data.tar.gz: 929a14c4f917aad08c6bf7faa7f93c7be071dc43ad72641ddf0394cf336022db
5
5
  SHA512:
6
- metadata.gz: 5ffa465ffa65e01a30adae44873edabe2fa25594b7c3c09beda6e0e2946ed4e8fdbec547dafc4d22974bbeddb0a978a96c65c5c83f3b300d4e9b0473dc809f23
7
- data.tar.gz: 601306df6e5de918612d12ed6084d2a6ea2b690d15ef13ff36d826a541dc19dabaf84b62ea74c0065f106baf256089df544e9814fe3ddcc4da5635ffc8e74c60
6
+ metadata.gz: 9e49887ad42376b60165ab1d5a8067942adde8d9861681e165b0617155eb74f42a5ea97f2bd94ce929ee5627156ab249ab392b5259e775ecdb53414444bb94be
7
+ data.tar.gz: cc68b4daf7520de18dbe22bd0b74a73e1a4b009d031d8703b86f7e6d5ad11cfd5999159b48cdb0bfc7801235fe8587bc5c7cfe56a76fb18eee3fef7b48c903a4
data/MIGRATING.md CHANGED
@@ -90,7 +90,7 @@ end
90
90
  would become:
91
91
 
92
92
  ```rb
93
- class MyNewRule < SolidusFriendlyShipping::PromotionRule
93
+ class MyNewRule < SolidusFriendlyPromotions::PromotionRule
94
94
  include LineItemLevelRule
95
95
  def eligible?(promotable)
96
96
  promotable.quantity > 3
@@ -32,7 +32,7 @@ module SolidusFriendlyPromotions
32
32
  return @collection if @collection
33
33
 
34
34
  params[:q] ||= HashWithIndifferentAccess.new
35
- params[:q][:s] ||= "id desc"
35
+ params[:q][:s] ||= "updated_at desc"
36
36
 
37
37
  @collection = super
38
38
  @search = @collection.ransack(params[:q])
@@ -2,6 +2,26 @@
2
2
 
3
3
  module SolidusFriendlyPromotions
4
4
  module LineItemDecorator
5
+ def self.prepended(base)
6
+ base.attr_accessor :quantity_setter
7
+ base.belongs_to :managed_by_order_action, class_name: "SolidusFriendlyPromotions::PromotionAction", optional: true
8
+ base.validate :validate_managed_quantity_same, on: :update
9
+ base.after_save :reset_quantity_setter
10
+ end
11
+
12
+ private
13
+
14
+ def validate_managed_quantity_same
15
+ if managed_by_order_action && quantity_changed? && quantity_setter != managed_by_order_action
16
+ errors.add(:quantity, :cannot_be_changed_for_automated_items)
17
+ end
18
+ end
19
+
20
+ def reset_quantity_setter
21
+ @quantity_setter = nil
22
+ end
23
+
24
+ Spree::LineItem.prepend self
5
25
  Spree::LineItem.prepend SolidusFriendlyPromotions::DiscountableAmount
6
26
  end
7
27
  end
@@ -29,6 +29,13 @@ module SolidusFriendlyPromotions
29
29
  shipments.each(&:reset_current_discounts)
30
30
  end
31
31
 
32
+ # This helper method excludes line items that are managed by an order action for the benefit
33
+ # of calculators and actions that discount normal line items. Line items that are managed by an
34
+ # order actions handle their discounts themselves.
35
+ def discountable_line_items
36
+ line_items.reject(&:managed_by_order_action)
37
+ end
38
+
32
39
  def apply_shipping_promotions
33
40
  if Spree::Config.promotion_adjuster_class <= SolidusFriendlyPromotions::FriendlyPromotionAdjuster
34
41
  recalculate
@@ -37,6 +44,10 @@ module SolidusFriendlyPromotions
37
44
  end
38
45
  end
39
46
 
47
+ def free_from_order_action?(line_item, _options)
48
+ !line_item.managed_by_order_action
49
+ end
50
+
40
51
  Spree::Order.prepend self
41
52
  end
42
53
  end
@@ -4,9 +4,12 @@ import "solidus_friendly_promotions/jquery/option_value_picker"
4
4
 
5
5
  Turbo.session.drive = false;
6
6
 
7
- document.addEventListener("turbo:frame-load", ({ _target }) => {
7
+ const initPickers = ({ _target }) => {
8
8
  Spree.initNumberWithCurrency();
9
9
  $(".product_picker").productAutocomplete();
10
10
  $(".user_picker").userAutocomplete();
11
11
  $(".taxon_picker").taxonAutocomplete();
12
- });
12
+ $(".variant_autocomplete").variantAutocomplete();
13
+ };
14
+ document.addEventListener("turbo:frame-load", initPickers);
15
+ document.addEventListener("DOMContentLoaded", initPickers);
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Actions
5
+ module OrderLevelAction
6
+ def can_discount?(_)
7
+ false
8
+ end
9
+
10
+ def level
11
+ :order
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Calculators
5
+ module PromotionCalculator
6
+ def description
7
+ self.class.human_attribute_name(:description)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -58,7 +58,7 @@ module SolidusFriendlyPromotions
58
58
  adjustment_amount = adjustment_amount.abs
59
59
 
60
60
  order = line_item.order
61
- line_items = actionable_line_items(order)
61
+ line_items = promotion.applicable_line_items(order)
62
62
 
63
63
  item_units = line_items.sort_by do |line_item|
64
64
  [-line_item.quantity, line_item.id]
@@ -78,14 +78,6 @@ module SolidusFriendlyPromotions
78
78
 
79
79
  private
80
80
 
81
- def actionable_line_items(order)
82
- order.line_items.select do |item|
83
- promotion.rules.select do |rule|
84
- rule.applicable?(item)
85
- end.all? { |rule| rule.eligible?(item) }
86
- end
87
- end
88
-
89
81
  ##
90
82
  # Used specifically for PercentOnLineItem calculator. That calculator uses
91
83
  # `line_item.amount`, however we might not necessarily want to discount the
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Actions
5
+ class CreateDiscountedItem < PromotionAction
6
+ include OrderLevelAction
7
+ preference :variant_id, :integer
8
+ preference :quantity, :integer, default: 1
9
+ preference :necessary_quantity, :integer, default: 1
10
+
11
+ def perform(order)
12
+ line_item = find_item(order) || create_item(order)
13
+ set_quantity(line_item, determine_item_quantity(order))
14
+ line_item.current_discounts << discount(line_item)
15
+ end
16
+
17
+ def remove_from(order)
18
+ line_item = find_item(order)
19
+ order.line_items.destroy(line_item)
20
+ end
21
+
22
+ private
23
+
24
+ def find_item(order)
25
+ order.line_items.detect { |line_item| line_item.managed_by_order_action == self }
26
+ end
27
+
28
+ def create_item(order)
29
+ order.line_items.create!(quantity: determine_item_quantity(order), variant: variant, managed_by_order_action: self)
30
+ end
31
+
32
+ def determine_item_quantity(order)
33
+ applicable_line_items = promotion.applicable_line_items(order)
34
+ # Integer division will floor automatically, which is what we want here:
35
+ # 1 Item, 2 needed: 1 * 1 / 2 => 0
36
+ # 5 items, 2 preferred, 2 needed: 5 / 2 * 2 => 4
37
+ applicable_line_items.sum(&:quantity) / preferred_necessary_quantity * preferred_quantity
38
+ end
39
+
40
+ def set_quantity(line_item, quantity)
41
+ line_item.quantity_setter = self
42
+ line_item.quantity = quantity
43
+ end
44
+
45
+ def variant
46
+ Spree::Variant.find(preferred_variant_id)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -9,6 +9,8 @@ require_dependency "spree/calculator"
9
9
  module SolidusFriendlyPromotions
10
10
  module Calculators
11
11
  class DistributedAmount < Spree::Calculator
12
+ include PromotionCalculator
13
+
12
14
  preference :amount, :decimal, default: 0
13
15
  preference :currency, :string, default: -> { Spree::Config[:currency] }
14
16
 
@@ -16,7 +18,7 @@ module SolidusFriendlyPromotions
16
18
  return 0 unless line_item
17
19
  return 0 unless preferred_currency.casecmp(line_item.currency).zero?
18
20
 
19
- distributable_line_items = eligible_line_items(line_item.order)
21
+ distributable_line_items = calculable.promotion.applicable_line_items(line_item.order)
20
22
  return 0 unless line_item.in?(distributable_line_items)
21
23
 
22
24
  DistributedAmountsHandler.new(
@@ -24,14 +26,6 @@ module SolidusFriendlyPromotions
24
26
  preferred_amount
25
27
  ).amount(line_item)
26
28
  end
27
-
28
- private
29
-
30
- def eligible_line_items(order)
31
- order.line_items.select do |line_item|
32
- calculable.promotion.eligible_by_applicable_rules?(line_item)
33
- end
34
- end
35
29
  end
36
30
  end
37
31
  end
@@ -5,6 +5,8 @@ require_dependency "spree/calculator"
5
5
  module SolidusFriendlyPromotions
6
6
  module Calculators
7
7
  class FlatRate < Spree::Calculator
8
+ include PromotionCalculator
9
+
8
10
  preference :amount, :decimal, default: 0
9
11
  preference :currency, :string, default: -> { Spree::Config[:currency] }
10
12
 
@@ -5,6 +5,8 @@ require_dependency "spree/calculator"
5
5
  module SolidusFriendlyPromotions
6
6
  module Calculators
7
7
  class FlexiRate < Spree::Calculator
8
+ include PromotionCalculator
9
+
8
10
  preference :first_item, :decimal, default: 0
9
11
  preference :additional_item, :decimal, default: 0
10
12
  preference :max_items, :integer, default: 0
@@ -5,6 +5,8 @@ require_dependency "spree/calculator"
5
5
  module SolidusFriendlyPromotions
6
6
  module Calculators
7
7
  class Percent < Spree::Calculator
8
+ include PromotionCalculator
9
+
8
10
  preference :percent, :decimal, default: 0
9
11
 
10
12
  def compute(object)
@@ -5,6 +5,8 @@ require_dependency "spree/calculator"
5
5
  module SolidusFriendlyPromotions
6
6
  module Calculators
7
7
  class TieredFlatRate < Spree::Calculator
8
+ include PromotionCalculator
9
+
8
10
  preference :base_amount, :decimal, default: 0
9
11
  preference :tiers, :hash, default: {}
10
12
  preference :currency, :string, default: -> { Spree::Config[:currency] }
@@ -5,6 +5,8 @@ require_dependency "spree/calculator"
5
5
  module SolidusFriendlyPromotions
6
6
  module Calculators
7
7
  class TieredPercent < Spree::Calculator
8
+ include PromotionCalculator
9
+
8
10
  preference :base_percent, :decimal, default: 0
9
11
  preference :tiers, :hash, default: {}
10
12
  preference :currency, :string, default: -> { Spree::Config[:currency] }
@@ -0,0 +1,36 @@
1
+ require_dependency "spree/calculator"
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Calculators
5
+ class TieredPercentOnEligibleItemQuantity < SolidusFriendlyPromotions::Calculators::TieredPercent
6
+ before_validation do
7
+ # Convert tier values to decimals. Strings don't do us much good.
8
+ if preferred_tiers.is_a?(Hash)
9
+ self.preferred_tiers = preferred_tiers.map do |key, value|
10
+ [cast_to_d(key.to_i), cast_to_d(value.to_s)]
11
+ end.to_h
12
+ end
13
+ end
14
+
15
+ def compute_line_item(line_item)
16
+ order = line_item.order
17
+
18
+ _base, percent = preferred_tiers.sort.reverse.detect do |value, _|
19
+ eligible_line_items_quantity_total(order) >= value
20
+ end
21
+ if preferred_currency.casecmp(order.currency).zero?
22
+ currency_exponent = ::Money::Currency.find(preferred_currency).exponent
23
+ (line_item.discountable_amount * (percent || preferred_base_percent) / 100).round(currency_exponent)
24
+ else
25
+ 0
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def eligible_line_items_quantity_total(order)
32
+ calculable.promotion.applicable_line_items(order).sum(&:quantity)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -16,6 +16,7 @@ module SolidusFriendlyPromotions
16
16
 
17
17
  SolidusFriendlyPromotions::Promotion.ordered_lanes.each do |lane, _index|
18
18
  lane_promotions = eligible_promotions_for_promotable(promotions.select { |promotion| promotion.lane == lane }, order)
19
+ perform_order_actions(lane_promotions, lane) unless dry_run
19
20
  line_item_discounts = adjust_line_items(lane_promotions)
20
21
  shipment_discounts = adjust_shipments(lane_promotions)
21
22
  shipping_rate_discounts = adjust_shipping_rates(lane_promotions)
@@ -29,8 +30,25 @@ module SolidusFriendlyPromotions
29
30
 
30
31
  private
31
32
 
33
+ def perform_order_actions(lane_promotions, lane)
34
+ lane_promotions.each do |promotion|
35
+ promotion.actions.select { |action| action.level == :order }.each { |action| action.perform(order) }
36
+ end
37
+
38
+ automated_line_items = order.line_items.select(&:managed_by_order_action)
39
+ return if automated_line_items.empty?
40
+
41
+ ineligible_line_items = automated_line_items.select do |line_item|
42
+ line_item.managed_by_order_action.promotion.lane == lane && !line_item.managed_by_order_action.in?(lane_promotions.flat_map(&:actions))
43
+ end
44
+
45
+ ineligible_line_items.each do |line_item|
46
+ line_item.managed_by_order_action.remove_from(order)
47
+ end
48
+ end
49
+
32
50
  def adjust_line_items(promotions)
33
- order.line_items.select do |line_item|
51
+ order.discountable_line_items.select do |line_item|
34
52
  line_item.variant.product.promotionable?
35
53
  end.map do |line_item|
36
54
  discounts = generate_discounts(promotions, line_item)
@@ -27,7 +27,6 @@ module SolidusFriendlyPromotions
27
27
  end
28
28
  end
29
29
  order.reset_current_discounts
30
- order.promo_total = (order.line_items + order.shipments).sum(&:promo_total)
31
30
  order
32
31
  end
33
32
 
@@ -19,6 +19,13 @@ module SolidusFriendlyPromotions
19
19
  PersistDiscountedOrder.new(discounted_order).call unless dry_run
20
20
 
21
21
  order.reset_current_discounts
22
+
23
+ unless dry_run
24
+ # Since automations might have added a line item, we need to recalculate item total and item count here.
25
+ order.item_total = order.line_items.sum(&:amount)
26
+ order.item_count = order.line_items.sum(&:quantity)
27
+ order.promo_total = (order.line_items + order.shipments).sum(&:promo_total)
28
+ end
22
29
  order
23
30
  end
24
31
  end
@@ -48,7 +48,7 @@ module SolidusFriendlyPromotions
48
48
  end
49
49
 
50
50
  self.allowed_ransackable_associations = ["codes"]
51
- self.allowed_ransackable_attributes = %w[name path promotion_category_id]
51
+ self.allowed_ransackable_attributes = %w[name customer_label path promotion_category_id lane updated_at]
52
52
  self.allowed_ransackable_scopes = %i[active]
53
53
 
54
54
  # All orders that have been discounted using this promotion
@@ -155,6 +155,12 @@ module SolidusFriendlyPromotions
155
155
  end.all?
156
156
  end
157
157
 
158
+ def applicable_line_items(order)
159
+ order.discountable_line_items.select do |line_item|
160
+ eligible_by_applicable_rules?(line_item)
161
+ end
162
+ end
163
+
158
164
  private
159
165
 
160
166
  def apply_automatically_disallowed_with_paths
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module PromotionHandler
5
+ # We handle shipping promotions just like other promotions, so we don't need a
6
+ # special promotion handler for shipping. However, Solidus wants us to implement one.
7
+ # This is what this class is for.
8
+ class Null
9
+ attr_reader :order
10
+ attr_accessor :error, :success
11
+
12
+ def initialize(order)
13
+ @order = order
14
+ end
15
+
16
+ def activate
17
+ end
18
+ end
19
+ end
20
+ end
@@ -49,7 +49,7 @@ module SolidusFriendlyPromotions
49
49
  end
50
50
 
51
51
  def eligibility_error_message(key, options = {})
52
- I18n.t(key, **{scope: [:solidus_friendly_promotions, :eligibility_errors, self.class.name.underscore]}.merge(options))
52
+ I18n.t(key, scope: [:solidus_friendly_promotions, :eligibility_errors, self.class.name.underscore], **options)
53
53
  end
54
54
  end
55
55
  end
@@ -27,13 +27,7 @@ module SolidusFriendlyPromotions
27
27
  # @param order [Spree::Order] the order we want to check eligibility on
28
28
  # @return [Boolean] true if promotion is eligible, false otherwise
29
29
  def eligible?(order)
30
- applicable_line_items = order.line_items.select do |line_item|
31
- promotion.rules.select do |rule|
32
- rule.applicable?(line_item)
33
- end.all? { _1.eligible?(line_item) }
34
- end
35
-
36
- if applicable_line_items.sum(&:quantity) < preferred_minimum_quantity
30
+ if promotion.applicable_line_items(order).sum(&:quantity) < preferred_minimum_quantity
37
31
  eligibility_errors.add(
38
32
  :base,
39
33
  eligibility_error_message(:quantity_less_than_minimum, count: preferred_minimum_quantity),
@@ -1,6 +1,7 @@
1
1
 
2
2
  <% calculator = promotion_action.calculator %>
3
3
  <% type_name = calculator.type.demodulize.underscore %>
4
+ <%= calculator.description %>
4
5
  <% if lookup_context.exists?("fields", ["solidus_friendly_promotions/admin/promotion_actions/calculators/#{type_name}"], true) %>
5
6
  <%= render "solidus_friendly_promotions/admin/promotion_actions/calculators/#{type_name}/fields", calculator: calculator, prefix: param_prefix, form: form %>
6
7
  <% else %>
@@ -0,0 +1,19 @@
1
+ <%= fields_for param_prefix, promotion_action do |form| %>
2
+ <div class="field">
3
+ <%= form.label :preferred_variant_id %>
4
+ <%= form.text_field :preferred_variant_id, class: "variant_autocomplete fullwidth" %>
5
+ <%= form.label :preferred_quantity %>
6
+ <%= form.number_field :preferred_quantity, class: "fullwidth" %>
7
+ </div>
8
+ <div class="field">
9
+ <%= form.label :preferred_necessary_quantity %>
10
+ <%= form.number_field :preferred_necessary_quantity, class: "fullwidth" %>
11
+ </div>
12
+ <% end %>
13
+
14
+ <%= render(
15
+ "solidus_friendly_promotions/admin/promotion_actions/actions/calculator_fields",
16
+ promotion_action: promotion_action,
17
+ param_prefix: param_prefix,
18
+ form: form
19
+ ) %>
@@ -28,7 +28,7 @@
28
28
  <%= render "solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_flat_rate/tier_fields", tier: tier, form: form %>
29
29
  <% end %>
30
30
  <div class="mb-3" data-calculator-tiers-target="links">
31
- <%= link_to "Add Tier", "#", class: "btn btn-outline-primary", data: { action: "click->calculator-tiers#add_association" } %>
31
+ <%= link_to t(:add_tier, scope: [:solidus_friendly_promotions, :admin, :promotions, :calculator]), "#", class: "btn btn-outline-primary", data: { action: "click->calculator-tiers#add_association" } %>
32
32
  </div>
33
33
  </div>
34
34
  </div>
@@ -28,7 +28,7 @@
28
28
  <%= render "solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/tier_fields", tier: tier, form: form %>
29
29
  <% end %>
30
30
  <div class="mb-3" data-calculator-tiers-target="links">
31
- <%= link_to "Add Tier", "#", class: "btn btn-outline-primary", data: { action: "click->calculator-tiers#add_association" } %>
31
+ <%= link_to t(:add_tier, scope: [:solidus_friendly_promotions, :admin, :promotions, :calculator]), "#", class: "btn btn-outline-primary", data: { action: "click->calculator-tiers#add_association" } %>
32
32
  </div>
33
33
  </div>
34
34
  </div>
@@ -0,0 +1,34 @@
1
+ <%= render "spree/admin/shared/preference_fields/#{calculator.preference_type(:base_percent)}",
2
+ name: "#{prefix}[calculator_attributes][preferred_base_percent]",
3
+ value: calculator.preferred_base_percent,
4
+ label: calculator.class.human_attribute_name(:preferred_base_percent) %>
5
+
6
+ <div class="field">
7
+ <%= label_tag(
8
+ "#{prefix}[calculator_attributes][preferred_currency]",
9
+ t('spree.currency')
10
+ ) %>
11
+ <%= select_tag(
12
+ "#{prefix}[calculator_attributes][preferred_currency]",
13
+ options_for_select(
14
+ Spree::Config.available_currencies,
15
+ calculator.preferred_currency || Spree::Config[:currency]
16
+ ),
17
+ { class: 'custom-select fullwidth' }
18
+ ) %>
19
+ </div>
20
+
21
+ <div class="field">
22
+ <%= label_tag nil, calculator.class.human_attribute_name(:tiers) %>
23
+ <div data-controller="calculator-tiers">
24
+ <template data-calculator-tiers-target="template">
25
+ <%= render "solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/tier_fields", tier: [nil, nil], form: form %>
26
+ </template>
27
+ <% form.object.calculator.preferred_tiers.each do |tier| %>
28
+ <%= render "solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/tier_fields", tier: tier, form: form %>
29
+ <% end %>
30
+ <div class="mb-3" data-calculator-tiers-target="links">
31
+ <%= link_to t(:add_tier, scope: [:solidus_friendly_promotions, :admin, :promotions, :calculator]), "#", class: "btn btn-outline-primary", data: { action: "click->calculator-tiers#add_association" } %>
32
+ </div>
33
+ </div>
34
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="fullwidth tier calculator-tiers">
2
+ <a class="fa fa-trash remove" data-action="click->calculator-tiers#remove_association"></a>
3
+ <div class="row">
4
+ <div class="col-6">
5
+ <input
6
+ class="js-base-input form-control"
7
+ data-action="calculator-tiers#propagate_base_to_value_input"
8
+ type="text"
9
+ value="<%= tier[0] %>"
10
+ >
11
+ </div>
12
+ <div class="col-6">
13
+ <div class="input-group">
14
+ <input
15
+ class="js-value-input form-control right-align"
16
+ name="<%= form.object_name %>[calculator_attributes][preferred_tiers][<%= tier[0] %>]"
17
+ type="text"
18
+ value="<%= tier[1] %>"
19
+ >
20
+ <div class="input-group-append">
21
+ <span class="input-group-text">%</span>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div class="clear"></div>
27
+ </div>
@@ -0,0 +1,56 @@
1
+ <table class="index">
2
+ <thead>
3
+ <tr>
4
+ <th><%= sort_link @search, :name %></th>
5
+ <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:code) %></th>
6
+ <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:status) %></th>
7
+ <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:usage) %></th>
8
+ <th><%= sort_link @search, :starts_at %></th>
9
+ <th><%= sort_link @search, :expires_at %></th>
10
+ <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:lane) %></th>
11
+ <th><%= sort_link @search, :updated_at %></th>
12
+ <th class="actions"></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% promotions.each do |promotion| %>
17
+ <tr id="<%= spree_dom_id promotion %>">
18
+ <td><%= promotion.name %></td>
19
+ <td>
20
+ <%= (promotion.codes.size == 1) ? promotion.codes.pluck(:value).first : t('solidus_friendly_promotions.number_of_codes', count: promotion.codes.size) %>
21
+ </td>
22
+ <td>
23
+ <span class="pill pill-<%= promotion.active? ? 'active' : 'inactive' %>">
24
+ <%= t(admin_promotion_status(promotion), scope: 'solidus_friendly_promotions.admin.promotion_status') %>
25
+ </span>
26
+ </td>
27
+ <td>
28
+ <% if promotion.usage_limit.nil? %>
29
+ <%= promotion.discounted_orders.exists? ? t(:say_yes, scope: :spree) : t(:say_no, scope: :spree) %>
30
+ <% else %>
31
+ <%= promotion.usage_count %> / <%= promotion.usage_limit %>
32
+ <% end %>
33
+ <td>
34
+ <%= l(promotion.starts_at, format: :short) if promotion.starts_at %>
35
+ </td>
36
+ <td>
37
+ <%= l(promotion.expires_at, format: :short) if promotion.expires_at %>
38
+ </td>
39
+ <td>
40
+ <%= SolidusFriendlyPromotions::Promotion.human_enum_name(:lane, promotion.lane) %>
41
+ </td>
42
+ <td>
43
+ <%= l(promotion.updated_at, format: :short) %>
44
+ </td>
45
+ <td class="actions">
46
+ <% if can?(:edit, promotion) %>
47
+ <%= link_to_edit promotion, no_text: true %>
48
+ <% end %>
49
+ <% if can?(:destroy, promotion) %>
50
+ <%= link_to_delete promotion, no_text: true %>
51
+ <% end %>
52
+ </td>
53
+ </tr>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
@@ -0,0 +1,69 @@
1
+ <% content_for :table_filter_title do %>
2
+ <%= t('spree.search') %>
3
+ <% end %>
4
+
5
+ <% content_for :table_filter do %>
6
+ <div data-hook="admin_promotions_index_search">
7
+ <%= search_form_for [:admin, search] do |f| %>
8
+ <div class="row">
9
+ <div class="col-4">
10
+ <div class="field">
11
+ <%= label_tag :q_name_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:name) %>
12
+ <%= f.text_field :name_cont, tabindex: 1 %>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="col-4">
17
+ <div class="field">
18
+ <%= label_tag :q_customer_label_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:customer_label) %>
19
+ <%= f.text_field :customer_label_cont, tabindex: 1 %>
20
+ </div>
21
+ </div>
22
+
23
+ <div class="col-2">
24
+ <div class="field">
25
+ <%= label_tag :q_codes_value_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:code) %>
26
+ <%= f.text_field :codes_value_cont, tabindex: 1 %>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="col-2">
31
+ <div class="field">
32
+ <%= label_tag :q_path_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:path) %>
33
+ <%= f.text_field :path_cont, tabindex: 1 %>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="col-2">
38
+ <div class="date-range-filter field">
39
+ <%= label_tag :q_active, SolidusFriendlyPromotions::Promotion.human_attribute_name(:active) %>
40
+ <%= f.text_field :active, value: params[:q][:active],
41
+ class: 'datepicker datepicker-from fullwidth',
42
+ data: { :'enable-time' => true, :'default-hour' => 0 } %>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="col-2">
47
+ <div class="field">
48
+ <%= label_tag :q_promotion_category_id_eq, SolidusFriendlyPromotions::PromotionCategory.model_name.human %><br>
49
+ <%= f.collection_select(:promotion_category_id_eq, @promotion_categories, :id, :name, { include_blank: t(:all, scope: :spree) }, { class: 'custom-select fullwidth' }) %>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="col-2">
54
+ <div class="field">
55
+ <%= label_tag :q_lane_eq, SolidusFriendlyPromotions::Promotion.human_attribute_name(:lane) %><br>
56
+ <%= f.select(:lane_eq, SolidusFriendlyPromotions::Promotion.lane_options, { include_blank: t(:all, scope: :spree) }, { class: 'custom-select fullwidth' }) %>
57
+ </div>
58
+ </div>
59
+
60
+ </div>
61
+
62
+ <div class="actions filter-actions">
63
+ <div data-hook="admin_promotions_index_search_buttons">
64
+ <%= button_tag t('spree.filter_results'), class: 'btn btn-primary' %>
65
+ </div>
66
+ </div>
67
+ <% end %>
68
+ </div>
69
+ <% end %>
@@ -48,26 +48,28 @@
48
48
  </div>
49
49
 
50
50
  <div class="row">
51
- <% [:line_item, :shipment].each do |level| %>
51
+ <% [:order, :line_item, :shipment].each do |level| %>
52
52
  <% if promotion_actions_by_level(@promotion, level).any? %>
53
- <div class="col-6">
53
+ <div class="col-<%= level == :order ? 12 : 6 %>">
54
54
  <fieldset>
55
55
  <legend align="center"><%= t("#{level}_actions", scope: :solidus_friendly_promotions) %></legend>
56
56
 
57
57
  <%= render partial: 'solidus_friendly_promotions/admin/promotion_actions/promotion_action', collection: promotion_actions_by_level(@promotion, level), locals: {} %>
58
58
  </fieldset>
59
59
  </div>
60
- <div class="col-6">
61
- <fieldset>
62
- <legend align="center"><%= t("#{level}_rules", scope: :solidus_friendly_promotions) %></legend>
60
+ <% if level != :order %>
61
+ <div class="col-6">
62
+ <fieldset>
63
+ <legend align="center"><%= t("#{level}_rules", scope: :solidus_friendly_promotions) %></legend>
63
64
 
64
- <%= render partial: 'solidus_friendly_promotions/admin/promotion_rules/promotion_rule', collection: promotion_rules_by_level(@promotion, level), locals: { level: level } %>
65
+ <%= render partial: 'solidus_friendly_promotions/admin/promotion_rules/promotion_rule', collection: promotion_rules_by_level(@promotion, level), locals: { level: level } %>
65
66
 
66
- <%= turbo_frame_tag @promotion, "new_#{level}_promotion_rule" do %>
67
- <%= link_to t(:add_rule, scope: :solidus_friendly_promotions), solidus_friendly_promotions.new_admin_promotion_promotion_rule_path(@promotion, level: level), class: 'btn btn-secondary' %>
68
- <% end %>
69
- </fieldset>
70
- </div>
67
+ <%= turbo_frame_tag @promotion, "new_#{level}_promotion_rule" do %>
68
+ <%= link_to t(:add_rule, scope: :solidus_friendly_promotions), solidus_friendly_promotions.new_admin_promotion_promotion_rule_path(@promotion, level: level), class: 'btn btn-secondary' %>
69
+ <% end %>
70
+ </fieldset>
71
+ </div>
72
+ <% end %>
71
73
  <% end %>
72
74
  <% end %>
73
75
  </div>
@@ -8,113 +8,12 @@
8
8
  <% end %>
9
9
  <% end %>
10
10
 
11
- <% content_for :table_filter_title do %>
12
- <%= t('spree.search') %>
13
- <% end %>
14
-
15
- <% content_for :table_filter do %>
16
- <div data-hook="admin_promotions_index_search">
17
- <%= search_form_for [:admin, @search] do |f| %>
18
- <div class="row">
19
- <div class="col-4">
20
- <div class="field">
21
- <%= label_tag :q_name_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:name) %>
22
- <%= f.text_field :name_cont, tabindex: 1 %>
23
- </div>
24
- </div>
25
-
26
- <div class="col-2">
27
- <div class="field">
28
- <%= label_tag :q_codes_value_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:code) %>
29
- <%= f.text_field :codes_value_cont, tabindex: 1 %>
30
- </div>
31
- </div>
32
-
33
- <div class="col-2">
34
- <div class="field">
35
- <%= label_tag :q_path_cont, SolidusFriendlyPromotions::Promotion.human_attribute_name(:path) %>
36
- <%= f.text_field :path_cont, tabindex: 1 %>
37
- </div>
38
- </div>
39
-
40
- <div class="col-2">
41
- <div class="field">
42
- <%= label_tag :q_promotion_category_id_eq, SolidusFriendlyPromotions::PromotionCategory.model_name.human %><br>
43
- <%= f.collection_select(:promotion_category_id_eq, @promotion_categories, :id, :name, { include_blank: t('solidus_friendly_promotions.match_choices.all') }, { class: 'custom-select fullwidth' }) %>
44
- </div>
45
- </div>
46
-
47
- <div class="col-2">
48
- <div class="field">
49
- <%= label_tag :active, SolidusFriendlyPromotions::Promotion.human_attribute_name(:active) %><br>
50
- <%= f.check_box :active, label: false, as: :boolean, checked_value: true %>
51
- </div>
52
- </div>
53
- </div>
54
-
55
- <div class="clearfix"></div>
56
-
57
- <div class="actions filter-actions">
58
- <div data-hook="admin_promotions_index_search_buttons">
59
- <%= button_tag t('spree.filter_results'), class: 'btn btn-primary' %>
60
- </div>
61
- </div>
62
- <% end %>
63
- </div>
64
- <% end %>
11
+ <%= render "table_filter", search: @search %>
65
12
 
66
13
  <%= paginate @promotions, theme: "solidus_admin" %>
67
14
 
68
15
  <% if @promotions.length > 0 %>
69
- <table class="index">
70
- <thead>
71
- <tr>
72
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:name) %></th>
73
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:code) %></th>
74
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:status) %></th>
75
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:usage_limit) %></th>
76
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:uses) %></th>
77
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:starts_at) %></th>
78
- <th><%= SolidusFriendlyPromotions::Promotion.human_attribute_name(:expires_at) %></th>
79
- <th class="actions"></th>
80
- </tr>
81
- </thead>
82
- <tbody>
83
- <% @promotions.each do |promotion| %>
84
- <tr id="<%= spree_dom_id promotion %>">
85
- <td><%= promotion.name %></td>
86
- <td>
87
- <%= (promotion.codes.size == 1) ? promotion.codes.pluck(:value).first : t('solidus_friendly_promotions.number_of_codes', count: promotion.codes.size) %>
88
- </td>
89
- <td>
90
- <span class="pill pill-<%= promotion.active? ? 'active' : 'inactive' %>">
91
- <%= t(admin_promotion_status(promotion), scope: 'solidus_friendly_promotions.admin.promotion_status') %>
92
- </span>
93
- </td>
94
- <td>
95
- <%= promotion.usage_limit.nil? ? "∞" : promotion.usage_limit %>
96
- </td>
97
- <td>
98
- <%= promotion.usage_count %>
99
- </td>
100
- <td>
101
- <%= promotion.starts_at.to_fs(:long) if promotion.starts_at %>
102
- </td>
103
- <td>
104
- <%= promotion.expires_at.to_fs(:long) if promotion.expires_at %>
105
- </td>
106
- <td class="actions">
107
- <% if can?(:edit, promotion) %>
108
- <%= link_to_edit promotion, no_text: true %>
109
- <% end %>
110
- <% if can?(:destroy, promotion) %>
111
- <%= link_to_delete promotion, no_text: true %>
112
- <% end %>
113
- </td>
114
- </tr>
115
- <% end %>
116
- </tbody>
117
- </table>
16
+ <%= render "table", promotions: @promotions %>
118
17
  <% else %>
119
18
  <div class="no-objects-found">
120
19
  <%= render 'spree/admin/shared/no_objects_found',
@@ -23,6 +23,7 @@ en:
23
23
  shipment_rules: Shipment Rules
24
24
  line_item_actions: Line Item Actions
25
25
  shipment_actions: Shipment Actions
26
+ order_actions: Order Actions
26
27
  invalid_promotion_rule_level: Invalid Promotion Rule Level. Must be one of "order", "shipment", or "line_item"
27
28
  invalid_promotion_action: Invalid promotion action.
28
29
  invalid_promotion_rule: Invalid promotion rule.
@@ -33,7 +34,7 @@ en:
33
34
  new_promotion: New Promotion
34
35
  new_promotion_category: New Promotion Category
35
36
  new_promotion_code_batch: New Promotion Code Batch
36
- number_of_codes: Number of codes
37
+ number_of_codes: "%{count} codes"
37
38
  legacy_promotions: Legacy Promotions
38
39
  no_rules_added: No Rules Added
39
40
  promotion_successfully_created: Promotion has been successfully created!
@@ -145,6 +146,8 @@ en:
145
146
  starts_at_placeholder: Immediately
146
147
  edit:
147
148
  order_rules: Order Rules
149
+ calculator:
150
+ add_tier: Add tier
148
151
  promotion_status:
149
152
  active: Active
150
153
  expired: Expired
@@ -161,6 +164,7 @@ en:
161
164
  models:
162
165
  solidus_friendly_promotions/actions/adjust_shipment: Discount matching shipments
163
166
  solidus_friendly_promotions/actions/adjust_line_item: Discount matching line items
167
+ solidus_friendly_promotions/actions/create_discounted_item: Create discounted line item
164
168
  solidus_friendly_promotions/actions/adjust_line_item_quantity_groups: Discount matching line items based on quantity groups
165
169
  solidus_friendly_promotions/calculators/distributed_amount: Distributed Amount
166
170
  solidus_friendly_promotions/calculators/percent: Flat Percent
@@ -168,6 +172,7 @@ en:
168
172
  solidus_friendly_promotions/calculators/flexi_rate: Flexible Rate
169
173
  solidus_friendly_promotions/calculators/tiered_flat_rate: Tiered Flat Rate
170
174
  solidus_friendly_promotions/calculators/tiered_percent: Tiered Percent
175
+ solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity: Tiered Percent based on eligible item quantity
171
176
  solidus_friendly_promotions/rules/first_order: First Order
172
177
  solidus_friendly_promotions/rules/first_repeat_purchase_since: First Repeat Purchase Since
173
178
  solidus_friendly_promotions/rules/item_total: Item Total
@@ -189,6 +194,7 @@ en:
189
194
  solidus_friendly_promotions/promotion:
190
195
  active: Active
191
196
  customer_label: Customer-facing label
197
+ usage: Usage
192
198
  lanes:
193
199
  pre: Pre
194
200
  default: Default
@@ -199,6 +205,10 @@ en:
199
205
  description: Creates a promotion credit on matching line items
200
206
  solidus_friendly_promotions/actions/adjust_shipment:
201
207
  description: Creates a promotion credit on matching shipments
208
+ solidus_friendly_promotions/actions/create_discounted_item:
209
+ description: Creates a discounted item with the quantity the applicable line items.
210
+ preferred_quantity: Quantity per applicable line item quantity
211
+ preferred_necessary_quantity: Number of items needed for a discounted item
202
212
  solidus_friendly_promotions/rules/first_order:
203
213
  description: Must be the customer's first order
204
214
  solidus_friendly_promotions/rules/first_repeat_purchase_since:
@@ -242,14 +252,31 @@ en:
242
252
  solidus_friendly_promotions/rules/user_role:
243
253
  description: Order includes User with specified Role(s)
244
254
  solidus_friendly_promotions/calculators/tiered_flat_rate:
255
+ description: Flat Rate in tiers based on item amount
245
256
  preferred_base_amount: Base Amount
246
257
  tiers: Tiers
247
258
  solidus_friendly_promotions/calculators/tiered_percent:
259
+ description: Tiered percentage based on order's item total
248
260
  preferred_base_percent: Base Percent
249
261
  tiers: Tiers
262
+ solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity:
263
+ description: Tiered percentage based on eligible item quantity
264
+ preferred_base_percent: Base Percent
265
+ tiers: Tiers
266
+ solidus_friendly_promotions/calculators/flat_rate:
267
+ description: Provides a flat rate discount
268
+ solidus_friendly_promotions/calculators/percent:
269
+ description: Provides a discount calculated by percent of the discountable amount of the item being discounted
270
+ solidus_friendly_promotions/calculators/distributed_amount:
271
+ description: Distributed the configured amount among all eligible line items of the order
272
+
250
273
  errors:
251
274
  models:
252
275
  solidus_friendly_promotions/promotion_code:
253
276
  attributes:
254
277
  base:
255
278
  disallowed_with_apply_automatically: Could not create promotion code on promotion that apply automatically
279
+ spree/line_item:
280
+ attributes:
281
+ quantity:
282
+ cannot_be_changed_for_automated_items: cannot be changed on a line item managed by a promotion action
@@ -0,0 +1,5 @@
1
+ class AddManagedByOrderActionToLineItems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_reference :spree_line_items, :managed_by_order_action, foreign_key: {to_table: :friendly_promotion_actions, null: true}
4
+ end
5
+ end
@@ -4,6 +4,10 @@
4
4
  Spree::Config.order_contents_class = "SolidusFriendlyPromotions::SimpleOrderContents"
5
5
  Spree::Config.promotion_adjuster_class = "SolidusFriendlyPromotions::FriendlyPromotionAdjuster"
6
6
 
7
+ Rails.application.config.to_prepare do |config|
8
+ Spree::Order.line_item_comparison_hooks << :free_from_order_action?
9
+ end
10
+
7
11
  # Replace the promotions menu from core with ours
8
12
  Spree::Backend::Config.configure do |config|
9
13
  config.menu_items = config.menu_items.map do |item|
@@ -13,7 +17,7 @@ Spree::Backend::Config.configure do |config|
13
17
  if item.respond_to?(:children)
14
18
  Spree::BackendConfiguration::MenuItem.new(
15
19
  label: :promotions,
16
- icon: "bullhorn",
20
+ icon: config.admin_updated_navbar ? "ri-megaphone-line" : "bullhorn",
17
21
  condition: -> { can?(:admin, SolidusFriendlyPromotions::Promotion) },
18
22
  url: -> { SolidusFriendlyPromotions::Engine.routes.url_helpers.admin_promotions_path },
19
23
  data_hook: :admin_promotion_sub_tabs,
@@ -69,7 +73,8 @@ SolidusFriendlyPromotions.configure do |config|
69
73
  "SolidusFriendlyPromotions::Calculators::FlexiRate",
70
74
  "SolidusFriendlyPromotions::Calculators::Percent",
71
75
  "SolidusFriendlyPromotions::Calculators::TieredFlatRate",
72
- "SolidusFriendlyPromotions::Calculators::TieredPercent"
76
+ "SolidusFriendlyPromotions::Calculators::TieredPercent",
77
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
73
78
  ],
74
79
  "SolidusFriendlyPromotions::Actions::AdjustLineItem" => [
75
80
  "SolidusFriendlyPromotions::Calculators::DistributedAmount",
@@ -77,11 +82,18 @@ SolidusFriendlyPromotions.configure do |config|
77
82
  "SolidusFriendlyPromotions::Calculators::FlexiRate",
78
83
  "SolidusFriendlyPromotions::Calculators::Percent",
79
84
  "SolidusFriendlyPromotions::Calculators::TieredFlatRate",
80
- "SolidusFriendlyPromotions::Calculators::TieredPercent"
85
+ "SolidusFriendlyPromotions::Calculators::TieredPercent",
86
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
81
87
  ],
82
88
  "SolidusFriendlyPromotions::Actions::AdjustLineItemQuantityGroups" => [
83
89
  "SolidusFriendlyPromotions::Calculators::FlatRate",
84
- "SolidusFriendlyPromotions::Calculators::Percent"
90
+ "SolidusFriendlyPromotions::Calculators::Percent",
91
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
92
+ ],
93
+ "SolidusFriendlyPromotions::Actions::CreateDiscountedItem" => [
94
+ "SolidusFriendlyPromotions::Calculators::FlatRate",
95
+ "SolidusFriendlyPromotions::Calculators::Percent",
96
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
85
97
  ]
86
98
  )
87
99
 
@@ -113,6 +125,7 @@ SolidusFriendlyPromotions.configure do |config|
113
125
  config.actions = [
114
126
  "SolidusFriendlyPromotions::Actions::AdjustLineItem",
115
127
  "SolidusFriendlyPromotions::Actions::AdjustLineItemQuantityGroups",
116
- "SolidusFriendlyPromotions::Actions::AdjustShipment"
128
+ "SolidusFriendlyPromotions::Actions::AdjustShipment",
129
+ "SolidusFriendlyPromotions::Actions::CreateDiscountedItem"
117
130
  ]
118
131
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusFriendlyPromotions
4
- VERSION = "1.0.0.pre"
4
+ VERSION = "1.0.0.rc.2"
5
5
  end
@@ -6,6 +6,7 @@ require "solidus_support"
6
6
  require "turbo-rails"
7
7
  require "importmap-rails"
8
8
  require "stimulus-rails"
9
+ require "ransack-enum"
9
10
  require "solidus_friendly_promotions/nested_class_set"
10
11
  require "solidus_friendly_promotions/configuration"
11
12
  require "solidus_friendly_promotions/version"
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_dependency "turbo-rails", "~> 1.4"
34
34
  spec.add_dependency "stimulus-rails", "~> 1.2"
35
35
  spec.add_dependency "importmap-rails", "~> 1.2"
36
+ spec.add_dependency "ransack-enum", "~> 1.0"
36
37
  spec.add_development_dependency "rspec-activemodel-mocks", "~> 1.0"
37
38
  spec.add_development_dependency "shoulda-matchers", "~> 5.3"
38
39
  spec.add_development_dependency "solidus_dev_support", "~> 2.6"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus_friendly_promotions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre
4
+ version: 1.0.0.rc.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-07 00:00:00.000000000 Z
11
+ date: 2023-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: solidus_core
@@ -86,6 +86,20 @@ dependencies:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
88
  version: '1.2'
89
+ - !ruby/object:Gem::Dependency
90
+ name: ransack-enum
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.0'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.0'
89
103
  - !ruby/object:Gem::Dependency
90
104
  name: rspec-activemodel-mocks
91
105
  requirement: !ruby/object:Gem::Requirement
@@ -188,6 +202,8 @@ files:
188
202
  - app/javascript/solidus_friendly_promotions/jquery/option_value_picker.js
189
203
  - app/jobs/solidus_friendly_promotions/promotion_code_batch_job.rb
190
204
  - app/mailers/solidus_friendly_promotions/promotion_code_batch_mailer.rb
205
+ - app/models/concerns/solidus_friendly_promotions/actions/order_level_action.rb
206
+ - app/models/concerns/solidus_friendly_promotions/calculators/promotion_calculator.rb
191
207
  - app/models/concerns/solidus_friendly_promotions/discountable_amount.rb
192
208
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_applicable_order_rule.rb
193
209
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_level_rule.rb
@@ -197,12 +213,14 @@ files:
197
213
  - app/models/solidus_friendly_promotions/actions/adjust_line_item.rb
198
214
  - app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb
199
215
  - app/models/solidus_friendly_promotions/actions/adjust_shipment.rb
216
+ - app/models/solidus_friendly_promotions/actions/create_discounted_item.rb
200
217
  - app/models/solidus_friendly_promotions/calculators/distributed_amount.rb
201
218
  - app/models/solidus_friendly_promotions/calculators/flat_rate.rb
202
219
  - app/models/solidus_friendly_promotions/calculators/flexi_rate.rb
203
220
  - app/models/solidus_friendly_promotions/calculators/percent.rb
204
221
  - app/models/solidus_friendly_promotions/calculators/tiered_flat_rate.rb
205
222
  - app/models/solidus_friendly_promotions/calculators/tiered_percent.rb
223
+ - app/models/solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb
206
224
  - app/models/solidus_friendly_promotions/distributed_amounts_handler.rb
207
225
  - app/models/solidus_friendly_promotions/eligibility_result.rb
208
226
  - app/models/solidus_friendly_promotions/eligibility_results.rb
@@ -223,6 +241,7 @@ files:
223
241
  - app/models/solidus_friendly_promotions/promotion_code/batch_builder.rb
224
242
  - app/models/solidus_friendly_promotions/promotion_code_batch.rb
225
243
  - app/models/solidus_friendly_promotions/promotion_handler/coupon.rb
244
+ - app/models/solidus_friendly_promotions/promotion_handler/null.rb
226
245
  - app/models/solidus_friendly_promotions/promotion_handler/page.rb
227
246
  - app/models/solidus_friendly_promotions/promotion_rule.rb
228
247
  - app/models/solidus_friendly_promotions/promotion_rules_store.rb
@@ -256,6 +275,7 @@ files:
256
275
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_adjust_line_item_quantity_groups.html.erb
257
276
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_adjust_shipment.html.erb
258
277
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_calculator_fields.erb
278
+ - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb
259
279
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/_default_fields.html.erb
260
280
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/distributed_amount/_fields.html.erb
261
281
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/flat_rate/_fields.html.erb
@@ -263,6 +283,8 @@ files:
263
283
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_flat_rate/_tier_fields.html.erb
264
284
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/_fields.html.erb
265
285
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/_tier_fields.html.erb
286
+ - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_fields.html.erb
287
+ - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_tier_fields.html.erb
266
288
  - app/views/solidus_friendly_promotions/admin/promotion_actions/edit.html.erb
267
289
  - app/views/solidus_friendly_promotions/admin/promotion_actions/new.html.erb
268
290
  - app/views/solidus_friendly_promotions/admin/promotion_categories/_form.html.erb
@@ -296,6 +318,8 @@ files:
296
318
  - app/views/solidus_friendly_promotions/admin/promotions/_activations_edit.html.erb
297
319
  - app/views/solidus_friendly_promotions/admin/promotions/_activations_new.html.erb
298
320
  - app/views/solidus_friendly_promotions/admin/promotions/_form.html.erb
321
+ - app/views/solidus_friendly_promotions/admin/promotions/_table.html.erb
322
+ - app/views/solidus_friendly_promotions/admin/promotions/_table_filter.html.erb
299
323
  - app/views/solidus_friendly_promotions/admin/promotions/edit.html.erb
300
324
  - app/views/solidus_friendly_promotions/admin/promotions/index.html.erb
301
325
  - app/views/solidus_friendly_promotions/admin/promotions/new.html.erb
@@ -338,6 +362,7 @@ files:
338
362
  - db/migrate/20231012020928_add_db_comments_to_friendly_products_promotion_rules.rb
339
363
  - db/migrate/20231012120928_add_db_comments_to_friendly_promotion_categories.rb
340
364
  - db/migrate/20231013181921_add_original_promotion_ids.rb
365
+ - db/migrate/20231104135812_add_managed_by_order_action_to_line_items.rb
341
366
  - lib/generators/solidus_friendly_promotions/install/install_generator.rb
342
367
  - lib/generators/solidus_friendly_promotions/install/templates/initializer.rb
343
368
  - lib/solidus_friendly_promotions.rb