spree_core 4.10.1 → 5.0.0.rc1

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 (476) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/spree_core_manifest.js +4 -0
  3. data/app/assets/images/google_on_white_hdpi.png +0 -0
  4. data/app/controllers/spree/base_controller.rb +1 -4
  5. data/app/finders/spree/orders/find_complete.rb +14 -2
  6. data/app/finders/spree/products/find.rb +74 -23
  7. data/app/finders/spree/stores/find_current.rb +6 -2
  8. data/app/helpers/spree/addresses_helper.rb +106 -0
  9. data/app/helpers/spree/base_helper.rb +83 -55
  10. data/app/helpers/spree/currency_helper.rb +12 -0
  11. data/app/helpers/spree/images_helper.rb +18 -0
  12. data/app/helpers/spree/mail_helper.rb +24 -0
  13. data/app/helpers/spree/products_helper.rb +1 -20
  14. data/app/javascript/spree/core/controllers/address_autocomplete_controller.js +233 -0
  15. data/app/javascript/spree/core/controllers/address_form_controller.js +98 -0
  16. data/app/javascript/spree/core/controllers/enable_button_controller.js +29 -0
  17. data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js +179 -0
  18. data/app/javascript/spree/core/helpers/debounce.js +9 -0
  19. data/app/javascript/spree/core/helpers/scroll_lock.js +25 -0
  20. data/app/jobs/spree/addresses/geocode_address_job.rb +31 -0
  21. data/app/jobs/spree/base_job.rb +1 -0
  22. data/app/jobs/spree/coupon_codes/bulk_generate_job.rb +17 -0
  23. data/app/jobs/spree/exports/generate_job.rb +12 -0
  24. data/app/jobs/spree/products/auto_match_taxons_job.rb +14 -0
  25. data/app/jobs/spree/products/touch_taxons_job.rb +12 -0
  26. data/app/jobs/spree/reports/generate_job.rb +12 -0
  27. data/app/jobs/spree/stock_locations/stock_items/create_job.rb +1 -1
  28. data/app/jobs/spree/themes/duplicate_components_job.rb +59 -0
  29. data/app/jobs/spree/themes/screenshot_job.rb +81 -0
  30. data/app/jobs/spree/variants/remove_from_incomplete_orders_job.rb +2 -0
  31. data/app/jobs/spree/variants/remove_line_item_job.rb +2 -0
  32. data/app/mailers/spree/base_mailer.rb +51 -0
  33. data/app/mailers/spree/export_mailer.rb +19 -0
  34. data/app/mailers/spree/report_mailer.rb +20 -0
  35. data/app/mailers/spree/test_mailer.rb +8 -0
  36. data/app/models/action_text/rich_text_decorator.rb +11 -0
  37. data/app/models/action_text/video_embed.rb +13 -0
  38. data/app/models/acts_as_taggable_on/tag_decorator.rb +10 -0
  39. data/app/models/concerns/spree/calculated_adjustments.rb +2 -0
  40. data/app/models/concerns/spree/display_on.rb +13 -0
  41. data/app/models/concerns/spree/has_one_link.rb +42 -0
  42. data/app/models/concerns/spree/has_page_links.rb +53 -0
  43. data/app/models/concerns/spree/metadata.rb +3 -7
  44. data/app/models/concerns/spree/multi_searchable.rb +21 -0
  45. data/app/models/concerns/spree/multi_store_resource.rb +9 -0
  46. data/app/models/concerns/spree/number_as_param.rb +4 -2
  47. data/app/models/concerns/spree/parameterizable_name.rb +21 -0
  48. data/app/models/concerns/spree/previewable.rb +17 -0
  49. data/app/models/concerns/spree/product_scopes.rb +7 -7
  50. data/app/models/concerns/spree/stores/setup.rb +52 -0
  51. data/app/models/concerns/spree/stores/socials.rb +68 -0
  52. data/app/models/concerns/spree/user_address.rb +4 -0
  53. data/app/models/concerns/spree/user_methods.rb +61 -11
  54. data/app/models/concerns/spree/user_reporting.rb +14 -6
  55. data/app/models/concerns/spree/user_roles.rb +1 -1
  56. data/app/models/spree/ability.rb +4 -2
  57. data/app/models/spree/address.rb +122 -28
  58. data/app/models/spree/adjustment.rb +4 -1
  59. data/app/models/spree/asset/support/active_storage.rb +1 -1
  60. data/app/models/spree/asset.rb +18 -6
  61. data/app/models/spree/base.rb +38 -7
  62. data/app/models/spree/base_analytics_event_handler.rb +49 -0
  63. data/app/models/spree/calculator.rb +1 -1
  64. data/app/models/spree/classification.rb +10 -1
  65. data/app/models/spree/country.rb +1 -1
  66. data/app/models/spree/coupon_code.rb +38 -0
  67. data/app/models/spree/credit_card.rb +4 -3
  68. data/app/models/spree/current.rb +9 -0
  69. data/app/models/spree/custom_domain.rb +53 -0
  70. data/app/models/spree/customer_return.rb +13 -1
  71. data/app/models/spree/data_feed.rb +1 -1
  72. data/app/models/spree/digital.rb +5 -6
  73. data/app/models/spree/digital_link.rb +11 -6
  74. data/app/models/spree/export.rb +172 -0
  75. data/app/models/spree/exports/orders.rb +23 -0
  76. data/app/models/spree/exports/products.rb +35 -0
  77. data/app/models/spree/gateway/bogus.rb +5 -0
  78. data/app/models/spree/gateway.rb +14 -4
  79. data/app/models/spree/gateway_customer.rb +13 -0
  80. data/app/models/spree/image/configuration/active_storage.rb +1 -1
  81. data/app/models/spree/inventory_unit.rb +1 -1
  82. data/app/models/spree/legacy_user.rb +3 -1
  83. data/app/models/spree/line_item.rb +37 -2
  84. data/app/models/spree/log_entry.rb +1 -1
  85. data/app/models/spree/option_type.rb +40 -6
  86. data/app/models/spree/option_type_prototype.rb +1 -1
  87. data/app/models/spree/option_value.rb +35 -5
  88. data/app/models/spree/option_value_variant.rb +2 -2
  89. data/app/models/spree/order/address_book.rb +34 -15
  90. data/app/models/spree/order/checkout.rb +12 -2
  91. data/app/models/spree/order/currency_updater.rb +1 -1
  92. data/app/models/spree/order/digital.rb +14 -3
  93. data/app/models/spree/order/emails.rb +1 -1
  94. data/app/models/spree/order/payments.rb +1 -13
  95. data/app/models/spree/order/store_credit.rb +17 -5
  96. data/app/models/spree/order/webhooks.rb +1 -1
  97. data/app/models/spree/order.rb +179 -49
  98. data/app/models/spree/order_promotion.rb +1 -1
  99. data/app/models/spree/order_updater.rb +9 -3
  100. data/app/models/spree/page.rb +184 -0
  101. data/app/models/spree/page_block.rb +69 -0
  102. data/app/models/spree/page_blocks/buttons.rb +29 -0
  103. data/app/models/spree/page_blocks/heading.rb +18 -0
  104. data/app/models/spree/page_blocks/image.rb +17 -0
  105. data/app/models/spree/page_blocks/link.rb +21 -0
  106. data/app/models/spree/page_blocks/mega_nav.rb +33 -0
  107. data/app/models/spree/page_blocks/mega_nav_with_subcategories.rb +32 -0
  108. data/app/models/spree/page_blocks/nav.rb +15 -0
  109. data/app/models/spree/page_blocks/newsletter_form.rb +18 -0
  110. data/app/models/spree/page_blocks/products/buy_buttons.rb +12 -0
  111. data/app/models/spree/page_blocks/products/price.rb +18 -0
  112. data/app/models/spree/page_blocks/products/quantity_selector.rb +12 -0
  113. data/app/models/spree/page_blocks/products/share.rb +8 -0
  114. data/app/models/spree/page_blocks/products/title.rb +15 -0
  115. data/app/models/spree/page_blocks/products/variant_picker.rb +9 -0
  116. data/app/models/spree/page_blocks/subheading.rb +17 -0
  117. data/app/models/spree/page_blocks/text.rb +16 -0
  118. data/app/models/spree/page_link.rb +59 -0
  119. data/app/models/spree/page_section.rb +215 -0
  120. data/app/models/spree/page_sections/announcement_bar.rb +30 -0
  121. data/app/models/spree/page_sections/collection_banner.rb +18 -0
  122. data/app/models/spree/page_sections/custom_code.rb +11 -0
  123. data/app/models/spree/page_sections/featured_posts.rb +43 -0
  124. data/app/models/spree/page_sections/featured_product.rb +46 -0
  125. data/app/models/spree/page_sections/featured_taxon.rb +88 -0
  126. data/app/models/spree/page_sections/featured_taxons.rb +45 -0
  127. data/app/models/spree/page_sections/footer.rb +94 -0
  128. data/app/models/spree/page_sections/header.rb +62 -0
  129. data/app/models/spree/page_sections/image_banner.rb +38 -0
  130. data/app/models/spree/page_sections/image_with_text.rb +48 -0
  131. data/app/models/spree/page_sections/main_password_footer.rb +18 -0
  132. data/app/models/spree/page_sections/main_password_header.rb +20 -0
  133. data/app/models/spree/page_sections/newsletter.rb +47 -0
  134. data/app/models/spree/page_sections/page_title.rb +19 -0
  135. data/app/models/spree/page_sections/post_details.rb +19 -0
  136. data/app/models/spree/page_sections/post_grid.rb +19 -0
  137. data/app/models/spree/page_sections/product_details.rb +16 -0
  138. data/app/models/spree/page_sections/product_grid.rb +13 -0
  139. data/app/models/spree/page_sections/related_products.rb +60 -0
  140. data/app/models/spree/page_sections/rich_text.rb +20 -0
  141. data/app/models/spree/page_sections/taxon_banner.rb +18 -0
  142. data/app/models/spree/page_sections/taxon_grid.rb +17 -0
  143. data/app/models/spree/page_sections/video.rb +99 -0
  144. data/app/models/spree/pages/account.rb +15 -0
  145. data/app/models/spree/pages/cart.rb +9 -0
  146. data/app/models/spree/pages/checkout.rb +9 -0
  147. data/app/models/spree/pages/custom.rb +34 -0
  148. data/app/models/spree/pages/homepage.rb +68 -0
  149. data/app/models/spree/pages/login.rb +15 -0
  150. data/app/models/spree/pages/password.rb +57 -0
  151. data/app/models/spree/pages/post.rb +27 -0
  152. data/app/models/spree/pages/post_list.rb +32 -0
  153. data/app/models/spree/pages/product_details.rb +29 -0
  154. data/app/models/spree/pages/search_results.rb +43 -0
  155. data/app/models/spree/pages/shop_all.rb +36 -0
  156. data/app/models/spree/pages/taxon.rb +29 -0
  157. data/app/models/spree/pages/taxon_list.rb +41 -0
  158. data/app/models/spree/pages/wishlist.rb +11 -0
  159. data/app/models/spree/payment/gateway_options.rb +13 -15
  160. data/app/models/spree/payment/processing.rb +1 -1
  161. data/app/models/spree/payment/webhooks.rb +1 -1
  162. data/app/models/spree/payment.rb +61 -12
  163. data/app/models/spree/payment_capture_event.rb +1 -1
  164. data/app/models/spree/payment_method/store_credit.rb +9 -1
  165. data/app/models/spree/payment_method.rb +26 -8
  166. data/app/models/spree/payment_source.rb +2 -2
  167. data/app/models/spree/post.rb +108 -0
  168. data/app/models/spree/post_category.rb +33 -0
  169. data/app/models/spree/preference.rb +1 -1
  170. data/app/models/spree/price.rb +10 -3
  171. data/app/models/spree/product/webhooks.rb +1 -1
  172. data/app/models/spree/product.rb +325 -54
  173. data/app/models/spree/product_option_type.rb +1 -1
  174. data/app/models/spree/product_promotion_rule.rb +1 -1
  175. data/app/models/spree/product_property.rb +4 -1
  176. data/app/models/spree/promotion/actions/create_line_items.rb +37 -2
  177. data/app/models/spree/promotion/rules/country.rb +22 -4
  178. data/app/models/spree/promotion/rules/currency.rb +21 -0
  179. data/app/models/spree/promotion/rules/item_total.rb +12 -3
  180. data/app/models/spree/promotion/rules/option_value.rb +5 -20
  181. data/app/models/spree/promotion/rules/product.rb +33 -1
  182. data/app/models/spree/promotion/rules/taxon.rb +31 -0
  183. data/app/models/spree/promotion/rules/user.rb +33 -4
  184. data/app/models/spree/promotion.rb +136 -15
  185. data/app/models/spree/promotion_action.rb +31 -1
  186. data/app/models/spree/promotion_action_line_item.rb +1 -1
  187. data/app/models/spree/promotion_category.rb +1 -1
  188. data/app/models/spree/promotion_handler/cart.rb +1 -1
  189. data/app/models/spree/promotion_handler/coupon.rb +46 -8
  190. data/app/models/spree/promotion_handler/free_shipping.rb +1 -1
  191. data/app/models/spree/promotion_handler/promotion_duplicator.rb +6 -5
  192. data/app/models/spree/promotion_rule.rb +23 -1
  193. data/app/models/spree/promotion_rule_taxon.rb +1 -1
  194. data/app/models/spree/promotion_rule_user.rb +1 -1
  195. data/app/models/spree/property.rb +11 -3
  196. data/app/models/spree/property_prototype.rb +1 -1
  197. data/app/models/spree/prototype.rb +1 -1
  198. data/app/models/spree/prototype_taxon.rb +1 -1
  199. data/app/models/spree/refund.rb +24 -4
  200. data/app/models/spree/refund_reason.rb +12 -2
  201. data/app/models/spree/reimbursement/credit.rb +1 -1
  202. data/app/models/spree/reimbursement/emails.rb +1 -1
  203. data/app/models/spree/reimbursement.rb +5 -3
  204. data/app/models/spree/reimbursement_type.rb +1 -1
  205. data/app/models/spree/report.rb +110 -0
  206. data/app/models/spree/report_line_item.rb +27 -0
  207. data/app/models/spree/report_line_items/products_performance.rb +76 -0
  208. data/app/models/spree/report_line_items/sales_total.rb +49 -0
  209. data/app/models/spree/reports/products_performance.rb +44 -0
  210. data/app/models/spree/reports/sales_total.rb +14 -0
  211. data/app/models/spree/return_authorization.rb +4 -1
  212. data/app/models/spree/return_authorization_reason.rb +1 -1
  213. data/app/models/spree/return_item.rb +2 -1
  214. data/app/models/spree/role.rb +5 -2
  215. data/app/models/spree/role_user.rb +2 -2
  216. data/app/models/spree/shipment/emails.rb +1 -1
  217. data/app/models/spree/shipment/webhooks.rb +1 -1
  218. data/app/models/spree/shipment.rb +14 -4
  219. data/app/models/spree/shipping_category.rb +1 -1
  220. data/app/models/spree/shipping_method.rb +48 -8
  221. data/app/models/spree/shipping_method_category.rb +1 -1
  222. data/app/models/spree/shipping_method_zone.rb +1 -1
  223. data/app/models/spree/shipping_rate.rb +21 -3
  224. data/app/models/spree/state.rb +1 -1
  225. data/app/models/spree/state_change.rb +1 -1
  226. data/app/models/spree/stock_item/webhooks.rb +1 -1
  227. data/app/models/spree/stock_item.rb +9 -6
  228. data/app/models/spree/stock_location.rb +35 -8
  229. data/app/models/spree/stock_movement/webhooks.rb +1 -1
  230. data/app/models/spree/stock_movement.rb +9 -3
  231. data/app/models/spree/stock_transfer.rb +41 -3
  232. data/app/models/spree/store.rb +345 -72
  233. data/app/models/spree/store_credit.rb +12 -4
  234. data/app/models/spree/store_credit_category.rb +1 -1
  235. data/app/models/spree/store_credit_event.rb +21 -1
  236. data/app/models/spree/store_credit_type.rb +1 -1
  237. data/app/models/spree/store_payment_method.rb +1 -1
  238. data/app/models/spree/store_product.rb +1 -1
  239. data/app/models/spree/store_promotion.rb +1 -1
  240. data/app/models/spree/tax_category.rb +15 -1
  241. data/app/models/spree/tax_rate.rb +1 -1
  242. data/app/models/spree/taxon.rb +210 -27
  243. data/app/models/spree/taxon_image/configuration/active_storage.rb +1 -1
  244. data/app/models/spree/taxon_rule.rb +25 -0
  245. data/app/models/spree/taxon_rules/available_on.rb +20 -0
  246. data/app/models/spree/taxon_rules/sale.rb +16 -0
  247. data/app/models/spree/taxon_rules/tag.rb +19 -0
  248. data/app/models/spree/taxonomy.rb +3 -2
  249. data/app/models/spree/theme.rb +200 -0
  250. data/app/models/spree/themes/default.rb +94 -0
  251. data/app/models/spree/variant/webhooks.rb +1 -1
  252. data/app/models/spree/variant.rb +211 -62
  253. data/app/models/spree/wished_item.rb +1 -1
  254. data/app/models/spree/wishlist.rb +1 -1
  255. data/app/models/spree/zone.rb +1 -1
  256. data/app/models/spree/zone_member.rb +1 -1
  257. data/app/presenters/spree/csv/order_line_item_presenter.rb +145 -0
  258. data/app/presenters/spree/csv/product_presenter.rb +63 -0
  259. data/app/presenters/spree/csv/product_variant_presenter.rb +130 -0
  260. data/app/services/spree/addresses/create.rb +31 -6
  261. data/app/services/spree/addresses/helper.rb +7 -4
  262. data/app/services/spree/addresses/update.rb +79 -12
  263. data/app/services/spree/cart/remove_out_of_stock_items.rb +56 -0
  264. data/app/services/spree/checkout/add_store_credit.rb +6 -1
  265. data/app/services/spree/checkout/advance.rb +44 -4
  266. data/app/services/spree/checkout/select_shipping_method.rb +2 -0
  267. data/app/services/spree/coupon_codes/bulk_generate.rb +37 -0
  268. data/app/services/spree/coupon_codes/coupon_codes_handler.rb +34 -0
  269. data/app/services/spree/orders/create_user_account.rb +70 -0
  270. data/app/services/spree/orders/update_contact_information.rb +19 -0
  271. data/app/services/spree/products/auto_match_taxons.rb +64 -0
  272. data/app/services/spree/products/duplicator.rb +121 -0
  273. data/app/services/spree/products/prepare_nested_attributes.rb +169 -0
  274. data/app/services/spree/seeds/admin_user.rb +22 -0
  275. data/app/services/spree/seeds/all.rb +1 -0
  276. data/app/services/spree/seeds/stores.rb +5 -10
  277. data/app/services/spree/seeds/zones.rb +2 -0
  278. data/app/services/spree/tags/bulk_add.rb +38 -0
  279. data/app/services/spree/tags/bulk_remove.rb +24 -0
  280. data/app/services/spree/taxons/add_products.rb +39 -0
  281. data/app/services/spree/taxons/regenerate_products.rb +53 -0
  282. data/app/services/spree/taxons/remove_products.rb +53 -0
  283. data/app/services/spree/taxons/touch_featured_sections.rb +21 -0
  284. data/app/services/spree/themes/duplicator.rb +40 -0
  285. data/app/services/spree/variants/remove_line_items.rb +1 -1
  286. data/app/sorters/spree/classifications/sort.rb +68 -0
  287. data/app/sorters/spree/products/sort.rb +2 -3
  288. data/app/views/action_text/video_embeds/_thumbnail.html.erb +1 -0
  289. data/app/views/action_text/video_embeds/_video_embed.html.erb +3 -0
  290. data/app/views/active_storage/blobs/_blob.html.erb +20 -0
  291. data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
  292. data/app/views/layouts/spree/base_mailer.html.erb +46 -0
  293. data/app/views/spree/addresses/_form.html.erb +58 -0
  294. data/app/views/spree/addresses/_suggestions_box.erb +14 -0
  295. data/app/views/spree/export_mailer/export_done.html.erb +15 -0
  296. data/app/views/spree/report_mailer/report_done.html.erb +15 -0
  297. data/app/views/spree/shared/_base_mailer_footer.html.erb +12 -0
  298. data/app/views/spree/shared/_base_mailer_header.html.erb +6 -0
  299. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +456 -0
  300. data/app/views/spree/shared/_google_places_api_loader.html.erb +7 -0
  301. data/app/views/spree/shared/_mailer_line_item.html.erb +12 -0
  302. data/app/views/spree/shared/_mailer_logo.html.erb +41 -0
  303. data/app/views/spree/shared/_payment.html.erb +41 -0
  304. data/app/views/spree/test_mailer/test_email.html.erb +40 -0
  305. data/app/views/spree/test_mailer/test_email.text.erb +4 -0
  306. data/config/i18n-tasks.yml +178 -0
  307. data/config/importmap.rb +14 -0
  308. data/config/initializers/acts_as_taggable_on.rb +2 -0
  309. data/config/initializers/inflections.rb +2 -0
  310. data/config/initializers/mobility.rb +4 -0
  311. data/config/initializers/oembed.rb +1 -0
  312. data/config/locales/en.yml +823 -413
  313. data/config/routes.rb +6 -5
  314. data/db/migrate/20210914000000_spree_four_three.rb +0 -79
  315. data/db/migrate/20240913143518_change_default_value_of_spree_option_types_filterable.rb +5 -0
  316. data/db/migrate/20240914153106_add_display_on_to_spree_properties.rb +5 -0
  317. data/db/migrate/20240915144935_add_position_to_spree_properties.rb +6 -0
  318. data/db/migrate/20241004181911_create_spree_coupon_codes.rb +15 -0
  319. data/db/migrate/20241005093437_add_multi_code_to_spree_promotions.rb +14 -0
  320. data/db/migrate/20241014140140_translate_rich_texts.rb +17 -0
  321. data/db/migrate/20241030134309_create_spree_exports.rb +23 -0
  322. data/db/migrate/20241104083457_migrate_spree_promotion_rules_option_value_eligible_values.rb +16 -0
  323. data/db/migrate/20241123110646_add_estimated_transit_fields_to_spree_shipping_methods.rb +6 -0
  324. data/db/migrate/20241127193411_create_spree_taxon_rules.rb +13 -0
  325. data/db/migrate/20241127223627_add_rules_match_policy_and_sort_order_to_spree_taxons.rb +6 -0
  326. data/db/migrate/20241128103947_add_automatic_to_spree_taxons.rb +5 -0
  327. data/db/migrate/20241218183905_add_gateway_customer_profile_id_to_spree_payment_sources.rb +5 -0
  328. data/db/migrate/20250107220432_add_accept_marketing_and_signup_for_an_account_to_spree_orders.rb +6 -0
  329. data/db/migrate/20250110171203_add_quick_checkout_to_spree_addresses.rb +6 -0
  330. data/db/migrate/20250113180019_add_preferences_to_spree_stores.rb +5 -0
  331. data/db/migrate/20250114193857_add_metadata_to_spree_stores.rb +13 -0
  332. data/db/migrate/20250119165904_create_spree_custom_domains.rb +22 -0
  333. data/db/migrate/20250120094216_create_page_builder_models.rb +78 -0
  334. data/db/migrate/20250120152208_add_default_locale_to_action_text_rich_texts.rb +7 -0
  335. data/db/migrate/20250121160028_create_spree_posts_and_spree_post_categories.rb +33 -0
  336. data/db/migrate/20250122113708_add_first_and_last_name_to_spree_admin_class.rb +9 -0
  337. data/db/migrate/20250123135358_add_custom_code_fields_to_spree_store.rb +7 -0
  338. data/db/migrate/20250127083740_add_kind_to_spree_properties.rb +5 -0
  339. data/db/migrate/20250127112758_add_latitude_and_longitude_to_spree_addresses.rb +6 -0
  340. data/db/migrate/20250127151258_add_phone_to_spree_users.rb +5 -0
  341. data/db/migrate/20250204162453_create_spree_reports.rb +13 -0
  342. data/db/migrate/20250207084000_add_accepts_email_marketing_field_to_spree_users_table.rb +6 -0
  343. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +11 -0
  344. data/db/migrate/20250304115943_add_performed_by_to_spree_reimbursements.rb +5 -0
  345. data/db/migrate/20250305121352_remove_page_builder_indices.rb +11 -0
  346. data/db/migrate/20250305121657_remove_spree_posts_indices.rb +7 -0
  347. data/db/migrate/20250311105934_create_spree_gateway_customers.rb +18 -0
  348. data/db/migrate/20250312202705_add_unique_index_on_promotion_rules_associations.rb +75 -0
  349. data/db/migrate/20250313104226_add_user_type_to_spree_role_users.rb +18 -0
  350. data/db/migrate/20250313175830_add_unique_index_on_promotion_action_line_items.rb +29 -0
  351. data/db/migrate/20250314144210_create_spree_taggings_and_spree_tags.rb +38 -0
  352. data/lib/generators/spree/authentication/custom/custom_generator.rb +49 -0
  353. data/lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt +50 -0
  354. data/lib/generators/spree/authentication/devise/devise_generator.rb +57 -0
  355. data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +50 -0
  356. data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
  357. data/lib/generators/spree/dummy/templates/rails/database.yml +1 -0
  358. data/lib/generators/spree/dummy/templates/rails/test.rb +1 -1
  359. data/lib/generators/spree/install/install_generator.rb +21 -36
  360. data/lib/generators/spree/install/templates/config/initializers/spree.rb +40 -6
  361. data/lib/normalize_string.rb +18 -0
  362. data/lib/spree/analytics.rb +11 -0
  363. data/lib/spree/core/components.rb +1 -1
  364. data/lib/spree/core/configuration.rb +14 -11
  365. data/lib/spree/core/controller_helpers/auth.rb +0 -26
  366. data/lib/spree/core/controller_helpers/order.rb +77 -12
  367. data/lib/spree/core/controller_helpers/search.rb +2 -0
  368. data/lib/spree/core/controller_helpers/store.rb +0 -5
  369. data/lib/spree/core/controller_helpers/strong_parameters.rb +4 -0
  370. data/lib/spree/core/controller_helpers/turbo.rb +19 -0
  371. data/lib/spree/core/dependencies.rb +3 -5
  372. data/lib/spree/core/engine.rb +158 -7
  373. data/lib/spree/core/preferences/configuration.rb +2 -44
  374. data/lib/spree/core/preferences/preferable.rb +9 -0
  375. data/lib/spree/core/preferences/preferable_class_methods.rb +11 -1
  376. data/lib/spree/core/preferences/runtime_configuration.rb +13 -2
  377. data/lib/spree/core/search/base.rb +2 -0
  378. data/lib/spree/core/version.rb +1 -1
  379. data/lib/spree/core.rb +61 -11
  380. data/lib/spree/i18n.rb +5 -8
  381. data/lib/spree/migrations.rb +1 -1
  382. data/lib/spree/permitted_attributes.rb +24 -15
  383. data/lib/spree/testing_support/authorization_helpers.rb +9 -6
  384. data/lib/spree/testing_support/capybara_config.rb +11 -0
  385. data/lib/spree/testing_support/common_rake.rb +20 -12
  386. data/lib/spree/testing_support/factories/calculator_factory.rb +16 -0
  387. data/lib/spree/testing_support/factories/country_factory.rb +8 -0
  388. data/lib/spree/testing_support/factories/coupon_code_factory.rb +7 -0
  389. data/lib/spree/testing_support/factories/custom_domain_factory.rb +7 -0
  390. data/lib/spree/testing_support/factories/export_factory.rb +32 -0
  391. data/lib/spree/testing_support/factories/gateway_customer_factory.rb +7 -0
  392. data/lib/spree/testing_support/factories/order_factory.rb +14 -3
  393. data/lib/spree/testing_support/factories/page_block_factory.rb +21 -0
  394. data/lib/spree/testing_support/factories/page_factory.rb +33 -0
  395. data/lib/spree/testing_support/factories/page_link_factory.rb +7 -0
  396. data/lib/spree/testing_support/factories/page_section_factory.rb +24 -0
  397. data/lib/spree/testing_support/factories/payment_factory.rb +2 -2
  398. data/lib/spree/testing_support/factories/post_category_factory.rb +7 -0
  399. data/lib/spree/testing_support/factories/post_factory.rb +10 -0
  400. data/lib/spree/testing_support/factories/product_factory.rb +3 -8
  401. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -1
  402. data/lib/spree/testing_support/factories/promotion_action_factory.rb +5 -0
  403. data/lib/spree/testing_support/factories/promotion_factory.rb +7 -9
  404. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +12 -0
  405. data/lib/spree/testing_support/factories/property_factory.rb +1 -1
  406. data/lib/spree/testing_support/factories/report_factory.rb +33 -0
  407. data/lib/spree/testing_support/factories/role_user_factory.rb +7 -0
  408. data/lib/spree/testing_support/factories/shipping_method_factory.rb +9 -0
  409. data/lib/spree/testing_support/factories/stock_transfer_factory.rb +5 -2
  410. data/lib/spree/testing_support/factories/store_factory.rb +6 -9
  411. data/lib/spree/testing_support/factories/taxon_factory.rb +13 -0
  412. data/lib/spree/testing_support/factories/taxon_rule_factory.rb +38 -0
  413. data/lib/spree/testing_support/factories/theme_factory.rb +14 -0
  414. data/lib/spree/testing_support/factories/user_factory.rb +13 -11
  415. data/lib/spree/testing_support/factories/variant_factory.rb +23 -5
  416. data/lib/spree/testing_support/jobs.rb +1 -7
  417. data/lib/spree/testing_support/metadata.rb +13 -1
  418. data/lib/spree/testing_support/order_walkthrough.rb +1 -7
  419. data/lib/spree/testing_support/preferences.rb +1 -4
  420. data/lib/spree/testing_support/store.rb +26 -0
  421. data/lib/spree_core.rb +1 -0
  422. data/vendor/javascript/@rails--request.js.js +4 -0
  423. data/vendor/javascript/@stimulus-components--auto-submit.js +4 -0
  424. data/vendor/javascript/stimulus-reveal-controller.js +4 -0
  425. data/vendor/javascript/tailwindcss-stimulus-components.js +4 -0
  426. metadata +340 -197
  427. data/Gemfile +0 -3
  428. data/app/finders/spree/cms_pages/find.rb +0 -41
  429. data/app/finders/spree/menus/find.rb +0 -11
  430. data/app/models/concerns/spree/display_link.rb +0 -30
  431. data/app/models/spree/cms/pages/feature_page.rb +0 -7
  432. data/app/models/spree/cms/pages/homepage.rb +0 -20
  433. data/app/models/spree/cms/pages/standard_page.rb +0 -4
  434. data/app/models/spree/cms/sections/featured_article.rb +0 -22
  435. data/app/models/spree/cms/sections/hero_image.rb +0 -38
  436. data/app/models/spree/cms/sections/image_gallery.rb +0 -97
  437. data/app/models/spree/cms/sections/product_carousel.rb +0 -14
  438. data/app/models/spree/cms/sections/rich_text_content.rb +0 -13
  439. data/app/models/spree/cms/sections/side_by_side_images.rb +0 -68
  440. data/app/models/spree/cms_page.rb +0 -70
  441. data/app/models/spree/cms_section.rb +0 -69
  442. data/app/models/spree/cms_section_image.rb +0 -9
  443. data/app/models/spree/cms_section_image_one.rb +0 -4
  444. data/app/models/spree/cms_section_image_three.rb +0 -4
  445. data/app/models/spree/cms_section_image_two.rb +0 -4
  446. data/app/models/spree/icon.rb +0 -7
  447. data/app/models/spree/linkable/homepage.rb +0 -3
  448. data/app/models/spree/linkable/uri.rb +0 -3
  449. data/app/models/spree/menu.rb +0 -66
  450. data/app/models/spree/menu_item.rb +0 -72
  451. data/brakeman.ignore +0 -328
  452. data/db/migrate/20211201202851_update_linkable_resource_types.rb +0 -10
  453. data/db/migrate/20220329113557_fix_cms_pages_unique_indexes.rb +0 -8
  454. data/lib/generators/spree/custom_authentication/custom_authentication_generator.rb +0 -23
  455. data/lib/generators/spree/custom_authentication/templates/authentication_helpers.rb.tt +0 -23
  456. data/lib/generators/spree/custom_user/custom_user_generator.rb +0 -46
  457. data/lib/generators/spree/custom_user/templates/current_user_helpers.rb.tt +0 -15
  458. data/lib/generators/spree/custom_user/templates/initializer.rb.tt +0 -1
  459. data/lib/generators/spree/custom_user/templates/migration.rb.tt +0 -7
  460. data/lib/generators/spree/dummy/templates/package.json +0 -12
  461. data/lib/spree/core/product_duplicator.rb +0 -85
  462. data/lib/spree/core/runtime_configuration.rb +0 -10
  463. data/lib/spree/testing_support/factories/cms_page_factory.rb +0 -20
  464. data/lib/spree/testing_support/factories/cms_section_factory.rb +0 -31
  465. data/lib/spree/testing_support/factories/icon_factory.rb +0 -9
  466. data/lib/spree/testing_support/factories/menu_factory.rb +0 -16
  467. data/lib/spree/testing_support/factories/menu_item_factory.rb +0 -10
  468. data/script/rails +0 -9
  469. data/spec/fixtures/favicon.ico +0 -0
  470. data/spec/fixtures/files/icon_256x256.gif +0 -0
  471. data/spec/fixtures/files/icon_256x256.png +0 -0
  472. data/spec/fixtures/files/icon_512x512.png +0 -0
  473. data/spec/fixtures/files/img_256x128.png +0 -0
  474. data/spec/fixtures/text-file.txt +0 -1
  475. data/spec/fixtures/thinking-cat.jpg +0 -0
  476. data/spree_core.gemspec +0 -62
