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,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class PromotionCodeBatchJob < ActiveJob::Base
5
- queue_as :default
6
-
7
- def perform(promotion_code_batch)
8
- PromotionCode::BatchBuilder.new(
9
- promotion_code_batch
10
- ).build_promotion_codes
11
-
12
- if promotion_code_batch.email?
13
- Spree::Config.promotion_code_batch_mailer_class
14
- .promotion_code_batch_finished(promotion_code_batch)
15
- .deliver_now
16
- end
17
- rescue StandardError => error
18
- if promotion_code_batch.email?
19
- Spree::Config.promotion_code_batch_mailer_class
20
- .promotion_code_batch_errored(promotion_code_batch)
21
- .deliver_now
22
- end
23
- raise error
24
- end
25
- end
26
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class PromotionCodeBatchMailer < Spree::BaseMailer
5
- def promotion_code_batch_finished(promotion_code_batch)
6
- @promotion_code_batch = promotion_code_batch
7
- mail(to: promotion_code_batch.email)
8
- end
9
-
10
- def promotion_code_batch_errored(promotion_code_batch)
11
- @promotion_code_batch = promotion_code_batch
12
- mail(to: promotion_code_batch.email)
13
- end
14
- end
15
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- # This is a calculator for line item adjustment actions. It accepts a line item
6
- # and calculates its weighted adjustment amount based on the value of the
7
- # preferred amount and the price of the other line items. More expensive line
8
- # items will receive a greater share of the preferred amount.
9
-
10
- module Spree
11
- class Calculator::DistributedAmount < Calculator
12
- preference :amount, :decimal, default: 0
13
- preference :currency, :string, default: -> { Spree::Config[:currency] }
14
-
15
- def compute_line_item(line_item)
16
- return 0 unless line_item
17
- return 0 unless preferred_currency.casecmp(line_item.currency).zero?
18
- return 0 unless calculable.promotion.line_item_actionable?(line_item.order, line_item)
19
- Spree::DistributedAmountsHandler.new(
20
- actionable_line_items(line_item.order),
21
- preferred_amount
22
- ).amount(line_item)
23
- end
24
-
25
- private
26
-
27
- def actionable_line_items(order)
28
- order.line_items.select do |line_item|
29
- calculable.promotion.line_item_actionable?(order, line_item)
30
- end
31
- end
32
- end
33
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- module Spree
6
- class Calculator::FlatPercentItemTotal < Calculator
7
- preference :flat_percent, :decimal, default: 0
8
-
9
- def compute(object)
10
- order = object.is_a?(Order) ? object : object.order
11
- preferred_currency = order.currency
12
- currency_exponent = ::Money::Currency.find(preferred_currency).exponent
13
- computed_amount = (object.amount * preferred_flat_percent / 100).round(currency_exponent)
14
-
15
- # We don't want to cause the promotion adjustments to push the order into a negative total.
16
- if computed_amount > object.amount
17
- object.amount
18
- else
19
- computed_amount
20
- end
21
- end
22
- end
23
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- module Spree
6
- class Calculator::FlexiRate < Calculator
7
- preference :first_item, :decimal, default: 0
8
- preference :additional_item, :decimal, default: 0
9
- preference :max_items, :integer, default: 0
10
- preference :currency, :string, default: ->{ Spree::Config[:currency] }
11
-
12
- def compute(object)
13
- items_count = object.quantity
14
- items_count = [items_count, preferred_max_items].min unless preferred_max_items.zero?
15
-
16
- return BigDecimal(0) if items_count == 0
17
-
18
- additional_items_count = items_count - 1
19
- preferred_first_item + preferred_additional_item * additional_items_count
20
- end
21
- end
22
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- module Spree
6
- class Calculator::PercentOnLineItem < Calculator
7
- preference :percent, :decimal, default: 0
8
-
9
- def compute(object)
10
- (object.amount * preferred_percent) / 100
11
- end
12
- end
13
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- module Spree
6
- class Calculator::TieredFlatRate < Calculator
7
- preference :base_amount, :decimal, default: 0
8
- preference :tiers, :hash, default: {}
9
- preference :currency, :string, default: -> { Spree::Config[:currency] }
10
-
11
- before_validation do
12
- # Convert tier values to decimals. Strings don't do us much good.
13
- if preferred_tiers.is_a?(Hash)
14
- self.preferred_tiers = preferred_tiers.map do |key, value|
15
- [cast_to_d(key.to_s), cast_to_d(value.to_s)]
16
- end.to_h
17
- end
18
- end
19
-
20
- validate :preferred_tiers_content
21
-
22
- def compute(object)
23
- _base, amount = preferred_tiers.sort.reverse.detect do |value, _|
24
- object.amount >= value
25
- end
26
-
27
- if preferred_currency.casecmp(object.currency).zero?
28
- amount || preferred_base_amount
29
- else
30
- 0
31
- end
32
- end
33
-
34
- private
35
-
36
- def cast_to_d(value)
37
- value.to_s.to_d
38
- rescue ArgumentError
39
- BigDecimal(0)
40
- end
41
-
42
- def preferred_tiers_content
43
- if preferred_tiers.is_a? Hash
44
- unless preferred_tiers.keys.all?{ |key| key.is_a?(Numeric) && key > 0 }
45
- errors.add(:base, :keys_should_be_positive_number)
46
- end
47
- else
48
- errors.add(:preferred_tiers, :should_be_hash)
49
- end
50
- end
51
- end
52
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency 'spree/calculator'
4
-
5
- module Spree
6
- class Calculator::TieredPercent < Calculator
7
- preference :base_percent, :decimal, default: 0
8
- preference :tiers, :hash, default: {}
9
- preference :currency, :string, default: -> { Spree::Config[:currency] }
10
-
11
- before_validation do
12
- # Convert tier values to decimals. Strings don't do us much good.
13
- if preferred_tiers.is_a?(Hash)
14
- self.preferred_tiers = preferred_tiers.map do |key, value|
15
- [cast_to_d(key.to_s), cast_to_d(value.to_s)]
16
- end.to_h
17
- end
18
- end
19
-
20
- validates :preferred_base_percent, numericality: {
21
- greater_than_or_equal_to: 0,
22
- less_than_or_equal_to: 100
23
- }
24
- validate :preferred_tiers_content
25
-
26
- def compute(object)
27
- order = object.is_a?(Order) ? object : object.order
28
-
29
- _base, percent = preferred_tiers.sort.reverse.detect do |value, _|
30
- order.item_total >= value
31
- end
32
-
33
- if preferred_currency.casecmp(order.currency).zero?
34
- currency_exponent = ::Money::Currency.find(preferred_currency).exponent
35
- (object.amount * (percent || preferred_base_percent) / 100).round(currency_exponent)
36
- else
37
- 0
38
- end
39
- end
40
-
41
- private
42
-
43
- def cast_to_d(value)
44
- value.to_s.to_d
45
- rescue ArgumentError
46
- BigDecimal(0)
47
- end
48
-
49
- def preferred_tiers_content
50
- if preferred_tiers.is_a? Hash
51
- unless preferred_tiers.keys.all?{ |key| key.is_a?(Numeric) && key > 0 }
52
- errors.add(:base, :keys_should_be_positive_number)
53
- end
54
- unless preferred_tiers.values.all?{ |key| key.is_a?(Numeric) && key >= 0 && key <= 100 }
55
- errors.add(:base, :values_should_be_percent)
56
- end
57
- else
58
- errors.add(:preferred_tiers, :should_be_hash)
59
- end
60
- end
61
- end
62
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class LineItemAction < Spree::Base
5
- belongs_to :line_item, optional: true
6
- belongs_to :action, class_name: "Spree::PromotionAction", optional: true
7
- end
8
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- # Spree::OrderPromotion represents the relationship between:
5
- #
6
- # 1. A promotion that a user attempted to apply to their order
7
- # 2. The specific code that they used
8
- class OrderPromotion < Spree::Base
9
- self.table_name = 'spree_orders_promotions'
10
-
11
- belongs_to :order, class_name: 'Spree::Order', optional: true
12
- belongs_to :promotion, class_name: 'Spree::Promotion', optional: true
13
- belongs_to :promotion_code, class_name: 'Spree::PromotionCode', optional: true
14
-
15
- validates :order, presence: true
16
- validates :promotion, presence: true
17
- validates :promotion_code, presence: true, if: :require_promotion_code?
18
-
19
- self.whitelisted_ransackable_associations = %w[promotion_code]
20
-
21
- private
22
-
23
- def require_promotion_code?
24
- promotion && !promotion.apply_automatically && promotion.codes.any?
25
- end
26
- end
27
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class Promotion < Spree::Base
5
- module Actions
6
- class CreateAdjustment < PromotionAction
7
- include Spree::CalculatedAdjustments
8
- include Spree::AdjustmentSource
9
-
10
- has_many :adjustments, as: :source
11
-
12
- delegate :eligible?, to: :promotion
13
-
14
- before_validation :ensure_action_has_calculator
15
- before_destroy :remove_adjustments_from_incomplete_orders
16
- before_discard :remove_adjustments_from_incomplete_orders
17
-
18
- # Creates the adjustment related to a promotion for the order passed
19
- # through options hash
20
- #
21
- # Returns `true` if an adjustment is applied to an order,
22
- # `false` if the promotion has already been applied.
23
- def perform(options = {})
24
- order = options[:order]
25
- return if promotion_credit_exists?(order)
26
-
27
- amount = compute_amount(order)
28
- order.adjustments.create!(
29
- amount: amount,
30
- order: order,
31
- source: self,
32
- promotion_code: options[:promotion_code],
33
- label: I18n.t('spree.adjustment_labels.order', promotion: Spree::Promotion.model_name.human, promotion_name: promotion.name)
34
- )
35
- true
36
- end
37
-
38
- # Ensure a negative amount which does not exceed the sum of the order's
39
- # item_total and ship_total
40
- def compute_amount(calculable)
41
- amount = calculator.compute(calculable)
42
- amount ||= BigDecimal(0)
43
- amount = amount.abs
44
- [(calculable.item_total + calculable.ship_total), amount].min * -1
45
- end
46
-
47
- # Removes any adjustments generated by this action from the order.
48
- # @param order [Spree::Order] the order to remove the action from.
49
- # @return [void]
50
- def remove_from(order)
51
- order.adjustments.each do |adjustment|
52
- if adjustment.source == self
53
- order.adjustments.destroy(adjustment)
54
- end
55
- end
56
- end
57
-
58
- private
59
-
60
- # Tells us if there if the specified promotion is already associated with the line item
61
- # regardless of whether or not its currently eligible. Useful because generally
62
- # you would only want a promotion action to apply to order no more than once.
63
- #
64
- # Receives an adjustment +source+ (here a PromotionAction object) and tells
65
- # if the order has adjustments from that already
66
- def promotion_credit_exists?(adjustable)
67
- adjustments.where(adjustable_id: adjustable.id).exists?
68
- end
69
-
70
- def ensure_action_has_calculator
71
- return if calculator
72
- self.calculator = Calculator::FlatPercentItemTotal.new
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class Promotion < Spree::Base
5
- module Actions
6
- class CreateItemAdjustments < PromotionAction
7
- include Spree::CalculatedAdjustments
8
- include Spree::AdjustmentSource
9
-
10
- has_many :adjustments, as: :source
11
-
12
- delegate :eligible?, to: :promotion
13
-
14
- before_validation :ensure_action_has_calculator
15
- before_destroy :remove_adjustments_from_incomplete_orders
16
- before_discard :remove_adjustments_from_incomplete_orders
17
-
18
- def perform(payload = {})
19
- order = payload[:order]
20
- promotion = payload[:promotion]
21
- promotion_code = payload[:promotion_code]
22
-
23
- results = line_items_to_adjust(promotion, order).map do |line_item|
24
- create_adjustment(line_item, order, promotion_code)
25
- end
26
-
27
- results.any?
28
- end
29
-
30
- # Ensure a negative amount which does not exceed the sum of the order's
31
- # item_total and ship_total
32
- def compute_amount(adjustable)
33
- order = adjustable.is_a?(Order) ? adjustable : adjustable.order
34
- return 0 unless promotion.line_item_actionable?(order, adjustable)
35
- promotion_amount = calculator.compute(adjustable)
36
- promotion_amount ||= BigDecimal(0)
37
- promotion_amount = promotion_amount.abs
38
- [adjustable.amount, promotion_amount].min * -1
39
- end
40
-
41
- # Removes any adjustments generated by this action from the order's
42
- # line items.
43
- # @param order [Spree::Order] the order to remove the action from.
44
- # @return [void]
45
- def remove_from(order)
46
- order.line_items.each do |line_item|
47
- line_item.adjustments.each do |adjustment|
48
- if adjustment.source == self
49
- line_item.adjustments.destroy(adjustment)
50
- end
51
- end
52
- end
53
- end
54
-
55
- private
56
-
57
- def create_adjustment(adjustable, order, promotion_code)
58
- amount = compute_amount(adjustable)
59
- return if amount == 0
60
- adjustable.adjustments.create!(
61
- source: self,
62
- amount: amount,
63
- order: order,
64
- promotion_code: promotion_code,
65
- label: I18n.t('spree.adjustment_labels.line_item', promotion: Spree::Promotion.model_name.human, promotion_name: promotion.name)
66
- )
67
- true
68
- end
69
-
70
- # Tells us if there if the specified promotion is already associated with the line item
71
- # regardless of whether or not its currently eligible. Useful because generally
72
- # you would only want a promotion action to apply to line item no more than once.
73
- #
74
- # Receives an adjustment +source+ (here a PromotionAction object) and tells
75
- # if the order has adjustments from that already
76
- def promotion_credit_exists?(adjustable)
77
- adjustments.where(adjustable_id: adjustable.id).exists?
78
- end
79
-
80
- def ensure_action_has_calculator
81
- return if calculator
82
- self.calculator = Calculator::PercentOnLineItem.new
83
- end
84
-
85
- def line_items_to_adjust(promotion, order)
86
- excluded_ids = adjustments.
87
- where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
88
- pluck(:adjustable_id).
89
- to_set
90
-
91
- order.line_items.select do |line_item|
92
- !excluded_ids.include?(line_item.id) &&
93
- promotion.line_item_actionable?(order, line_item)
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
@@ -1,139 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class Promotion < Spree::Base
5
- module Actions
6
- class CreateQuantityAdjustments < CreateItemAdjustments
7
- preference :group_size, :integer, default: 1
8
-
9
- has_many :line_item_actions, foreign_key: :action_id, dependent: :destroy
10
- has_many :line_items, through: :line_item_actions
11
-
12
- ##
13
- # Computes the amount for the adjustment based on the line item and any
14
- # other applicable items in the order. The rules for this specific
15
- # adjustment are as follows:
16
- #
17
- # = Setup
18
- #
19
- # We have a quantity group promotion on t-shirts. If a user orders 3
20
- # t-shirts, they get $5 off of each. The shirts come in one size and three
21
- # colours: red, blue, and white.
22
- #
23
- # == Scenario 1
24
- #
25
- # User has 2 red shirts, 1 white shirt, and 1 blue shirt in their
26
- # order. We want to compute the adjustment amount for the white shirt.
27
- #
28
- # *Result:* -$5
29
- #
30
- # *Reasoning:* There are a total of 4 items that are eligible for the
31
- # promotion. Since that is greater than 3, we can discount the items. The
32
- # white shirt has a quantity of 1, therefore it will get discounted by
33
- # +adjustment_amount * 1+ or $5.
34
- #
35
- # === Scenario 1-1
36
- #
37
- # What about the blue shirt? How much does it get discounted?
38
- #
39
- # *Result:* $0
40
- #
41
- # *Reasoning:* We have a total quantity of 4. However, we only apply the
42
- # adjustment to groups of 3. Assuming the white and red shirts have already
43
- # had their adjustment calculated, that means 3 units have been discounted.
44
- # Leaving us with a lonely blue shirt that isn't part of a group of 3.
45
- # Therefore, it does not receive the discount.
46
- #
47
- # == Scenario 2
48
- #
49
- # User has 4 red shirts in their order. What is the amount?
50
- #
51
- # *Result:* -$15
52
- #
53
- # *Reasoning:* The total quantity of eligible items is 4, so we the
54
- # adjustment will be non-zero. However, we only apply it to groups of 3,
55
- # therefore there is one extra item that is not eligible for the
56
- # adjustment. +adjustment_amount * 3+ or $15.
57
- #
58
- def compute_amount(line_item)
59
- adjustment_amount = calculator.compute(PartialLineItem.new(line_item))
60
- adjustment_amount ||= BigDecimal(0)
61
- adjustment_amount = adjustment_amount.abs
62
-
63
- order = line_item.order
64
- line_items = actionable_line_items(order)
65
-
66
- actioned_line_items = order.line_item_adjustments.reload.
67
- select { |adjustment| adjustment.source == self && adjustment.amount < 0 }.
68
- map(&:adjustable)
69
- other_line_items = actioned_line_items - [line_item]
70
-
71
- applicable_quantity = total_applicable_quantity(line_items)
72
- used_quantity = total_used_quantity(other_line_items)
73
- usable_quantity = [
74
- applicable_quantity - used_quantity,
75
- line_item.quantity
76
- ].min
77
-
78
- persist_quantity(usable_quantity, line_item)
79
-
80
- amount = adjustment_amount * usable_quantity
81
- [line_item.amount, amount].min * -1
82
- end
83
-
84
- private
85
-
86
- def actionable_line_items(order)
87
- order.line_items.select do |item|
88
- promotion.line_item_actionable? order, item
89
- end
90
- end
91
-
92
- def total_applicable_quantity(line_items)
93
- total_quantity = line_items.sum(&:quantity)
94
- extra_quantity = total_quantity % preferred_group_size
95
-
96
- total_quantity - extra_quantity
97
- end
98
-
99
- def total_used_quantity(line_items)
100
- line_item_actions.where(
101
- line_item_id: line_items.map(&:id)
102
- ).sum(:quantity)
103
- end
104
-
105
- def persist_quantity(quantity, line_item)
106
- line_item_action = line_item_actions.where(
107
- line_item_id: line_item.id
108
- ).first_or_initialize
109
- line_item_action.quantity = quantity
110
- line_item_action.save!
111
- end
112
-
113
- ##
114
- # Used specifically for PercentOnLineItem calculator. That calculator uses
115
- # `line_item.amount`, however we might not necessarily want to discount the
116
- # entire amount. This class allows us to determine the discount per
117
- # quantity and then calculate the adjustment amount the way we normally do
118
- # for flat rate adjustments.
119
- class PartialLineItem
120
- def initialize(line_item)
121
- @line_item = line_item
122
- end
123
-
124
- def amount
125
- @line_item.price
126
- end
127
-
128
- def order
129
- @line_item.order
130
- end
131
-
132
- def currency
133
- @line_item.currency
134
- end
135
- end
136
- end
137
- end
138
- end
139
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- class Promotion < Spree::Base
5
- module Actions
6
- class FreeShipping < Spree::PromotionAction
7
- def perform(payload = {})
8
- order = payload[:order]
9
- promotion_code = payload[:promotion_code]
10
-
11
- created_adjustments = order.shipments.map do |shipment|
12
- next if promotion_credit_exists?(shipment)
13
-
14
- shipment.adjustments.create!(
15
- order: shipment.order,
16
- amount: compute_amount(shipment),
17
- source: self,
18
- promotion_code: promotion_code,
19
- label: label
20
- )
21
- end
22
-
23
- # Did we actually end up creating any adjustments?
24
- # If so, then this action should be classed as 'successful'
25
- created_adjustments.any?
26
- end
27
-
28
- def label
29
- "#{I18n.t('spree.promotion')} (#{promotion.name})"
30
- end
31
-
32
- def compute_amount(shipment)
33
- shipment.cost * -1
34
- end
35
-
36
- # Removes any adjustments generated by this action from the order's
37
- # shipments.
38
- # @param order [Spree::Order] the order to remove the action from.
39
- # @return [void]
40
- def remove_from(order)
41
- order.shipments.each do |shipment|
42
- shipment.adjustments.each do |adjustment|
43
- if adjustment.source == self
44
- shipment.adjustments.destroy(adjustment)
45
- end
46
- end
47
- end
48
- end
49
-
50
- private
51
-
52
- def promotion_credit_exists?(shipment)
53
- shipment.adjustments.where(source: self).exists?
54
- end
55
- end
56
- end
57
- end
58
- end