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
@@ -6,9 +6,10 @@ require_dependency 'spree/order/store_credit'
6
6
  require_dependency 'spree/order/emails'
7
7
 
8
8
  module Spree
9
- class Order < Spree::Base
9
+ class Order < Spree.base_class
10
10
  PAYMENT_STATES = %w(balance_due credit_owed failed paid void)
11
11
  SHIPMENT_STATES = %w(backorder canceled partial pending ready shipped)
12
+ LINE_ITEM_REMOVABLE_STATES = %w(cart address delivery payment confirm resumed)
12
13
 
13
14
  include Spree::Order::Checkout
14
15
  include Spree::Order::CurrencyUpdater
@@ -19,13 +20,13 @@ module Spree
19
20
  include Spree::Order::Emails
20
21
  include Spree::Order::Webhooks
21
22
  include Spree::Core::NumberGenerator.new(prefix: 'R')
22
- include Spree::Core::TokenGenerator
23
23
 
24
24
  include Spree::NumberIdentifier
25
25
  include Spree::NumberAsParam
26
26
  include Spree::SingleStoreResource
27
27
  include Spree::MemoizedData
28
28
  include Spree::Metadata
29
+ include Spree::MultiSearchable
29
30
  if defined?(Spree::Security::Orders)
30
31
  include Spree::Security::Orders
31
32
  end
@@ -33,13 +34,16 @@ module Spree
33
34
  include Spree::VendorConcern
34
35
  end
35
36
 
37
+ has_secure_token :token, length: 35
38
+
36
39
  MEMOIZED_METHODS = %w(tax_zone)
37
40
 
38
41
  extend Spree::DisplayMoney
39
42
  money_methods :outstanding_balance, :item_total, :adjustment_total,
40
43
  :included_tax_total, :additional_tax_total, :tax_total,
41
44
  :shipment_total, :promo_total, :total,
42
- :cart_promo_total, :pre_tax_item_amount, :pre_tax_total
45
+ :cart_promo_total, :pre_tax_item_amount, :pre_tax_total,
46
+ :payment_total
43
47
 
44
48
  alias display_ship_total display_shipment_total
45
49
  alias_attribute :ship_total, :shipment_total
@@ -75,28 +79,22 @@ module Spree
75
79
  self.whitelisted_ransackable_associations = %w[shipments user created_by approver canceler promotions bill_address ship_address line_items store]
76
80
  self.whitelisted_ransackable_attributes = %w[
77
81
  completed_at email number state payment_state shipment_state
78
- total item_total considered_risky channel
82
+ total item_total item_count considered_risky channel
79
83
  ]
84
+ self.whitelisted_ransackable_scopes = %w[refunded partially_refunded multi_search]
80
85
 
81
86
  attr_reader :coupon_code
82
87
  attr_accessor :temporary_address, :temporary_credit_card
83
88
 
84
89
  attribute :state_machine_resumed, :boolean
85
90
 
86
- if Spree.user_class
87
- belongs_to :user, class_name: "::#{Spree.user_class}", optional: true
88
- else
89
- belongs_to :user, optional: true
90
- end
91
- if Spree.admin_user_class
92
- belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", optional: true
93
- belongs_to :approver, class_name: "::#{Spree.admin_user_class}", optional: true
94
- belongs_to :canceler, class_name: "::#{Spree.admin_user_class}", optional: true
95
- else
96
- belongs_to :created_by, optional: true
97
- belongs_to :approver, optional: true
98
- belongs_to :canceler, optional: true
99
- end
91
+ acts_as_taggable_on :tags
92
+ acts_as_taggable_tenant :store_id
93
+
94
+ belongs_to :user, class_name: "::#{Spree.user_class}", optional: true, autosave: true
95
+ belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", optional: true
96
+ belongs_to :approver, class_name: "::#{Spree.admin_user_class}", optional: true
97
+ belongs_to :canceler, class_name: "::#{Spree.admin_user_class}", optional: true
100
98
 
101
99
  belongs_to :bill_address, foreign_key: :bill_address_id, class_name: 'Spree::Address',
102
100
  optional: true, dependent: :destroy
@@ -118,6 +116,7 @@ module Spree
118
116
  has_many :adjustments, -> { order(:created_at) }, as: :adjustable, class_name: 'Spree::Adjustment'
119
117
  end
120
118
  has_many :reimbursements, inverse_of: :order, class_name: 'Spree::Reimbursement'
