solidus_friendly_promotions 1.0.0.rc.1 → 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 (32) 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 +7 -1
  5. data/app/models/concerns/solidus_friendly_promotions/calculators/promotion_calculator.rb +11 -0
  6. data/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb +1 -9
  7. data/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb +16 -1
  8. data/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb +3 -9
  9. data/app/models/solidus_friendly_promotions/calculators/flat_rate.rb +2 -0
  10. data/app/models/solidus_friendly_promotions/calculators/flexi_rate.rb +2 -0
  11. data/app/models/solidus_friendly_promotions/calculators/percent.rb +2 -0
  12. data/app/models/solidus_friendly_promotions/calculators/tiered_flat_rate.rb +2 -0
  13. data/app/models/solidus_friendly_promotions/calculators/tiered_percent.rb +2 -0
  14. data/app/models/solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +36 -0
  15. data/app/models/solidus_friendly_promotions/promotion.rb +7 -1
  16. data/app/models/solidus_friendly_promotions/promotion_rule.rb +1 -1
  17. data/app/models/solidus_friendly_promotions/rules/minimum_quantity.rb +1 -7
  18. data/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_calculator_fields.erb +1 -0
  19. data/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb +4 -0
  20. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_flat_rate/_fields.html.erb +1 -1
  21. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/_fields.html.erb +1 -1
  22. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_fields.html.erb +34 -0
  23. data/app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent_on_eligible_item_quantity/_tier_fields.html.erb +27 -0
  24. data/app/views/solidus_friendly_promotions/admin/promotions/_table.html.erb +56 -0
  25. data/app/views/solidus_friendly_promotions/admin/promotions/_table_filter.html.erb +69 -0
  26. data/app/views/solidus_friendly_promotions/admin/promotions/index.html.erb +2 -103
  27. data/config/locales/en.yml +21 -2
  28. data/lib/generators/solidus_friendly_promotions/install/templates/initializer.rb +9 -5
  29. data/lib/solidus_friendly_promotions/version.rb +1 -1
  30. data/lib/solidus_friendly_promotions.rb +1 -0
  31. data/solidus_friendly_promotions.gemspec +1 -0
  32. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac98e07030bf9f63775dafdffc94d3a6be02b8050664037469f57a32dc71da43
4
- data.tar.gz: fdd1d7e3fbbfc92cb347af92c28c5f518d91724a330408a576d47449c6e8f201
3
+ metadata.gz: 84ea9571f3d2a4a2dfe9e789c0f3887a0aca2d9559a68004806cd67c395fc959
4
+ data.tar.gz: 929a14c4f917aad08c6bf7faa7f93c7be071dc43ad72641ddf0394cf336022db
5
5
  SHA512:
6
- metadata.gz: c9eb24c1fabf89ee0b61731b8541e1459dfe9d508ffc2721f8f1384df382c1653b94f407c04438a2047ebf0fc868e4849fc5f88b821ab141eac44b21bde910b9
7
- data.tar.gz: 1e89490f9daf5ad6480b37bc47581bb7ba4a948bb1af7aa7b9539b3c094c6549db4055132b53155e3fc419f881b71d64c81577ba447d188e704eda176bd5b808
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])
@@ -3,18 +3,24 @@
3
3
  module SolidusFriendlyPromotions
4
4
  module LineItemDecorator
5
5
  def self.prepended(base)
6
+ base.attr_accessor :quantity_setter
6
7
  base.belongs_to :managed_by_order_action, class_name: "SolidusFriendlyPromotions::PromotionAction", optional: true
7
8
  base.validate :validate_managed_quantity_same, on: :update
9
+ base.after_save :reset_quantity_setter
8
10
  end
9
11
 
10
12
  private
11
13
 
12
14
  def validate_managed_quantity_same
13
- if managed_by_order_action && quantity_changed?
15
+ if managed_by_order_action && quantity_changed? && quantity_setter != managed_by_order_action
14
16
  errors.add(:quantity, :cannot_be_changed_for_automated_items)
15
17
  end
16
18
  end
17
19
 
20
+ def reset_quantity_setter
21
+ @quantity_setter = nil
22
+ end
23
+
18
24
  Spree::LineItem.prepend self
19
25
  Spree::LineItem.prepend SolidusFriendlyPromotions::DiscountableAmount
20
26
  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.discountable_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
@@ -6,9 +6,11 @@ module SolidusFriendlyPromotions
6
6
  include OrderLevelAction
7
7
  preference :variant_id, :integer
