solidus_promotions 4.6.2 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/MIGRATING.md +10 -4
  3. data/README.md +7 -0
  4. data/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js +3 -26
  5. data/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js +36 -19
  6. data/app/javascript/backend/solidus_promotions/web_components/product_picker.js +6 -1
  7. data/app/jobs/solidus_promotions/promotion_code_batch_job.rb +1 -1
  8. data/app/models/concerns/solidus_promotions/adjustment_discounts.rb +20 -0
  9. data/app/models/concerns/solidus_promotions/benefits/line_item_benefit.rb +5 -0
  10. data/app/models/concerns/solidus_promotions/benefits/order_benefit.rb +5 -0
  11. data/app/models/concerns/solidus_promotions/benefits/shipment_benefit.rb +5 -0
  12. data/app/models/concerns/solidus_promotions/calculators/promotion_calculator.rb +7 -0
  13. data/app/models/concerns/solidus_promotions/conditions/line_item_applicable_order_level_condition.rb +17 -5
  14. data/app/models/concerns/solidus_promotions/conditions/line_item_level_condition.rb +15 -2
  15. data/app/models/concerns/solidus_promotions/conditions/option_value_condition.rb +21 -0
  16. data/app/models/concerns/solidus_promotions/conditions/order_level_condition.rb +15 -2
  17. data/app/models/concerns/solidus_promotions/conditions/product_condition.rb +28 -0
  18. data/app/models/concerns/solidus_promotions/conditions/shipment_level_condition.rb +15 -2
  19. data/app/models/concerns/solidus_promotions/conditions/taxon_condition.rb +77 -0
  20. data/app/models/concerns/solidus_promotions/coupon_code_normalizer.rb +37 -0
  21. data/app/models/concerns/solidus_promotions/discountable_amount.rb +3 -4
  22. data/app/models/concerns/solidus_promotions/discounted_amount.rb +54 -0
  23. data/app/models/solidus_promotions/benefit.rb +257 -36
  24. data/app/models/solidus_promotions/benefits/adjust_line_item.rb +28 -3
  25. data/app/models/solidus_promotions/benefits/adjust_line_item_quantity_groups.rb +1 -0
  26. data/app/models/solidus_promotions/benefits/adjust_shipment.rb +45 -3
  27. data/app/models/solidus_promotions/benefits/advertise_price.rb +28 -0
  28. data/app/models/solidus_promotions/benefits/create_discounted_item.rb +30 -7
  29. data/app/models/solidus_promotions/calculators/distributed_amount.rb +34 -8
  30. data/app/models/solidus_promotions/calculators/flat_rate.rb +52 -6
  31. data/app/models/solidus_promotions/calculators/flexi_rate.rb +69 -6
  32. data/app/models/solidus_promotions/calculators/percent.rb +40 -4
  33. data/app/models/solidus_promotions/calculators/percent_with_cap.rb +44 -3
  34. data/app/models/solidus_promotions/calculators/tiered_flat_rate.rb +81 -19
  35. data/app/models/solidus_promotions/calculators/tiered_percent.rb +96 -25
  36. data/app/models/solidus_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +101 -16
  37. data/app/models/solidus_promotions/condition.rb +186 -7
  38. data/app/models/solidus_promotions/conditions/first_order.rb +3 -1
  39. data/app/models/solidus_promotions/conditions/first_repeat_purchase_since.rb +3 -2
  40. data/app/models/solidus_promotions/conditions/item_total.rb +2 -1
  41. data/app/models/solidus_promotions/conditions/line_item_option_value.rb +4 -12
  42. data/app/models/solidus_promotions/conditions/line_item_product.rb +4 -22
  43. data/app/models/solidus_promotions/conditions/line_item_taxon.rb +7 -38
  44. data/app/models/solidus_promotions/conditions/minimum_quantity.rb +3 -2
  45. data/app/models/solidus_promotions/conditions/nth_order.rb +3 -2
  46. data/app/models/solidus_promotions/conditions/one_use_per_user.rb +2 -1
  47. data/app/models/solidus_promotions/conditions/option_value.rb +6 -11
  48. data/app/models/solidus_promotions/conditions/order_option_value.rb +19 -0
  49. data/app/models/solidus_promotions/conditions/order_product.rb +62 -0
  50. data/app/models/solidus_promotions/conditions/order_taxon.rb +60 -0
  51. data/app/models/solidus_promotions/conditions/price_option_value.rb +26 -0
  52. data/app/models/solidus_promotions/conditions/price_product.rb +36 -0
  53. data/app/models/solidus_promotions/conditions/price_taxon.rb +28 -0
  54. data/app/models/solidus_promotions/conditions/product.rb +17 -59
  55. data/app/models/solidus_promotions/conditions/shipping_method.rb +3 -5
  56. data/app/models/solidus_promotions/conditions/store.rb +2 -1
  57. data/app/models/solidus_promotions/conditions/taxon.rb +24 -73
  58. data/app/models/solidus_promotions/conditions/user.rb +2 -1
  59. data/app/models/solidus_promotions/conditions/user_logged_in.rb +1 -3
  60. data/app/models/solidus_promotions/conditions/user_role.rb +1 -3
  61. data/app/models/solidus_promotions/distributed_amounts_handler.rb +2 -6
  62. data/app/models/solidus_promotions/eligibility_results.rb +1 -0
  63. data/app/models/solidus_promotions/item_discount.rb +1 -0
  64. data/app/models/solidus_promotions/order_adjuster/discount_order.rb +29 -35
  65. data/app/models/solidus_promotions/order_adjuster/recalculate_promo_totals.rb +45 -0
  66. data/app/models/solidus_promotions/order_adjuster/set_discounts_to_zero.rb +33 -0
  67. data/app/models/solidus_promotions/order_adjuster.rb +4 -14
  68. data/app/models/solidus_promotions/order_promotion.rb +1 -0
  69. data/app/models/solidus_promotions/product_advertiser.rb +57 -0
  70. data/app/models/solidus_promotions/promotion.rb +12 -10
  71. data/app/models/solidus_promotions/promotion_code/batch_builder.rb +1 -1
  72. data/app/models/solidus_promotions/promotion_code.rb +4 -4
  73. data/app/models/solidus_promotions/promotion_code_batch.rb +1 -1
  74. data/app/models/solidus_promotions/promotion_handler/coupon.rb +1 -1
  75. data/app/models/solidus_promotions/promotion_handler/page.rb +1 -1
  76. data/app/models/solidus_promotions/promotion_lane.rb +48 -0
  77. data/app/models/solidus_promotions/shipping_rate_discount.rb +3 -0
  78. data/app/patches/models/solidus_promotions/line_item_patch.rb +2 -0
  79. data/app/patches/models/solidus_promotions/order_patch.rb +8 -0
  80. data/app/patches/models/solidus_promotions/order_recalculator_patch.rb +3 -1
  81. data/app/patches/models/solidus_promotions/price_patch.rb +31 -0
  82. data/app/patches/models/solidus_promotions/shipment_patch.rb +2 -0
  83. data/app/patches/models/solidus_promotions/shipping_rate_patch.rb +15 -0
  84. data/config/locales/en.yml +47 -11
  85. data/config/routes.rb +1 -1
  86. data/db/migrate/20230703101637_create_promotions.rb +2 -2
  87. data/db/migrate/20230703113625_create_promotion_benefits.rb +3 -3
  88. data/db/migrate/20230703141116_create_promotion_categories.rb +1 -1
  89. data/db/migrate/20230703143943_create_promotion_conditions.rb +1 -1
  90. data/db/migrate/20230704083830_add_condition_join_tables.rb +8 -8
  91. data/db/migrate/20230704102444_create_promotion_codes.rb +1 -1
  92. data/db/migrate/20230704102656_create_promotion_code_batches.rb +1 -1
  93. data/db/migrate/20230705171556_create_order_promotions.rb +3 -3
  94. data/db/migrate/20230725074235_create_shipping_rate_discounts.rb +2 -2
  95. data/db/migrate/20231104135812_add_managed_by_order_benefit_to_line_items.rb +1 -1
  96. data/db/migrate/20251104170913_update_promotion_code_value_collation.rb +38 -0
  97. data/db/migrate/20251104214304_separate_out_order_only_conditions.rb +41 -0
  98. data/eslint.config.mjs +29 -0
  99. data/lib/components/admin/solidus_promotions/promotion_categories/index/component.rb +6 -6
  100. data/lib/components/admin/solidus_promotions/promotions/index/component.rb +5 -5
  101. data/lib/solidus_promotions/configuration.rb +57 -12
  102. data/lib/solidus_promotions/promotion_map.rb +14 -14
  103. data/lib/solidus_promotions/testing_support/shared_examples/option_value_condition.rb +18 -0
  104. data/lib/solidus_promotions/testing_support/shared_examples/product_condition.rb +37 -0
  105. data/lib/solidus_promotions/testing_support/shared_examples/promotion_calculator.rb +11 -0
  106. data/lib/solidus_promotions/testing_support/shared_examples/taxon_condition.rb +37 -0
  107. data/lib/solidus_promotions/testing_support/shared_examples.rb +6 -0
  108. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_advertise_price.html.erb +7 -0
  109. data/lib/views/backend/solidus_promotions/admin/calculator_fields/_default_fields.html.erb +1 -1
  110. data/lib/views/backend/solidus_promotions/admin/calculator_fields/percent/_fields.html.erb +1 -1
  111. data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb +6 -5
  112. data/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb +6 -12
  113. data/lib/views/backend/solidus_promotions/admin/condition_fields/_price_option_value.html.erb +26 -0
  114. data/lib/views/backend/solidus_promotions/admin/condition_fields/_price_product.html.erb +21 -0
  115. data/lib/views/backend/solidus_promotions/admin/condition_fields/_price_taxon.html.erb +17 -0
  116. data/lib/views/backend/solidus_promotions/admin/condition_fields/_product.html.erb +0 -7
  117. data/lib/views/backend/solidus_promotions/admin/condition_fields/_taxon.html.erb +0 -7
  118. data/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb +10 -4
  119. data/solidus_promotions.gemspec +1 -1
  120. metadata +37 -6
  121. data/.eslintrc.json +0 -10
  122. data/app/models/solidus_promotions/order_adjuster/persist_discounted_order.rb +0 -79
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "an option value condition" do
4
+ let(:condition) do
5
+ super()
6
+ rescue NoMethodError
7
+ described_class.new
8
+ end
9
+
10
+ describe "#preferred_eligible_values" do
11
+ subject { condition.preferred_eligible_values }
12
+
13
+ it "assigns a nicely formatted hash" do
14
+ condition.preferred_eligible_values = {"5" => "1,2", "6" => "1"}
15
+ expect(subject).to eq({5 => [1, 2], 6 => [1]})
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "a product condition" do
4
+ let(:condition) do
5
+ super()
6
+ rescue NoMethodError
7
+ described_class.new
8
+ end
9
+
10
+ it { is_expected.to have_many :products }
11
+
12
+ describe "#product_ids_string" do
13
+ it "returns a string of product ids" do
14
+ condition.products = [create(:product), create(:product)]
15
+ expect(condition.product_ids_string).to eq("#{condition.products[0].id},#{condition.products[1].id}")
16
+ end
17
+ end
18
+
19
+ describe "#preload_relations" do
20
+ subject { condition.preload_relations }
21
+ it { is_expected.to eq([:products]) }
22
+ end
23
+
24
+ describe "#product_ids_string=" do
25
+ it "sets products based on a string of product ids" do
26
+ product_one = create(:product)
27
+ product_two = create(:product)
28
+ condition.product_ids_string = "#{product_one.id},#{product_two.id}"
29
+ expect(condition.products).to eq([product_one, product_two])
30
+ end
31
+ end
32
+
33
+ describe "#preload_relations" do
34
+ subject { condition.preload_relations }
35
+ it { is_expected.to eq([:products]) }
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples_for "a promotion calculator" do
4
+ describe ".description" do
5
+ subject { described_class.description }
6
+
7
+ it "has a description" do
8
+ expect(subject.size).to be > 0
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "a taxon condition" do
4
+ let(:condition) do
5
+ super()
6
+ rescue NoMethodError
7
+ described_class.new
8
+ end
9
+
10
+ it { is_expected.to have_many :taxons }
11
+
12
+ describe "#taxon_ids_string" do
13
+ it "returns a string of taxon ids" do
14
+ condition.taxons = [create(:taxon), create(:taxon)]
15
+ expect(condition.taxon_ids_string).to eq("#{condition.taxons[0].id},#{condition.taxons[1].id}")
16
+ end
17
+ end
18
+
19
+ describe "#preload_relations" do
20
+ subject { condition.preload_relations }
21
+ it { is_expected.to eq([:taxons]) }
22
+ end
23
+
24
+ describe "#taxon_ids_string=" do
25
+ it "sets taxons based on a string of taxon ids" do
26
+ taxon_one = create(:taxon)
27
+ taxon_two = create(:taxon)
28
+ condition.taxon_ids_string = "#{taxon_one.id},#{taxon_two.id}"
29
+ expect(condition.taxons).to eq([taxon_one, taxon_two])
30
+ end
31
+ end
32
+
33
+ describe "#preload_relations" do
34
+ subject { condition.preload_relations }
35
+ it { is_expected.to eq([:taxons]) }
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "solidus_promotions/testing_support/shared_examples/product_condition"
4
+ require "solidus_promotions/testing_support/shared_examples/taxon_condition"
5
+ require "solidus_promotions/testing_support/shared_examples/option_value_condition"
6
+ require "solidus_promotions/testing_support/shared_examples/promotion_calculator"
@@ -0,0 +1,7 @@
1
+
2
+ <%= render(
3
+ "solidus_promotions/admin/benefit_fields/calculator_fields",
4
+ benefit: benefit,
5
+ param_prefix: param_prefix,
6
+ form: form
7
+ ) %>
@@ -2,5 +2,5 @@
2
2
  <%= render "solidus_promotions/admin/shared/preference_fields/#{calculator.preference_type(name)}",
