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
@@ -27,6 +27,7 @@ en:
27
27
  line_item: "%{promotion} (%{promotion_customer_label})"
28
28
  shipment: "%{promotion} (%{promotion_customer_label})"
29
29
  shipping_rate: "%{promotion} (%{promotion_customer_label})"
30
+ price: "%{promotion} (%{promotion_customer_label})"
30
31
  adjustment_type: Adjustment type
31
32
  add_benefit: Add Benefit
32
33
  add_condition: Add Condition
@@ -59,6 +60,18 @@ en:
59
60
  match_policies:
60
61
  include: Line item's product is one of the chosen products
61
62
  exclude: Line item's product is not one of the chosen products
63
+ price_product:
64
+ match_policies:
65
+ include: Product is one of the chosen products
66
+ exclude: Product is not one of the chosen products
67
+ price_taxon:
68
+ match_policies:
69
+ include: Product has one of the chosen taxons
70
+ exclude: Product does not have one of the chosen taxons
71
+ price_option_value:
72
+ match_policies:
73
+ include: Variant has one of the chosen option value(s)
74
+ exclude: Variant does not have one of the chosen option value(s)
62
75
  line_item_taxon:
63
76
  match_policies:
64
77
  include: Line item's product has one of the chosen taxons
@@ -104,7 +117,7 @@ en:
104
117
  limit_once_per_user: This coupon code can only be used once per user.
105
118
  solidus_promotions/conditions/option_value:
106
119
  solidus_promotions/conditions/line_item_option_value:
107
- solidus_promotions/conditions/product:
120
+ solidus_promotions/conditions/order_product:
108
121
  has_excluded_product: Your cart contains a product that prevents this coupon code from being applied.
109
122
  missing_product: This coupon code can't be applied because you don't have all of the necessary products in your cart.
110
123
  no_applicable_products: You need to add an applicable product before applying this coupon code.
@@ -112,7 +125,16 @@ en:
112
125
  has_excluded_product: Your cart contains a product that prevents this coupon code from being applied.
113
126
  missing_product: This coupon code can't be applied because you don't have all of the necessary products in your cart.
114
127
  no_applicable_products: You need to add an applicable product before applying this coupon code.
115
- solidus_promotions/conditions/taxon:
128
+ solidus_promotions/conditions/price_product:
129
+ has_excluded_product: Price belongs to an excluded product.
130
+ no_applicable_products: Promotion is not applicable to this product.
131
+ solidus_promotions/conditions/price_taxon:
132
+ has_excluded_product: Price's product is excluded.
133
+ no_applicable_products: Promotion is not applicable to this product.
134
+ solidus_promotions/conditions/price_option_value:
135
+ has_excluded_product: Price matches option value(s).
136
+ no_applicable_products: Promotion is not applicable to this product.
137
+ solidus_promotions/conditions/order_taxon:
116
138
  has_excluded_taxon: Your cart contains a product from an excluded category that prevents this coupon code from being applied.
117
139
  missing_taxon: You need to add a product from all applicable categories before applying this coupon code.
118
140
  no_matching_taxons: You need to add a product from an applicable category before applying this coupon code.
@@ -191,10 +213,12 @@ en:
191
213
  models:
192
214
  solidus_promotions/benefits/adjust_shipment: Discount matching shipments
193
215
  solidus_promotions/benefits/adjust_line_item: Discount matching line items
216
+ solidus_promotions/benefits/advertise_price: Advertise discounted prices
194
217
  solidus_promotions/benefits/create_discounted_item: Create discounted line item
195
218
  solidus_promotions/benefits/adjust_line_item_quantity_groups: Discount matching line items based on quantity groups
196
219
  solidus_promotions/calculators/distributed_amount: Distributed Amount
197
220
  solidus_promotions/calculators/percent: Percent
221
+ solidus_promotions/calculators/percent_with_cap: Distributed Percent with cap
198
222
  solidus_promotions/calculators/flat_rate: Flat Rate
199
223
  solidus_promotions/calculators/flexi_rate: Flexible Rate
200
224
  solidus_promotions/calculators/tiered_flat_rate: Tiered Flat Rate
@@ -208,15 +232,21 @@ en:
208
232
  solidus_promotions/conditions/minimum_quantity: Minimum Quantity
209
233
  solidus_promotions/conditions/nth_order: Nth Order
