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
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StockTransfer < Spree::Base
2
+ class StockTransfer < Spree.base_class
3
3
  include Spree::Core::NumberGenerator.new(prefix: 'T')
4
4
  include Spree::NumberIdentifier
5
5
  include Spree::NumberAsParam
@@ -9,12 +9,20 @@ module Spree
9
9
  end
10
10
 
11
11
  has_many :stock_movements, as: :originator
12
+ accepts_nested_attributes_for :stock_movements, reject_if: proc { |attributes|
13
+ attributes[:quantity] = attributes[:quantity].to_i
14
+ attributes[:quantity].blank? || attributes[:quantity].zero? || attributes[:stock_item_id].blank?
15
+ }
12
16
 
13
17
  belongs_to :source_location, class_name: 'StockLocation', optional: true
14
18
  belongs_to :destination_location, class_name: 'StockLocation'
15
19
 
16
20
  self.whitelisted_ransackable_attributes = %w[reference source_location_id destination_location_id number]
17
21
 
22
+ validate :source_location_is_not_destination_location
23
+ validate :stock_movements_not_empty
24
+ validates :destination_location, presence: true
25
+
18
26
  def source_movements
19
27
  find_stock_location_with_location_id(source_location_id)
20
28
  end
@@ -24,16 +32,28 @@ module Spree
24
32
  end
25
33
 
26
34
  def transfer(source_location, destination_location, variants)
35
+ if variants.nil? || variants.empty?
36
+ errors.add(:base, Spree.t('stock_transfer.errors.must_have_variant'))
37
+ return false
38
+ end
39
+
40
+ unless variants_available_in_source_location?(source_location, variants)
41
+ errors.add(:base, Spree.t('stock_transfer.errors.variants_unavailable'))
42
+ return false
43
+ end
44
+
27
45
  transaction do
28
46
  variants.each_pair do |variant, quantity|
29
- source_location&.unstock(variant, quantity, self)
30
- destination_location.restock(variant, quantity, self)
47
+ source_location&.unstock(variant, quantity, self, persist: false)
48
+ destination_location.restock(variant, quantity, self, persist: false)
31
49
 
32
50
  self.source_location = source_location
33
51
  self.destination_location = destination_location
34
52
  save!
35
53
  end
36
54
  end
55
+
56
+ true
37
57
  end
38
58
 
39
59
  # receive inventory from external vendor
@@ -46,5 +66,23 @@ module Spree
46
66
  def find_stock_location_with_location_id(location_id)
47
67
  stock_movements.joins(:stock_item).where('spree_stock_items.stock_location_id' => location_id)
48
68
  end
69
+
70
+ def source_location_is_not_destination_location
71
+ return unless source_location_id.present?
72
+ return unless destination_location_id.present?
73
+ return if source_location_id != destination_location_id
74
+
75
+ errors.add(:source_location, Spree.t('stock_transfer.errors.same_location'))
76
+ end
77
+
78
+ def stock_movements_not_empty
79
+ errors.add(:base, Spree.t('stock_transfer.errors.must_have_variant')) if stock_movements.empty?
80
+ end
81
+
82
+ def variants_available_in_source_location?(source_location, variants)
83
+ return true if source_location.nil?
84
+
85
+ source_location.stock_items.where(variant: variants.keys).where(Spree::StockItem.arel_table[:count_on_hand].gt(0)).size == variants.keys.size
86
+ end
49
87
  end
50
88
  end
@@ -1,39 +1,66 @@
1
+ require 'uri'
2
+
1
3
  module Spree
2
- class Store < Spree::Base
4
+ class Store < Spree.base_class
5
+ RESERVED_CODES = %w(
6
+ admin default app api www cdn files assets checkout account auth login user
7
+ )
8
+
9
+ include FriendlyId
3
10
  include Spree::TranslatableResource