@@ -0,0 +1,130 @@
1
+ module Spree
2
+ module CSV
3
+ class ProductVariantPresenter
4
+ CSV_HEADERS = [
5
+ 'product_id',
6
+ 'sku',
7
+ 'name',
8
+ 'slug',
9
+ 'status',
10
+ 'vendor_name',
11
+ 'brand_name',
12
+ 'description',
13
+ 'meta_title',
14
+ 'meta_description',
15
+ 'meta_keywords',
16
+ 'tags',
17
+ 'labels',
18
+ 'price',
19
+ 'compare_at_price',
20
+ 'currency',
21
+ 'width',
22
+ 'height',
23
+ 'depth',
24
+ 'dimensions_unit',
25
+ 'weight',
26
+ 'weight_unit',
27
+ 'available_on',
28
+ 'discontinue_on',
29
+ 'track_inventory',
30
+ 'inventory_count',
31
+ 'inventory_backorderable',
32
+ 'tax_category',
33
+ 'digital',
34
+ 'image1_src',
35
+ 'image2_src',
36
+ 'image3_src',
37
+ 'option1_name',
38
+ 'option1_value',
39
+ 'option2_name',
40
+ 'option2_value',
41
+ 'option3_name',
42
+ 'option3_value',
43
+ 'category1',
44
+ 'category2',
45
+ 'category3',
46
+ ].freeze
47
+
48
+ def initialize(product, variant, index = 0, properties = [], taxons = [], store = nil)
49
+ @product = product
50
+ @variant = variant
51
+ @index = index
52
+ @properties = properties
53
+ @taxons = taxons
54
+ @store = store || product.stores.first
55
+ @currency = @store.default_currency
56
+ end
57
+
58
+ attr_accessor :product, :variant, :index, :properties, :taxons, :store, :currency
59
+
60
+ ##
61
+ # Generates an array representing a CSV row of product variant data.
62
+ #
63
+ # For the primary variant row (when the index is zero), product-level details such as name,
64
+ # slug, status, vendor and brand names, description, meta tags, and tag/label lists are included.
65
+ # In all cases, variant-specific attributes (e.g., id, SKU, pricing, dimensions, weight,
66
+ # availability dates, inventory count, digital flag, tax category, image URLs via original_url,
67
+ # and the first three option types and corresponding option values) are appended.
68
+ # Additionally, when the index is zero, associated taxons and properties are added.
69
+ #
70
+ # @return [Array] An array containing the combined product and variant CSV data.
71
+ def call
72
+ csv = [
73
+ product.id,
74
+ variant.sku,
75
+ index.zero? ? product.name : nil,
76
+ index.zero? ? product.slug : nil,
77
+ index.zero? ? product.status : nil,
78
+ index.zero? ? product.try(:vendor_name) : nil,
79
+ index.zero? ? product.try(:brand_name) : nil,
80
+ index.zero? ? product.description&.html_safe : nil,
81
+ index.zero? ? product.meta_title : nil,
82
+ index.zero? ? product.meta_description : nil,
83
+ index.zero? ? product.meta_keywords : nil,
84
+ index.zero? ? product.tag_list.to_s : nil,
85
+ index.zero? ? product.label_list.to_s : nil,
86
+ variant.amount_in(currency).to_f,
87
+ variant.compare_at_price&.to_f,
88
+ currency,
89
+ variant.width,
90
+ variant.height,
91
+ variant.depth,
92
+ variant.dimensions_unit,
93
+ variant.weight,
94
+ variant.weight_unit,
95
+ variant.available_on&.strftime('%Y-%m-%d %H:%M:%S'),
96
+ (variant.discontinue_on || product.discontinue_on)&.strftime('%Y-%m-%d %H:%M:%S'),
97
+ variant.track_inventory?,
98
+ variant.total_on_hand == BigDecimal::INFINITY ? '∞' : variant.total_on_hand,
99
+ variant.backorderable?,
100
+ variant.tax_category&.name,
101
+ variant.digital?,
102
+ variant.images[0]&.original_url,
103
+ variant.images[1]&.original_url,
104
+ variant.images[2]&.original_url,
105
+ index.positive? ? option_type(0)&.presentation : nil,
106
+ index.positive? ? option_value(option_type(0)) : nil,
107
+ index.positive? ? option_type(1)&.presentation : nil,
108
+ index.positive? ? option_value(option_type(1)) : nil,
109
+ index.positive? ? option_type(2)&.presentation : nil,
110
+ index.positive? ? option_value(option_type(2)) : nil
111
+ ]
112
+
113
+ if index.zero?
114
+ csv += taxons
115
+ csv += properties
116
+ end
117
+
118
+ csv
119
+ end
120
+
121
+ def option_type(index)
122
+ product.option_types[index]
123
+ end
124
+
125
+ def option_value(option_type)
126
+ variant.option_values.find { |ov| ov.option_type == option_type }&.presentation
127
+ end
128
+ end
129
+ end
130
+ end
@@ -6,19 +6,44 @@ module Spree
6
6
 