119
+ has_many :customer_returns, class_name: 'Spree::CustomerReturn', through: :return_authorizations
121
120
  has_many :line_item_adjustments, through: :line_items, source: :adjustments
122
121
  has_many :inventory_units, inverse_of: :order, class_name: 'Spree::InventoryUnit'
123
122
  has_many :return_items, through: :inventory_units, class_name: 'Spree::ReturnItem'
@@ -151,9 +150,9 @@ module Spree
151
150
  before_validation :ensure_currency_presence
152
151
 
153
152
  before_validation :clone_billing_address, if: :use_billing?
154
- attr_accessor :use_billing
153
+ before_validation :clone_shipping_address, if: :use_shipping?
154
+ attr_accessor :use_billing, :use_shipping
155
155
 
156
- before_create :create_token
157
156
  before_create :link_by_email
158
157
  before_update :ensure_updated_shipments, :homogenize_line_item_currencies, if: :currency_changed?
159
158
 
@@ -187,13 +186,46 @@ module Spree
187
186
  scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
188
187
  scope :complete, -> { where.not(completed_at: nil) }
189
188
  scope :incomplete, -> { where(completed_at: nil) }
190
- scope :not_canceled, -> { where.not(state: 'canceled') }
189
+ scope :canceled, -> { where(state: %w[canceled partially_canceled]) }
190
+ scope :not_canceled, -> { where.not(state: %w[canceled partially_canceled]) }
191
+ scope :ready_to_ship, -> { where(shipment_state: %w[ready pending]) }
192
+ scope :partially_shipped, -> { where(shipment_state: %w[partial]) }
193
+ scope :not_shipped, -> { where(shipment_state: %w[ready pending partial]) }
194
+ scope :shipped, -> { where(shipment_state: %w[shipped]) }
195
+ scope :refunded, lambda {
196
+ joins(:refunds).group(:id).having("sum(#{Spree::Refund.table_name}.amount) = #{Spree::Order.table_name}.total")
197
+ }
198
+ scope :partially_refunded, lambda {
199
+ joins(:refunds).group(:id).having("sum(#{Spree::Refund.table_name}.amount) < #{Spree::Order.table_name}.total")
200
+ }
191
201
  scope :with_deleted_bill_address, -> { joins(:bill_address).where.not(Address.table_name => { deleted_at: nil }) }
192
202
  scope :with_deleted_ship_address, -> { joins(:ship_address).where.not(Address.table_name => { deleted_at: nil }) }
193
203
 
194
204
  # shows completed orders first, by their completed_at date, then uncompleted orders by their created_at
195
205
  scope :reverse_chronological, -> { order(Arel.sql('spree_orders.completed_at IS NULL'), completed_at: :desc, created_at: :desc) }
196
206
 
207
+ def self.multi_search(query)
208
+ sanitized_query = sanitize_query_for_multi_search(query)
209
+ return none if query.blank?
210
+
211
+ query_pattern = "%#{sanitized_query}%"
212
+
213
+ conditions = []
214
+ conditions << arel_table[:number].lower.matches(query_pattern)
215
+
216
+ conditions << multi_search_condition(Spree::Address, :firstname, sanitized_query)
217
+ conditions << multi_search_condition(Spree::Address, :lastname, sanitized_query)
218
+
219
+ full_name = NameOfPerson::PersonName.full(sanitized_query)
220
+
221
+ if full_name.first.present? && full_name.last.present?
222
+ conditions << multi_search_condition(Spree::Address, :firstname, full_name.first)
223
+ conditions << multi_search_condition(Spree::Address, :lastname, full_name.last)
224
+ end
225
+
226
+ left_joins(:bill_address).where(email: sanitized_query).or(where(conditions.reduce(:or)))
227
+ end
228
+
197
229
  # Use this method in other gems that wish to register their own custom logic
198
230
  # that should be called after Order#update
199
231
  def self.register_update_hook(hook)
@@ -223,6 +255,19 @@ module Spree
223
255
  completed_at.present?
224
256
  end
225
257
 
258
+ def order_refunded?
259
+ (payment_state == 'void' && refunds.sum(:amount).positive?) || refunds.sum(:amount) == total
260
+ end
261
+
262
+ def partially_refunded?
263
+ return false if refunds.empty?
264
+
265
+ # we must deduct not returned amount, otherwise it can look like order has not been fully refunded
266
+ amount_not_refunded = additional_tax_total.abs + total_applied_store_credit
267
+
268
+ refunds.sum(:amount) < total - amount_not_refunded
269
+ end
270
+
226
271
  # Indicates whether or not the user is allowed to proceed to checkout.