8
8
  preference :quantity, :integer, default: 1
9
+ preference :necessary_quantity, :integer, default: 1
9
10
 
10
11
  def perform(order)
11
12
  line_item = find_item(order) || create_item(order)
13
+ set_quantity(line_item, determine_item_quantity(order))
12
14
  line_item.current_discounts << discount(line_item)
13
15
  end
14
16
 
@@ -24,7 +26,20 @@ module SolidusFriendlyPromotions
24
26
  end
25
27
 
26
28
  def create_item(order)
27
- order.line_items.create!(quantity: preferred_quantity, variant: variant, managed_by_order_action: self)
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
28
43
  end
29
44
 
30
45
  def variant
@@ -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.discountable_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
@@ -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
@@ -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 %>
@@ -5,6 +5,10 @@
5
5
  <%= form.label :preferred_quantity %>
6
6
  <%= form.number_field :preferred_quantity, class: "fullwidth" %>
7
7
  </div>
8
+ <div class="field">
9
+ <%= form.label :preferred_necessary_quantity %>
10
+ <%= form.number_field :preferred_necessary_quantity, class: "fullwidth" %>
11
+ </div>
8
12
  <% end %>
9
13
 
10
14
  <%= render(
@@ -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 %>
@@ -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',
@@ -34,7 +34,7 @@ en:
34
34
  new_promotion: New Promotion
35
35
  new_promotion_category: New Promotion Category
36
36
  new_promotion_code_batch: New Promotion Code Batch
37
- number_of_codes: Number of codes
37
+ number_of_codes: "%{count} codes"
38
38
  legacy_promotions: Legacy Promotions
39
39
  no_rules_added: No Rules Added
40
40
  promotion_successfully_created: Promotion has been successfully created!
@@ -146,6 +146,8 @@ en:
146
146
  starts_at_placeholder: Immediately
147
147
  edit:
148
148
  order_rules: Order Rules
149
+ calculator:
150
+ add_tier: Add tier
149
151
  promotion_status:
150
152
  active: Active
151
153
  expired: Expired
@@ -170,6 +172,7 @@ en:
170
172
  solidus_friendly_promotions/calculators/flexi_rate: Flexible Rate
171
173
  solidus_friendly_promotions/calculators/tiered_flat_rate: Tiered Flat Rate
172
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
173
176
  solidus_friendly_promotions/rules/first_order: First Order
174
177
  solidus_friendly_promotions/rules/first_repeat_purchase_since: First Repeat Purchase Since
175
178
  solidus_friendly_promotions/rules/item_total: Item Total
@@ -191,6 +194,7 @@ en:
191
194
  solidus_friendly_promotions/promotion:
192
195
  active: Active
193
196
  customer_label: Customer-facing label
197
+ usage: Usage
194
198
  lanes:
195
199
  pre: Pre
196
200
  default: Default
@@ -202,7 +206,9 @@ en:
202
206
  solidus_friendly_promotions/actions/adjust_shipment:
203
207
  description: Creates a promotion credit on matching shipments
204
208
  solidus_friendly_promotions/actions/create_discounted_item:
205
- description: Creates a 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
206
212
  solidus_friendly_promotions/rules/first_order:
207
213
  description: Must be the customer's first order
208
214
  solidus_friendly_promotions/rules/first_repeat_purchase_since:
@@ -246,11 +252,24 @@ en:
246
252
  solidus_friendly_promotions/rules/user_role:
247
253
  description: Order includes User with specified Role(s)
248
254
  solidus_friendly_promotions/calculators/tiered_flat_rate:
255
+ description: Flat Rate in tiers based on item amount
249
256
  preferred_base_amount: Base Amount
250
257
  tiers: Tiers
251
258
  solidus_friendly_promotions/calculators/tiered_percent:
259
+ description: Tiered percentage based on order's item total
252
260
  preferred_base_percent: Base Percent
253
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
+
254
273
  errors:
255
274
  models:
256
275
  solidus_friendly_promotions/promotion_code:
@@ -17,7 +17,7 @@ Spree::Backend::Config.configure do |config|
17
17
  if item.respond_to?(:children)
18
18
  Spree::BackendConfiguration::MenuItem.new(
19
19
  label: :promotions,
20
- icon: "bullhorn",
20
+ icon: config.admin_updated_navbar ? "ri-megaphone-line" : "bullhorn",
21
21
  condition: -> { can?(:admin, SolidusFriendlyPromotions::Promotion) },
22
22
  url: -> { SolidusFriendlyPromotions::Engine.routes.url_helpers.admin_promotions_path },
23
23
  data_hook: :admin_promotion_sub_tabs,
@@ -73,7 +73,8 @@ SolidusFriendlyPromotions.configure do |config|
73
73
  "SolidusFriendlyPromotions::Calculators::FlexiRate",
74
74
  "SolidusFriendlyPromotions::Calculators::Percent",
75
75
  "SolidusFriendlyPromotions::Calculators::TieredFlatRate",
76
- "SolidusFriendlyPromotions::Calculators::TieredPercent"
76
+ "SolidusFriendlyPromotions::Calculators::TieredPercent",
77
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
77
78
  ],
78
79
  "SolidusFriendlyPromotions::Actions::AdjustLineItem" => [
79
80
  "SolidusFriendlyPromotions::Calculators::DistributedAmount",
@@ -81,15 +82,18 @@ SolidusFriendlyPromotions.configure do |config|
81
82
  "SolidusFriendlyPromotions::Calculators::FlexiRate",
82
83
  "SolidusFriendlyPromotions::Calculators::Percent",
83
84
  "SolidusFriendlyPromotions::Calculators::TieredFlatRate",
84
- "SolidusFriendlyPromotions::Calculators::TieredPercent"
85
+ "SolidusFriendlyPromotions::Calculators::TieredPercent",
86
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
85
87
  ],
86
88
  "SolidusFriendlyPromotions::Actions::AdjustLineItemQuantityGroups" => [
87
89
  "SolidusFriendlyPromotions::Calculators::FlatRate",
88
- "SolidusFriendlyPromotions::Calculators::Percent"
90
+ "SolidusFriendlyPromotions::Calculators::Percent",
91
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
89
92
  ],
90
93
  "SolidusFriendlyPromotions::Actions::CreateDiscountedItem" => [
91
94
  "SolidusFriendlyPromotions::Calculators::FlatRate",
92
- "SolidusFriendlyPromotions::Calculators::Percent"
95
+ "SolidusFriendlyPromotions::Calculators::Percent",
96
+ "SolidusFriendlyPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
93
97
  ]
94
98
  )