4
- if defined?(Spree::Webhooks::HasWebhooks)
5
- include Spree::Webhooks::HasWebhooks
6
- end
7
- if defined?(Spree::Security::Stores)
8
- include Spree::Security::Stores
9
- end
11
+ include Spree::Metadata
12
+ include Spree::Stores::Setup
13
+ include Spree::Stores::Socials
14
+ include Spree::Webhooks::HasWebhooks if defined?(Spree::Webhooks::HasWebhooks)
15
+ include Spree::Security::Stores if defined?(Spree::Security::Stores)
16
+
17
+ #
18
+ # Magic methods
19
+ #
20
+ acts_as_paranoid
21
+ friendly_id :slug_candidates, use: [:slugged, :history], slug_column: :code, routes: :normal
10
22
 
23
+ #
24
+ # Translations
25
+ #
11
26
  TRANSLATABLE_FIELDS = %i[name meta_description meta_keywords seo_title facebook
12
27
  twitter instagram customer_support_email description
13
28
  address contact_phone new_order_notifications_email].freeze
14
29
  translates(*TRANSLATABLE_FIELDS, column_fallback: !Spree.always_use_translations?)
15
-
16
30
  self::Translation.class_eval do
17
31
  acts_as_paranoid
18
32
  # deleted translation values still need to be accessible - remove deleted_at scope
19
33
  default_scope { unscope(where: :deleted_at) }
20
34
  end
21
35
 
22
- typed_store :settings, coder: ActiveRecord::TypedStore::IdentityCoder do |s|
23
- # Spree Digital Asset Configurations
24
- s.boolean :limit_digital_download_count, default: true, null: false
25
- s.boolean :limit_digital_download_days, default: true, null: false
26
- s.integer :digital_asset_authorized_clicks, default: 5, null: false # number of times a customer can download a digital file.
27
- s.integer :digital_asset_authorized_days, default: 7, null: false # number of days after initial purchase the customer can download a file.
28
- s.integer :digital_asset_link_expire_time, default: 300, null: false # 5 minutes in seconds
29
- end
30
-
31
- attr_accessor :skip_validate_not_last
32
-
33
- acts_as_paranoid
34
-
36
+ #
37
+ # Preferences
38
+ #
39
+ # general preferences
40
+ preference :timezone, :string, default: Time.zone.name
41
+ preference :weight_unit, :string, default: 'lb'
42
+ preference :unit_system, :string, default: 'imperial'
43
+ # email preferences
44
+ preference :send_consumer_transactional_emails, :boolean, default: true
45
+ # SEO preferences
46
+ preference :index_in_search_engines, :boolean, default: false
47
+ preference :password_protected, :boolean, default: false
48
+ # Checkout preferences
49
+ preference :guest_checkout, :boolean, default: true
50
+ # digital assets preferences
51
+ preference :limit_digital_download_count, :boolean, default: true
52
+ preference :limit_digital_download_days, :boolean, default: true
53
+ preference :digital_asset_authorized_clicks, :integer, default: 5
54
+ preference :digital_asset_authorized_days, :integer, default: 7
55
+ preference :digital_asset_link_expire_time, :integer, default: 300
56
+
57
+ #
58
+ # Associations
59
+ #
60
+ has_many :checkouts, -> { incomplete }, class_name: 'Spree::Order', inverse_of: :store
35
61
  has_many :orders, class_name: 'Spree::Order'
36
62
  has_many :line_items, through: :orders, class_name: 'Spree::LineItem'
63
+ has_many :digital_links, through: :line_items, class_name: 'Spree::DigitalLink'
37
64
  has_many :shipments, through: :orders, class_name: 'Spree::Shipment'
38
65
  has_many :payments, through: :orders, class_name: 'Spree::Payment'
39
66
  has_many :return_authorizations, through: :orders, class_name: 'Spree::ReturnAuthorization'
@@ -42,18 +69,13 @@ module Spree
42
69
  has_many :store_payment_methods, class_name: 'Spree::StorePaymentMethod'