210
234
  solidus_promotions/conditions/one_use_per_user: One Use Per User
211
- solidus_promotions/conditions/option_value: Option Value(s)
212
- solidus_promotions/conditions/line_item_option_value: Line Item Option Value(s)
213
- solidus_promotions/conditions/product: Order Product(s)
214
- solidus_promotions/conditions/line_item_product: Line Item Product(s)
215
- solidus_promotions/conditions/taxon: Order Taxon(s)
216
- solidus_promotions/conditions/line_item_taxon: Line Item Taxon(s)
235
+ solidus_promotions/conditions/order_option_value: Order matches selected option values
236
+ solidus_promotions/conditions/option_value: Order matches selected option values, restricts line items
237
+ solidus_promotions/conditions/line_item_option_value: Line Item variant matches option values
238
+ solidus_promotions/conditions/order_product: Order contains selected products
239
+ solidus_promotions/conditions/product: Order contains selected products, restricts line items
240
+ solidus_promotions/conditions/line_item_product: Line item has selected product
241
+ solidus_promotions/conditions/order_taxon: Order contains product with taxons
242
+ solidus_promotions/conditions/taxon: Order contains products with taxons, restricts matching line items
243
+ solidus_promotions/conditions/line_item_taxon: Line item has product with taxons
217
244
  solidus_promotions/conditions/user: User
218
245
  solidus_promotions/conditions/user_logged_in: User Logged In
219
246
  solidus_promotions/conditions/user_role: User Role(s)
247
+ solidus_promotions/conditions/price_product: Price matches specified product(s)
248
+ solidus_promotions/conditions/price_taxon: Price matches specified taxon(s)
249
+ solidus_promotions/conditions/price_option_value: Price matches specified option value(s)
220
250
  solidus_promotions/promotion_category:
221
251
  one: Promotion Category
222
252
  other: Promotion Categories
@@ -262,7 +292,6 @@ en:
262
292
  description: Only one use per user
263
293
  solidus_promotions/conditions/option_value:
264
294
  description: Order includes specified product(s) with matching option value(s)
265
- preferred_line_item_applicable: Should also apply to line items
266
295
  solidus_promotions/conditions/line_item_option_value:
267
296
  description: Line Item has specified product with matching option value
268
297
  solidus_promotions/conditions/minimum_quantity:
@@ -270,15 +299,22 @@ en:
270
299
  solidus_promotions/conditions/product:
271
300
  description: Order includes specified product(s)
272
301
  line_item_level_description: 'Line item matches the specified products:'
273
- preferred_line_item_applicable: Should also apply to line items
274
302
  solidus_promotions/conditions/line_item_product:
275
303
  description: Line item matches specified product(s)
276
304
  preferred_match_policy: Match Policy
305
+ solidus_promotions/conditions/price_product:
306
+ description: Price matches specified product(s)
307
+ preferred_match_policy: Match Policy
308
+ solidus_promotions/conditions/price_taxon:
309
+ description: Price matches specified taxon(s)
310
+ preferred_match_policy: Match Policy
311
+ solidus_promotions/conditions/price_option_value:
312
+ description: Price matches specified option value(s)
313
+ preferred_match_policy: Match Policy
277
314
  solidus_promotions/conditions/store:
278
315
  description: Available only to the specified stores
279
316
  solidus_promotions/conditions/taxon:
280
317
  description: Order includes products with specified taxon(s)
281
- preferred_line_item_applicable: Should also apply to line items
282
318
  solidus_promotions/conditions/line_item_taxon:
283
319
  description: Line Item has product with specified taxon(s)
284
320
  preferred_match_policy: Match Policy
data/config/routes.rb CHANGED
@@ -29,7 +29,7 @@ SolidusPromotions::Engine.routes.draw do
29
29
  end
30
30
  resources :promotion_codes, only: [:index, :new, :create]
31
31
  resources :promotion_code_batches, only: [:index, :new, :create] do
32
- get "/download", to: "promotion_code_batches#download", defaults: { format: "csv" }
32
+ get "/download", to: "promotion_code_batches#download", defaults: {format: "csv"}
33
33
  end
34
34
  end
35
35
  end
@@ -1,6 +1,6 @@
1
1
  class CreatePromotions < ActiveRecord::Migration[7.0]
2
2
  def change