3
3
  name: "#{prefix}[calculator_attributes][preferred_#{name}]",
4
4
  value: calculator.get_preference(name),
5
- label: calculator.class.human_attribute_name("preferred_#{name}", default: I18n.t(name, scope: :spree, default: name.humanize)) %>
5
+ label: calculator.class.human_attribute_name("preferred_#{name}", default: I18n.t(name, scope: :spree, default: name.to_s.humanize)) %>
6
6
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
2
  <%= fields_for "#{prefix}[calculator_attributes]", calculator do |f| %>
3
3
  <%= f.label :preferred_percent %>
4
- <%= f.number_field :preferred_percent, step: 1, min: 1, max: 100, default: 1, class: "form-control" %>
4
+ <%= f.number_field :preferred_percent, step: 0.01, min: 1, max: 100, default: 1, class: "form-control" %>
5
5
  <% end %>
6
6
  </div>
@@ -5,19 +5,20 @@
5
5
  <div class="field promo-condition-option-values">
6
6
  <div class="param-prefix hidden" data-param-prefix="<%= param_prefix %>"></div>
7
7
  <div class="row">
8
- <div class="col-6"><%= label_tag nil, Spree::Product.model_name.human %></div>
8
+ <div class="col-5"><%= label_tag nil, Spree::Product.model_name.human %></div>
9
9
  <div class="col-6"><%= label_tag nil, plural_resource_name(Spree::OptionValue) %></div>
