solidus_promotions 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (234) hide show
  1. checksums.yaml +7 -0
  2. data/.eslintrc.json +10 -0
  3. data/.github/stale.yml +1 -0
  4. data/MIGRATING.md +184 -0
  5. data/README.md +122 -0
  6. data/Rakefile +20 -0
  7. data/app/assets/config/solidus_promotions/manifest.js +13 -0
  8. data/app/decorators/models/solidus_promotions/adjustment_decorator.rb +11 -0
  9. data/app/decorators/models/solidus_promotions/line_item_decorator.rb +27 -0
  10. data/app/decorators/models/solidus_promotions/order_decorator.rb +42 -0
  11. data/app/decorators/models/solidus_promotions/order_recalculator_decorator.rb +15 -0
  12. data/app/decorators/models/solidus_promotions/shipment_decorator.rb +14 -0
  13. data/app/decorators/models/solidus_promotions/shipping_rate_decorator.rb +29 -0
  14. data/app/helpers/solidus_promotions/admin/benefits_helper.rb +19 -0
  15. data/app/helpers/solidus_promotions/admin/conditions_helper.rb +14 -0
  16. data/app/helpers/solidus_promotions/admin/promotions_helper.rb +15 -0
  17. data/app/javascript/backend/solidus_promotions/controllers/application.js +9 -0
  18. data/app/javascript/backend/solidus_promotions/controllers/calculator_tiers_controller.js +37 -0
  19. data/app/javascript/backend/solidus_promotions/controllers/flash_controller.js +10 -0
  20. data/app/javascript/backend/solidus_promotions/controllers/index.js +8 -0
  21. data/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js +62 -0
  22. data/app/javascript/backend/solidus_promotions/web_components/number_with_currency.js +35 -0
  23. data/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js +52 -0
  24. data/app/javascript/backend/solidus_promotions/web_components/product_picker.js +7 -0
  25. data/app/javascript/backend/solidus_promotions/web_components/select_two.js +11 -0
  26. data/app/javascript/backend/solidus_promotions/web_components/taxon_picker.js +7 -0
  27. data/app/javascript/backend/solidus_promotions/web_components/user_picker.js +7 -0
  28. data/app/javascript/backend/solidus_promotions/web_components/variant_picker.js +7 -0
  29. data/app/javascript/backend/solidus_promotions.js +11 -0
  30. data/app/jobs/solidus_promotions/promotion_code_batch_job.rb +26 -0
  31. data/app/mailers/solidus_promotions/promotion_code_batch_mailer.rb +15 -0
  32. data/app/models/concerns/solidus_promotions/benefits/line_item_benefit.rb +15 -0
  33. data/app/models/concerns/solidus_promotions/benefits/order_benefit.rb +15 -0
  34. data/app/models/concerns/solidus_promotions/benefits/shipment_benefit.rb +15 -0
  35. data/app/models/concerns/solidus_promotions/calculators/promotion_calculator.rb +11 -0
  36. data/app/models/concerns/solidus_promotions/conditions/line_item_applicable_order_level_condition.rb +23 -0
  37. data/app/models/concerns/solidus_promotions/conditions/line_item_level_condition.rb +15 -0
  38. data/app/models/concerns/solidus_promotions/conditions/order_level_condition.rb +15 -0
  39. data/app/models/concerns/solidus_promotions/conditions/shipment_level_condition.rb +15 -0
  40. data/app/models/concerns/solidus_promotions/discountable_amount.rb +21 -0
  41. data/app/models/solidus_promotions/benefit.rb +123 -0
  42. data/app/models/solidus_promotions/benefits/adjust_line_item.rb +13 -0
  43. data/app/models/solidus_promotions/benefits/adjust_line_item_quantity_groups.rb +107 -0
  44. data/app/models/solidus_promotions/benefits/adjust_shipment.rb +13 -0
  45. data/app/models/solidus_promotions/benefits/create_discounted_item.rb +49 -0
  46. data/app/models/solidus_promotions/calculators/distributed_amount.rb +31 -0
  47. data/app/models/solidus_promotions/calculators/flat_rate.rb +23 -0
  48. data/app/models/solidus_promotions/calculators/flexi_rate.rb +26 -0
  49. data/app/models/solidus_promotions/calculators/percent.rb +19 -0
  50. data/app/models/solidus_promotions/calculators/tiered_flat_rate.rb +58 -0
  51. data/app/models/solidus_promotions/calculators/tiered_percent.rb +68 -0
  52. data/app/models/solidus_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +40 -0
  53. data/app/models/solidus_promotions/condition.rb +61 -0
  54. data/app/models/solidus_promotions/condition_product.rb +8 -0
  55. data/app/models/solidus_promotions/condition_store.rb +8 -0
  56. data/app/models/solidus_promotions/condition_taxon.rb +8 -0
  57. data/app/models/solidus_promotions/condition_user.rb +8 -0
  58. data/app/models/solidus_promotions/conditions/discounted_item_total.rb +22 -0
  59. data/app/models/solidus_promotions/conditions/first_order.rb +31 -0
  60. data/app/models/solidus_promotions/conditions/first_repeat_purchase_since.rb +31 -0
  61. data/app/models/solidus_promotions/conditions/item_total.rb +75 -0
  62. data/app/models/solidus_promotions/conditions/line_item_option_value.rb +37 -0
  63. data/app/models/solidus_promotions/conditions/line_item_product.rb +56 -0
  64. data/app/models/solidus_promotions/conditions/line_item_taxon.rb +59 -0
  65. data/app/models/solidus_promotions/conditions/minimum_quantity.rb +42 -0
  66. data/app/models/solidus_promotions/conditions/nth_order.rb +40 -0
  67. data/app/models/solidus_promotions/conditions/one_use_per_user.rb +25 -0
  68. data/app/models/solidus_promotions/conditions/option_value.rb +28 -0
  69. data/app/models/solidus_promotions/conditions/product.rb +85 -0
  70. data/app/models/solidus_promotions/conditions/shipping_method.rb +19 -0
  71. data/app/models/solidus_promotions/conditions/store.rb +26 -0
  72. data/app/models/solidus_promotions/conditions/taxon.rb +98 -0
  73. data/app/models/solidus_promotions/conditions/user.rb +35 -0
  74. data/app/models/solidus_promotions/conditions/user_logged_in.rb +16 -0
  75. data/app/models/solidus_promotions/conditions/user_role.rb +42 -0
  76. data/app/models/solidus_promotions/distributed_amounts_handler.rb +39 -0
  77. data/app/models/solidus_promotions/eligibility_result.rb +5 -0
  78. data/app/models/solidus_promotions/eligibility_results.rb +47 -0
  79. data/app/models/solidus_promotions/item_discount.rb +21 -0
  80. data/app/models/solidus_promotions/migration_support/order_promotion_syncer.rb +54 -0
  81. data/app/models/solidus_promotions/order_adjuster/choose_discounts.rb +21 -0
  82. data/app/models/solidus_promotions/order_adjuster/discount_order.rb +93 -0
  83. data/app/models/solidus_promotions/order_adjuster/load_promotions.rb +53 -0
  84. data/app/models/solidus_promotions/order_adjuster/persist_discounted_order.rb +79 -0
  85. data/app/models/solidus_promotions/order_adjuster.rb +33 -0
  86. data/app/models/solidus_promotions/order_promotion.rb +23 -0
  87. data/app/models/solidus_promotions/permission_sets/solidus_promotion_management.rb +15 -0
  88. data/app/models/solidus_promotions/promotion.rb +163 -0
  89. data/app/models/solidus_promotions/promotion_advertiser.rb +10 -0
  90. data/app/models/solidus_promotions/promotion_category.rb +9 -0
  91. data/app/models/solidus_promotions/promotion_code/batch_builder.rb +73 -0
  92. data/app/models/solidus_promotions/promotion_code.rb +56 -0
  93. data/app/models/solidus_promotions/promotion_code_batch.rb +25 -0
  94. data/app/models/solidus_promotions/promotion_finder.rb +10 -0
  95. data/app/models/solidus_promotions/promotion_handler/coupon.rb +113 -0
  96. data/app/models/solidus_promotions/promotion_handler/page.rb +30 -0
  97. data/app/models/solidus_promotions/shipping_rate_discount.rb +11 -0
  98. data/app/subscribers/solidus_promotions/order_promotion_subscriber.rb +20 -0
  99. data/bin/rails +13 -0
  100. data/config/importmap.rb +14 -0
  101. data/config/locales/en.yml +376 -0
  102. data/config/locales/promotion_categories.en.yml +6 -0
  103. data/config/locales/promotions.en.yml +6 -0
  104. data/config/routes.rb +38 -0
  105. data/db/migrate/20230703101637_create_promotions.rb +25 -0
  106. data/db/migrate/20230703113625_create_promotion_benefits.rb +15 -0
  107. data/db/migrate/20230703141116_create_promotion_categories.rb +14 -0
  108. data/db/migrate/20230703143943_create_promotion_conditions.rb +12 -0
  109. data/db/migrate/20230704083830_add_condition_join_tables.rb +31 -0
  110. data/db/migrate/20230704102444_create_promotion_codes.rb +11 -0
  111. data/db/migrate/20230704102656_create_promotion_code_batches.rb +34 -0
  112. data/db/migrate/20230705171556_create_order_promotions.rb +11 -0
  113. data/db/migrate/20230725074235_create_shipping_rate_discounts.rb +12 -0
  114. data/db/migrate/20231011100059_add_db_comments_to_order_promotions.rb +61 -0
  115. data/db/migrate/20231011120928_add_db_comments_to_condition_taxons.rb +54 -0
  116. data/db/migrate/20231011131324_add_db_comments_to_conditions.rb +60 -0
  117. data/db/migrate/20231011142040_add_db_comments_to_condition_users.rb +53 -0
  118. data/db/migrate/20231011155822_add_db_comments_to_promotions.rb +123 -0
  119. data/db/migrate/20231011163030_add_db_comments_to_promotion_codes.rb +60 -0
  120. data/db/migrate/20231011173312_add_db_comments_to_promotion_code_batches.rb +91 -0
  121. data/db/migrate/20231011184205_add_db_comments_to_condition_stores.rb +53 -0
  122. data/db/migrate/20231011190222_add_db_comments_to_benefits.rb +61 -0
  123. data/db/migrate/20231012020928_add_db_comments_to_condition_products.rb +52 -0
  124. data/db/migrate/20231012120928_add_db_comments_to_promotion_categories.rb +52 -0
  125. data/db/migrate/20231104135812_add_managed_by_order_benefit_to_line_items.rb +5 -0
  126. data/lib/components/admin/solidus_admin/orders/show/adjustments/index/source/solidus_promotions_benefit/component.rb +17 -0
  127. data/lib/components/admin/solidus_promotions/orders/index/component.rb +15 -0
  128. data/lib/components/admin/solidus_promotions/orders/index/component.yml +3 -0
  129. data/lib/components/admin/solidus_promotions/promotion_categories/index/component.rb +60 -0
  130. data/lib/components/admin/solidus_promotions/promotions/index/component.rb +108 -0
  131. data/lib/components/admin/solidus_promotions/promotions/index/component.yml +10 -0
  132. data/lib/controllers/admin/solidus_promotions/promotion_categories_controller.rb +29 -0
  133. data/lib/controllers/admin/solidus_promotions/promotions_controller.rb +46 -0
  134. data/lib/controllers/backend/solidus_promotions/admin/base_controller.rb +15 -0
  135. data/lib/controllers/backend/solidus_promotions/admin/benefits_controller.rb +85 -0
  136. data/lib/controllers/backend/solidus_promotions/admin/conditions_controller.rb +69 -0
  137. data/lib/controllers/backend/solidus_promotions/admin/promotion_categories_controller.rb +13 -0
  138. data/lib/controllers/backend/solidus_promotions/admin/promotion_code_batches_controller.rb +42 -0
  139. data/lib/controllers/backend/solidus_promotions/admin/promotion_codes_controller.rb +58 -0
  140. data/lib/controllers/backend/solidus_promotions/admin/promotions_controller.rb +65 -0
  141. data/lib/generators/solidus_promotions/install/install_generator.rb +56 -0
  142. data/lib/generators/solidus_promotions/install/templates/initializer.rb +6 -0
  143. data/lib/solidus_promotions/configuration.rb +125 -0
  144. data/lib/solidus_promotions/engine.rb +115 -0
  145. data/lib/solidus_promotions/migrate_adjustments.rb +62 -0
  146. data/lib/solidus_promotions/migrate_order_promotions.rb +73 -0
  147. data/lib/solidus_promotions/promotion_map.rb +126 -0
  148. data/lib/solidus_promotions/promotion_migrator.rb +103 -0
  149. data/lib/solidus_promotions/testing_support/factories/completed_order_with_solidus_promotion_factory.rb +20 -0
  150. data/lib/solidus_promotions/testing_support/factories/solidus_order_promotion_factory.rb +8 -0
  151. data/lib/solidus_promotions/testing_support/factories/solidus_promotion_category_factory.rb +7 -0
  152. data/lib/solidus_promotions/testing_support/factories/solidus_promotion_code_factory.rb +8 -0
  153. data/lib/solidus_promotions/testing_support/factories/solidus_promotion_factory.rb +65 -0
  154. data/lib/solidus_promotions/testing_support/factories/solidus_shipping_rate_discount_factory.rb +14 -0
  155. data/lib/solidus_promotions/testing_support/factory_bot.rb +30 -0
  156. data/lib/solidus_promotions.rb +37 -0
  157. data/lib/tasks/solidus_promotions/migrate_adjustments.rake +17 -0
  158. data/lib/tasks/solidus_promotions/migrate_existing_promotions.rake +12 -0
  159. data/lib/tasks/solidus_promotions/migrate_order_promotions.rake +17 -0
  160. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item.html.erb +6 -0
  161. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item_quantity_groups.html.erb +13 -0
  162. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_shipment.html.erb +6 -0
  163. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_calculator_fields.erb +11 -0
  164. data/lib/views/backend/solidus_promotions/admin/benefit_fields/_create_discounted_item.html.erb +21 -0
  165. data/lib/views/backend/solidus_promotions/admin/benefits/_benefit.html.erb +50 -0
  166. data/lib/views/backend/solidus_promotions/admin/benefits/_calculator_select.html.erb +16 -0
  167. data/lib/views/backend/solidus_promotions/admin/benefits/_form.html.erb +3 -0
  168. data/lib/views/backend/solidus_promotions/admin/benefits/_new_benefit.html.erb +33 -0
  169. data/lib/views/backend/solidus_promotions/admin/benefits/_type_select.html.erb +16 -0
  170. data/lib/views/backend/solidus_promotions/admin/benefits/edit.html.erb +19 -0
  171. data/lib/views/backend/solidus_promotions/admin/benefits/new.html.erb +1 -0
  172. data/lib/views/backend/solidus_promotions/admin/calculator_fields/_default_fields.html.erb +6 -0
  173. data/lib/views/backend/solidus_promotions/admin/calculator_fields/distributed_amount/_fields.html.erb +18 -0
  174. data/lib/views/backend/solidus_promotions/admin/calculator_fields/flat_rate/_fields.html.erb +6 -0
  175. data/lib/views/backend/solidus_promotions/admin/calculator_fields/flexi_rate/_fields.html.erb +24 -0
  176. data/lib/views/backend/solidus_promotions/admin/calculator_fields/percent/_fields.html.erb +6 -0
  177. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_flat_rate/_fields.html.erb +32 -0
  178. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_flat_rate/_tier_fields.html.erb +31 -0
  179. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_fields.html.erb +47 -0
  180. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_tier_fields.html.erb +33 -0
  181. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_fields.html.erb +36 -0
  182. data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_tier_fields.html.erb +35 -0
  183. data/lib/views/backend/solidus_promotions/admin/condition_fields/_first_order.html.erb +3 -0
  184. data/lib/views/backend/solidus_promotions/admin/condition_fields/_first_repeat_purchase_since.html.erb +7 -0
  185. data/lib/views/backend/solidus_promotions/admin/condition_fields/_item_total.html.erb +15 -0
  186. data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb +25 -0
  187. data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_product.html.erb +21 -0
  188. data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_taxon.html.erb +17 -0
  189. data/lib/views/backend/solidus_promotions/admin/condition_fields/_minimum_quantity.html.erb +5 -0
  190. data/lib/views/backend/solidus_promotions/admin/condition_fields/_nth_order.html.erb +15 -0
  191. data/lib/views/backend/solidus_promotions/admin/condition_fields/_one_use_per_user.html.erb +3 -0
  192. data/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb +63 -0
  193. data/lib/views/backend/solidus_promotions/admin/condition_fields/_product.html.erb +27 -0
  194. data/lib/views/backend/solidus_promotions/admin/condition_fields/_shipping_method.html.erb +10 -0
  195. data/lib/views/backend/solidus_promotions/admin/condition_fields/_store.html.erb +9 -0
  196. data/lib/views/backend/solidus_promotions/admin/condition_fields/_taxon.html.erb +27 -0
  197. data/lib/views/backend/solidus_promotions/admin/condition_fields/_user.html.erb +7 -0
  198. data/lib/views/backend/solidus_promotions/admin/condition_fields/_user_logged_in.html.erb +3 -0
  199. data/lib/views/backend/solidus_promotions/admin/condition_fields/_user_role.html.erb +15 -0
  200. data/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb +21 -0
  201. data/lib/views/backend/solidus_promotions/admin/conditions/_condition.html.erb +26 -0
  202. data/lib/views/backend/solidus_promotions/admin/conditions/_new_condition.html.erb +26 -0
  203. data/lib/views/backend/solidus_promotions/admin/conditions/_type_select.html.erb +20 -0
  204. data/lib/views/backend/solidus_promotions/admin/conditions/new.html.erb +1 -0
  205. data/lib/views/backend/solidus_promotions/admin/promotion_categories/_form.html.erb +14 -0
  206. data/lib/views/backend/solidus_promotions/admin/promotion_categories/edit.html.erb +10 -0
  207. data/lib/views/backend/solidus_promotions/admin/promotion_categories/index.html.erb +47 -0
  208. data/lib/views/backend/solidus_promotions/admin/promotion_categories/new.html.erb +10 -0
  209. data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/_form_fields.html.erb +22 -0
  210. data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/download.csv.ruby +8 -0
  211. data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/index.html.erb +65 -0
  212. data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/new.html.erb +8 -0
  213. data/lib/views/backend/solidus_promotions/admin/promotion_codes/index.csv.ruby +8 -0
  214. data/lib/views/backend/solidus_promotions/admin/promotion_codes/index.html.erb +32 -0
  215. data/lib/views/backend/solidus_promotions/admin/promotion_codes/new.html.erb +31 -0
  216. data/lib/views/backend/solidus_promotions/admin/promotions/_form.html.erb +124 -0
  217. data/lib/views/backend/solidus_promotions/admin/promotions/_table.html.erb +57 -0
  218. data/lib/views/backend/solidus_promotions/admin/promotions/_table_filter.html.erb +78 -0
  219. data/lib/views/backend/solidus_promotions/admin/promotions/edit.html.erb +41 -0
  220. data/lib/views/backend/solidus_promotions/admin/promotions/index.html.erb +23 -0
  221. data/lib/views/backend/solidus_promotions/admin/promotions/new.html.erb +9 -0
  222. data/lib/views/backend/solidus_promotions/admin/shared/_number_with_currency.html.erb +20 -0
  223. data/lib/views/backend/solidus_promotions/admin/shared/_promotion_sub_menu.html.erb +14 -0
  224. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_boolean.html.erb +14 -0
  225. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_decimal.html.erb +12 -0
  226. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_encrypted_string.html.erb +12 -0
  227. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_integer.html.erb +12 -0
  228. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_password.html.erb +12 -0
  229. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_string.html.erb +12 -0
  230. data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_text.html.erb +12 -0
  231. data/lib/views/backend/solidus_promotions/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
  232. data/lib/views/backend/solidus_promotions/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
  233. data/solidus_promotions.gemspec +33 -0
  234. metadata +366 -0
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ module Admin
5
+ class PromotionCategoriesController < BaseController
6
+ private
7
+
8
+ def model_class
9
+ SolidusPromotions::PromotionCategory
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ module Admin
5
+ class PromotionCodeBatchesController < BaseController
6
+ belongs_to "solidus_promotions/promotion"
7
+
8
+ create.after :build_promotion_code_batch
9
+
10
+ def download
11
+ require "csv"
12
+
13
+ @promotion_code_batch = SolidusPromotions::PromotionCodeBatch.find(
14
+ params[:promotion_code_batch_id]
15
+ )
16
+
17
+ send_data(
18
+ render_to_string,
19
+ filename: "promotion-code-batch-list-#{@promotion_code_batch.id}.csv"
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ def build_promotion_code_batch
26
+ @promotion_code_batch.process
27
+ end
28
+
29
+ def model_class
30
+ SolidusPromotions::PromotionCodeBatch
31
+ end
32
+
33
+ def collection
34
+ parent.code_batches
35
+ end
36
+
37
+ def build_resource
38
+ parent.code_batches.build
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+
5
+ module SolidusPromotions
6
+ module Admin
7
+ class PromotionCodesController < BaseController
8
+ before_action :load_promotion
9
+
10
+ def index
11
+ @promotion_codes = @promotion.codes.order(:value)
12
+
13
+ respond_to do |format|
14
+ format.html do
15
+ @promotion_codes = @promotion_codes.page(params[:page]).per(50)
16
+ end
17
+ format.csv do
18
+ filename = "promotion-code-list-#{@promotion.id}.csv"
19
+ headers["Content-Type"] = "text/csv"
20
+ headers["Content-disposition"] = "attachment; filename=\"#{filename}\""
21
+ end
22
+ end
23
+ end
24
+
25
+ def new
26
+ if @promotion.apply_automatically
27
+ flash[:error] = t(
28
+ :disallowed_with_apply_automatically,
29
+ scope: "activerecord.errors.models.solidus_promotions/promotion_code.attributes.base"
30
+ )
31
+ redirect_to solidus_promotions.admin_promotion_promotion_codes_url(@promotion)
32
+ else
33
+ @promotion_code = @promotion.codes.build
34
+ end
35
+ end
36
+
37
+ def create
38
+ @promotion_code = @promotion.codes.build(value: params[:promotion_code][:value])
39
+
40
+ if @promotion_code.save
41
+ flash[:success] = flash_message_for(@promotion_code, :successfully_created)
42
+ redirect_to solidus_promotions.admin_promotion_promotion_codes_url(@promotion)
43
+ else
44
+ flash.now[:error] = @promotion_code.errors.full_messages.to_sentence
45
+ render_after_create_error
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def load_promotion
52
+ @promotion = SolidusPromotions::Promotion
53
+ .accessible_by(current_ability, :show)
54
+ .find(params[:promotion_id])
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ module Admin
5
+ class PromotionsController < BaseController
6
+ before_action :load_data
7
+
8
+ helper "solidus_promotions/admin/conditions"
9
+ helper "solidus_promotions/admin/benefits"
10
+ helper "solidus_promotions/admin/promotions"
11
+
12
+ def create
13
+ @promotion = model_class.new(permitted_resource_params)
14
+ @promotion.codes.new(value: params[:single_code]) if params[:single_code].present?
15
+
16
+ if params[:code_batch]
17
+ @code_batch = @promotion.code_batches.new(code_batch_params)
18
+ end
19
+
20
+ if @promotion.save
21
+ @code_batch&.process
22
+ flash[:success] = t("solidus_promotions.promotion_successfully_created")
23
+ redirect_to location_after_save
24
+ else
25
+ flash[:error] = @promotion.errors.full_messages.to_sentence
26
+ render action: "new"
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def collection
33
+ return @collection if @collection
34
+
35
+ params[:q] ||= HashWithIndifferentAccess.new
36
+ params[:q][:s] ||= "updated_at desc"
37
+
38
+ @collection = super
39
+ @search = @collection.ransack(params[:q])
40
+ @collection = @search.result(distinct: true)
41
+ .includes(promotion_includes)
42
+ .page(params[:page])
43
+ .per(params[:per_page] || SolidusPromotions.config.promotions_per_page)
44
+
45
+ @collection
46
+ end
47
+
48
+ def promotion_includes
49
+ [:benefits]
50
+ end
51
+
52
+ def model_class
53
+ SolidusPromotions::Promotion
54
+ end
55
+
56
+ def load_data
57
+ @promotion_categories = PromotionCategory.order(:name)
58
+ end
59
+
60
+ def location_after_save
61
+ solidus_promotions.edit_admin_promotion_url(@promotion)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ class_option :auto_run_migrations, type: :boolean, default: false
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ def copy_initializer
14
+ template "initializer.rb", "config/initializers/solidus_promotions.rb"
15
+ end
16
+
17
+ def add_migrations
18
+ run "bin/rails railties:install:migrations FROM=solidus_promotions"
19
+ end
20
+
21
+ def modify_spree_initializer
22
+ spree_rb_path = "config/initializers/spree.rb"
23
+ new_content = <<-RUBY.gsub(/^ {8}/, "")
24
+ # Make sure we use Spree::SimpleOrderContents
25
+ # Spree::Config.order_contents_class = "Spree::SimpleOrderContents"
26
+ # Set the promotion configuration to ours
27
+ # Spree::Config.promotions = SolidusPromotions.configuration
28
+ RUBY
29
+ insert_into_file spree_rb_path, new_content, after: "Spree.config do |config|\n"
30
+ end
31
+
32
+ def mount_engine
33
+ inject_into_file "config/routes.rb",
34
+ " mount SolidusPromotions::Engine => '/'\n",
35
+ before: / mount Spree::Core::Engine.*/,
36
+ verbose: true
37
+ end
38
+
39
+ def run_migrations
40
+ run_migrations = options[:auto_run_migrations] || ["", "y", "Y"].include?(ask("Would you like to run the migrations now? [Y/n]"))
41
+ if run_migrations
42
+ run "bin/rails db:migrate"
43
+ else
44
+ puts "Skipping bin/rails db:migrate, don't forget to run it!"
45
+ end
46
+ end
47
+
48
+ def explain_promotion_config
49
+ say "SolidusPromotions is now installed. You can configure it by editing the initializer at `config/initializers/solidus_promotions.rb`."
50
+ say "By default, it is not activated. In order to activate it, you need to set `Spree::Config.promotions` to `SolidusPromotions.configuration`" \
51
+ "in your `config/initializers/spree.rb` file."
52
+ say "If you have been running the legacy promotion system, we recommend converting your existing promotions using the `solidus_promotions:migrate_existing_promotions` rake task."
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SolidusPromotions.configure do |config|
4
+ # # Add your own configuration here. See https://github.com/solidusio/solidus/blob/main/promotions/lib/solidus_promotions/configuration.rb
5
+ # # to learn more about the available options.
6
+ # end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spree/core/environment_extension"
4
+
5
+ module SolidusPromotions
6
+ class Configuration < Spree::Preferences::Configuration
7
+ include Spree::Core::EnvironmentExtension
8
+
9
+ class_name_attribute :order_adjuster_class, default: "SolidusPromotions::OrderAdjuster"
10
+
11
+ class_name_attribute :coupon_code_handler_class, default: "SolidusPromotions::PromotionHandler::Coupon"
12
+
13
+ class_name_attribute :promotion_finder_class, default: "SolidusPromotions::PromotionFinder"
14
+
15
+ # Allows providing a different promotion advertiser.
16
+ # @!attribute [rw] advertiser_class
17
+ # @see Spree::NullPromotionAdvertiser
18
+ # @return [Class] an object that conforms to the API of
19
+ # the standard promotion advertiser class
20
+ # Spree::NullPromotionAdvertiser.
21
+ class_name_attribute :advertiser_class, default: "SolidusPromotions::PromotionAdvertiser"
22
+
23
+ # In case solidus_legacy_promotions is loaded, we need to define this.
24
+ class_name_attribute :shipping_promotion_handler_class, default: "Spree::NullPromotionHandler"
25
+
26
+ add_class_set :order_conditions, default: [
27
+ "SolidusPromotions::Conditions::FirstOrder",
28
+ "SolidusPromotions::Conditions::FirstRepeatPurchaseSince",
29
+ "SolidusPromotions::Conditions::ItemTotal",
30
+ "SolidusPromotions::Conditions::DiscountedItemTotal",
31
+ "SolidusPromotions::Conditions::MinimumQuantity",
32
+ "SolidusPromotions::Conditions::NthOrder",
33
+ "SolidusPromotions::Conditions::OneUsePerUser",
34
+ "SolidusPromotions::Conditions::OptionValue",
35
+ "SolidusPromotions::Conditions::Product",
36
+ "SolidusPromotions::Conditions::Store",
37
+ "SolidusPromotions::Conditions::Taxon",
38
+ "SolidusPromotions::Conditions::UserLoggedIn",
39
+ "SolidusPromotions::Conditions::UserRole",
40
+ "SolidusPromotions::Conditions::User"
41
+ ]
42
+
43
+ add_class_set :line_item_conditions, default: [
44
+ "SolidusPromotions::Conditions::LineItemOptionValue",
45
+ "SolidusPromotions::Conditions::LineItemProduct",
46
+ "SolidusPromotions::Conditions::LineItemTaxon"
47
+ ]
48
+ add_class_set :shipment_conditions, default: [
49
+ "SolidusPromotions::Conditions::ShippingMethod"
50
+ ]
51
+
52
+ add_class_set :benefits, default: [
53
+ "SolidusPromotions::Benefits::AdjustLineItem",
54
+ "SolidusPromotions::Benefits::AdjustLineItemQuantityGroups",
55
+ "SolidusPromotions::Benefits::AdjustShipment",
56
+ "SolidusPromotions::Benefits::CreateDiscountedItem"
57
+ ]
58
+
59
+ add_nested_class_set :promotion_calculators, default: {
60
+ "SolidusPromotions::Benefits::AdjustShipment" => [
61
+ "SolidusPromotions::Calculators::FlatRate",
62
+ "SolidusPromotions::Calculators::FlexiRate",
63
+ "SolidusPromotions::Calculators::Percent",
64
+ "SolidusPromotions::Calculators::TieredFlatRate",
65
+ "SolidusPromotions::Calculators::TieredPercent",
66
+ "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
67
+ ],
68
+ "SolidusPromotions::Benefits::AdjustLineItem" => [
69
+ "SolidusPromotions::Calculators::DistributedAmount",
70
+ "SolidusPromotions::Calculators::FlatRate",
71
+ "SolidusPromotions::Calculators::FlexiRate",
72
+ "SolidusPromotions::Calculators::Percent",
73
+ "SolidusPromotions::Calculators::TieredFlatRate",
74
+ "SolidusPromotions::Calculators::TieredPercent",
75
+ "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
76
+ ],
77
+ "SolidusPromotions::Benefits::AdjustLineItemQuantityGroups" => [
78
+ "SolidusPromotions::Calculators::FlatRate",
79
+ "SolidusPromotions::Calculators::Percent",
80
+ "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
81
+ ],
82
+ "SolidusPromotions::Benefits::CreateDiscountedItem" => [
83
+ "SolidusPromotions::Calculators::FlatRate",
84
+ "SolidusPromotions::Calculators::Percent",
85
+ "SolidusPromotions::Calculators::TieredPercentOnEligibleItemQuantity"
86
+ ]
87
+ }
88
+
89
+ class_name_attribute :discount_chooser_class, default: "SolidusPromotions::OrderAdjuster::ChooseDiscounts"
90
+ class_name_attribute :promotion_code_batch_mailer_class,
91
+ default: "SolidusPromotions::PromotionCodeBatchMailer"
92
+
93
+ # @!attribute [rw] promotions_per_page
94
+ # @return [Integer] Promotions to show per-page in the admin (default: +25+)
95
+ preference :promotions_per_page, :integer, default: 25
96
+
97
+ preference :lanes, :hash, default: {
98
+ pre: 0,
99
+ default: 1,
100
+ post: 2
101
+ }
102
+
103
+ preference :recalculate_complete_orders, :boolean, default: true
104
+
105
+ preference :sync_order_promotions, :boolean, default: false
106
+
107
+ preference :use_new_admin, :boolean, default: false
108
+
109
+ def use_new_admin?
110
+ SolidusSupport.admin_available? && preferred_use_new_admin
111
+ end
112
+ end
113
+
114
+ class << self
115
+ def configuration
116
+ @configuration ||= Configuration.new
117
+ end
118
+
119
+ alias_method :config, :configuration
120
+
121
+ def configure
122
+ yield configuration
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "solidus_core"
4
+ require "solidus_support"
5
+
6
+ module SolidusPromotions
7
+ class Engine < Rails::Engine
8
+ include SolidusSupport::EngineExtensions
9
+
10
+ isolate_namespace ::SolidusPromotions
11
+
12
+ engine_name "solidus_promotions"
13
+
14
+ # use rspec for tests
15
+ config.generators do |g|
16
+ g.test_framework :rspec
17
+ end
18
+
19
+ initializer "solidus_promotions.assets" do |app|
20
+ if SolidusSupport.backend_available?
21
+ app.config.assets.precompile << "solidus_promotions/manifest.js"
22
+ end
23
+ end
24
+
25
+ initializer "solidus_promotions.importmap" do |app|
26
+ if SolidusSupport.backend_available?
27
+ SolidusPromotions.importmap.draw(Engine.root.join("config", "importmap.rb"))
28
+
29
+ package_path = Engine.root.join("app/javascript")
30
+ app.config.assets.paths << package_path
31
+
32
+ if app.config.importmap.sweep_cache
33
+ SolidusPromotions.importmap.cache_sweeper(watches: package_path)
34
+ ActiveSupport.on_load(:action_controller_base) do
35
+ before_action { SolidusPromotions.importmap.cache_sweeper.execute_if_updated }
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ initializer "solidus_promotions.spree_config", after: "spree.load_config_initializers" do
42
+ Spree::Config.adjustment_promotion_source_types << "SolidusPromotions::Benefit"
43
+
44
+ Rails.application.config.to_prepare do
45
+ Spree::Order.line_item_comparison_hooks << :free_from_order_benefit?
46
+ end
47
+ end
48
+
49
+ initializer "solidus_promotions.core.pub_sub", after: "spree.core.pub_sub" do |app|
50
+ app.reloader.to_prepare do
51
+ SolidusPromotions::OrderPromotionSubscriber.new.subscribe_to(Spree::Bus)
52
+ end
53
+ end
54
+
55
+ initializer "solidus_promotions.add_admin_order_index_component", after: "solidus_legacy_promotions.add_admin_order_index_component" do
56
+ if SolidusSupport.admin_available?
57
+ SolidusAdmin::Config.components["orders/index"] = "SolidusPromotions::Orders::Index::Component"
58
+ SolidusAdmin::Config.components["promotions/index"] = "SolidusPromotions::Promotions::Index::Component"
59
+ SolidusAdmin::Config.components["promotion_categories/index"] = "SolidusPromotions::PromotionCategories::Index::Component"
60
+ end
61
+ end
62
+
63
+ initializer "solidus_promotions.add_backend_menus", after: "spree.backend.environment" do
64
+ if SolidusSupport.backend_available?
65
+ # Replace the promotions menu from core with ours
66
+ Spree::Backend::Config.configure do |config|
67
+ config.menu_items = config.menu_items.flat_map do |item|
68
+ next item unless item.label.to_sym == :promotions
69
+
70
+ [
71
+ Spree::BackendConfiguration::MenuItem.new(
72
+ label: :promotions,
73
+ icon: config.admin_updated_navbar ? "ri-megaphone-line" : "bullhorn",
74
+ condition: -> { can?(:admin, SolidusPromotions::Promotion) },
75
+ url: -> { SolidusPromotions::Engine.routes.url_helpers.admin_promotions_path },
76
+ data_hook: :admin_solidus_promotion_sub_tabs,
77
+ children: [
78
+ Spree::BackendConfiguration::MenuItem.new(
79
+ label: :promotions,
80
+ url: -> { SolidusPromotions::Engine.routes.url_helpers.admin_promotions_path },
81
+ condition: -> { can?(:admin, SolidusPromotions::Promotion) }
82
+ ),
83
+ Spree::BackendConfiguration::MenuItem.new(
84
+ label: :promotion_categories,
85
+ url: -> { SolidusPromotions::Engine.routes.url_helpers.admin_promotion_categories_path },
86
+ condition: -> { can?(:admin, SolidusPromotions::PromotionCategory) }
87
+ )
88
+ ]
89
+ ),
90
+ Spree::BackendConfiguration::MenuItem.new(
91
+ label: :legacy_promotions,
92
+ icon: config.admin_updated_navbar ? "ri-megaphone-line" : "bullhorn",
93
+ condition: -> { can?(:admin, SolidusPromotions::Promotion) },
94
+ url: -> { Spree::Core::Engine.routes.url_helpers.admin_promotions_path },
95
+ data_hook: :admin_promotion_sub_tabs,
96
+ children: [
97
+ Spree::BackendConfiguration::MenuItem.new(
98
+ label: :legacy_promotions,
99
+ condition: -> { can?(:admin, Spree::Promotion && Spree::Promotion.any?) },
100
+ url: -> { Spree::Core::Engine.routes.url_helpers.admin_promotions_path },
101
+ ),
102
+ Spree::BackendConfiguration::MenuItem.new(
103
+ label: :legacy_promotion_categories,
104
+ condition: -> { can?(:admin, Spree::PromotionCategory && Spree::Promotion.any?) },
105
+ url: -> { Spree::Core::Engine.routes.url_helpers.admin_promotion_categories_path },
106
+ )
107
+ ]
108
+ )
109
+ ]
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ class MigrateAdjustments
5
+ class << self
6
+ def up
7
+ sql = if ActiveRecord::Base.connection_db_config.adapter == "mysql2"
8
+ <<~SQL
9
+ UPDATE spree_adjustments
10
+ INNER JOIN spree_promotion_actions ON spree_adjustments.source_id = spree_promotion_actions.id and spree_adjustments.source_type = 'Spree::PromotionAction'
11
+ INNER JOIN solidus_promotions_benefits ON solidus_promotions_benefits.original_promotion_action_id = spree_promotion_actions.id
12
+ SET source_id = solidus_promotions_benefits.id,
13
+ source_type = 'SolidusPromotions::Benefit'
14
+ SQL
15
+ else
16
+ <<~SQL
17
+ UPDATE spree_adjustments
18
+ SET source_id = solidus_promotions_benefits.id,
19
+ source_type = 'SolidusPromotions::Benefit'
20
+ FROM spree_promotion_actions
21
+ INNER JOIN solidus_promotions_benefits ON solidus_promotions_benefits.original_promotion_action_id = spree_promotion_actions.id
22
+ WHERE spree_adjustments.source_id = spree_promotion_actions.id and spree_adjustments.source_type = 'Spree::PromotionAction'
23
+ SQL
24
+ end
25
+
26
+ execute(sql)
27
+ end
28
+
29
+ def down
30
+ sql = if ActiveRecord::Base.connection_db_config.adapter == "mysql2"
31
+ <<~SQL
32
+ UPDATE spree_adjustments
33
+ INNER JOIN solidus_promotions_benefits
34
+ INNER JOIN spree_promotion_actions ON spree_adjustments.source_id = solidus_promotions_benefits.id and spree_adjustments.source_type = 'SolidusPromotions::Benefit'
35
+ SET source_id = spree_promotion_actions.id,
36
+ source_type = 'Spree::PromotionAction'
37
+ WHERE solidus_promotions_benefits.original_promotion_action_id = spree_promotion_actions.id
38
+ SQL
39
+ else
40
+ <<~SQL
41
+ UPDATE spree_adjustments
42
+ SET source_id = spree_promotion_actions.id,
43
+ source_type = 'Spree::PromotionAction'
44
+ FROM spree_promotion_actions
45
+ INNER JOIN solidus_promotions_benefits ON solidus_promotions_benefits.original_promotion_action_id = spree_promotion_actions.id
46
+ WHERE spree_adjustments.source_id = solidus_promotions_benefits.id and spree_adjustments.source_type = 'SolidusPromotions::Benefit'
47
+ SQL
48
+ end
49
+
50
+ execute(sql)
51
+ end
52
+
53
+ private
54
+
55
+ def execute(sql)
56
+ Spree::Adjustment.transaction do
57
+ ActiveRecord::Base.connection.execute(sql)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPromotions
4
+ class MigrateOrderPromotions
5
+ class << self
6
+ def up
7
+ sql = <<~SQL
8
+ INSERT INTO solidus_promotions_order_promotions (
9
+ order_id,
10
+ promotion_id,
11
+ promotion_code_id,
12
+ created_at,
13
+ updated_at
14
+ )
15
+ SELECT
16
+ spree_orders_promotions.order_id AS order_id,
17
+ solidus_promotions_promotions.id AS promotion_id,
18
+ solidus_promotions_promotion_codes.id AS promotion_code_id,
19
+ spree_orders_promotions.created_at,
20
+ spree_orders_promotions.updated_at
21
+ FROM spree_orders_promotions
22
+ INNER JOIN spree_promotions ON spree_orders_promotions.promotion_id = spree_promotions.id
23
+ INNER JOIN solidus_promotions_promotions ON spree_promotions.id = solidus_promotions_promotions.original_promotion_id
24
+ LEFT OUTER JOIN spree_promotion_codes ON spree_orders_promotions.promotion_code_id = spree_promotion_codes.id
25
+ LEFT OUTER JOIN solidus_promotions_promotion_codes ON spree_promotion_codes.value = solidus_promotions_promotion_codes.value
26
+ WHERE NOT EXISTS (
27
+ SELECT NULL
28
+ FROM solidus_promotions_order_promotions
29
+ WHERE solidus_promotions_order_promotions.order_id = order_id
30
+ AND (solidus_promotions_order_promotions.promotion_code_id = promotion_code_id OR promotion_code_id IS NULL)
31
+ AND solidus_promotions_order_promotions.promotion_id = promotion_id
32
+ );
33
+ SQL
34
+ ActiveRecord::Base.connection.execute(sql)
35
+
36
+ Spree::OrderPromotion.delete_all
37
+ end
38
+
39
+ def down
40
+ sql = <<~SQL
41
+ INSERT INTO spree_orders_promotions (
42
+ order_id,
43
+ promotion_id,
44
+ promotion_code_id,
45
+ created_at,
46
+ updated_at
47
+ )
48
+ SELECT
49
+ solidus_promotions_order_promotions.order_id AS order_id,
50
+ spree_promotions.id AS promotion_id,
51
+ spree_promotion_codes.id AS promotion_code_id,
52
+ solidus_promotions_order_promotions.created_at,
53
+ solidus_promotions_order_promotions.updated_at
54
+ FROM solidus_promotions_order_promotions
55
+ INNER JOIN solidus_promotions_promotions ON solidus_promotions_order_promotions.promotion_id = solidus_promotions_promotions.id
56
+ INNER JOIN spree_promotions ON spree_promotions.id = solidus_promotions_promotions.original_promotion_id
57
+ LEFT OUTER JOIN solidus_promotions_promotion_codes ON solidus_promotions_order_promotions.promotion_code_id = solidus_promotions_promotion_codes.id
58
+ LEFT OUTER JOIN spree_promotion_codes ON spree_promotion_codes.value = solidus_promotions_promotion_codes.value
59
+ WHERE NOT EXISTS (
60
+ SELECT NULL
61
+ FROM spree_orders_promotions
62
+ WHERE spree_orders_promotions.order_id = order_id
63
+ AND (spree_orders_promotions.promotion_code_id = promotion_code_id OR promotion_code_id IS NULL)
64
+ AND spree_orders_promotions.promotion_id = promotion_id
65
+ );
66
+ SQL
67
+ ActiveRecord::Base.connection.execute(sql)
68
+
69
+ SolidusPromotions::OrderPromotion.delete_all
70
+ end
71
+ end
72
+ end
73
+ end