43
70
  has_many :payment_methods, through: :store_payment_methods, class_name: 'Spree::PaymentMethod'
44
71
 
45
- has_many :cms_pages, class_name: 'Spree::CmsPage'
46
- has_many :cms_sections, through: :cms_pages, class_name: 'Spree::CmsSection'
47
-
48
- has_many :menus, class_name: 'Spree::Menu'
49
- has_many :menu_items, through: :menus, class_name: 'Spree::MenuItem'
50
-
51
72
  has_many :store_products, class_name: 'Spree::StoreProduct'
52
73
  has_many :products, through: :store_products, class_name: 'Spree::Product'
53
74
  has_many :product_properties, through: :products, class_name: 'Spree::ProductProperty'
54
75
  has_many :variants, through: :products, class_name: 'Spree::Variant', source: :variants_including_master
55
76
  has_many :stock_items, through: :variants, class_name: 'Spree::StockItem'
56
77
  has_many :inventory_units, through: :variants, class_name: 'Spree::InventoryUnit'
78
+ has_many :option_value_variants, through: :variants, class_name: 'Spree::OptionValueVariant'
57
79
  has_many :customer_returns, class_name: 'Spree::CustomerReturn', inverse_of: :store
58
80
 
59
81
  has_many :store_credits, class_name: 'Spree::StoreCredit'
@@ -72,15 +94,59 @@ module Spree
72
94
  belongs_to :default_country, class_name: 'Spree::Country'
73
95
  belongs_to :checkout_zone, class_name: 'Spree::Zone'
74
96
 
97
+ has_many :reports, class_name: 'Spree::Report'
98
+ has_many :exports, class_name: 'Spree::Export'
99
+
100
+ has_many :custom_domains, class_name: 'Spree::CustomDomain', dependent: :destroy
101
+ has_one :default_custom_domain, -> { where(default: true) }, class_name: 'Spree::CustomDomain'
102
+
103
+ has_many :posts
104
+ has_many :post_categories
105
+
106
+ #
107
+ # Page Builder associations
108
+ #
109
+ has_many :themes, -> { without_previews }, class_name: 'Spree::Theme', dependent: :destroy, inverse_of: :store
110
+ has_many :theme_previews,
111
+ -> { only_previews },
112
+ class_name: 'Spree::Theme',
113
+ through: :themes,
114
+ source: :previews,
115
+ inverse_of: :store,
116
+ dependent: :destroy
117
+ has_one :default_theme, -> { without_previews.where(default: true) }, class_name: 'Spree::Theme', inverse_of: :store
118
+ has_many :theme_pages, class_name: 'Spree::Page', through: :themes, source: :pages
119
+ has_many :theme_page_previews, class_name: 'Spree::Page', through: :theme_pages, source: :previews
120
+ has_many :pages, -> { without_previews.custom }, class_name: 'Spree::Pages::Custom', dependent: :destroy, as: :pageable
121
+ has_many :page_previews, class_name: 'Spree::Pages::Custom', through: :pages, as: :pageable, source: :previews
122
+
123
+ #
124
+ # ActionText
125
+ #
126
+ has_rich_text :checkout_message
127
+ has_rich_text :customer_terms_of_service
128
+ has_rich_text :customer_privacy_policy
129
+ has_rich_text :customer_returns_policy
130
+ has_rich_text :customer_shipping_policy
131
+
132
+ #
133
+ # Virtual attributes
134
+ #
135
+ attribute :import_products_from_store_id, :string, default: nil
136
+ attribute :import_payment_methods_from_store_id, :string, default: nil
137
+ attr_accessor :skip_validate_not_last
138
+ store_accessor :private_metadata, :storefront_password
139
+
140
+ #
141
+ # Validations
142
+ #
75
143
  with_options presence: true do
76
- validates :name, :url, :mail_from_address, :default_currency, :code
144
+ validates :name, :url, :mail_from_address, :default_currency, :default_country, :code
77
145
  end