227
272
  # Currently this is implemented as a check for whether or not there is at
228
273
  # least one LineItem in the Order. Feel free to override this logic in your
@@ -231,9 +276,9 @@ module Spree
231
276
  line_items.exists?
232
277
  end
233
278
 
234
- # Does this order require a physical delivery.
279
+ # Does this order require a delivery (physical or digital).
235
280
  def delivery_required?
236
- !digital?
281
+ true # true for Spree, can be decorated
237
282
  end
238
283
 
239
284
  # Is this a free order in which case the payment step should be skipped
@@ -251,10 +296,18 @@ module Spree
251
296
  confirm?
252
297
  end
253
298
 
299
+ def email_required?
300
+ require_email
301
+ end
302
+
254
303
  def backordered?
255
304
  shipments.any?(&:backordered?)
256
305
  end
257
306
 
307
+ def quick_checkout?
308
+ shipping_address.present? && shipping_address.quick_checkout?
309
+ end
310
+
258
311
  # Returns the relevant zone (if any) to be used for taxation purposes.
259
312
  # Uses default tax zone unless there is a specific match
260
313
  def tax_zone
@@ -285,7 +338,7 @@ module Spree
285
338
  def allow_cancel?
286
339
  return false if !completed? || canceled?
287
340
 
288
- shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
341
+ shipment_state.nil? || %w{ready backorder pending canceled}.include?(shipment_state)
289
342
  end
290
343
 
291
344
  def all_inventory_units_returned?
@@ -296,15 +349,19 @@ module Spree
296
349
  def associate_user!(user, override_email = true)
297
350
  self.user = user
298
351
  self.email = user.email if override_email
299
- self.created_by ||= user
300
- self.bill_address ||= user.bill_address.try(:clone)
301
- self.ship_address ||= user.ship_address.try(:clone)
352
+ # we need to check if user is of admin user class to avoid mismatch type error
353
+ # in a scenario where we have separate classes for admin and regular users
354
+ self.created_by ||= user if user.is_a?(Spree.admin_user_class)
355
+ self.bill_address ||= user.bill_address
356
+ self.ship_address ||= user.ship_address
302
357
 
303
358
  changes = slice(:user_id, :email, :created_by_id, :bill_address_id, :ship_address_id)
304
359
 
305
360
  # immediately persist the changes we just made, but don't use save
306
361
  # since we might have an invalid address associated
307
- self.class.unscoped.where(id: self).update_all(changes)
362
+ ActiveRecord::Base.connected_to(role: :writing) do
363
+ self.class.unscoped.where(id: self).update_all(changes)
364
+ end
308
365
  end
309
366
 
310
367
  def quantity_of(variant, options = {})
@@ -359,6 +416,37 @@ module Spree
359
416
  end
360
417
  end
361
418
 
419
+ def full_name
420
+ @full_name ||= if user.present? && user.name.present?
421
+ user.name.full
422
+ else
423
+ billing_address&.full_name || email
424
+ end
425
+ end
426
+
427
+ # Returns the payment method for the order
428
+ #
429
+ # @return [Spree::PaymentMethod] the payment method for the order
430
+ def payment_method
431
+ payments.valid.not_store_credits.first&.payment_method
432
+ end
433
+
434
+ # Returns the payment source for the order
435
+ #
436
+ # @return [Spree::PaymentSource] the payment source for the order
437
+ def payment_source
438
+ payments.valid.not_store_credits.first&.source
439
+ end
440
+
441
+ # Returns the backordered variants for the order
442
+ #
443
+ # @return [Array<Spree::Variant>] the backordered variants for the order
444
+ def backordered_variants
445
+ variants.
446
+ joins(:stock_items, :product).
447
+ where(Spree::StockItem.table_name => { count_on_hand: ..0, backorderable: true })
448
+ end
449
+
362
450
  def can_ship?
363
451
  complete? || resumed? || awaiting_return? || returned?
364
452
  end
@@ -451,17 +539,16 @@ module Spree
451
539
  end
452
540
 
453
541
  def empty!
454
- Spree::Deprecation.warn(<<-DEPRECATION, caller)
455
- `Order#empty!` is deprecated and will be removed in Spree 5.0.
456
- Please use `Spree::Cart::Empty.call(order: order)` instead.
457
- DEPRECATION
458
-
459
542
  raise Spree.t(:cannot_empty_completed_order) if completed?