10
+ <div class="col-1">&nbsp;</div>
10
11
  </div>
11
12
 
12
13
  <div class="form-group">
13
14
  <div data-controller="product-option-values">
14
15
  <template data-product-option-values-target="template">
15
- <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: [nil, []], form: form %>
16
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: [nil, []], form: form, index: 0 %>
16
17
  </template>
17
- <% form.object.preferred_eligible_values.each do |product_option_values| %>
18
- <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: product_option_values, form: form %>
18
+ <% form.object.preferred_eligible_values.each.with_index do |product_option_values, index| %>
19
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values:, form:, index: %>
19
20
  <% end %>
20
- <div class="mb-3" data-product-option-values-target="links">
21
+ <div class="mt-3" data-product-option-values-target="links">
21
22
  <%= link_to t(:add_product, scope: [:solidus_promotions, :line_item_option_value_condition]), "#", class: "btn btn-outline-primary", data: { action: "click->product-option-values#add_row" } %>
22
23
  </div>
23
24
  </div>
@@ -7,30 +7,24 @@
7
7
  <div class="field promo-condition-option-values">
8
8
  <div class="param-prefix hidden" data-param-prefix="<%= param_prefix %>"></div>
9
9
  <div class="row">
10
- <div class="col-6"><%= label_tag nil, Spree::Product.model_name.human %></div>
10
+ <div class="col-5"><%= label_tag nil, Spree::Product.model_name.human %></div>
11
11
  <div class="col-6"><%= label_tag nil, plural_resource_name(Spree::OptionValue) %></div>