78
-
79
- validates :digital_asset_authorized_clicks, numericality: { only_integer: true, greater_than: 0 }
80
- validates :digital_asset_authorized_days, numericality: { only_integer: true, greater_than: 0 }
81
- validates :code, uniqueness: { case_sensitive: false, conditions: -> { with_deleted } }
146
+ validates :preferred_digital_asset_authorized_clicks, numericality: { only_integer: true, greater_than: 0 }
147
+ validates :preferred_digital_asset_authorized_days, numericality: { only_integer: true, greater_than: 0 }
148
+ validates :code, uniqueness: { case_sensitive: false, conditions: -> { with_deleted } }, exclusion: RESERVED_CODES
82
149
  validates :mail_from_address, email: { allow_blank: false }
83
-
84
150
  # FIXME: we should remove this condition in v5
85
151
  if !ENV['SPREE_DISABLE_DB_CONNECTION'] &&
86
152
  connected? &&
@@ -88,29 +154,48 @@ module Spree
88
154
  connection.column_exists?(:spree_stores, :new_order_notifications_email)
89
155
  validates :new_order_notifications_email, email: { allow_blank: true }
90
156
  end
91
-
92
- default_scope { order(created_at: :asc) }
93
-
94
- has_one :logo, class_name: 'Spree::StoreLogo', dependent: :destroy, as: :viewable
95
- accepts_nested_attributes_for :logo, reject_if: :all_blank
96
-
97
- has_one :mailer_logo, class_name: 'Spree::StoreMailerLogo', dependent: :destroy, as: :viewable
98
- accepts_nested_attributes_for :mailer_logo, reject_if: :all_blank
99
-
100
- has_one :favicon_image, class_name: 'Spree::StoreFaviconImage', dependent: :destroy, as: :viewable
101
- accepts_nested_attributes_for :favicon_image, reject_if: :all_blank
102
-
157
+ validates :favicon_image, :social_image, :mailer_logo, content_type: Rails.application.config.active_storage.web_image_content_types
158
+
159
+ #
160
+ # Attachments
161
+ #
162
+ has_one_attached :logo, service: Spree.public_storage_service_name
163
+ has_one_attached :favicon_image, service: Spree.public_storage_service_name
164
+ has_one_attached :social_image, service: Spree.public_storage_service_name
165
+ has_one_attached :mailer_logo, service: Spree.public_storage_service_name
166
+
167
+ #
168
+ # Callbacks
169
+ before_validation :ensure_default_country
170
+ before_validation :set_code, on: :create
171
+ before_validation :set_url
103
172
  before_save :ensure_default_exists_and_is_unique
104
- before_save :ensure_supported_currencies, :ensure_supported_locales, :ensure_default_country
173
+ before_save :ensure_supported_currencies, :ensure_supported_locales
174
+ after_create :ensure_default_taxonomies_are_created
175
+ after_create :ensure_default_automatic_taxons
176
+ after_create :ensure_default_post_categories_are_created
177
+ after_create :import_products_from_store, if: -> { import_products_from_store_id.present? }
178
+ after_create :import_payment_methods_from_store, if: -> { import_payment_methods_from_store_id.present? }
179
+ after_create :create_default_theme
105
180
  before_destroy :validate_not_last, unless: :skip_validate_not_last
106
181
  before_destroy :pass_default_flag_to_other_store
182
+ after_commit :clear_cache
183
+ after_commit :handle_code_changes, on: :update, if: -> { code_previously_changed? }
107
184
 
108
- scope :by_url, ->(url) { where('url like ?', "%#{url}%") }
185
+ #
186
+ # Scopes
187
+ #
188
+ default_scope { order(created_at: :asc) }
189
+ scope :by_custom_domain, ->(url) { left_joins(:custom_domains).where("#{Spree::CustomDomain.table_name}.url" => url) }
190
+ scope :by_url, ->(url) { where("#{table_name}.url like ?", "%#{url}%") }
109
191
 