3
- promotion_foreign_key = table_exists?(:spree_promotions) ? { to_table: :spree_promotions } : false
3
+ promotion_foreign_key = table_exists?(:spree_promotions) ? {to_table: :spree_promotions} : false
4
4
 
5
5
  create_table :solidus_promotions_promotions do |t|
6
6
  t.string :description
@@ -15,7 +15,7 @@ class CreatePromotions < ActiveRecord::Migration[7.0]
15
15
  t.integer :lane, null: false, default: 1
16
16
  t.string :customer_label
17
17
  t.datetime :deleted_at
18
- t.references :original_promotion, type: :integer, index: { name: :index_original_promotion_id }, foreign_key: promotion_foreign_key
18
+ t.references :original_promotion, type: :integer, index: {name: :index_original_promotion_id}, foreign_key: promotion_foreign_key
19
19
 
20
20
  t.timestamps
21
21
  end
@@ -1,12 +1,12 @@
1
1
  class CreatePromotionBenefits < ActiveRecord::Migration[7.0]
2
2
  def change
3
- promotion_action_foreign_key = table_exists?(:spree_promotion_actions) ? { to_table: :spree_promotion_actions } : false
3
+ promotion_action_foreign_key = table_exists?(:spree_promotion_actions) ? {to_table: :spree_promotion_actions} : false
4
4
 
5
5
  create_table :solidus_promotions_benefits do |t|
6
- t.references :promotion, index: true, null: false, foreign_key: { to_table: :solidus_promotions_promotions }
6
+ t.references :promotion, index: true, null: false, foreign_key: {to_table: :solidus_promotions_promotions}
7
7
  t.string :type
8
8
  t.text :preferences
9
- t.references :original_promotion_action, type: :integer, index: { name: :index_original_promotion_action_id }, foreign_key: promotion_action_foreign_key
9
+ t.references :original_promotion_action, type: :integer, index: {name: :index_original_promotion_action_id}, foreign_key: promotion_action_foreign_key
10
10
  t.index [:id, :type], name: :index_solidus_promotions_benefits_on_id_and_type
11
11
 
12
12
  t.timestamps
@@ -9,6 +9,6 @@ class CreatePromotionCategories < ActiveRecord::Migration[7.0]
9
9
 
10
10
  add_reference :solidus_promotions_promotions,
11
11
  :promotion_category,
12
- foreign_key: { to_table: :solidus_promotions_promotion_categories }
12
+ foreign_key: {to_table: :solidus_promotions_promotion_categories}
13
13
  end
14
14
  end
@@ -2,7 +2,7 @@ class CreatePromotionConditions < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :solidus_promotions_conditions do |t|
4
4
  t.references :benefit,
5
- foreign_key: { to_table: :solidus_promotions_benefits }
5
+ foreign_key: {to_table: :solidus_promotions_benefits}
6
6
  t.string :type
7
7
  t.text :preferences
8
8
 
@@ -1,29 +1,29 @@
1
1
  class AddConditionJoinTables < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :solidus_promotions_condition_products, force: :cascade do |t|
4
- t.references :product, type: :integer, index: true, null: false, foreign_key: { to_table: :spree_products }
5
- t.references :condition, index: true, null: false, foreign_key: { to_table: :solidus_promotions_conditions }
4
+ t.references :product, type: :integer, index: true, null: false, foreign_key: {to_table: :spree_products}
5
+ t.references :condition, index: true, null: false, foreign_key: {to_table: :solidus_promotions_conditions}
6
6
 
7
7
  t.timestamps
8
8
  end
9
9
 
10
10
  create_table :solidus_promotions_condition_taxons, force: :cascade do |t|
11
- t.references :taxon, type: :integer, index: true, null: false, foreign_key: { to_table: :spree_taxons }
12
- t.references :condition, index: true, null: false, foreign_key: { to_table: :solidus_promotions_conditions }
11
+ t.references :taxon, type: :integer, index: true, null: false, foreign_key: {to_table: :spree_taxons}
12
+ t.references :condition, index: true, null: false, foreign_key: {to_table: :solidus_promotions_conditions}
13
13
 
14
14
  t.timestamps
15
15
  end
16
16
 
17
17
  create_table :solidus_promotions_condition_users, force: :cascade do |t|