12
+ <div class="col-1">&nbsp;</div>
12
13
  </div>
13
14
 
14
15
  <div class="form-group">
15
16
  <div data-controller="product-option-values">
16
17
  <template data-product-option-values-target="template">
17
- <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: [nil, []], form: form %>
18
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: [nil, []], form:, index: 0 %>
18
19
  </template>
19
- <% form.object.preferred_eligible_values.each do |product_option_values| %>
20
- <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: product_option_values, form: form %>
20
+ <% form.object.preferred_eligible_values.each.with_index do |product_option_values, index| %>
21
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values:, form:, index: %>
21
22
  <% end %>
22
- <div class="mb-3" data-product-option-values-target="links">
23
+ <div class="mt-3" data-product-option-values-target="links">
23
24
  <%= link_to t(:add_product, scope: [:solidus_promotions, :option_value_condition]), "#", class: "btn btn-outline-primary", data: { action: "click->product-option-values#add_row" } %>
24
25
  </div>
25
26
  </div>
26
27
  </div>
27
-
28
- <div class="form-group">
29
- <%= form.label :preferred_line_item_applicable do %>
30
- <%= form.check_box :preferred_line_item_applicable %>
31
- <%= condition.class.human_attribute_name(:preferred_line_item_applicable) %>
32
- <% end %>
33
- </div>
34
28
  </div>
35
29
  <% else %>
36
30
  <p>