7
7
  attr_accessor :country
8
8
 
9
- def call(address_params: {}, user: nil)
9
+ def call(address_params: {}, user: nil, **opts)
10
+ order = opts[:order]
11
+ default_billing = opts.fetch(:default_billing, false)
12
+ default_shipping = opts.fetch(:default_shipping, false)
13
+
10
14
  address_params = fill_country_and_state_ids(address_params)
11
15
 
12
16
  address = Spree::Address.new(address_params)
13
17
  address.user = user if user.present?
14
18
 
15
- if address.save
16
- assign_to_user_as_default(user: user, address_id: address.id) if user.present?
17
- success(address)
18
- else
19
- failure(address)
19
+ ApplicationRecord.transaction do
20
+ if address.save
21
+ if user.present?
22
+ if user.addresses.pluck(:id) == [address.id]
23
+ user.update(bill_address_id: address.id, ship_address_id: address.id)
24
+ else
25
+ assign_to_user_as_default(
26
+ user: user,
27
+ address_id: address.id,
28
+ default_billing: default_billing,
29
+ default_shipping: default_shipping
30
+ )
31
+ end
32
+ end
33
+
34
+ assign_to_order(order: order, address_id: address.id) if order.present?
35
+ success(address)
36
+ else
37
+ failure(address)
38
+ end
20
39
  end