18
- t.references :user, type: :integer, index: true, null: false, foreign_key: { to_table: Spree.user_class.table_name }
19
- t.references :condition, index: true, null: false, foreign_key: { to_table: :solidus_promotions_conditions }
18
+ t.references :user, type: :integer, index: true, null: false, foreign_key: {to_table: Spree.user_class.table_name}
19
+ t.references :condition, index: true, null: false, foreign_key: {to_table: :solidus_promotions_conditions}
20
20
 
21
21
  t.timestamps
22
22
  end
23
23
 
24
24
  create_table :solidus_promotions_condition_stores do |t|
25
- t.references :store, type: :integer, index: true, null: false, foreign_key: { to_table: :spree_stores }
26
- t.references :condition, index: true, null: false, foreign_key: { to_table: :solidus_promotions_conditions }
25
+ t.references :store, type: :integer, index: true, null: false, foreign_key: {to_table: :spree_stores}
26
+ t.references :condition, index: true, null: false, foreign_key: {to_table: :solidus_promotions_conditions}
27
27
 
28
28
  t.timestamps
29
29
  end
@@ -1,7 +1,7 @@
1
1
  class CreatePromotionCodes < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :solidus_promotions_promotion_codes, force: :cascade do |t|
4
- t.references :promotion, null: false, index: true, foreign_key: { to_table: :solidus_promotions_promotions }
4
+ t.references :promotion, null: false, index: true, foreign_key: {to_table: :solidus_promotions_promotions}
5
5
  t.string :value, null: false
6
6
  t.timestamps
7
7
 
@@ -3,7 +3,7 @@
3
3
  class CreatePromotionCodeBatches < ActiveRecord::Migration[7.0]
4
4
  def change
5
5
  create_table :solidus_promotions_promotion_code_batches do |t|
6
- t.references :promotion, null: false, index: true, foreign_key: { to_table: :solidus_promotions_promotions }
6
+ t.references :promotion, null: false, index: true, foreign_key: {to_table: :solidus_promotions_promotions}
7
7
  t.string :base_code, null: false
8
8
  t.integer :number_of_codes, null: false
9
9
  t.string :join_characters, null: false, default: "_"
@@ -1,9 +1,9 @@
1
1
  class CreateOrderPromotions < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :solidus_promotions_order_promotions do |t|
4
- t.references :order, type: :integer, index: true, null: false, foreign_key: { to_table: :spree_orders }
5
- t.references :promotion, index: true, null: false, foreign_key: { to_table: :solidus_promotions_promotions }
6
- t.references :promotion_code, index: true, null: true, foreign_key: { to_table: :solidus_promotions_promotion_codes }
4
+ t.references :order, type: :integer, index: true, null: false, foreign_key: {to_table: :spree_orders}
5
+ t.references :promotion, index: true, null: false, foreign_key: {to_table: :solidus_promotions_promotions}
6
+ t.references :promotion_code, index: true, null: true, foreign_key: {to_table: :solidus_promotions_promotion_codes}
7
7
 
8
8
  t.timestamps
9
9
  end
@@ -1,8 +1,8 @@
1
1
  class CreateShippingRateDiscounts < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :solidus_promotions_shipping_rate_discounts do |t|
4
- t.references :benefit, type: :bigint, null: false, foreign_key: { to_table: :solidus_promotions_benefits }, index: { name: "index_shipping_rate_discounts_on_benefit_id" }
5
- t.references :shipping_rate, type: :integer, null: false, foreign_key: { to_table: :spree_shipping_rates }, index: { name: "index_shipping_rate_discounts_on_shipping_rate_id" }
4
+ t.references :benefit, type: :bigint, null: false, foreign_key: {to_table: :solidus_promotions_benefits}, index: {name: "index_shipping_rate_discounts_on_benefit_id"}
5
+ t.references :shipping_rate, type: :integer, null: false, foreign_key: {to_table: :spree_shipping_rates}, index: {name: "index_shipping_rate_discounts_on_shipping_rate_id"}
6
6
  t.decimal :amount, precision: 10, scale: 2, null: false
7
7
  t.string :label, null: false
8
8
 
@@ -1,5 +1,5 @@
1
1
  class AddManagedByOrderBenefitToLineItems < ActiveRecord::Migration[7.0]
2
2
  def change
3
- add_reference :spree_line_items, :managed_by_order_benefit, foreign_key: { to_table: :solidus_promotions_benefits, null: true }
3
+ add_reference :spree_line_items, :managed_by_order_benefit, foreign_key: {to_table: :solidus_promotions_benefits, null: true}
4
4
  end
