solidus_core 3.0.0 → 4.5.1

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 (444) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/Rakefile +7 -2
  4. data/app/assets/images/logo/solidus.svg +18 -1
  5. data/app/assets/images/logo/solidus_logo.png +0 -0
  6. data/app/controllers/spree/base_controller.rb +1 -3
  7. data/app/helpers/spree/base_helper.rb +3 -3
  8. data/app/helpers/spree/core/controller_helpers/auth.rb +66 -0
  9. data/app/helpers/spree/core/controller_helpers/common.rb +82 -0
  10. data/app/helpers/spree/core/controller_helpers/order.rb +86 -0
  11. data/app/helpers/spree/core/controller_helpers/payment_parameters.rb +165 -0
  12. data/{lib/spree/core/controller_helpers/current_host.rb → app/helpers/spree/core/controller_helpers/pricing.rb} +6 -4
  13. data/app/helpers/spree/core/controller_helpers/search.rb +16 -0
  14. data/app/helpers/spree/core/controller_helpers/store.rb +19 -0
  15. data/app/helpers/spree/core/controller_helpers/strong_parameters.rb +74 -0
  16. data/app/helpers/spree/products_helper.rb +2 -2
  17. data/app/mailers/spree/base_mailer.rb +1 -1
  18. data/app/mailers/spree/carton_mailer.rb +1 -1
  19. data/app/mailers/spree/order_mailer.rb +3 -3
  20. data/app/mailers/spree/reimbursement_mailer.rb +1 -1
  21. data/app/models/concerns/spree/active_storage_adapter/attachment.rb +30 -11
  22. data/app/models/concerns/spree/active_storage_adapter/normalization.rb +1 -1
  23. data/app/models/concerns/spree/active_storage_adapter.rb +1 -18
  24. data/app/models/concerns/spree/default_price.rb +30 -10
  25. data/app/models/concerns/spree/display_money.rb +1 -1
  26. data/app/models/concerns/spree/metadata.rb +64 -0
  27. data/app/models/concerns/spree/named_type.rb +2 -0
  28. data/app/models/concerns/spree/ordered_property_value_list.rb +2 -2
  29. data/app/models/concerns/spree/ransackable_attributes.rb +9 -5
  30. data/app/models/concerns/spree/user_address_book.rb +19 -10
  31. data/app/models/concerns/spree/user_methods.rb +40 -6
  32. data/app/models/spree/address.rb +11 -9
  33. data/app/models/spree/adjustment.rb +15 -76
  34. data/app/models/spree/adjustment_reason.rb +2 -0
  35. data/app/models/spree/calculator/flat_fee.rb +21 -0
  36. data/app/models/spree/calculator/returns/default_refund_amount.rb +1 -1
  37. data/app/models/spree/carton.rb +3 -3
  38. data/app/models/spree/core/state_machines/inventory_unit.rb +42 -0
  39. data/app/models/spree/core/state_machines/order/class_methods.rb +217 -0
  40. data/app/models/spree/core/state_machines/order.rb +42 -0
  41. data/app/models/spree/core/state_machines/payment.rb +61 -0
  42. data/app/models/spree/core/state_machines/reimbursement.rb +33 -0
  43. data/app/models/spree/core/state_machines/return_authorization.rb +32 -0
  44. data/app/models/spree/core/state_machines/return_item/acceptance_status.rb +51 -0
  45. data/app/models/spree/core/state_machines/return_item/reception_status.rb +42 -0
  46. data/app/models/spree/core/state_machines/shipment.rb +58 -0
  47. data/app/models/spree/country.rb +1 -1
  48. data/app/models/spree/credit_card.rb +13 -10
  49. data/app/models/spree/customer_return.rb +6 -3
  50. data/app/models/spree/deprecated_configurable_class.rb +40 -0
  51. data/app/models/spree/fulfilment_changer.rb +56 -29
  52. data/app/models/spree/image/active_storage_attachment.rb +2 -7
  53. data/app/models/spree/image/paperclip_attachment.rb +2 -2
  54. data/app/models/spree/inventory_unit.rb +2 -2
  55. data/app/models/spree/item_total.rb +28 -0
  56. data/app/models/spree/legacy_user.rb +1 -0
  57. data/app/models/spree/line_item.rb +26 -8
  58. data/app/models/spree/log_entry.rb +98 -1
  59. data/app/models/spree/money.rb +120 -0
  60. data/app/models/spree/null_promotion_adjuster.rb +13 -0
  61. data/app/models/spree/null_promotion_advertiser.rb +9 -0
  62. data/app/models/spree/null_promotion_finder.rb +9 -0
  63. data/app/models/spree/null_promotion_handler.rb +44 -0
  64. data/app/models/spree/option_type.rb +1 -1
  65. data/app/models/spree/option_value.rb +4 -3
  66. data/app/models/spree/order/number_generator.rb +7 -1
  67. data/app/models/spree/order.rb +143 -96
  68. data/app/models/spree/order_cancellations.rb +8 -8
  69. data/app/models/spree/order_inventory.rb +7 -5
  70. data/app/models/spree/order_merger.rb +5 -7
  71. data/app/models/spree/order_mutex.rb +2 -2
  72. data/app/models/spree/order_shipping.rb +15 -19
  73. data/app/models/spree/order_taxation.rb +7 -4
  74. data/app/models/spree/order_update_attributes.rb +3 -1
  75. data/app/models/spree/order_updater.rb +25 -63
  76. data/app/models/spree/payment/processing.rb +60 -57
  77. data/app/models/spree/payment.rb +14 -27
  78. data/app/models/spree/payment_create.rb +1 -1
  79. data/app/models/spree/payment_method/bogus_credit_card.rb +18 -14
  80. data/app/models/spree/payment_method/credit_card.rb +0 -4
  81. data/app/models/spree/payment_method/simple_bogus_credit_card.rb +18 -4
  82. data/app/models/spree/payment_method/store_credit.rb +1 -1
  83. data/app/models/spree/payment_method.rb +29 -10
  84. data/app/models/spree/payment_source.rb +5 -1
  85. data/app/models/spree/permission_set.rb +11 -0
  86. data/app/models/spree/permission_sets/base.rb +45 -0
  87. data/app/models/spree/permission_sets/configuration_display.rb +53 -0
  88. data/app/models/spree/permission_sets/configuration_management.rb +52 -0
  89. data/app/models/spree/permission_sets/dashboard_display.rb +28 -0
  90. data/app/models/spree/permission_sets/default_customer.rb +83 -0
  91. data/app/models/spree/permission_sets/order_display.rb +50 -0
  92. data/app/models/spree/permission_sets/order_management.rb +50 -0
  93. data/app/models/spree/permission_sets/product_display.rb +43 -0
  94. data/app/models/spree/permission_sets/product_management.rb +47 -0
  95. data/app/models/spree/permission_sets/restricted_stock_display.rb +33 -0
  96. data/app/models/spree/permission_sets/restricted_stock_management.rb +33 -0
  97. data/app/models/spree/permission_sets/stock_display.rb +26 -0
  98. data/app/models/spree/permission_sets/stock_management.rb +26 -0
  99. data/app/models/spree/permission_sets/super_user.rb +26 -0
  100. data/app/models/spree/permission_sets/user_display.rb +27 -0
  101. data/app/models/spree/permission_sets/user_management.rb +44 -0
  102. data/app/models/spree/preference.rb +1 -1
  103. data/app/models/spree/price.rb +3 -3
  104. data/app/models/spree/product/scopes.rb +23 -10
  105. data/app/models/spree/product.rb +30 -23
  106. data/app/models/spree/product_property.rb +1 -1
  107. data/app/models/spree/property.rb +1 -1
  108. data/app/models/spree/refund.rb +13 -3
  109. data/app/models/spree/refund_reason.rb +6 -1
  110. data/app/models/spree/reimbursement.rb +6 -6
  111. data/app/models/spree/reimbursement_performer.rb +3 -3
  112. data/app/models/spree/reimbursement_tax_calculator.rb +3 -3
  113. data/app/models/spree/reimbursement_type/credit.rb +1 -1
  114. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +6 -6
  115. data/app/models/spree/reimbursement_type/store_credit.rb +1 -1
  116. data/app/models/spree/reimbursement_type.rb +6 -1
  117. data/app/models/spree/return_authorization.rb +3 -1
  118. data/app/models/spree/return_item.rb +13 -15
  119. data/app/models/spree/return_reason.rb +6 -1
  120. data/app/models/spree/role.rb +3 -1
  121. data/app/models/spree/role_permission.rb +8 -0
  122. data/app/models/spree/shipment.rb +12 -10
  123. data/app/models/spree/shipping_category.rb +2 -0
  124. data/app/models/spree/shipping_rate.rb +2 -3
  125. data/app/models/spree/shipping_rate_tax.rb +1 -1
  126. data/app/models/spree/{order_contents.rb → simple_order_contents.rb} +10 -13
  127. data/app/models/spree/state.rb +1 -1
  128. data/app/models/spree/stock/allocator/on_hand_first.rb +2 -2
  129. data/app/models/spree/stock/availability.rb +11 -3
  130. data/app/models/spree/stock/estimator.rb +1 -1
  131. data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
  132. data/app/models/spree/stock/package.rb +2 -2
  133. data/app/models/spree/stock/quantifier.rb +12 -8
  134. data/app/models/spree/stock/simple_coordinator.rb +38 -28
  135. data/app/models/spree/stock/splitter/shipping_category.rb +1 -1
  136. data/app/models/spree/stock_item.rb +2 -1
  137. data/app/models/spree/stock_location.rb +7 -7
  138. data/app/models/spree/stock_movement.rb +2 -2
  139. data/app/models/spree/store.rb +3 -1
  140. data/app/models/spree/store_credit.rb +31 -16
  141. data/app/models/spree/store_credit_event.rb +4 -3
  142. data/app/models/spree/store_credit_prioritizer.rb +17 -0
  143. data/app/models/spree/store_credit_reason.rb +6 -1
  144. data/app/models/spree/store_selector/by_server_name.rb +1 -1
  145. data/app/models/spree/tax/item_tax.rb +3 -2
  146. data/app/models/spree/tax/order_tax.rb +3 -1
  147. data/app/models/spree/tax/tax_helpers.rb +14 -3
  148. data/app/models/spree/tax/tax_location.rb +4 -7
  149. data/app/models/spree/tax_calculator/default.rb +32 -1
  150. data/app/models/spree/tax_calculator/shipping_rate.rb +2 -2
  151. data/app/models/spree/tax_category.rb +3 -1
  152. data/app/models/spree/tax_rate.rb +14 -1
  153. data/app/models/spree/taxon/active_storage_attachment.rb +2 -2
  154. data/app/models/spree/taxon/paperclip_attachment.rb +4 -4
  155. data/app/models/spree/taxon.rb +25 -3
  156. data/app/models/spree/taxon_brand_selector.rb +22 -0
  157. data/app/models/spree/taxonomy.rb +4 -3
  158. data/app/models/spree/unauthorized_redirect_handler.rb +24 -0
  159. data/app/models/spree/unit_cancel.rb +1 -2
  160. data/app/models/spree/user_address.rb +9 -3
  161. data/app/models/spree/user_last_url_storer/rules/authentication_rule.rb +1 -1
  162. data/app/models/spree/variant/price_selector.rb +25 -8
  163. data/app/models/spree/variant/scopes.rb +4 -0
  164. data/app/models/spree/variant/vat_price_generator.rb +1 -1
  165. data/app/models/spree/variant.rb +64 -37
  166. data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +4 -4
  167. data/app/models/spree/wallet.rb +2 -2
  168. data/app/models/spree/zone.rb +1 -1
  169. data/app/subscribers/spree/order_mailer_subscriber.rb +35 -0
  170. data/app/views/layouts/spree/base_mailer.html.erb +2 -2
  171. data/app/views/spree/order_mailer/cancel_email.html.erb +1 -1
  172. data/app/views/spree/order_mailer/cancel_email.text.erb +1 -1
  173. data/app/views/spree/order_mailer/confirm_email.html.erb +5 -5
  174. data/app/views/spree/order_mailer/confirm_email.text.erb +5 -5
  175. data/app/views/spree/order_mailer/inventory_cancellation_email.html.erb +0 -1
  176. data/config/i18n-tasks.yml +134 -0
  177. data/config/locales/en.yml +439 -338
  178. data/db/default/spree/permission_sets.rb +10 -0
  179. data/db/default/spree/return_reasons.rb +3 -1
  180. data/db/default/spree/states.rb +2 -2
  181. data/db/migrate/20160101010000_solidus_one_four.rb +0 -127
  182. data/db/migrate/20180710170104_create_spree_store_credit_reasons_table.rb +2 -2
  183. data/db/migrate/20201127212108_add_type_before_removal_to_spree_payment_methods.rb +7 -0
  184. data/db/migrate/20210312061050_change_column_null_on_prices.rb +7 -0
  185. data/db/migrate/20210815004823_add_unique_index_to_option_values_variants.rb +16 -0
  186. data/db/migrate/20220419170826_remove_archived_user_addresses.rb +12 -0
  187. data/db/migrate/20220805202442_add_level_to_spree_tax_rates.rb +5 -0
  188. data/db/migrate/20221123152807_add_shipping_category_to_spree_variants.rb +5 -0
  189. data/db/migrate/20230321161854_change_column_null_option_values_option_type_id.rb +5 -0
  190. data/db/migrate/20230425103509_remove_taxon_position.rb +5 -0
  191. data/db/migrate/20230427095534_drop_deprecated_address_id_from_shipments.rb +11 -0
  192. data/db/migrate/20240821173254_create_spree_permission_sets_in_core.rb +9 -0
  193. data/db/migrate/20240821173341_create_spree_roles_permissions_in_core.rb +9 -0
  194. data/db/migrate/20240821173641_add_description_to_spree_roles.rb +5 -0
  195. data/db/migrate/20240904152041_add_privilege_and_category_to_spree_permission_sets.rb +6 -0
  196. data/db/migrate/20250129061658_add_metadata_to_spree_resources.rb +28 -0
  197. data/db/migrate/20250201172950_add_gtin_and_condition_to_spree_variant.rb +6 -0
  198. data/db/migrate/20250207104016_add_primary_taxon_to_products.rb +7 -0
  199. data/db/migrate/20250221152004_add_metadata_to_users.rb +13 -0
  200. data/db/seeds.rb +5 -1
  201. data/lib/generators/solidus/install/app_templates/authentication/custom.rb +16 -0
  202. data/lib/generators/solidus/install/app_templates/authentication/devise.rb +16 -0
  203. data/lib/generators/solidus/install/app_templates/authentication/existing.rb +10 -0
  204. data/lib/generators/solidus/install/app_templates/authentication/none.rb +1 -0
  205. data/lib/generators/solidus/install/app_templates/frontend/none.rb +1 -0
  206. data/lib/generators/solidus/install/app_templates/frontend/starter.rb +1 -0
  207. data/lib/generators/solidus/install/app_templates/payment_method/braintree.rb +5 -0
  208. data/lib/generators/solidus/install/app_templates/payment_method/none.rb +1 -0
  209. data/lib/generators/solidus/install/app_templates/payment_method/paypal.rb +5 -0
  210. data/lib/generators/solidus/install/app_templates/payment_method/stripe.rb +5 -0
  211. data/lib/generators/solidus/install/install_generator.rb +210 -156
  212. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +27 -33
  213. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/backend/all.js +2 -2
  214. data/lib/generators/solidus/update/templates/config/initializers/new_solidus_defaults.rb.tt +30 -0
  215. data/lib/generators/solidus/update/update_generator.rb +124 -0
  216. data/lib/generators/spree/custom_user/custom_user_generator.rb +6 -4
  217. data/lib/generators/spree/custom_user/templates/authentication_helpers.rb.tt +2 -6
  218. data/lib/generators/spree/custom_user/templates/migration.rb.tt +7 -3
  219. data/lib/generators/spree/dummy/dummy_generator.rb +12 -9
  220. data/lib/generators/spree/dummy/templates/rails/application.rb.tt +1 -2
  221. data/lib/generators/spree/dummy/templates/rails/database.yml +48 -63
  222. data/lib/generators/spree/dummy/templates/rails/manifest.js +3 -0
  223. data/lib/generators/spree/dummy/templates/rails/test.rb +7 -2
  224. data/lib/spree/app_configuration.rb +220 -71
  225. data/lib/spree/bus.rb +11 -0
  226. data/lib/spree/core/class_constantizer.rb +2 -2
  227. data/lib/spree/core/controller_helpers/auth.rb +5 -69
  228. data/lib/spree/core/controller_helpers/common.rb +5 -80
  229. data/lib/spree/core/controller_helpers/order.rb +5 -86
  230. data/lib/spree/core/controller_helpers/payment_parameters.rb +5 -165
  231. data/lib/spree/core/controller_helpers/pricing.rb +5 -17
  232. data/lib/spree/core/controller_helpers/search.rb +5 -14
  233. data/lib/spree/core/controller_helpers/store.rb +5 -17
  234. data/lib/spree/core/controller_helpers/strong_parameters.rb +5 -71
  235. data/lib/spree/core/engine.rb +49 -16
  236. data/lib/spree/core/environment/calculators.rb +35 -3
  237. data/lib/spree/core/environment/promotions.rb +25 -4
  238. data/lib/spree/core/environment_extension.rb +16 -2
  239. data/lib/spree/core/importer/order.rb +6 -6
  240. data/lib/spree/core/importer/product.rb +3 -3
  241. data/lib/spree/core/nested_class_set.rb +28 -0
  242. data/lib/spree/core/null_promotion_configuration.rb +84 -0
  243. data/lib/spree/core/product_duplicator.rb +1 -1
  244. data/lib/spree/core/product_filters.rb +2 -2
  245. data/lib/spree/core/search/base.rb +18 -9
  246. data/lib/spree/core/search/variant.rb +2 -2
  247. data/lib/spree/core/state_machines/inventory_unit.rb +5 -40
  248. data/lib/spree/core/state_machines/order.rb +5 -247
  249. data/lib/spree/core/state_machines/payment.rb +5 -59
  250. data/lib/spree/core/state_machines/reimbursement.rb +5 -31
  251. data/lib/spree/core/state_machines/return_authorization.rb +5 -30
  252. data/lib/spree/core/state_machines/return_item/acceptance_status.rb +5 -49
  253. data/lib/spree/core/state_machines/return_item/reception_status.rb +5 -40
  254. data/lib/spree/core/state_machines/shipment.rb +5 -56
  255. data/lib/spree/core/state_machines.rb +48 -81
  256. data/lib/spree/core/stock_configuration.rb +18 -0
  257. data/lib/spree/core/validators/email.rb +2 -4
  258. data/lib/spree/core/version.rb +5 -1
  259. data/lib/spree/core/versioned_value.rb +75 -0
  260. data/lib/spree/core.rb +40 -16
  261. data/lib/spree/deprecated_instance_variable_proxy.rb +57 -0
  262. data/lib/spree/deprecation.rb +3 -51
  263. data/lib/spree/deprecator.rb +9 -0
  264. data/lib/spree/i18n.rb +1 -1
  265. data/lib/spree/mailer_previews/carton_preview.rb +1 -1
  266. data/lib/spree/migration_helpers.rb +3 -3
  267. data/lib/spree/migrations.rb +13 -11
  268. data/lib/spree/money.rb +5 -118
  269. data/lib/spree/permission_sets/base.rb +5 -30
  270. data/lib/spree/permission_sets/configuration_display.rb +5 -23
  271. data/lib/spree/permission_sets/configuration_management.rb +5 -23
  272. data/lib/spree/permission_sets/dashboard_display.rb +5 -9
  273. data/lib/spree/permission_sets/default_customer.rb +5 -35
  274. data/lib/spree/permission_sets/order_display.rb +5 -19
  275. data/lib/spree/permission_sets/order_management.rb +5 -20
  276. data/lib/spree/permission_sets/product_display.rb +5 -17
  277. data/lib/spree/permission_sets/product_management.rb +5 -19
  278. data/lib/spree/permission_sets/restricted_stock_display.rb +5 -16
  279. data/lib/spree/permission_sets/restricted_stock_management.rb +5 -16
  280. data/lib/spree/permission_sets/stock_display.rb +5 -10
  281. data/lib/spree/permission_sets/stock_management.rb +5 -10
  282. data/lib/spree/permission_sets/super_user.rb +5 -9
  283. data/lib/spree/permission_sets/user_display.rb +5 -11
  284. data/lib/spree/permission_sets/user_management.rb +5 -23
  285. data/lib/spree/permission_sets.rb +5 -18
  286. data/lib/spree/permitted_attributes.rb +35 -14
  287. data/lib/spree/preferences/configuration.rb +88 -0
  288. data/lib/spree/preferences/persistable.rb +7 -1
  289. data/lib/spree/preferences/preferable.rb +13 -0
  290. data/lib/spree/preferences/preferable_class_methods.rb +12 -4
  291. data/lib/spree/preferences/preference_differentiator.rb +29 -0
  292. data/lib/spree/preferences/static_model_preferences.rb +25 -10
  293. data/lib/spree/preferences/store.rb +2 -2
  294. data/lib/spree/testing_support/blacklist_urls.rb +1 -1
  295. data/lib/spree/testing_support/bus_helpers.rb +101 -0
  296. data/lib/spree/testing_support/capybara_ext.rb +11 -1
  297. data/lib/spree/testing_support/common_rake.rb +76 -23
  298. data/lib/spree/testing_support/dummy_ability.rb +7 -0
  299. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
  300. data/lib/spree/testing_support/dummy_app/assets/stylesheets/solidus_admin/tailwind.css +1 -0
  301. data/lib/spree/testing_support/dummy_app/database.yml +39 -28
  302. data/lib/spree/testing_support/dummy_app/migrations.rb +8 -15
  303. data/lib/spree/testing_support/dummy_app/rake_tasks.rb +3 -10
  304. data/lib/spree/testing_support/dummy_app.rb +71 -42
  305. data/lib/spree/testing_support/extension_rake.rb +2 -2
  306. data/lib/spree/testing_support/factories/address_factory.rb +9 -13
  307. data/lib/spree/testing_support/factories/adjustment_factory.rb +1 -13
  308. data/lib/spree/testing_support/factories/adjustment_reason_factory.rb +0 -5
  309. data/lib/spree/testing_support/factories/calculator_factory.rb +4 -10
  310. data/lib/spree/testing_support/factories/carton_factory.rb +2 -10
  311. data/lib/spree/testing_support/factories/country_factory.rb +1 -7
  312. data/lib/spree/testing_support/factories/credit_card_factory.rb +0 -5
  313. data/lib/spree/testing_support/factories/customer_return_factory.rb +2 -11
  314. data/lib/spree/testing_support/factories/image_factory.rb +0 -5
  315. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +4 -14
  316. data/lib/spree/testing_support/factories/line_item_factory.rb +0 -8
  317. data/lib/spree/testing_support/factories/option_type_factory.rb +0 -8
  318. data/lib/spree/testing_support/factories/option_value_factory.rb +0 -5
  319. data/lib/spree/testing_support/factories/order_factory.rb +19 -38
  320. data/lib/spree/testing_support/factories/payment_factory.rb +0 -10
  321. data/lib/spree/testing_support/factories/payment_method_factory.rb +0 -5
  322. data/lib/spree/testing_support/factories/price_factory.rb +0 -7
  323. data/lib/spree/testing_support/factories/product_factory.rb +5 -13
  324. data/lib/spree/testing_support/factories/product_option_type_factory.rb +0 -8
  325. data/lib/spree/testing_support/factories/product_property_factory.rb +0 -8
  326. data/lib/spree/testing_support/factories/property_factory.rb +0 -5
  327. data/lib/spree/testing_support/factories/refund_factory.rb +0 -8
  328. data/lib/spree/testing_support/factories/refund_reason_factory.rb +0 -5
  329. data/lib/spree/testing_support/factories/reimbursement_factory.rb +0 -7
  330. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +0 -5
  331. data/lib/spree/testing_support/factories/return_authorization_factory.rb +0 -9
  332. data/lib/spree/testing_support/factories/return_item_factory.rb +0 -9
  333. data/lib/spree/testing_support/factories/return_reason_factory.rb +0 -5
  334. data/lib/spree/testing_support/factories/role_factory.rb +0 -5
  335. data/lib/spree/testing_support/factories/shipment_factory.rb +1 -11
  336. data/lib/spree/testing_support/factories/shipping_category_factory.rb +0 -5
  337. data/lib/spree/testing_support/factories/shipping_method_factory.rb +0 -9
  338. data/lib/spree/testing_support/factories/shipping_rate_factory.rb +0 -8
  339. data/lib/spree/testing_support/factories/state_factory.rb +8 -10
  340. data/lib/spree/testing_support/factories/stock_item_factory.rb +5 -9
  341. data/lib/spree/testing_support/factories/stock_location_factory.rb +0 -9
  342. data/lib/spree/testing_support/factories/stock_movement_factory.rb +0 -7
  343. data/lib/spree/testing_support/factories/stock_package_factory.rb +1 -9
  344. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +0 -5
  345. data/lib/spree/testing_support/factories/store_credit_event_factory.rb +0 -8
  346. data/lib/spree/testing_support/factories/store_credit_factory.rb +4 -13
  347. data/lib/spree/testing_support/factories/store_credit_reason_factory.rb +0 -5
  348. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +0 -5
  349. data/lib/spree/testing_support/factories/store_factory.rb +0 -5
  350. data/lib/spree/testing_support/factories/tax_category_factory.rb +0 -8
  351. data/lib/spree/testing_support/factories/tax_rate_factory.rb +0 -9
  352. data/lib/spree/testing_support/factories/taxon_factory.rb +5 -10
  353. data/lib/spree/testing_support/factories/taxonomy_factory.rb +3 -6
  354. data/lib/spree/testing_support/factories/user_factory.rb +6 -9
  355. data/lib/spree/testing_support/factories/variant_factory.rb +0 -10
  356. data/lib/spree/testing_support/factories/variant_property_rule_condition_factory.rb +0 -8
  357. data/lib/spree/testing_support/factories/variant_property_rule_factory.rb +0 -9
  358. data/lib/spree/testing_support/factories/variant_property_rule_value_factory.rb +0 -8
  359. data/lib/spree/testing_support/factories/zone_factory.rb +1 -9
  360. data/lib/spree/testing_support/factory_bot.rb +6 -29
  361. data/lib/spree/testing_support/flaky.rb +22 -0
  362. data/lib/spree/testing_support/order_walkthrough.rb +7 -6
  363. data/lib/spree/testing_support/sequences.rb +0 -5
  364. data/lib/spree/testing_support/shared_examples/calculator.rb +10 -0
  365. data/lib/spree/testing_support/shared_examples/order_factory.rb +141 -0
  366. data/lib/spree/testing_support/shared_examples/working_factory.rb +15 -0
  367. data/lib/spree/testing_support/silence_deprecations.rb +9 -0
  368. data/lib/spree/user_class_handle.rb +2 -2
  369. data/lib/tasks/colorado_delivery_fee.rake +28 -0
  370. data/lib/tasks/payment_method.rake +29 -0
  371. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +8 -0
  372. data/solidus_core.gemspec +18 -8
  373. metadata +233 -117
  374. data/app/jobs/spree/promotion_code_batch_job.rb +0 -26
  375. data/app/mailers/spree/promotion_code_batch_mailer.rb +0 -15
  376. data/app/models/spree/calculator/distributed_amount.rb +0 -33
  377. data/app/models/spree/calculator/flat_percent_item_total.rb +0 -23
  378. data/app/models/spree/calculator/flexi_rate.rb +0 -22
  379. data/app/models/spree/calculator/percent_on_line_item.rb +0 -13
  380. data/app/models/spree/calculator/tiered_flat_rate.rb +0 -52
  381. data/app/models/spree/calculator/tiered_percent.rb +0 -62
  382. data/app/models/spree/line_item_action.rb +0 -8
  383. data/app/models/spree/order_promotion.rb +0 -27
  384. data/app/models/spree/promotion/actions/create_adjustment.rb +0 -77
  385. data/app/models/spree/promotion/actions/create_item_adjustments.rb +0 -99
  386. data/app/models/spree/promotion/actions/create_quantity_adjustments.rb +0 -139
  387. data/app/models/spree/promotion/actions/free_shipping.rb +0 -58
  388. data/app/models/spree/promotion/rules/first_order.rb +0 -38
  389. data/app/models/spree/promotion/rules/first_repeat_purchase_since.rb +0 -36
  390. data/app/models/spree/promotion/rules/item_total.rb +0 -53
  391. data/app/models/spree/promotion/rules/nth_order.rb +0 -45
  392. data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -25
  393. data/app/models/spree/promotion/rules/option_value.rb +0 -50
  394. data/app/models/spree/promotion/rules/product.rb +0 -74
  395. data/app/models/spree/promotion/rules/store.rb +0 -22
  396. data/app/models/spree/promotion/rules/taxon.rb +0 -87
  397. data/app/models/spree/promotion/rules/user.rb +0 -30
  398. data/app/models/spree/promotion/rules/user_logged_in.rb +0 -20
  399. data/app/models/spree/promotion/rules/user_role.rb +0 -45
  400. data/app/models/spree/promotion.rb +0 -284
  401. data/app/models/spree/promotion_action.rb +0 -43
  402. data/app/models/spree/promotion_category.rb +0 -8
  403. data/app/models/spree/promotion_chooser.rb +0 -34
  404. data/app/models/spree/promotion_code/batch_builder.rb +0 -64
  405. data/app/models/spree/promotion_code.rb +0 -50
  406. data/app/models/spree/promotion_code_batch.rb +0 -27
  407. data/app/models/spree/promotion_handler/cart.rb +0 -55
  408. data/app/models/spree/promotion_handler/coupon.rb +0 -123
  409. data/app/models/spree/promotion_handler/page.rb +0 -26
  410. data/app/models/spree/promotion_handler/shipping.rb +0 -61
  411. data/app/models/spree/promotion_rule.rb +0 -55
  412. data/app/models/spree/promotion_rule_role.rb +0 -8
  413. data/app/models/spree/promotion_rule_store.rb +0 -10
  414. data/app/models/spree/promotion_rule_taxon.rb +0 -8
  415. data/app/models/spree/promotion_rule_user.rb +0 -10
  416. data/app/models/spree/tax/shipping_rate_taxer.rb +0 -24
  417. data/app/subscribers/spree/mailer_subscriber.rb +0 -25
  418. data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +0 -2
  419. data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +0 -2
  420. data/db/migrate/20161017102621_create_spree_promotion_code_batch.rb +0 -38
  421. data/db/migrate/20180202190713_create_promotion_rule_stores.rb +0 -12
  422. data/db/migrate/20180328172631_add_join_characters_to_promotion_code_batch.rb +0 -11
  423. data/db/migrate/20190106184413_remove_code_from_spree_promotions.rb +0 -42
  424. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -10
  425. data/lib/generators/solidus/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -9
  426. data/lib/generators/spree/dummy/templates/rails/script/rails +0 -6
  427. data/lib/solidus/migrations/promotions_with_code_handlers.rb +0 -66
  428. data/lib/spree/event/adapters/active_support_notifications.rb +0 -67
  429. data/lib/spree/event/configuration.rb +0 -25
  430. data/lib/spree/event/subscriber.rb +0 -86
  431. data/lib/spree/event/subscriber_registry.rb +0 -94
  432. data/lib/spree/event.rb +0 -119
  433. data/lib/spree/permission_sets/promotion_display.rb +0 -15
  434. data/lib/spree/permission_sets/promotion_management.rb +0 -15
  435. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +0 -10
  436. data/lib/spree/testing_support/dummy_app/assets/stylesheets/spree/frontend/all.css +0 -9
  437. data/lib/spree/testing_support/dummy_app/views/layouts/application.html.erb +0 -1
  438. data/lib/spree/testing_support/factories/order_promotion_factory.rb +0 -16
  439. data/lib/spree/testing_support/factories/promotion_category_factory.rb +0 -12
  440. data/lib/spree/testing_support/factories/promotion_code_factory.rb +0 -16
  441. data/lib/spree/testing_support/factories/promotion_factory.rb +0 -92
  442. data/lib/spree/testing_support/factories.rb +0 -11
  443. data/lib/spree/testing_support.rb +0 -31
  444. data/lib/tasks/upgrade.rake +0 -15
