solidus_friendly_promotions 1.0.0.pre → 1.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.
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