5
5
  end
@@ -0,0 +1,38 @@
1
+ class UpdatePromotionCodeValueCollation < ActiveRecord::Migration[7.0]
2
+ def up
3
+ return unless mysql?
4
+
5
+ collation = use_accent_sensitive_collation? ? "utf8mb4_0900_as_cs" : "utf8mb4_bin"
6
+ change_column :solidus_promotions_promotion_codes, :value, :string,
7
+ collation: collation
8
+ end
9
+
10
+ def down
11
+ return unless mysql?
12
+
13
+ change_column :solidus_promotions_promotion_codes, :value, :string,
14
+ collation: "utf8mb4_general_ci"
15
+ end
16
+
17
+ private
18
+
19
+ def mysql?
20
+ ActiveRecord::Base.connection.adapter_name.downcase.include?("mysql")
21
+ end
22
+
23
+ def use_accent_sensitive_collation?
24
+ !mariadb? && mysql_version >= 8.0
25
+ end
26
+
27
+ def mariadb?
28
+ version_string.include?("mariadb")
29
+ end
30
+
31
+ def mysql_version
32
+ version_string.to_f
33
+ end
34
+
35
+ def version_string
36
+ @version_string ||= ActiveRecord::Base.connection.select_value("SELECT VERSION()").downcase
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SeparateOutOrderOnlyConditions < ActiveRecord::Migration[7.0]
4
+ def up
5
+ order_only_conditions = SolidusPromotions::Condition.all.select do |condition|
6
+ condition.respond_to?(:preferred_line_item_applicable) && condition.preferred_line_item_applicable == false
7
+ end
8
+
9
+ order_only_conditions.each do |condition|
10
+ case condition
11
+ when SolidusPromotions::Conditions::Product
12
+ condition.type = "SolidusPromotions::Conditions::OrderProduct"
13
+ condition.preferences.delete(:preferred_line_item_applicable)
14
+ condition.save!
15
+ when SolidusPromotions::Conditions::Taxon
16
+ condition.type = "SolidusPromotions::Conditions::OrderTaxon"
17
+ condition.preferences.delete(:preferred_line_item_applicable)
18
+ condition.save!
19
+ when SolidusPromotions::Conditions::OptionValue
20
+ condition.type = "SolidusPromotions::Conditions::OrderOptionValue"
21
+ condition.preferences.delete(:preferred_line_item_applicable)
22
+ condition.save!
23
+ else
24
+ raise NotImplementedError, <<~MSG
25
+ Please create a new condition that is only applicable to orders for #{condition.class}
26
+ and update the corresponding record on the #{condition.benefit.promotion.name} Promotion.
27
+ MSG
28
+ end
29
+ end
30
+
31
+ SolidusPromotions::Condition.all.select do |condition|
32
+ condition.preferences[:preferred_line_item_applicable]
33
+ end.each do |condition|
34
+ condition.preferences.delete(:preferred_line_item_applicable)
35
+ end
36
+ end
37
+
38
+ def down
39
+ raise ActiveRecord::IrreversibleMigration
40
+ end
41
+ end
data/eslint.config.mjs ADDED
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from "eslint/config";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import js from "@eslint/js";
5
+ import { FlatCompat } from "@eslint/eslintrc";
6
+ import rootConfig from "../eslint.config.mjs";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const compat = new FlatCompat({
11
+ baseDirectory: __dirname,
12
+ recommendedConfig: js.configs.recommended,
13
+ allConfig: js.configs.all
14
+ });
15
+
16
+ export default defineConfig([
17
+ {
18
+ extends: [compat.extends("eslint:recommended"), rootConfig],
19
+
20
+ languageOptions: {
21
+ globals: {
22
+ Turbo: "readonly",
23
+ },
24
+
25
+ ecmaVersion: 2023,
26
+ sourceType: "module",
27
+ },
28
+ }
29
+ ]);
@@ -6,7 +6,7 @@ class SolidusPromotions::PromotionCategories::Index::Component < SolidusAdmin::U
6
6
  end
7
7
 
8
8
  def title
9
- t('solidus_promotions.promotion_categories.title')
9
+ t("solidus_promotions.promotion_categories.title")
10
10
  end
11
11
 
12
12
  def edit_path(record)