21
40
  end
41
+
42
+ private
43
+
44
+ def assign_to_order(order:, address_id:)
45
+ order.update(ship_address_id: address_id, state: 'address')
46
+ end
22
47
  end
23
48
  end
24
49
  end
@@ -31,10 +31,13 @@ module Spree
31
31
  params
32
32
  end
33
33
 
34
- def assign_to_user_as_default(user:, address_id:)
35
- if user.addresses.pluck(:id) == [address_id] # check if it's user first address
36
- user.update(bill_address_id: address_id, ship_address_id: address_id)
37
- end
34
+ def assign_to_user_as_default(user:, address_id:, default_billing: true, default_shipping: true)
35
+ attributes_to_update = {
36
+ ship_address_id: (address_id if default_shipping),
37
+ bill_address_id: (address_id if default_billing),
38
+ }.compact_blank
39
+
40
+ user.update_columns(**attributes_to_update, updated_at: Time.current) if attributes_to_update.present?
38
41
  end
39
42
  end
40
43
  end
@@ -6,27 +6,94 @@ module Spree
6
6
 
7
7
  attr_accessor :country
8
8
 
9
- def call(address:, address_params:)
10
- address_params[:country_id] ||= address.country_id
11
- address_params = fill_country_and_state_ids(address_params)
12
- address_params[:user_id] = address.user_id if address.user_id.present?
9
+ def call(address:, address_params:, **opts)
10
+ order = opts[:order]
11
+ default_billing = opts.fetch(:default_billing, false)
12
+ default_shipping = opts.fetch(:default_shipping, false)
13
+ address_changes_except = opts.fetch(:address_changes_except, [])
14
+ create_new_address_on_update = opts.fetch(:create_new_address_on_update, false)
13
15
 