@@ -0,0 +1,26 @@
1
+
2
+ <p>
3
+ <%= condition.class.human_attribute_name(:description) %>
4
+ </p>
5
+ <div class="field promo-condition-option-values">
6
+ <div class="param-prefix hidden" data-param-prefix="<%= param_prefix %>"></div>
7
+ <div class="row">
8
+ <div class="col-5"><%= label_tag nil, Spree::Product.model_name.human %></div>
9
+ <div class="col-6"><%= label_tag nil, plural_resource_name(Spree::OptionValue) %></div>
10
+ <div class="col-1">&nbsp;</div>
11
+ </div>
12
+
13
+ <div class="form-group">
14
+ <div data-controller="product-option-values">
15
+ <template data-product-option-values-target="template">
16
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: [nil, []], form: form, index: 0 %>
17
+ </template>
18
+ <% form.object.preferred_eligible_values.each.with_index do |product_option_values, index| %>
19
+ <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values:, form:, index: %>
20
+ <% end %>
21
+ <div class="mt-3" data-product-option-values-target="links">
22
+ <%= link_to t(:add_product, scope: [:solidus_promotions, :line_item_option_value_condition]), "#", class: "btn btn-outline-primary", data: { action: "click->product-option-values#add_row" } %>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ </div>
@@ -0,0 +1,21 @@
1
+ <p>
2
+ <%= condition.class.human_attribute_name(:description) %>
3
+ </p>
4
+ <div class="row">
5
+ <div class="col-12">
6
+ <div class="field products_condition_products">
7
+ <%= label_tag "#{param_prefix}_product_ids_string", t('solidus_promotions.product_condition.choose_products') %>
8
+ <%= hidden_field_tag "#{param_prefix}[product_ids_string]", condition.product_ids.join(","), class: "fullwidth", is: "product-picker" %>
9
+ </div>
10
+ </div>
11
+ <div class="col-12">
12
+ <div class="form-group">
13
+ <%= label_tag("#{param_prefix}_preferred_match_policy", condition.class.human_attribute_name(:preferred_match_policy)) %>
14
+ <%= select_tag(
15
+ "#{param_prefix}[preferred_match_policy]",
16
+ options_for_select(I18n.t("solidus_promotions.conditions.price_product.match_policies").to_a.map(&:reverse), condition.preferred_match_policy),
17
+ class: "custom-select fullwidth"
18
+ ) %>
19
+ </div>
20
+ </div>
21
+ </div>
@@ -0,0 +1,17 @@
1
+ <p>
2
+ <%= condition.class.human_attribute_name(:description) %>
3
+ </p>
4
+ <div class="field taxons_condition_taxons">
5
+ <%= label_tag "#{param_prefix}_taxon_ids_string", t("solidus_promotions.taxon_condition.choose_taxons") %>
6
+ <%= hidden_field_tag "#{param_prefix}[taxon_ids_string]", condition.taxon_ids.join(","), is: "taxon-picker", class: "fullwidth", id: "product_taxon_ids" %>
7
+ </div>
8
+ <div class="form-group">
9
+ <div class="form-group">
10
+ <%= label_tag("#{param_prefix}_preferred_match_policy", condition.class.human_attribute_name(:preferred_match_policy)) %>
11
+ <%= select_tag(
12
+ "#{param_prefix}[preferred_match_policy]",
13
+ options_for_select(I18n.t("solidus_promotions.conditions.price_taxon.match_policies").to_a.map(&:reverse), condition.preferred_match_policy),
14
+ class: "custom-select fullwidth",
15
+ ) %>
16
+ </div>
17
+ </div>
@@ -17,11 +17,4 @@
17
17
  <% select = form.select :preferred_match_policy, match_policy_options %>
18
18
  <%= form.label :preferred_match_policy, t('solidus_promotions.product_condition.label', select: select).html_safe %>
19
19
  </div>
20
-
21
- <div class="form-group">
22
- <%= form.label :preferred_line_item_applicable do %>
23
- <%= form.check_box :preferred_line_item_applicable %>
24
- <%= condition.class.human_attribute_name(:preferred_line_item_applicable) %>
25
- <% end %>
26
- </div>
27
20
  <% end %>
@@ -17,11 +17,4 @@
17
17
  <% select = form.select :preferred_match_policy, match_policy_options %>
18
18
  <%= form.label :preferred_match_policy, t('solidus_promotions.taxon_condition.label', select: select).html_safe %>
19
19
  </div>
20
-
21
- <div class="form-group">
22
- <%= form.label :preferred_line_item_applicable do %>
23
- <%= form.check_box :preferred_line_item_applicable %>
24
- <%= condition.class.human_attribute_name(:preferred_line_item_applicable) %>
25
- <% end %>
26
- </div>
27
20
  <% end %>
@@ -1,21 +1,27 @@
1
- <div class="fullwidth promo-condition-option-value d-flex align-content-stretch mb-3">
2
- <div class="mr-2" style="flex-grow: 1;">
1
+ <div class="promo-condition-option-value row">
2
+ <div class="col-5">
3
3
  <input
4
4
  is="product-picker"
5
+ id="ov-product-picker-<%= index %>"
5
6
  class="w-100"