@@ -22,7 +22,7 @@ class SolidusPromotions::PromotionCategories::Index::Component < SolidusAdmin::U
22
22
  tag: :a,
23
23
  text: t(".add"),
24
24
  href: solidus_promotions.new_promotion_category_path(**search_filter_params),
25
- data: { turbo_frame: :resource_form },
25
+ data: {turbo_frame: :resource_form},
26
26
  icon: "add-line"
27
27
  )
28
28
  end
@@ -50,8 +50,8 @@ class SolidusPromotions::PromotionCategories::Index::Component < SolidusAdmin::U
50
50
  header: :name,
51
51
  data: ->(record) do
52
52
  link_to record.name, edit_path(record),
53
- data: { turbo_frame: :resource_form },
54
- class: 'body-link'
53
+ data: {turbo_frame: :resource_form},
54
+ class: "body-link"
55
55
  end
56
56
  }
57
57
  end
@@ -61,8 +61,8 @@ class SolidusPromotions::PromotionCategories::Index::Component < SolidusAdmin::U
61
61
  header: :code,
62
62
  data: ->(record) do
63
63
  link_to record.code, edit_path(record),
64
- data: { turbo_frame: :resource_form },
65
- class: 'body-link'
64
+ data: {turbo_frame: :resource_form},
65
+ class: "body-link"
66
66
  end
67
67
  }
68
68
  end
@@ -39,11 +39,11 @@ class SolidusPromotions::Promotions::Index::Component < SolidusAdmin::UI::Pages:
39
39
 
40
40
  def scopes
41
41
  [
42
- { name: :active, label: t(".scopes.active"), default: true },
43
- { name: :draft, label: t(".scopes.draft") },
44
- { name: :future, label: t(".scopes.future") },
45
- { name: :expired, label: t(".scopes.expired") },
46
- { name: :all, label: t(".scopes.all") }
42
+ {name: :active, label: t(".scopes.active"), default: true},
43
+ {name: :draft, label: t(".scopes.draft")},
44
+ {name: :future, label: t(".scopes.future")},
45
+ {name: :expired, label: t(".scopes.expired")},
46
+ {name: :all, label: t(".scopes.all")}
47
47
  ]
48
48
  end
49
49
 
@@ -10,6 +10,15 @@ module SolidusPromotions
10
10
 
11
11
  class_name_attribute :coupon_code_handler_class, default: "SolidusPromotions::PromotionHandler::Coupon"
12
12
 
13
+ # The class used to normalize coupon codes before saving or lookup.
14
+ # By default, this normalizes codes to lowercase for case-insensitive matching.
15
+ # You can customize this by creating your own normalizer class or by overriding
16
+ # the existing SolidusPromotions::CouponCodeNormalizer class using a decorator.
17
+ # @!attribute [rw] coupon_code_normalizer_class
18
+ # @return [String] The class used to normalize coupon codes.
19
+ # Defaults to "SolidusPromotions::CouponCodeNormalizer".
20
+ class_name_attribute :coupon_code_normalizer_class, default: "SolidusPromotions::CouponCodeNormalizer"
21
+
13
22
  class_name_attribute :promotion_finder_class, default: "SolidusPromotions::PromotionFinder"
14
23
 
15
24
  # Allows providing a different promotion advertiser.
@@ -23,7 +32,37 @@ module SolidusPromotions
23
32
  # In case solidus_legacy_promotions is loaded, we need to define this.
24
33
  class_name_attribute :shipping_promotion_handler_class, default: "Spree::NullPromotionHandler"
25
34
 