110
- after_commit :clear_cache
192
+ #
193
+ # Delegations
194
+ #
195
+ delegate :iso, to: :default_country, prefix: true, allow_nil: true
111
196
 
112
197
  def self.current(url = nil)
113
- Spree::Dependencies.current_store_finder.constantize.new(url: url).execute
198
+ Spree::Dependencies.current_store_finder.constantize.new(url: url).execute || Spree::Current.store
114
199
  end
115
200
 
116
201
  # FIXME: we need to drop `or_initialize` in v5
@@ -130,22 +215,34 @@ module Spree
130
215
  Spree::Store.all.map(&:supported_locales_list).flatten.uniq
131
216
  end
132
217
 
133
- def default_menu(location)
134
- menu = menus.find_by(location: location, locale: default_locale) || menus.find_by(location: location)
218
+ def default_country_iso=(iso)
219
+ return if iso.blank?
220
+
221
+ @default_country_iso = iso
222
+
223
+ country = Spree::Country.by_iso(iso)
135
224
 
136
- menu.root if menu.present?
225
+ if country.present?
226
+ self.default_country = country
227
+ elsif iso_country = ::Country[iso]
228
+ new_country = Spree::Country.create!(
229
+ iso_name: iso_country.local_name&.upcase,
230
+ iso: iso_country.alpha2,
231
+ iso3: iso_country.alpha3,
232
+ name: iso_country.local_name,
233
+ numcode: iso_country.number,
234
+ states_required: Spree::Address::STATES_REQUIRED.include?(iso),
235
+ zipcode_required: !Spree::Address::NO_ZIPCODE_ISO_CODES.include?(iso)
236
+ )
237
+
238
+ self.default_country = new_country
239
+ end
137
240
  end
138
241
 
139
242
  def supported_currencies_list
140
- @supported_currencies_list ||= (read_attribute(:supported_currencies).to_s.split(',') << default_currency).sort.map(&:to_s).map do |code|
243
+ @supported_currencies_list ||= ([default_currency] + read_attribute(:supported_currencies).to_s.split(',')).uniq.map(&:to_s).map do |code|
141
244
  ::Money::Currency.find(code.strip)
142
- end.uniq.compact
143
- end
144
-
145
- def homepage(requested_locale)
146
- cms_pages.by_locale(requested_locale).find_by(type: 'Spree::Cms::Pages::Homepage') ||
147
- cms_pages.by_locale(default_locale).find_by(type: 'Spree::Cms::Pages::Homepage') ||
148
- cms_pages.find_by(type: 'Spree::Cms::Pages::Homepage')
245
+ end.compact.sort_by { |currency| currency.iso_code == default_currency ? 0 : 1 }
149
246
  end
150
247
 
151
248
  def seo_meta_description
@@ -159,7 +256,6 @@ module Spree
159
256
  end
160
257
 
161
258
  def supported_locales_list
162
- # TODO: add support of multiple supported languages to a single Store
163
259
  @supported_locales_list ||= (read_attribute(:supported_locales).to_s.split(',') << default_locale).compact.uniq.sort
164
260
  end
165
261
 
@@ -168,21 +264,64 @@ module Spree
168
264
  end
169
265
 
170
266
  def formatted_url