6
7
  data-action="select2Change->product-option-values#propagate_product_id_to_value_input"
8
+ data-multiple="false"
7
9
  type="hidden"
8
10
  value="<%= product_option_values[0] %>"
9
11
  >
10
12
  </div>
11
- <div class="mr-2 ml-2" style="flex-grow: 1;">
13
+ <div class="col-6">
12
14
  <input
13
15
  is="option-value-picker"
16
+ id="option-value-picker-<%= index %>"
14
17
  class="fullwidth"
15
18
  name="<%= form.object_name %>[preferred_eligible_values][<%= product_option_values[0] %>]"
16
19
  type="hidden"
20
+ data-product-id="<%= product_option_values[0] %>"
17
21
  value="<%= product_option_values[1].join(",") %>"
18
22
  >
19
23
  </div>
20
- <a class="fa fa-trash remove p-2" data-action="click->product-option-values#remove_row"></a>
24
+ <div class="col-1">
25
+ <a class="fa fa-trash remove p-2 float-right" data-action="click->product-option-values#remove_row"></a>
26
+ </div>
21
27
  </div>
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
 
20
- spec.required_ruby_version = ">= 3.1.0"
20
+ spec.required_ruby_version = ">= 3.2.0"
21
21
 
22
22
  # Specify which files should be added to the gem when it is released.
23
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus_promotions
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.2
4
+ version: 4.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  - Solidus Team
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 1980-01-02 00:00:00.000000000 Z
12
+ date: 2026-04-15 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: csv
@@ -126,7 +127,6 @@ executables: []
126
127
  extensions: []
127
128
  extra_rdoc_files: []
128
129
  files:
129
- - ".eslintrc.json"
130
130
  - ".github/stale.yml"
131
131
  - MIGRATING.md
132
132
  - README.md
@@ -150,19 +150,26 @@ files:
150
150
  - app/javascript/backend/solidus_promotions/web_components/variant_picker.js
151
151
  - app/jobs/solidus_promotions/promotion_code_batch_job.rb
152
152
  - app/mailers/solidus_promotions/promotion_code_batch_mailer.rb
153
+ - app/models/concerns/solidus_promotions/adjustment_discounts.rb
153
154
  - app/models/concerns/solidus_promotions/benefits/line_item_benefit.rb
154
155
  - app/models/concerns/solidus_promotions/benefits/order_benefit.rb
155
156
  - app/models/concerns/solidus_promotions/benefits/shipment_benefit.rb
156
157
  - app/models/concerns/solidus_promotions/calculators/promotion_calculator.rb
157
158
  - app/models/concerns/solidus_promotions/conditions/line_item_applicable_order_level_condition.rb
158
159
  - app/models/concerns/solidus_promotions/conditions/line_item_level_condition.rb
160
+ - app/models/concerns/solidus_promotions/conditions/option_value_condition.rb
159
161
  - app/models/concerns/solidus_promotions/conditions/order_level_condition.rb
162
+ - app/models/concerns/solidus_promotions/conditions/product_condition.rb
160
163
  - app/models/concerns/solidus_promotions/conditions/shipment_level_condition.rb
164
+ - app/models/concerns/solidus_promotions/conditions/taxon_condition.rb
165
+ - app/models/concerns/solidus_promotions/coupon_code_normalizer.rb
161
166
  - app/models/concerns/solidus_promotions/discountable_amount.rb
167
+ - app/models/concerns/solidus_promotions/discounted_amount.rb
162
168
  - app/models/solidus_promotions/benefit.rb
163
169
  - app/models/solidus_promotions/benefits/adjust_line_item.rb
164
170
  - app/models/solidus_promotions/benefits/adjust_line_item_quantity_groups.rb
165
171
  - app/models/solidus_promotions/benefits/adjust_shipment.rb
172
+ - app/models/solidus_promotions/benefits/advertise_price.rb
166
173
  - app/models/solidus_promotions/benefits/create_discounted_item.rb
167
174
  - app/models/solidus_promotions/calculators/distributed_amount.rb
168
175
  - app/models/solidus_promotions/calculators/flat_rate.rb
@@ -188,6 +195,12 @@ files:
188
195
  - app/models/solidus_promotions/conditions/nth_order.rb
189
196
  - app/models/solidus_promotions/conditions/one_use_per_user.rb
190
197
  - app/models/solidus_promotions/conditions/option_value.rb
198
+ - app/models/solidus_promotions/conditions/order_option_value.rb
199
+ - app/models/solidus_promotions/conditions/order_product.rb
200
+ - app/models/solidus_promotions/conditions/order_taxon.rb
201
+ - app/models/solidus_promotions/conditions/price_option_value.rb
202
+ - app/models/solidus_promotions/conditions/price_product.rb
203
+ - app/models/solidus_promotions/conditions/price_taxon.rb
191
204
  - app/models/solidus_promotions/conditions/product.rb