14
- if address&.editable?
15
- address.update(address_params) ? success(address) : failure(address)
16
- else
17
- if new_address(address_params).valid?
18
- address.destroy
19
- success(new_address)
16
+ prepare_address_params!(address, address_params)
17
+ address.assign_attributes(address_params)
18
+
19
+ address_changed = address.changes.except(*address_changes_except).any?
20
+
21
+ if !address_changed && defaults_changed?(address, default_billing, default_shipping)
22
+ assign_to_user_as_default(
23
+ user: address.user,
24
+ address_id: address.id,
25
+ default_billing: default_billing,
26
+ default_shipping: default_shipping
27
+ )
28
+ end
29
+
30
+ return success(address) unless address_changed
31
+
32
+ if address.editable? && !create_new_address_on_update
33
+ if address.update(address_params)
34
+ if address.user.present?
35
+ assign_to_user_as_default(
36
+ user: address.user,
37
+ address_id: address.id,
38
+ default_billing: default_billing,
39
+ default_shipping: default_shipping
40
+ )
41
+ end
42
+
43
+ order.update(state: 'address') if order.present?
44
+
45
+ success(address)
20
46
  else
21
- failure(new_address)
47
+ failure(address)
48
+ end
49
+ elsif new_address(address_params).valid?
50
+ address.destroy unless create_new_address_on_update
51
+
52
+ if new_address.user.present?
53
+ default_billing = (!create_new_address_on_update && address.user_default_billing?) || default_billing
54
+ default_shipping = (!create_new_address_on_update && address.user_default_shipping?) || default_shipping
55
+
56
+ assign_to_user_as_default(
57
+ user: new_address.user,
58
+ address_id: new_address.id,
59
+ default_billing: default_billing,
60
+ default_shipping: default_shipping
61
+ )
62
+ end
63
+
64
+ if order.present?
65
+ order.ship_address = new_address if !create_new_address_on_update && order.ship_address_id == address.id
66
+ order.bill_address = new_address if !create_new_address_on_update && order.bill_address_id == address.id
67
+ order.state = 'address'
68
+ order.save
22
69
  end