26
- add_class_set :order_conditions, default: [
35
+ def order_conditions
36
+ SolidusPromotions::Condition.applicable_to([Spree::Order])
37
+ end
38
+ deprecate order_conditions: :conditions, deprecator: Spree.deprecator
39
+
40
+ def order_conditions=(conditions)
41
+ conditions.each { self.conditions << _1 }
42
+ end
43
+ deprecate :order_conditions= => :conditions=, :deprecator => Spree.deprecator
44
+
45
+ def line_item_conditions
46
+ SolidusPromotions::Condition.applicable_to([Spree::LineItem])
47
+ end
48
+ deprecate line_item_conditions: :conditions, deprecator: Spree.deprecator
49
+
50
+ def line_item_conditions=(conditions)
51
+ conditions.each { self.conditions << _1 }
52
+ end
53
+ deprecate :line_item_conditions= => :conditions=, :deprecator => Spree.deprecator
54
+
55
+ def shipment_conditions
56
+ SolidusPromotions::Condition.applicable_to([Spree::Shipment])
57
+ end
58
+ deprecate shipment_conditions: :conditions, deprecator: Spree.deprecator
59
+
60
+ def shipment_conditions=(conditions)
61
+ conditions.each { self.conditions << _1 }
62
+ end
63
+ deprecate :shipment_conditions= => :conditions=, :deprecator => Spree.deprecator
64
+
65
+ add_class_set :conditions, default: [
27
66
  "SolidusPromotions::Conditions::FirstOrder",
28
67
  "SolidusPromotions::Conditions::FirstRepeatPurchaseSince",
29
68
  "SolidusPromotions::Conditions::ItemTotal",
@@ -32,34 +71,35 @@ module SolidusPromotions
32
71
  "SolidusPromotions::Conditions::NthOrder",
33
72
  "SolidusPromotions::Conditions::OneUsePerUser",
34
73
  "SolidusPromotions::Conditions::OptionValue",
74
+ "SolidusPromotions::Conditions::OrderOptionValue",
75
+ "SolidusPromotions::Conditions::OrderProduct",
35
76
  "SolidusPromotions::Conditions::Product",
36
77
  "SolidusPromotions::Conditions::Store",
37
78
  "SolidusPromotions::Conditions::Taxon",
79
+ "SolidusPromotions::Conditions::OrderTaxon",
38
80
  "SolidusPromotions::Conditions::UserLoggedIn",
39
81
  "SolidusPromotions::Conditions::UserRole",
40
- "SolidusPromotions::Conditions::User"
41
- ]
42
-
43
- add_class_set :line_item_conditions, default: [
82
+ "SolidusPromotions::Conditions::User",
44
83
  "SolidusPromotions::Conditions::LineItemOptionValue",
45
84
  "SolidusPromotions::Conditions::LineItemProduct",
46
- "SolidusPromotions::Conditions::LineItemTaxon"
47
- ]
48
- add_class_set :shipment_conditions, default: [
49
- "SolidusPromotions::Conditions::ShippingMethod"
85
+ "SolidusPromotions::Conditions::LineItemTaxon",
86
+ "SolidusPromotions::Conditions::ShippingMethod",
87
+ "SolidusPromotions::Conditions::PriceProduct",
88
+ "SolidusPromotions::Conditions::PriceTaxon",
89
+ "SolidusPromotions::Conditions::PriceOptionValue"
50
90
  ]
51
91
 
52
92
  add_class_set :benefits, default: [
53
93
  "SolidusPromotions::Benefits::AdjustLineItem",
54
94
  "SolidusPromotions::Benefits::AdjustLineItemQuantityGroups",
55
95
  "SolidusPromotions::Benefits::AdjustShipment",
56
- "SolidusPromotions::Benefits::CreateDiscountedItem"
96
+ "SolidusPromotions::Benefits::CreateDiscountedItem",
97
+ "SolidusPromotions::Benefits::AdvertisePrice"
57
98
  ]
58
99
 
59
100
  add_nested_class_set :promotion_calculators, default: {
60
101
  "SolidusPromotions::Benefits::AdjustShipment" => [
61
102
  "SolidusPromotions::Calculators::FlatRate",
62
- "SolidusPromotions::Calculators::FlexiRate",
63
103
  "SolidusPromotions::Calculators::Percent",
64
104
  "SolidusPromotions::Calculators::TieredFlatRate",
65
105
  "SolidusPromotions::Calculators::TieredPercent",
@@ -70,6 +110,7 @@ module SolidusPromotions
70
110
  "SolidusPromotions::Calculators::FlatRate",
71
111
  "SolidusPromotions::Calculators::FlexiRate",
72
112
  "SolidusPromotions::Calculators::Percent",
113
+ "SolidusPromotions::Calculators::PercentWithCap",
73
114
  "SolidusPromotions::Calculators::TieredFlatRate",
74
115
  "SolidusPromotions::Calculators::TieredPercent",
75
116
  "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
@@ -83,6 +124,11 @@ module SolidusPromotions
83
124
  "SolidusPromotions::Calculators::FlatRate",
84
125
  "SolidusPromotions::Calculators::Percent",
85
126
  "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
127
+ ],
128
+ "SolidusPromotions::Benefits::AdvertisePrice" => [
129
+ "SolidusPromotions::Calculators::Percent",
130
+ "SolidusPromotions::Calculators::FlatRate",
131
+ "SolidusPromotions::Calculators::FlexiRate"
86
132
  ]
87
133
  }
88
134
 
@@ -105,7 +151,6 @@ module SolidusPromotions
105
151
  preference :sync_order_promotions, :boolean, default: false
106
152
 
107
153
  preference :use_new_admin, :boolean, default: false
108
-
109
154
  def use_new_admin?
110
155
  SolidusSupport.admin_available? && preferred_use_new_admin
111
156
  end
@@ -61,9 +61,9 @@ module SolidusPromotions
61
61
  actions: {
62
62
  Spree::Promotion::Actions::CreateAdjustment => ->(old_action) {
63
63
  calculator = case old_action.calculator
64
- when Spree::Calculator::FlatRate
64
+ when Spree::Calculator::FlatRate
65
65
  SolidusPromotions::Calculators::DistributedAmount.new(preferences: old_action.calculator.preferences)
66
- when Spree::Calculator::FlatPercentItemTotal
66
+ when Spree::Calculator::FlatPercentItemTotal
67
67
  SolidusPromotions::Calculators::Percent.new(preferred_percent: old_action.calculator.preferred_flat_percent)
68
68
  end
69
69
 
@@ -74,17 +74,17 @@ module SolidusPromotions
74
74
  Spree::Promotion::Actions::CreateItemAdjustments => ->(old_action) {
75
75
  preferences = old_action.calculator.preferences
76
76
  calculator = case old_action.calculator
77
- when Spree::Calculator::FlatRate
77
+ when Spree::Calculator::FlatRate
78
78
  SolidusPromotions::Calculators::FlatRate.new(preferences: preferences)
79
- when Spree::Calculator::PercentOnLineItem
79
+ when Spree::Calculator::PercentOnLineItem
80
80
  SolidusPromotions::Calculators::Percent.new(preferences: preferences)
81
- when Spree::Calculator::FlexiRate
81
+ when Spree::Calculator::FlexiRate
82
82
  SolidusPromotions::Calculators::FlexiRate.new(preferences: preferences)
83
- when Spree::Calculator::DistributedAmount
83
+ when Spree::Calculator::DistributedAmount
84
84
  SolidusPromotions::Calculators::DistributedAmount.new(preferences: preferences)
85
- when Spree::Calculator::TieredFlatRate
85
+ when Spree::Calculator::TieredFlatRate
86
86
  SolidusPromotions::Calculators::TieredFlatRate.new(preferences: preferences)
87
- when Spree::Calculator::TieredPercent
87
+ when Spree::Calculator::TieredPercent
88
88
  SolidusPromotions::Calculators::TieredPercent.new(preferences: preferences)
89
89
  end
90
90
 
@@ -95,17 +95,17 @@ module SolidusPromotions
95
95
  Spree::Promotion::Actions::CreateQuantityAdjustments => ->(old_action) {
96
96
  preferences = old_action.calculator.preferences
97
97
  calculator = case old_action.calculator
98
- when Spree::Calculator::FlatRate
98
+ when Spree::Calculator::FlatRate
99
99
  SolidusPromotions::Calculators::FlatRate.new(preferences: preferences)
100
- when Spree::Calculator::PercentOnLineItem
100
+ when Spree::Calculator::PercentOnLineItem
101
101
  SolidusPromotions::Calculators::Percent.new(preferences: preferences)
102
- when Spree::Calculator::FlexiRate
102
+ when Spree::Calculator::FlexiRate
103
103
  SolidusPromotions::Calculators::FlexiRate.new(preferences: preferences)
104
- when Spree::Calculator::DistributedAmount
104
+ when Spree::Calculator::DistributedAmount
105
105
  SolidusPromotions::Calculators::DistributedAmount.new(preferences: preferences)
106
- when Spree::Calculator::TieredFlatRate
106
+ when Spree::Calculator::TieredFlatRate
107
107
  SolidusPromotions::Calculators::TieredFlatRate.new(preferences: preferences)
108
- when Spree::Calculator::TieredPercent
108
+ when Spree::Calculator::TieredPercent
109
109
  SolidusPromotions::Calculators::TieredPercent.new(preferences: preferences)
110
110
  end
111
111