@@ -1,284 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class Promotion < Spree::Base
5
- MATCH_POLICIES = %w(all any)
6
- UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]
7
-
8
- attr_reader :eligibility_errors
9
-
10
- belongs_to :promotion_category, optional: true
11
-
12
- has_many :promotion_rules, autosave: true, dependent: :destroy, inverse_of: :promotion
13
- alias_method :rules, :promotion_rules
14
-
15
- has_many :promotion_actions, autosave: true, dependent: :destroy, inverse_of: :promotion
16
- alias_method :actions, :promotion_actions
17
-
18
- has_many :order_promotions, class_name: "Spree::OrderPromotion"
19
- has_many :orders, through: :order_promotions
20
-
21
- has_many :codes, class_name: "Spree::PromotionCode", inverse_of: :promotion, dependent: :destroy
22
- alias_method :promotion_codes, :codes
23
-
24
- has_many :promotion_code_batches, class_name: "Spree::PromotionCodeBatch", dependent: :destroy
25
-
26
- accepts_nested_attributes_for :promotion_actions, :promotion_rules
27
-
28
- validates_associated :rules
29
-
30
- validates :name, presence: true
31
- validates :path, uniqueness: { allow_blank: true, case_sensitive: true }
32
- validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
33
- validates :per_code_usage_limit, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
34
- validates :description, length: { maximum: 255 }
35
- validate :apply_automatically_disallowed_with_paths
36
-
37
- before_save :normalize_blank_values
38
-
39
- scope :coupons, -> { joins(:codes).distinct }
40
- scope :advertised, -> { where(advertise: true) }
41
- scope :active, -> { has_actions.started_and_unexpired }
42
- scope :started_and_unexpired, -> do
43
- table = arel_table
44
- time = Time.current
45
-
46
- where(table[:starts_at].eq(nil).or(table[:starts_at].lt(time))).
47
- where(table[:expires_at].eq(nil).or(table[:expires_at].gt(time)))
48
- end
49
- scope :has_actions, -> do
50
- joins(:promotion_actions)
51
- end
52
- scope :applied, -> { joins(:order_promotions).distinct }
53
-
54
- self.whitelisted_ransackable_associations = ['codes']
55
- self.whitelisted_ransackable_attributes = %w[name path promotion_category_id]
56
- def self.ransackable_scopes(*)
57
- %i(active)
58
- end
59
-
60
- def self.order_activatable?(order)
61
- order && !UNACTIVATABLE_ORDER_STATES.include?(order.state)
62
- end
63
-
64
- def self.with_coupon_code(val)
65
- joins(:codes).where(
66
- PromotionCode.arel_table[:value].eq(val.downcase)
67
- ).first
68
- end
69
-
70
- def as_json(options = {})
71
- options[:except] ||= :code
72
- super
73
- end
74
-
75
- def not_started?
76
- !started?
77
- end
78
-
79
- def started?
80
- starts_at.nil? || starts_at < Time.current
81
- end
82
-
83
- def expired?
84
- expires_at.present? && expires_at < Time.current
85
- end
86
-
87
- def not_expired?
88
- !expired?
89
- end
90
-
91
- def active?
92
- started? && not_expired? && actions.present?
93
- end
94
-
95
- def inactive?
96
- !active?
97
- end
98
-
99
- def activate(order:, line_item: nil, user: nil, path: nil, promotion_code: nil)
100
- return unless self.class.order_activatable?(order)
101
-
102
- payload = {
103
- order: order,
104
- promotion: self,
105
- line_item: line_item,
106
- user: user,
107
- path: path,
108
- promotion_code: promotion_code
109
- }
110
-
111
- # Track results from actions to see if any action has been taken.
112
- # Actions should return nil/false if no action has been taken.
113
- # If an action returns true, then an action has been taken.
114
- results = actions.map do |action|
115
- action.perform(payload)
116
- end
117
- # If an action has been taken, report back to whatever activated this promotion.
118
- action_taken = results.include?(true)
119
-
120
- if action_taken
121
- # connect to the order
122
- order.order_promotions.find_or_create_by!(
123
- promotion: self,
124
- promotion_code: promotion_code,
125
- )
126
- order.promotions.reset
127
- order_promotions.reset
128
- orders.reset
129
- end
130
-
131
- action_taken
132
- end
133
-
134
- # called anytime order.recalculate happens
135
- def eligible?(promotable, promotion_code: nil)
136
- return false if inactive?
137
- return false if blacklisted?(promotable)
138
-
139
- excluded_orders = eligibility_excluded_orders(promotable)
140
- return false if usage_limit_exceeded?(excluded_orders: excluded_orders)
141
- return false if promotion_code&.usage_limit_exceeded?(excluded_orders: excluded_orders)
142
-
143
- !!eligible_rules(promotable, {})
144
- end
145
-
146
- # eligible_rules returns an array of promotion rules where eligible? is true for the promotable
147
- # if there are no such rules, an empty array is returned
148
- # if the rules make this promotable ineligible, then nil is returned (i.e. this promotable is not eligible)
149
- def eligible_rules(promotable, options = {})
150
- # Promotions without rules are eligible by default.
151
- return [] if rules.none?
152
-
153
- eligible = lambda { |rule| rule.eligible?(promotable, options) }
154
- specific_rules = rules.for(promotable)
155
- return [] if specific_rules.none?
156
-
157
- if match_all?
158
- # If there are rules for this promotion, but no rules for this
159
- # particular promotable, then the promotion is ineligible by default.
160
- unless specific_rules.all?(&eligible)
161
- @eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
162
- return nil
163
- end
164
- specific_rules
165
- else
166
- unless specific_rules.any?(&eligible)
167
- @eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
168
- return nil
169
- end
170
- specific_rules.select(&eligible)
171
- end
172
- end
173
-
174
- def products
175
- rules.where(type: "Spree::Promotion::Rules::Product").flat_map(&:products).uniq
176
- end
177
-
178
- # Whether the promotion has exceeded its usage restrictions.
179
- #
180
- # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage limit
181
- # @return true or false
182
- def usage_limit_exceeded?(excluded_orders: [])
183
- if usage_limit
184
- usage_count(excluded_orders: excluded_orders) >= usage_limit
185
- end
186
- end
187
-
188
- # Number of times the code has been used overall
189
- #
190
- # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
191
- # @return [Integer] usage count
192
- def usage_count(excluded_orders: [])
193
- Spree::Adjustment.promotion.
194
- eligible.
195
- in_completed_orders(excluded_orders: excluded_orders).
196
- where(source_id: actions).
197
- count(:order_id)
198
- end
199
-
200
- def line_item_actionable?(order, line_item, promotion_code: nil)
201
- return false if blacklisted?(line_item)
202
-
203
- if eligible?(order, promotion_code: promotion_code)
204
- rules = eligible_rules(order)
205
- if rules.blank?
206
- true
207
- else
208
- rules.send(match_all? ? :all? : :any?) do |rule|
209
- rule.actionable? line_item
210
- end
211
- end
212
- else
213
- false
214
- end
215
- end
216
-
217
- def used_by?(user, excluded_orders = [])
218
- [
219
- :adjustments,
220
- :line_item_adjustments,
221
- :shipment_adjustments
222
- ].any? do |adjustment_type|
223
- user.orders.complete.joins(adjustment_type).where(
224
- spree_adjustments: {
225
- source_type: "Spree::PromotionAction",
226
- source_id: actions.map(&:id),
227
- eligible: true
228
- }
229
- ).where.not(
230
- id: excluded_orders.map(&:id)
231
- ).any?
232
- end
233
- end
234
-
235
- # Removes a promotion and any adjustments or other side effects from an
236
- # order.
237
- # @param order [Spree::Order] the order to remove the promotion from.
238
- # @return [void]
239
- def remove_from(order)
240
- actions.each do |action|
241
- action.remove_from(order)
242
- end
243
- # note: this destroys the join table entry, not the promotion itself
244
- order.promotions.destroy(self)
245
- order.order_promotions.reset
246
- order_promotions.reset
247
- end
248
-
249
- private
250
-
251
- def blacklisted?(promotable)
252
- case promotable
253
- when Spree::LineItem
254
- !promotable.product.promotionable?
255
- when Spree::Order
256
- promotable.line_items.joins(:product).where(spree_products: { promotionable: false }).exists?
257
- end
258
- end
259
-
260
- def normalize_blank_values
261
- self[:path] = nil if self[:path].blank?
262
- end
263
-
264
- def match_all?
265
- match_policy == "all"
266
- end
267
-
268
- def apply_automatically_disallowed_with_paths
269
- return unless apply_automatically
270
-
271
- errors.add(:apply_automatically, :disallowed_with_path) if path.present?
272
- end
273
-
274
- def eligibility_excluded_orders(promotable)
275
- if promotable.is_a?(Spree::Order)
276
- [promotable]
277
- elsif promotable.respond_to?(:order)
278
- [promotable.order]
279
- else
280
- []
281
- end
282
- end
283
- end
284
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spree/preferences/persistable'
4
-
5
- module Spree
6
- # Base class for all types of promotion action.
7
- #
8
- # PromotionActions perform the necessary tasks when a promotion is activated
9
- # by an event and determined to be eligible.
10
- class PromotionAction < Spree::Base
11
- include Spree::Preferences::Persistable
12
- include Spree::SoftDeletable
13
-
14
- belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_actions, optional: true
15
-
16
- scope :of_type, ->(type) { where(type: Array.wrap(type).map(&:to_s)) }
17
- scope :shipping, -> { of_type(Spree::Config.environment.promotions.shipping_actions.to_a) }
18
-
19
- # Updates the state of the order or performs some other action depending on
20
- # the subclass options will contain the payload from the event that
21
- # activated the promotion. This will include the key :user which allows
22
- # user based actions to be performed in addition to actions on the order
23
- #
24
- # @note This method should be overriden in subclassses.
25
- def perform(_options = {})
26
- raise 'perform should be implemented in a sub-class of PromotionAction'
27
- end
28
-
29
- # Removes the action from an order
30
- #
31
- # @note This method should be overriden in subclassses.
32
- #
33
- # @param order [Spree::Order] the order to remove the action from
34
- # @return [void]
35
- def remove_from(_order)
36
- raise 'remove_from should be implemented in a sub-class of PromotionAction'
37
- end
38
-
39
- def to_partial_path
40
- "spree/admin/promotions/actions/#{model_name.element}"
41
- end
42
- end
43
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class PromotionCategory < Spree::Base
5
- validates_presence_of :name
6
- has_many :promotions
7
- end
8
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class PromotionChooser
5
- def initialize(adjustments)
6
- @adjustments = adjustments
7
- end
8
-
9
- # Picks the best promotion from this set of adjustments, all others are
10
- # marked as ineligible.
11
- #
12
- # @return [BigDecimal] The amount of the best adjustment
13
- def update
14
- if best_promotion_adjustment
15
- @adjustments.select(&:eligible?).each do |adjustment|
16
- next if adjustment == best_promotion_adjustment
17
- adjustment.update_columns(eligible: false, updated_at: Time.current)
18
- end
19
- best_promotion_adjustment.amount
20
- else
21
- BigDecimal('0')
22
- end
23
- end
24
-
25
- private
26
-
27
- # @return The best promotion from this set of adjustments.
28
- def best_promotion_adjustment
29
- @best_promotion_adjustment ||= @adjustments.select(&:eligible?).min_by do |adjustment|
30
- [adjustment.amount, -adjustment.id]
31
- end
32
- end
33
- end
34
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ::Spree::PromotionCode::BatchBuilder
4
- attr_reader :promotion_code_batch, :options
5
- delegate :promotion, :number_of_codes, :base_code, to: :promotion_code_batch
6
-
7
- DEFAULT_OPTIONS = {
8
- random_code_length: 6,
9
- batch_size: 1000,
10
- sample_characters: ('a'..'z').to_a + (2..9).to_a.map(&:to_s)
11
- }
12
-
13
- def initialize(promotion_code_batch, options = {})
14
- @promotion_code_batch = promotion_code_batch
15
- options.assert_valid_keys(*DEFAULT_OPTIONS.keys)
16
- @options = DEFAULT_OPTIONS.merge(options)
17
- end
18
-
19
- def build_promotion_codes
20
- generate_random_codes
21
- promotion_code_batch.update!(state: "completed")
22
- rescue StandardError => error
23
- promotion_code_batch.update!(
24
- error: error.inspect,
25
- state: "failed"
26
- )
27
- raise error
28
- end
29
-
30
- private
31
-
32
- def generate_random_codes
33
- created_codes = 0
34
- batch_size = @options[:batch_size]
35
-
36
- while created_codes < number_of_codes
37
- max_codes_to_generate = [batch_size, number_of_codes - created_codes].min
38
-
39
- new_codes = Array.new(max_codes_to_generate) { generate_random_code }.uniq
40
- codes_for_current_batch = get_unique_codes(new_codes)
41
-
42
- codes_for_current_batch.each do |value|
43
- Spree::PromotionCode.create!(
44
- value: value,
45
- promotion: promotion,
46
- promotion_code_batch: promotion_code_batch
47
- )
48
- end
49
- created_codes += codes_for_current_batch.size
50
- end
51
- end
52
-
53
- def generate_random_code
54
- suffix = Array.new(@options[:random_code_length]) do
55
- @options[:sample_characters].sample
56
- end.join
57
-
58
- "#{base_code}#{@promotion_code_batch.join_characters}#{suffix}"
59
- end
60
-
61
- def get_unique_codes(code_set)
62
- code_set - Spree::PromotionCode.where(value: code_set.to_a).pluck(:value)
63
- end
64
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Spree::PromotionCode < Spree::Base
4
- belongs_to :promotion, inverse_of: :codes, optional: true
5
- belongs_to :promotion_code_batch, class_name: "Spree::PromotionCodeBatch", optional: true
6
- has_many :adjustments
7
-
8
- validates :value, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
9
- validates :promotion, presence: true
10
- validate :promotion_not_apply_automatically, on: :create
11
-
12
- before_save :normalize_code
13
-
14
- self.whitelisted_ransackable_attributes = ['value']
15
-
16
- # Whether the promotion code has exceeded its usage restrictions
17
- #
18
- # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage limit
19
- # @return true or false
20
- def usage_limit_exceeded?(excluded_orders: [])
21
- if usage_limit
22
- usage_count(excluded_orders: excluded_orders) >= usage_limit
23
- end
24
- end
25
-
26
- # Number of times the code has been used overall
27
- #
28
- # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
29
- # @return [Integer] usage count
30
- def usage_count(excluded_orders: [])
31
- adjustments.
32
- eligible.
33
- in_completed_orders(excluded_orders: excluded_orders).
34
- count(:order_id)
35
- end
36
-
37
- def usage_limit
38
- promotion.per_code_usage_limit
39
- end
40
-
41
- def promotion_not_apply_automatically
42
- errors.add(:base, :disallowed_with_apply_automatically) if promotion.apply_automatically
43
- end
44
-
45
- private
46
-
47
- def normalize_code
48
- self.value = value.downcase.strip
49
- end
50
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class PromotionCodeBatch < Spree::Base
5
- class CantProcessStartedBatch < StandardError
6
- end
7
-
8
- belongs_to :promotion, class_name: "Spree::Promotion", optional: true
9
- has_many :promotion_codes, class_name: "Spree::PromotionCode", dependent: :destroy
10
-
11
- validates :number_of_codes, numericality: { greater_than: 0 }
12
- validates_presence_of :base_code, :number_of_codes
13
-
14
- def finished?
15
- state == "completed"
16
- end
17
-
18
- def process
19
- if state == "pending"
20
- update!(state: "processing")
21
- PromotionCodeBatchJob.perform_later(self)
22
- else
23
- raise CantProcessStartedBatch.new("Batch #{id} already started")
24
- end
25
- end
26
- end
27
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- module PromotionHandler
5
- # Decides which promotion should be activated given the current order context
6
- #
7
- # By activated it doesn't necessarily mean that the order will have a
8
- # discount for every activated promotion. It means that the discount will be
9
- # created and might eventually become eligible. The intention here is to
10
- # reduce overhead. e.g. a promotion that requires item A to be eligible
11
- # shouldn't be eligible unless item A is added to the order.
12
- #
13
- # It can be used as a wrapper for custom handlers as well. Different
14
- # applications might have completely different requirements to make
15
- # the promotions system accurate and performant. Here they can plug custom
16
- # handler to activate promos as they wish once an item is added to cart
17
- class Cart
18
- attr_reader :line_item, :order
19
- attr_accessor :error, :success
20
-
21
- def initialize(order, line_item = nil)
22
- @order, @line_item = order, line_item
23
- end
24
-
25
- def activate
26
- promotions.each do |promotion|
27
- if (line_item && promotion.eligible?(line_item, promotion_code: promotion_code(promotion))) || promotion.eligible?(order, promotion_code: promotion_code(promotion))
28
- promotion.activate(line_item: line_item, order: order, promotion_code: promotion_code(promotion))
29
- end
30
- end
31
- end
32
-
33
- private
34
-
35
- def promotions
36
- connected_order_promotions | sale_promotions
37
- end
38
-
39
- def connected_order_promotions
40
- Spree::Promotion.active.includes(:promotion_rules).
41
- joins(:order_promotions).
42
- where(spree_orders_promotions: { order_id: order.id }).readonly(false).to_a
43
- end
44
-
45
- def sale_promotions
46
- Spree::Promotion.where(apply_automatically: true).active.includes(:promotion_rules)
47
- end
48
-
49
- def promotion_code(promotion)
50
- order_promotion = Spree::OrderPromotion.where(order: order, promotion: promotion).first
51
- order_promotion.present? ? order_promotion.promotion_code : nil
52
- end
53
- end
54
- end
55
- end
@@ -1,123 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- module PromotionHandler
5
- class Coupon
6
- attr_reader :order, :coupon_code
7
- attr_accessor :error, :success, :status_code
8
-
9
- def initialize(order)
10
- @order = order
11
- @coupon_code = order.coupon_code && order.coupon_code.downcase
12
- end
13
-
14
- def apply
15
- if coupon_code.present?
16
- if promotion.present? && promotion.active? && promotion.actions.exists?
17
- handle_present_promotion(promotion)
18
- elsif promotion_code&.promotion&.expired?
19
- set_error_code :coupon_code_expired
20
- else
21
- set_error_code :coupon_code_not_found
22
- end
23
- end
24
-
25
- self
26
- end
27
-
28
- def remove
29
- if promotion.blank?
30
- set_error_code :coupon_code_not_found
31
- elsif !promotion_exists_on_order?(order, promotion)
32
- set_error_code :coupon_code_not_present
33
- else
34
- promotion.remove_from(order)
35
- order.recalculate
36
- set_success_code :coupon_code_removed
37
- end
38
-
39
- self
40
- end
41
-
42
- def set_success_code(status_code)
43
- @status_code = status_code
44
- @success = I18n.t(status_code, scope: 'spree')
45
- end
46
-
47
- def set_error_code(status_code, options = {})
48
- @status_code = status_code
49
- @error = options[:error] || I18n.t(status_code, scope: 'spree')
50
- end
51
-
52
- def promotion
53
- @promotion ||= begin
54
- if promotion_code && promotion_code.promotion.active?
55
- promotion_code.promotion
56
- end
57
- end
58
- end
59
-
60
- def successful?
61
- success.present? && error.blank?
62
- end
63
-
64
- private
65
-
66
- def promotion_code
67
- @promotion_code ||= Spree::PromotionCode.where(value: coupon_code).first
68
- end
69
-
70
- def handle_present_promotion(promotion)
71
- return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded? || promotion_code.usage_limit_exceeded?
72
- return promotion_applied if promotion_exists_on_order?(order, promotion)
73
-
74
- unless promotion.eligible?(order, promotion_code: promotion_code)
75
- set_promotion_eligibility_error_code(promotion)
76
- return (error || ineligible_for_this_order)
77
- end
78
-
79
- # If any of the actions for the promotion return `true`,
80
- # then result here will also be `true`.
81
- result = promotion.activate(order: order, promotion_code: promotion_code)
82
- if result
83
- order.recalculate
84
- set_success_code :coupon_code_applied
85
- else
86
- set_error_code :coupon_code_unknown_error
87
- end
88
- end
89
-
90
- def set_promotion_eligibility_error_code(promotion)
91
- return unless eligibility_error_code_present?(promotion)
92
-
93
- eligibility_error = promotion.eligibility_errors.details[:base].first
94
-
95
- @status_code = eligibility_error[:error_code]
96
- @error = eligibility_error[:error]
97
- end
98
-
99
- def promotion_usage_limit_exceeded
100
- set_error_code :coupon_code_max_usage
101
- end
102
-
103
- def ineligible_for_this_order
104
- set_error_code :coupon_code_not_eligible
105
- end
106
-
107
- def promotion_applied
108
- set_error_code :coupon_code_already_applied
109
- end
110
-
111
- def promotion_exists_on_order?(order, promotion)
112
- order.promotions.include? promotion
113
- end
114
-
115
- def eligibility_error_code_present?(promotion)
116
- promotion.eligibility_errors.present? &&
117
- promotion.eligibility_errors.details.present? &&
118
- promotion.eligibility_errors.details.key?(:base) &&
119
- promotion.eligibility_errors.details[:base].first[:error_code].present?
120
- end
121
- end
122
- end
123
- end