solidus_friendly_promotions 1.0.0.rc.1 → 1.0.0.rc.2

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