171
- return if url.blank?
267
+ @formatted_url ||= begin
268
+ clean_url = url.to_s.sub(%r{^https?://}, '').split(':').first
269
+
270
+ if Rails.env.development? || Rails.env.test?
271
+ scheme = Rails.application.routes.default_url_options[:protocol] || :http
272
+ port = Rails.application.routes.default_url_options[:port].presence || (Rails.env.development? ? 3000 : nil)
273
+
274
+ if scheme.to_sym == :https
275
+ URI::HTTPS.build(
276
+ host: clean_url,
277
+ port: port
278
+ ).to_s
279
+ else
280
+ URI::HTTP.build(
281
+ host: clean_url,
282
+ port: port
283
+ ).to_s
284
+ end
285
+ else
286
+ URI::HTTPS.build(
287
+ host: clean_url
288
+ ).to_s
289
+ end
290
+ end
291
+ end
292
+
293
+ def formatted_custom_domain
294
+ return unless default_custom_domain
172
295
 
173
- @formatted_url ||= if url.match(/http:\/\/|https:\/\//)
174
- url
175
- else
176
- Rails.env.development? || Rails.env.test? ? "http://#{url}" : "https://#{url}"
177
- end
296
+ @formatted_custom_domain ||= if Rails.env.development? || Rails.env.test?
297
+ URI::Generic.build(
298
+ scheme: Rails.application.routes.default_url_options[:protocol] || 'http',
299
+ host: default_custom_domain.url,
300
+ port: Rails.application.routes.default_url_options[:port]
301
+ ).to_s
302
+ else
303
+ URI::HTTPS.build(host: default_custom_domain.url).to_s
304
+ end
305
+ end
306
+
307
+ def url_or_custom_domain
308
+ default_custom_domain&.url || url
309
+ end
310
+
311
+ def formatted_url_or_custom_domain
312
+ formatted_custom_domain || formatted_url
178
313
  end
179
314
 
180
315
  def countries_available_for_checkout
181
- @countries_available_for_checkout ||= checkout_zone.try(:country_list) || Spree::Country.all
316
+ @countries_available_for_checkout ||= Rails.cache.fetch(countries_available_for_checkout_cache_key) do
317
+ checkout_zone.try(:country_list) || Spree::Country.all
318
+ end
182
319
  end
183
320
 
184
321
  def states_available_for_checkout(country)
185
- checkout_zone.try(:state_list_for, country) || country.states
322
+ Rails.cache.fetch(states_available_for_checkout_cache_key(country)) do
323
+ checkout_zone.try(:state_list_for, country) || country.states
324
+ end
186
325
  end
187
326
 
188
327
  def checkout_zone_or_default
@@ -191,18 +330,77 @@ module Spree
191
330
  @checkout_zone_or_default ||= checkout_zone || Spree::Zone.default_checkout_zone
192
331
  end
193
332
 
333
+ def supported_shipping_zones
334
+ @supported_shipping_zones ||= if checkout_zone_id.present?
335
+ [checkout_zone]
336
+ else
337
+ Spree::Zone.includes(zone_members: :zoneable).all
338
+ end
339
+ end
340
+
341
+ def default_stock_location
342
+ @default_stock_location ||= begin
343
+ stock_location_scope = Spree::StockLocation.order_default
344
+ stock_location_scope.first || ActiveRecord::Base.connected_to(role: :writing) do
345
+ stock_location_scope.create(default: true, name: Spree.t(:default_stock_location_name), country: default_country)
346
+ end
347
+ end
348
+ end
349
+
350
+ def admin_users
351
+ @admin_users ||= Spree.admin_user_class.joins(:spree_roles).where(spree_roles: { name: :admin })
352
+ end
353
+
194
354
  def favicon
195
- return unless favicon_image&.attachment&.attached?
355
+ return unless favicon_image.attached? && favicon_image.variable?
196
356
 
197
- favicon_image.attachment.variant(resize_to_limit: [32, 32])
357
+ favicon_image.variant(resize_to_limit: [32, 32])
198
358
  end
199
359
 
200
360
  def can_be_deleted?
201
361
  self.class.where.not(id: id).any?
202
362
  end
203
363
 
364
+ def metric_unit_system?
365
+ preferred_unit_system == 'metric'
366
+ end
367
+
368
+ def default_shipping_category
369
+ @default_shipping_category ||= ShippingCategory.find_or_create_by(name: 'Default')
370
+ end
371
+
372
+ def digital_shipping_category
373
+ @digital_shipping_category ||= ShippingCategory.find_or_create_by(name: 'Digital')
374
+ end
375
+
376
+ def import_products_from_store
377
+ store = Store.find(import_products_from_store_id)
378
+ product_ids = store.products.pluck(:id)
379
+
380
+ return if product_ids.empty?
381
+
382
+ StoreProduct.insert_all(product_ids.map { |product_id| { store_id: id, product_id: product_id } })
383
+ end
384
+
385
+ def import_payment_methods_from_store
386
+ store = Store.find(import_payment_methods_from_store_id)
387
+ payment_method_ids = store.payment_method_ids
388
+
389
+ return if payment_method_ids.empty?
390
+
391
+ StorePaymentMethod.insert_all(payment_method_ids.map { |payment_method_id| { store_id: id, payment_method_id: payment_method_id } })
392
+ end
393
+
204
394
  private
205
395
 
396
+ def countries_available_for_checkout_cache_key
397
+ "#{cache_key_with_version}/#{checkout_zone&.cache_key_with_version}/countries_available_for_checkout"
398
+ end
399
+
400
+ def states_available_for_checkout_cache_key(country)
401
+ "#{cache_key_with_version}/#{checkout_zone&.cache_key_with_version}/states_available_for_checkout/#{country&.cache_key_with_version}"
402
+ end
403
+
206
404
  def ensure_default_exists_and_is_unique
207
405
  if default
208
406
  Store.where.not(id: id).update_all(default: false)
@@ -246,7 +444,6 @@ module Spree
246
444
  end
247
445
 
248
446
  def ensure_default_country
249
- return unless has_attribute?(:default_country_id)
250
447
  return if default_country.present? && (checkout_zone.blank? || checkout_zone.country_list.blank? || checkout_zone.country_list.include?(default_country))
251
448
 
252
449
  self.default_country = if checkout_zone.present? && checkout_zone.country_list.any?
@@ -255,5 +452,81 @@ module Spree
255
452
  Country.find_by(iso: 'US') || Country.first
256
453
  end
257
454
  end
455
+
456
+ def ensure_default_taxonomies_are_created
457
+ taxonomies.find_or_create_by(name: I18n.t('spree.taxonomy_categories_name', default: I18n.t('spree.taxonomy_categories_name', locale: :en)))
458
+ taxonomies.find_or_create_by(name: I18n.t('spree.taxonomy_brands_name', default: I18n.t('spree.taxonomy_brands_name', locale: :en)))
459
+ taxonomies.find_or_create_by(name: I18n.t('spree.taxonomy_collections_name', default: I18n.t('spree.taxonomy_collections_name', locale: :en)))
460
+ rescue ActiveRecord::NotNullViolation
461
+ end
462
+
463
+ def ensure_default_automatic_taxons
464
+ collections_taxonomy = taxonomies.find_by(name: Spree.t(:taxonomy_collections_name))
465
+
466
+ if collections_taxonomy.present?
467
+ on_sale_taxon = collections_taxonomy.taxons.automatic.where(name: Spree.t('automatic_taxon_names.on_sale')).first_or_create! do |taxon|
468
+ taxon.parent = collections_taxonomy.root
469
+ taxon.rules.new(type: 'Spree::TaxonRules::Sale', value: 'true')
470
+ end
471
+
472
+ new_arrivals_taxon = collections_taxonomy.taxons.automatic.where(name: Spree.t('automatic_taxon_names.new_arrivals')).first_or_create! do |taxon|
473
+ taxon.parent = collections_taxonomy.root
474
+ taxon.rules.new(type: 'Spree::TaxonRules::AvailableOn', value: 30)
475
+ end
476
+
477
+ [on_sale_taxon, new_arrivals_taxon]
478
+ end
479
+ end
480
+
481
+ def ensure_default_post_categories_are_created
482
+ post_categories.find_or_create_by(title: Spree.t('default_post_categories.resources'))
483
+ post_categories.find_or_create_by(title: Spree.t('default_post_categories.articles'))
484
+ post_categories.find_or_create_by(title: Spree.t('default_post_categories.news'))
485
+ end
486
+
487
+ # code is slug, so we don't want to generate new slug when code changes
488
+ # we use friendlyId only for history feature
489
+ def should_generate_new_friendly_id?
490
+ false
491
+ end
492
+
493
+ def slug_candidates
494
+ []
495
+ end
496
+
497
+ def handle_code_changes
498
+ # implement your custom logic here
499
+ end
500
+
501
+ # This FriendlyId method is overwitten to keep our logic for generating code
502
+ # there is no option for own format
503
+ def set_code
504
+ self.code = if code.present?
505
+ code.parameterize.strip
506
+ elsif name.present?
507
+ name.parameterize.strip
508
+ end
509
+
510
+ return if self.code.blank?
511
+
512
+ # ensure code is unique
513
+ self.code = [name.parameterize, rand(9999)].join('-') while Spree::Store.with_deleted.where(code: self.code).exists?
514
+ end
515
+
516
+ # auto-assign internal URL for stores
517
+ def set_url
518
+ return if url_changed?
519
+ return unless code_changed?
520
+ return unless Spree.root_domain.present?
521
+
522
+ self.url = [code, Spree.root_domain].join('.')
523
+ end
524
+
525
+ def create_default_theme
526
+ themes.find_or_initialize_by(default: true) do |theme|
527
+ theme.name = Spree.t(:default_theme_name)
528
+ theme.save!
529
+ end
530
+ end
258
531
  end
259
532
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StoreCredit < Spree::Base
2
+ class StoreCredit < Spree.base_class
3
3
  include Spree::SingleStoreResource
4
4
  include Spree::Metadata
5
5
  if defined?(Spree::Webhooks::HasWebhooks)
@@ -40,7 +40,7 @@ module Spree
40
40
  scope :order_by_priority, -> { includes(:credit_type).order('spree_store_credit_types.priority ASC') }
41
41
 
42
42
  scope :not_authorized, -> { where(amount_authorized: 0) }
43
- scope :not_used, -> { where(amount_authorized: 0) }
43
+ scope :not_used, -> { where("#{Spree::StoreCredit.table_name}.amount_used < #{Spree::StoreCredit.table_name}.amount") }
44
44
  scope :available, -> { not_authorized.not_used }
45
45
 
46
46
  after_save :store_event
@@ -49,7 +49,7 @@ module Spree
49
49
  attr_accessor :action, :action_amount, :action_originator, :action_authorization_code
50
50
 
51
51
  extend Spree::DisplayMoney
52
- money_methods :amount, :amount_used
52
+ money_methods :amount, :amount_used, :amount_remaining, :amount_authorized
53
53
 
54
54
  self.whitelisted_ransackable_attributes = %w[user_id created_by_id amount currency type_id]
55
55
  self.whitelisted_ransackable_associations = %w[type user created_by]
@@ -170,6 +170,14 @@ module Spree
170
170
  payment.completed? && payment.credit_allowed > 0
171
171
  end
172
172
 
173
+ def editable?
174
+ amount_used.zero? && amount_authorized.zero?
175
+ end
176
+
177
+ def can_be_deleted?
178
+ amount_used.zero? && amount_authorized.zero?
179
+ end
180
+
173
181
  def generate_authorization_code
174
182
  "#{id}-SC-#{Time.now.utc.strftime('%Y%m%d%H%M%S%6N')}"
175
183
  end
@@ -228,7 +236,7 @@ module Spree
228
236
  event.update!(
229
237
  amount: action_amount || amount,
230
238
  authorization_code: action_authorization_code || event.authorization_code || generate_authorization_code,
231
- user_total_amount: user.total_available_store_credit,
239
+ user_total_amount: user&.total_available_store_credit || 0,
232
240
  originator: action_originator
233
241
  )
234
242
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StoreCreditCategory < Spree::Base
2
+ class StoreCreditCategory < Spree.base_class
3
3
  validates :name, presence: true
4
4
 
5
5
  before_destroy :validate_not_used