192
205
  - app/models/solidus_promotions/conditions/shipping_method.rb
193
206
  - app/models/solidus_promotions/conditions/store.rb
@@ -205,9 +218,11 @@ files:
205
218
  - app/models/solidus_promotions/order_adjuster/choose_discounts.rb
206
219
  - app/models/solidus_promotions/order_adjuster/discount_order.rb
207
220
  - app/models/solidus_promotions/order_adjuster/load_promotions.rb
208
- - app/models/solidus_promotions/order_adjuster/persist_discounted_order.rb
221
+ - app/models/solidus_promotions/order_adjuster/recalculate_promo_totals.rb
222
+ - app/models/solidus_promotions/order_adjuster/set_discounts_to_zero.rb
209
223
  - app/models/solidus_promotions/order_promotion.rb
210
224
  - app/models/solidus_promotions/permission_sets/solidus_promotion_management.rb
225
+ - app/models/solidus_promotions/product_advertiser.rb
211
226
  - app/models/solidus_promotions/promotion.rb
212
227
  - app/models/solidus_promotions/promotion_advertiser.rb
213
228
  - app/models/solidus_promotions/promotion_category.rb
@@ -217,11 +232,13 @@ files:
217
232
  - app/models/solidus_promotions/promotion_finder.rb
218
233
  - app/models/solidus_promotions/promotion_handler/coupon.rb
219
234
  - app/models/solidus_promotions/promotion_handler/page.rb
235
+ - app/models/solidus_promotions/promotion_lane.rb
220
236
  - app/models/solidus_promotions/shipping_rate_discount.rb
221
237
  - app/patches/models/solidus_promotions/adjustment_patch.rb
222
238
  - app/patches/models/solidus_promotions/line_item_patch.rb
223
239
  - app/patches/models/solidus_promotions/order_patch.rb
224
240
  - app/patches/models/solidus_promotions/order_recalculator_patch.rb
241
+ - app/patches/models/solidus_promotions/price_patch.rb
225
242
  - app/patches/models/solidus_promotions/shipment_patch.rb
226
243
  - app/patches/models/solidus_promotions/shipping_rate_patch.rb
227
244
  - app/subscribers/solidus_promotions/order_promotion_subscriber.rb
@@ -251,6 +268,9 @@ files:
251
268
  - db/migrate/20231012020928_add_db_comments_to_condition_products.rb
252
269
  - db/migrate/20231012120928_add_db_comments_to_promotion_categories.rb
253
270
  - db/migrate/20231104135812_add_managed_by_order_benefit_to_line_items.rb
271
+ - db/migrate/20251104170913_update_promotion_code_value_collation.rb
272
+ - db/migrate/20251104214304_separate_out_order_only_conditions.rb
273
+ - eslint.config.mjs
254
274
  - lib/components/admin/solidus_admin/orders/show/adjustments/index/source/solidus_promotions_benefit/component.rb
255
275
  - lib/components/admin/solidus_promotions/orders/index/component.rb
256
276
  - lib/components/admin/solidus_promotions/orders/index/component.yml
@@ -288,12 +308,18 @@ files:
288
308
  - lib/solidus_promotions/testing_support/factories/solidus_promotion_factory.rb
289
309
  - lib/solidus_promotions/testing_support/factories/solidus_shipping_rate_discount_factory.rb
290
310
  - lib/solidus_promotions/testing_support/factory_bot.rb
311
+ - lib/solidus_promotions/testing_support/shared_examples.rb
312
+ - lib/solidus_promotions/testing_support/shared_examples/option_value_condition.rb
313
+ - lib/solidus_promotions/testing_support/shared_examples/product_condition.rb
314
+ - lib/solidus_promotions/testing_support/shared_examples/promotion_calculator.rb
315
+ - lib/solidus_promotions/testing_support/shared_examples/taxon_condition.rb
291
316
  - lib/tasks/solidus_promotions/migrate_adjustments.rake
292
317
  - lib/tasks/solidus_promotions/migrate_existing_promotions.rake
293
318
  - lib/tasks/solidus_promotions/migrate_order_promotions.rake
294
319
  - lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item.html.erb
295
320
  - lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item_quantity_groups.html.erb
296
321
  - lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_shipment.html.erb
322
+ - lib/views/backend/solidus_promotions/admin/benefit_fields/_advertise_price.html.erb
297
323
  - lib/views/backend/solidus_promotions/admin/benefit_fields/_calculator_fields.erb
298
324
  - lib/views/backend/solidus_promotions/admin/benefit_fields/_create_discounted_item.html.erb
299
325
  - lib/views/backend/solidus_promotions/admin/benefits/_benefit.html.erb