460
543
 
461
544
  result = Spree::Dependencies.cart_empty_service.constantize.call(order: self)
462
545
  result.value
463
546
  end
464
547
 
548
+ def use_all_coupon_codes
549
+ Spree::CouponCodes::CouponCodesHandler.new(order: self).use_all_codes
550
+ end
551
+
465
552
  def has_step?(step)
466
553
  checkout_steps.include?(step)
467
554
  end
@@ -488,10 +575,10 @@ module Spree
488
575
 
489
576
  def coupon_code=(code)
490
577
  @coupon_code = begin
491
- code.strip.downcase
492
- rescue StandardError
493
- nil
494
- end
578
+ code.strip.downcase
579
+ rescue StandardError
580
+ nil
581
+ end
495
582
  end
496
583
 
497
584
  def can_add_coupon?
@@ -530,6 +617,22 @@ module Spree
530
617
  @total_weight ||= line_items.joins(:variant).includes(:variant).map { |li| li.variant.weight * li.quantity }.sum
531
618
  end
532
619
 
620
+ # Returns line items that have no shipping rates
621
+ #
622
+ # @return [Array<Spree::LineItem>]
623
+ def line_items_without_shipping_rates
624
+ @line_items_without_shipping_rates ||= shipments.map do |shipment|
625
+ shipment.manifest.map(&:line_item) if shipment.shipping_rates.blank?
626
+ end.flatten.compact
627
+ end
628
+
629
+ # Checks if all line items cannot be shipped
630
+ #
631
+ # @returns Boolean
632
+ def all_line_items_invalid?
633
+ line_items_without_shipping_rates.size == line_items.count
634
+ end
635
+
533
636
  def apply_free_shipping_promotions
534
637
  Spree::PromotionHandler::FreeShipping.new(self).activate
535
638
  shipments.each { |shipment| Spree::Adjustable::AdjustmentsUpdater.update(shipment) }
@@ -578,17 +681,28 @@ module Spree
578
681
  persist_totals
579
682
  end
580
683
 
684
+ def shipping_method
685
+ # This query will select the first available shipping method from the shipments.
686
+ # It will use subquery to first select the shipping method id from the shipments' selected_shipping_rate.
687
+ Spree::ShippingMethod.
688
+ where(id: shipments.with_selected_shipping_method.limit(1).pluck(:shipping_method_id)).
689
+ limit(1).
690
+ first
691
+ end
692
+
581
693
  def is_risky?
582
694
  !payments.risky.empty?
583
695
  end
584
696
 
585
- def canceled_by(user)
697
+ def canceled_by(user, canceled_at = nil)
698
+ canceled_at ||= Time.current
699
+
586
700
  transaction do
587
- cancel!
588
701
  update_columns(
589
702
  canceler_id: user.id,
590
- canceled_at: Time.current
703
+ canceled_at: canceled_at
591
704
  )
705
+ cancel!
592
706
  end
593
707
  end
594
708
 
@@ -640,7 +754,7 @@ module Spree
640
754
  end
641
755
 
642
756
  def collect_backend_payment_methods
643
- PaymentMethod.available_on_back_end.select { |pm| pm.available_for_order?(self) }
757
+ PaymentMethod.active.available_on_back_end.select { |pm| pm.available_for_order?(self) }
644
758
  end
645
759
 
646
760
  # determines whether the inventory is fully discounted
@@ -654,7 +768,7 @@ module Spree
654
768
  alias fully_discounted fully_discounted?
655
769
 
656
770
  def promo_code
657
- promotions.pluck(:code).compact.first
771
+ Spree::CouponCode.find_by(order: self, promotion: promotions).try(:code) || promotions.pluck(:code).compact.first
658
772
  end
659
773
 
660
774
  def validate_payments_attributes(attributes)
@@ -700,6 +814,14 @@ module Spree
700
814
  spree_promotion_actions: { type: 'Spree::Promotion::Actions::FreeShipping' }).exists?
701
815
  end
702
816
 
817
+ def to_csv(_store = nil)
818
+ csv_lines = []
819
+ all_line_items.each_with_index do |line_item, index|
820
+ csv_lines << Spree::CSV::OrderLineItemPresenter.new(self, line_item, index).call
821
+ end
822
+ csv_lines
823
+ end
824
+
703
825
  private
704
826
 
705
827
  def link_by_email