70
+
71
+ success(new_address)
72
+ else
73
+ failure(new_address)
23
74
  end
24
75
  end
25
76
 
26
77
  private
27
78
 
79
+ def prepare_address_params!(address, address_params)
80
+ address_params[:user_id] = address&.user_id
81
+ address_params[:country_id] ||= address.country_id
82
+ address_params = fill_country_and_state_ids(address_params)
83
+ address_params.transform_values!(&:presence)
84
+ end
85
+
86
+ def defaults_changed?(address, default_billing, default_shipping)
87
+ user = address.user
88
+
89
+ user.present? && (
90
+ (default_billing.present? && user.bill_address != address) ||
91
+ (default_shipping.present? && user.ship_address != address)
92
+ )
93
+ end
94
+
28
95
  def new_address(address_params = {})
29
- @new_address ||= ::Spree::Address.find_or_create_by(address_params.except(:id, :updated_at, :created_at))
96
+ @new_address ||= ::Spree::Address.not_deleted.find_or_create_by(address_params.except(:id, :updated_at, :created_at))
30
97
  end
31
98
  end
32
99
  end
@@ -0,0 +1,56 @@
1
+ module Spree
2
+ module Cart
3
+ class RemoveOutOfStockItems
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ def call(order:)
7
+ @messages = []
8
+ ActiveRecord::Base.transaction do
9
+ line_items(order).each do |line_item|
10
+ cart_remove_line_item_service.call(order: order, line_item: line_item) if !valid_status?(line_item) || !stock_available?(line_item)
11
+ end
12
+ end
13
+
14
+ if @messages.any? # If any line item was removed, reload the order
15
+ success([order.reload, @messages])
16
+ else
17
+ success([order, @messages])
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def line_items(order)
24
+ if order.line_items.empty?
25
+ []
26
+ elsif order.line_items.first.association_cached?(:variant) && order.line_items.first.variant.association_cached?(:product)
27
+ # Don't include associations if it is already included, because it breaks other includes
28
+ order.line_items
29
+ else
30
+ order.line_items.includes(variant: :product)
31
+ end
32
+ end
33
+
34
+ def valid_status?(line_item)
35
+ product = line_item.product
36
+ if !product.active? || product.deleted? || product.discontinued? || line_item.variant.discontinued?
37
+ @messages << Spree.t('cart_line_item.discontinued', li_name: line_item.name)
38
+ return false
39
+ end
40
+ true
41
+ end
42
+
43
+ def stock_available?(line_item)
44
+ if line_item.insufficient_stock?
45
+ @messages << Spree.t('cart_line_item.out_of_stock', li_name: line_item.name)
46
+ return false
47
+ end
48
+ true
49
+ end
50
+
51
+ def cart_remove_line_item_service
52
+ Spree::Dependencies.cart_remove_line_item_service.constantize
53
+ end
54
+ end
55
+ end
56
+ end
@@ -17,7 +17,12 @@ module Spree
17
17
  apply_store_credits(remaining_total)