@@ -324,6 +350,9 @@ files:
324
350
  - lib/views/backend/solidus_promotions/admin/condition_fields/_nth_order.html.erb
325
351
  - lib/views/backend/solidus_promotions/admin/condition_fields/_one_use_per_user.html.erb
326
352
  - lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb
353
+ - lib/views/backend/solidus_promotions/admin/condition_fields/_price_option_value.html.erb
354
+ - lib/views/backend/solidus_promotions/admin/condition_fields/_price_product.html.erb
355
+ - lib/views/backend/solidus_promotions/admin/condition_fields/_price_taxon.html.erb
327
356
  - lib/views/backend/solidus_promotions/admin/condition_fields/_product.html.erb
328
357
  - lib/views/backend/solidus_promotions/admin/condition_fields/_shipping_method.html.erb
329
358
  - lib/views/backend/solidus_promotions/admin/condition_fields/_store.html.erb
@@ -370,6 +399,7 @@ licenses:
370
399
  - BSD-3-Clause
371
400
  metadata:
372
401
  homepage_uri: https://github.com/solidusio/solidus/blob/main/promotions/README.md
402
+ post_install_message:
373
403
  rdoc_options: []
374
404
  require_paths:
375
405
  - lib
@@ -377,14 +407,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
377
407
  requirements:
378
408
  - - ">="
379
409
  - !ruby/object:Gem::Version
380
- version: 3.1.0
410
+ version: 3.2.0
381
411
  required_rubygems_version: !ruby/object:Gem::Requirement
382
412
  requirements:
383
413
  - - ">="
384
414
  - !ruby/object:Gem::Version
385
415
  version: '0'
386
416
  requirements: []
387
- rubygems_version: 3.7.2
417
+ rubygems_version: 3.5.22
418
+ signing_key:
388
419
  specification_version: 4
389
420
  summary: New promotion system for Solidus
390
421
  test_files: []
data/.eslintrc.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "eslint:recommended",
3
- "parserOptions": {
4
- "ecmaVersion": 2023,
5
- "sourceType": "module"
6
- },
7
- "globals": {
8
- "Turbo": "readonly"
9
- }
10
- }
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidusPromotions
4
- class OrderAdjuster
5
- class PersistDiscountedOrder
6
- def initialize(order)
7
- @order = order
8
- end
9
-
10
- def call
11
- order.line_items.each do |line_item|
12
- update_adjustments(line_item, line_item.current_discounts)
13
- end
14
-
15
- order.shipments.each do |shipment|
16
- update_adjustments(shipment, shipment.current_discounts)
17
- end
18
-
19
- order.shipments.flat_map(&:shipping_rates).each do |shipping_rate|
20
- shipping_rate.discounts = shipping_rate.current_discounts.map do |discount|
21
- SolidusPromotions::ShippingRateDiscount.create!(
22
- shipping_rate: shipping_rate,
23
- amount: discount.amount,
24
- label: discount.label,
25
- benefit: discount.source
26
- )
27
- end
28
- end
29
- order.reset_current_discounts
30
- order
31
- end
32
-
33
- private
34
-
35
- attr_reader :order
36
-
37
- # Walk through the discounts for an item and update adjustments for it. Once
38
- # all of the discounts have been added as adjustments, remove any old promotion
39
- # adjustments that weren't touched.
40
- #
41
- # @private
42
- # @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
43
- # @param [Array<SolidusPromotions::ItemDiscount>] item_discounts a list of calculated discounts for an item
44
- # @return [void]
45
- def update_adjustments(item, item_discounts)
46
- promotion_adjustments = item.adjustments.select(&:promotion?)
47
-
48
- active_adjustments = item_discounts.map do |item_discount|
49
- update_adjustment(item, item_discount)
50
- end
51
- item.update(promo_total: active_adjustments.sum(&:amount))
52
- # Remove any promotion adjustments tied to promotion benefits which no longer match.
53
- unmatched_adjustments = promotion_adjustments - active_adjustments
54
-
55
- item.adjustments.destroy(unmatched_adjustments)
56
- end
57
-
58
- # Update or create a new promotion adjustment on an item.
59
- #
60
- # @private
61
- # @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
62
- # @param [SolidusPromotions::ItemDiscount] discount_item calculated discounts for an item
63
- # @return [Spree::Adjustment] the created or updated promotion adjustment
64
- def update_adjustment(item, discount_item)
65
- adjustment = item.adjustments.detect do |item_adjustment|
66
- item_adjustment.source == discount_item.source
67
- end
68
-
69
- adjustment ||= item.adjustments.new(
70
- source: discount_item.source,
71
- order_id: item.is_a?(Spree::Order) ? item.id : item.order_id,
72
- label: discount_item.label
73
- )
74
- adjustment.update!(amount: discount_item.amount)
75
- adjustment
76
- end
77
- end
78
- end
79
- end