@@ -707,8 +829,9 @@ module Spree
707
829
  end
708
830
 
709
831
  # Determine if email is required (we don't want validation errors before we hit the checkout)
832
+ # we need to add delivery to the list for quick checkouts
710
833
  def require_email
711
- true unless new_record? || ['cart', 'address'].include?(state)
834
+ true unless new_record? || ['cart', 'address', 'delivery'].include?(state)
712
835
  end
713
836
 
714
837
  def ensure_line_items_present
@@ -718,11 +841,18 @@ module Spree
718
841
  end
719
842
 
720
843
  def ensure_available_shipping_rates
721
- if shipments.empty? || shipments.any? { |shipment| shipment.shipping_rates.blank? }
844
+ if shipments.empty? || line_items_without_shipping_rates.present?
722
845
  # After this point, order redirects back to 'address' state and asks user to pick a proper address
723
846
  # Therefore, shipments are not necessary at this point.
724
847
  shipments.destroy_all
725
- errors.add(:base, Spree.t(:items_cannot_be_shipped)) && (return false)
848
+
849
+ if line_items_without_shipping_rates.present?
850
+ errors.add(:base, Spree.t(:products_cannot_be_shipped, product_names: line_items_without_shipping_rates.map(&:name).to_sentence))
851
+ else
852
+ errors.add(:base, Spree.t(:items_cannot_be_shipped))
853
+ end
854
+
855
+ return false
726
856
  end
727
857
  end
728
858
 
@@ -748,12 +878,12 @@ module Spree
748
878
  use_billing.in?([true, 'true', '1'])
749
879
  end
750
880
 
751
- def ensure_currency_presence
752
- self.currency ||= store.default_currency
881
+ def use_shipping?
882
+ use_shipping.in?([true, 'true', '1'])
753
883
  end
754
884
 
755
- def create_token
756
- self.token ||= generate_token
885
+ def ensure_currency_presence
886
+ self.currency ||= store.default_currency
757
887
  end
758
888
 
759
889
  def collect_payment_methods(store = nil)
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class OrderPromotion < Spree::Base
2
+ class OrderPromotion < Spree.base_class
3
3
  belongs_to :order, class_name: 'Spree::Order'
4
4
  belongs_to :promotion, class_name: 'Spree::Promotion'
5
5
 
@@ -133,10 +133,16 @@ module Spree
133
133
  order.shipment_state = 'backorder'
134
134
  else
135
135
  # get all the shipment states for this order
136
- shipment_states = shipments.states
136
+ shipment_states = shipments.states.uniq
137
+
137
138
  order.shipment_state = if shipment_states.size > 1
138
- # multiple shipment states means it's most likely partially shipped
139
- 'partial'
139
+ if shipment_states.include?('shipped')
140
+ 'partial'
141
+ elsif shipment_states.include?('pending')
142
+ 'pending'
143
+ else
144
+ 'ready'
145
+ end
140
146
  else
141
147
  # will return nil if no shipments are found
142
148
  shipment_states.first