18
18
  end
19
19
 
20
- @order.reload.payments.store_credits.valid.any? ? success(@order) : failure(@order)
20
+ if @order.reload.payments.store_credits.valid.any?
21
+ @order.updater.run_hooks
22
+ success(@order)
23
+ else
24
+ failure(@order)
25
+ end
21
26
  end
22
27
 
23
28
  private
@@ -3,15 +3,55 @@ module Spree
3
3
  class Advance
4
4
  prepend Spree::ServiceModule::Base
5
5
 
6
- def call(order:)
7
- Spree::Dependencies.checkout_next_service.constantize.call(order: order) until cannot_make_transition?(order)
6
+ def call(order:, state: nil, shipping_method_id: nil)
7
+ return failure(order) if state.present? && !order.has_checkout_step?(state)
8
+ return success(order) if state.present? && order.passed_checkout_step?(state)
9
+
10
+ old_state = order.state
11
+ order_updater_ran = false
12
+
13
+ # We need to check how many times we transitioned between checkout steps and return the error if no transition has been made
14
+ # We'll always return an error when passing the `state` arg and not reaching the targeted state
15
+ transitions_count = 0
16
+
17
+ until cannot_make_transition?(order, state)
18
+ next_result = Spree::Dependencies.checkout_next_service.constantize.call(order: order)
19
+ return failure(order, order.errors) if next_result.failure? && (transitions_count.zero? || state.present?)
20
+
21
+ transitions_count +=1
22
+
23
+ # Quick Checkout with Google Pay not always sends events for shipping method selection
24
+ # we have to check this after payment
25
+
26
+ if order.delivery? &&
27
+ shipping_method_id.present? &&
28
+ order.shipments.count == 1 &&
29
+ order.shipping_method.id != shipping_method_id
30
+
31
+ result = Spree::Checkout::SelectShippingMethod.call(order: order, params: { shipping_method_id: shipping_method_id })
32
+
33
+ # We're running the order update inside Spree::Checkout::SelectShippingMethod
34
+ order_updater_ran = result.success?
35
+ end
36
+ end
37
+
38
+ if order.state != old_state && !order_updater_ran
39
+ order.updater.update_shipment_state
40
+ order.updater.update_payment_state
41
+ order.save! if order.changed?
42
+ end
43
+
8
44
  success(order)
9
45
  end
10
46
 
11
47
  private
12
48
 
13
- def cannot_make_transition?(order)
14
- order.confirm? || order.complete? || order.errors.present?
49
+ def cannot_make_transition?(order, state = nil)
50
+ order.state == state || order.confirm? || order.complete? || order.errors.present? || order.passed_checkout_step?(state)
51
+ end
52
+
53
+ def report_advance_error(error, order)
54
+ # You can report checkout advance error here
15
55
  end
16
56
  end
17
57
  end
@@ -26,6 +26,8 @@ module Spree
26
26
  end
27
27
  end
28
28
 
29
+ order.update_with_updater!
30
+
29
31
  success(order)
30
32
  end