95
99
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusFriendlyPromotions
4
- VERSION = "1.0.0.rc.1"
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.rc.1
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
@@ -189,6 +203,7 @@ files:
189
203
  - app/jobs/solidus_friendly_promotions/promotion_code_batch_job.rb
190
204
  - app/mailers/solidus_friendly_promotions/promotion_code_batch_mailer.rb
191
205
  - app/models/concerns/solidus_friendly_promotions/actions/order_level_action.rb
206
+ - app/models/concerns/solidus_friendly_promotions/calculators/promotion_calculator.rb
192
207
  - app/models/concerns/solidus_friendly_promotions/discountable_amount.rb
193
208
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_applicable_order_rule.rb
194
209
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_level_rule.rb
@@ -205,6 +220,7 @@ files:
205
220
  - app/models/solidus_friendly_promotions/calculators/percent.rb
206
221
  - app/models/solidus_friendly_promotions/calculators/tiered_flat_rate.rb
207
222
  - app/models/solidus_friendly_promotions/calculators/tiered_percent.rb
223
+ - app/models/solidus_friendly_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb
208
224
  - app/models/solidus_friendly_promotions/distributed_amounts_handler.rb
209
225
  - app/models/solidus_friendly_promotions/eligibility_result.rb
210
226
  - app/models/solidus_friendly_promotions/eligibility_results.rb
@@ -267,6 +283,8 @@ files:
267
283
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_flat_rate/_tier_fields.html.erb
268
284
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/tiered_percent/_fields.html.erb
269
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
270
288
  - app/views/solidus_friendly_promotions/admin/promotion_actions/edit.html.erb
271
289
  - app/views/solidus_friendly_promotions/admin/promotion_actions/new.html.erb
272
290
  - app/views/solidus_friendly_promotions/admin/promotion_categories/_form.html.erb
@@ -300,6 +318,8 @@ files:
300
318
  - app/views/solidus_friendly_promotions/admin/promotions/_activations_edit.html.erb
301
319
  - app/views/solidus_friendly_promotions/admin/promotions/_activations_new.html.erb
302
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
303
323
  - app/views/solidus_friendly_promotions/admin/promotions/edit.html.erb
304
324
  - app/views/solidus_friendly_promotions/admin/promotions/index.html.erb
305
325
  - app/views/solidus_friendly_promotions/admin/promotions/new.html.erb