@@ -0,0 +1,184 @@
1
+ module Spree
2
+ class Page < Spree.base_class
3
+ include Spree::Previewable
4
+
5
+ #
6
+ # Magic methods
7
+ #
8
+ extend FriendlyId
9
+ friendly_id :slug_candidates, use: [:slugged, :scoped, :history], scope: :pageable
10
+ acts_as_paranoid
11
+
12
+ #
13
+ # Associations
14
+ #
15
+ belongs_to :pageable, polymorphic: true # this can be either Store or Theme
16
+ has_many :sections, -> { order(position: :asc) }, class_name: 'Spree::PageSection', dependent: :destroy_async, as: :pageable
17
+ has_many :page_links, as: :linkable, class_name: 'Spree::PageLink', dependent: :destroy
18
+ alias page_sections sections
19
+
20
+ #
21
+ # Validations
22
+ #
23
+ validates :name, presence: true
24
+ validates :pageable, presence: true
25
+ validates :slug, uniqueness: { scope: :pageable, conditions: -> { where(deleted_at: nil).where.not(slug: nil) } }
26
+
27
+ #
28
+ # Callbacks
29
+ #
30
+ before_validation :set_name
31
+ after_create :create_default_sections, unless: :duplicating
32
+
33
+ #
34
+ # Scopes
35
+ #
36
+ scope :linkable, lambda {
37
+ where(type: [
38
+ 'Spree::Pages::Homepage',
39
+ 'Spree::Pages::ShopAll',
40
+ 'Spree::Pages::PostList',
41
+ 'Spree::Pages::Custom',
42
+ 'Spree::Pages::Account',
43
+ 'Spree::Pages::Login'
44
+ ])
45
+ }
46
+ scope :product_details, -> { where(type: 'Spree::Pages::ProductDetails') }
47
+ scope :standard, -> { where.not(type: 'Spree::Pages::Custom') }
48
+ scope :custom, -> { where(type: 'Spree::Pages::Custom') }
49
+
50
+ #
51
+ # Virtual attributes
52
+ #
53
+ attribute :duplicating, :boolean, default: false
54
+
55
+ def icon_name
56
+ 'file-earmark-richtext'
57
+ end
58
+
59
+ def store
60
+ if pageable.is_a?(Spree::Store)
61
+ pageable
62
+ else
63
+ pageable.store
64
+ end
65
+ end
66
+
67
+ def create_preview
68
+ return self if preview? # no need to create a preview for the preview
69
+
70
+ ActiveRecord::Base.connected_to(role: :writing) do
71
+ ApplicationRecord.transaction do
72
+ new_preview = dup
73
+ new_preview.parent = self
74
+ new_preview.duplicating = true
75
+ new_preview.save!
76
+
77
+ sections.includes(:links, { asset_attachment: :blob }, { blocks: [:rich_text_text, :links] }).each do |section|
78
+ section.deep_clone(new_preview)
79
+ end
80
+
81
+ new_preview
82
+ end
83
+ end
84
+ end
85
+
86
+ # Promotes the page preview to the main page
87
+ def promote
88
+ return unless preview?
89
+
90
+ ApplicationRecord.transaction do
91
+ old_page = parent
92
+ old_page.page_links.update_all(linkable_id: id)
93
+ update(parent: nil) # clear reference to the old page
94
+ Spree::Page.find(old_page.id).destroy # destroy the old page with their other previews, etc.
95
+ end
96
+ end
97
+
98
+ def default_sections
99
+ []
100
+ end
101
+
102
+ def customizable?
103
+ false
104
+ end
105
+
106
+ def layout_sections?
107
+ true
108
+ end
109
+
110
+ def preview_url(theme_preview = nil, page_preview = nil)
111
+ return if page_builder_url.blank?
112
+
113
+ "#{page_builder_url}?#{url_query_params(theme_preview, page_preview).to_query}"
114
+ end
115
+
116
+ def display_name
117
+ if custom?
118
+ name
119
+ else
120
+ name.titleize
121
+ end
122
+ end
123
+
124
+ def custom?
125
+ type == 'Spree::Pages::Custom'
126
+ end
127
+
128
+ def duplicate(target_pageable)
129
+ duplicated_page = dup
130
+ duplicated_page.duplicating = true
131
+ duplicated_page.pageable = target_pageable
132
+ duplicated_page.save!
133
+
134
+ sections_scope = sections.includes(:links, :blocks, { asset_attachment: :blob },
135
+ { blocks: [:rich_text_text, :links, { asset_attachment: :blob }] })
136
+ sections_scope.each { |section| section.deep_clone(duplicated_page) }
137
+
138
+ duplicated_page
139
+ end
140
+
141
+ def theme
142
+ @theme ||= if pageable.is_a?(Spree::Theme)
143
+ pageable
144
+ else
145
+ pageable.default_theme
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ def create_default_sections
152
+ default_sections.each do |section|
153
+ section.pageable = self
154
+ section.save!
155
+ end
156
+ end
157
+
158
+ def url_query_params(theme_preview, page_preview)
159
+ theme_preview_cache = theme_preview.updated_at.to_i.to_s + rand(999_999).to_s if theme_preview
160
+
161
+ {
162
+ theme_id: theme.id,
163
+ page_preview_id: page_preview&.id,
164
+ theme_preview_id: theme_preview&.id,
165
+ theme_preview_cache: theme_preview_cache
166
+ }
167
+ end
168
+
169
+ def set_name
170
+ return if custom?
171
+ return if name.present?
172
+
173
+ self.name = self.class.name.demodulize
174
+ end
175
+
176
+ def should_generate_new_friendly_id?
177
+ name_changed? && custom?
178
+ end
179
+
180
+ def page_builder_url_exists?(path)
181
+ Spree::Core::Engine.routes.url_helpers.respond_to?(path)
182
+ end
183
+ end
184
+ end