31
33
 
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ module CouponCodes
3
+ class BulkGenerate
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(promotion:, quantity: 10)
7
+ coupon_codes = []
8
+
9
+ Spree::CouponCode.transaction do
10
+ quantity.times do
11
+ coupon_codes << coupon_attributes(promotion).merge(code: create_code(promotion.code_prefix))
12
+ end
13
+ Spree::CouponCode.insert_all coupon_codes
14
+ end
15
+
16
+ success(promotion.reload.coupon_codes)
17
+ end
18
+
19
+ private
20
+
21
+ def create_code(prefix = nil)
22
+ loop do
23
+ code = "#{prefix}#{SecureRandom.hex(8)}".downcase
24
+ break code unless Spree::CouponCode.exists?(code: code)
25
+ end
26
+ end
27
+
28
+ def coupon_attributes(promotion)
29
+ {
30
+ promotion_id: promotion.id,
31
+ created_at: Time.current,
32
+ updated_at: Time.current
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module Spree
2
+ module CouponCodes
3
+ class CouponCodesHandler
4
+ attr_reader :order, :codes
5
+
6
+ def initialize(order:)
7
+ @order = order
8
+ @codes = Spree::CouponCode.where(order_id: order.id)
9
+ end
10
+
11
+ def use_all_codes
12
+ return unless codes.any?
13
+
14
+ promotion_ids = find_current_promotions_ids
15
+ use_all_current(promotion_ids)
16
+ clear_all_unused(promotion_ids)
17
+ end
18
+
19
+ private
20
+
21
+ def find_current_promotions_ids
22
+ order.all_adjustments.promotion.eligible.map { |a| a.source.try(:promotion_id) }.compact
23
+ end
24
+
25
+ def use_all_current(promotion_ids)
26
+ codes.in_promotions(promotion_ids).update_all(state: 1)
27
+ end
28
+
29
+ def clear_all_unused(promotion_ids)
30
+ codes.not_in_promotions(promotion_ids).update_all(order_id: nil)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,70 @@
1
+ # this service creates a user account when someone places an order
2
+ # and checks the box to create an account
3
+ module Spree
4
+ module Orders
5
+ class CreateUserAccount
6
+ prepend ::Spree::ServiceModule::Base
7
+
8
+ def call(order:, accepts_email_marketing: false)
9
+ existing_user = Spree.user_class.find_by(email: order.email)
10
+ return existing_user if existing_user.present?
11
+
12
+ user = create_new_user(order, accepts_email_marketing)
13
+ return failure(:user_creation_failed) unless user.persisted?
14
+
15
+ assign_ship_address(order, user)
16
+ assign_bill_address(order, user)
17
+
18
+ # assign newly created user to the order
19
+ # using update_columns to avoid running validations/callbacks
20
+ order.update_columns(user_id: user.id, updated_at: Time.current)
21
+ order.user = user
22
+
23
+ # send welcome email
24
+ user.send_welcome_email if user.respond_to?(:send_welcome_email)
25
+
26
+ success(user.reload)
27
+ end
28
+
29
+ private
30
+
31
+ def create_new_user(order, accepts_email_marketing = false)
32
+ firstname = order.bill_address&.firstname || order.ship_address&.firstname
33
+ lastname = order.bill_address&.lastname || order.ship_address&.lastname
34
+ phone = order.bill_address&.phone || order.ship_address&.phone
35
+
36
+ # we need to generate a password for the user
37
+ password = SecureRandom.base58(16)
38
+
39
+ user = Spree.user_class.new
40
+ user.email = order.email
41
+ user.first_name = firstname if user.respond_to?(:first_name)
42
+ user.last_name = lastname if user.respond_to?(:last_name)
43
+ user.phone = phone if user.respond_to?(:phone)
44
+ user.accepts_email_marketing = accepts_email_marketing.to_b if user.respond_to?(:accepts_email_marketing)
45
+ user.password = password if user.respond_to?(:password)
46
+ user.password_confirmation = password if user.respond_to?(:password_confirmation)
47
+
48
+ user.save
49
+
50
+ user
51
+ end
52
+
53
+ def assign_bill_address(order, user)
54
+ if order.bill_address.present?
55
+ order.bill_address.update_columns(user_id: user.id, updated_at: Time.current)
56
+
57
+ user.update_columns(bill_address_id: order.bill_address_id, updated_at: Time.current) unless user.bill_address_id.present?
58
+ end
59
+ end
60
+
61
+ def assign_ship_address(order, user)
62
+ if order.ship_address.present?
63
+ order.ship_address.update_columns(user_id: user.id, updated_at: Time.current)
64
+
65
+ user.update_columns(ship_address_id: order.ship_address_id, updated_at: Time.current) unless user.ship_address_id.present?
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Orders
3
+ class UpdateContactInformation
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ def call(order:, order_params:)
7
+ ActiveRecord::Base.transaction do
8
+ order.update_columns(email: order_params[:email], updated_at: Time.current)
9
+
10
+ if order.respond_to?(:vendor_orders) && order.vendor_orders.any?
11
+ order.vendor_orders.update_all(email: order_params[:email], updated_at: Time.current)
12
+ end
13
+ end
14
+
15
+ success(order.reload)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ module Spree
2
+ module Products
3
+ class AutoMatchTaxons
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ def call(product:)
7
+ return unless product.present?
8
+
9
+ taxons_to_remove = []
10
+ taxons_to_add = []
11
+
12
+ # we need to check if existing taxons still match
13
+ product.taxons.automatic.includes(:taxon_rules).each do |taxon|
14
+ # products shouldn't be part of the taxon anymore
15
+ # eg. product tags or category was changed
16
+ taxons_to_remove << taxon unless taxon.products_matching_rules.ids.include?(product.id)
17
+ end
18
+
19
+ Spree::Classification.where(taxon: taxons_to_remove, product: product).delete_all if taxons_to_remove.any?
20
+
21
+ # we need to check if product matches any existing taxons
22
+ Spree::Taxon.automatic.includes(:taxon_rules, :products).each do |taxon|
23
+ taxons_to_add << taxon if taxon.products.exclude?(product) && taxon.products_matching_rules.ids.include?(product.id)
24
+ end
25
+
26
+ if taxons_to_add.any?
27
+ products_counts = Spree::Taxon.where(id: taxons_to_add.pluck(:id)).
28
+ joins(:classifications).
29
+ reorder('').
30
+ group(:taxon_id).
31
+ count(:product_id)
32
+
33
+ Spree::Classification.insert_all(
34
+ taxons_to_add.map do |taxon|
35
+ position = products_counts[taxon.id].to_i + 1
36
+ classification_attributes(taxon, product, position)
37
+ end
38
+ )
39
+ end
40
+
41
+ all_affected_taxons = (taxons_to_remove + taxons_to_add).uniq
42
+
43
+ if all_affected_taxons.any?
44
+ Spree::Taxons::TouchFeaturedSections.call(taxon_ids: all_affected_taxons.pluck(:id))
45
+ product.touch
46
+ end
47
+
48
+ success(product)
49
+ end
50
+
51
+ private
52
+
53
+ def classification_attributes(taxon, product, position)
54
+ {
55
+ taxon_id: taxon.id,
56
+ product_id: product.id,
57
+ position: position,
58
+ created_at: Time.current,
59
+ updated_at: Time.current
60
+ }
61
+ end
62
+ end
63
+ end
64
+ end