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 StoreCreditEvent < Spree::Base
2
+ class StoreCreditEvent < Spree.base_class
3
3
  acts_as_paranoid
4
4
 
5
5
  belongs_to :store_credit
@@ -29,5 +29,25 @@ module Spree
29
29
  def order
30
30
  store.payments.find_by(response_code: authorization_code).try(:order)
31
31
  end
32
+
33
+ def allocation?
34
+ action == Spree::StoreCredit::ALLOCATION_ACTION
35
+ end
36
+
37
+ def credit?
38
+ action == Spree::StoreCredit::CREDIT_ACTION
39
+ end
40
+
41
+ def captured?
42
+ action == Spree::StoreCredit::CAPTURE_ACTION
43
+ end
44
+
45
+ def voided?
46
+ action == Spree::StoreCredit::VOID_ACTION
47
+ end
48
+
49
+ def authorized?
50
+ action == Spree::StoreCredit::AUTHORIZE_ACTION
51
+ end
32
52
  end
33
53
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StoreCreditType < Spree::Base
2
+ class StoreCreditType < Spree.base_class
3
3
  DEFAULT_TYPE_NAME = 'Expiring'.freeze
4
4
  has_many :store_credits, class_name: 'Spree::StoreCredit', foreign_key: 'type_id'
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StorePaymentMethod < Spree::Base
2
+ class StorePaymentMethod < Spree.base_class
3
3
  self.table_name = 'spree_payment_methods_stores'
4
4
 
5
5
  belongs_to :store, class_name: 'Spree::Store', touch: true
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StoreProduct < Spree::Base
2
+ class StoreProduct < Spree.base_class
3
3
  self.table_name = 'spree_products_stores'
4
4
 
5
5
  belongs_to :store, class_name: 'Spree::Store', touch: true
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class StorePromotion < Spree::Base
2
+ class StorePromotion < Spree.base_class
3
3
  self.table_name = 'spree_promotions_stores'
4
4
 
5
5
  belongs_to :store, class_name: 'Spree::Store', touch: true
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class TaxCategory < Spree::Base
2
+ class TaxCategory < Spree.base_class
3
3
  if defined?(Spree::Webhooks::HasWebhooks)
4
4
  include Spree::Webhooks::HasWebhooks
5
5
  end
@@ -12,9 +12,17 @@ module Spree
12
12
  has_many :variants, dependent: :nullify
13
13
 
14
14
  before_save :set_default_category
15
+ after_update :delete_cache
16
+ after_create :delete_cache
15
17
 
16
18
  self.whitelisted_ransackable_attributes = %w[name is_default tax_code]
17
19
 
20
+ def self.default
21
+ Rails.cache.fetch('default_tax_category') do
22
+ find_by(is_default: true)
23
+ end
24
+ end
25
+
18
26
  def set_default_category
19
27
  # set existing default tax category to false if this one has been marked as default
20
28
 
@@ -22,5 +30,11 @@ module Spree
22
30
  tax_category.update_columns(is_default: false, updated_at: Time.current)
23
31
  end
24
32
  end
33
+
34
+ private
35
+
36
+ def delete_cache
37
+ Rails.cache.delete('default_tax_category')
38
+ end
25
39
  end
26
40
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class TaxRate < Spree::Base
2
+ class TaxRate < Spree.base_class
3
3
  acts_as_paranoid
4
4
 
5
5
  include Spree::CalculatedAdjustments
@@ -2,7 +2,19 @@
2
2
  require 'stringex'
3
3
 
4
4
  module Spree
5
- class Taxon < Spree::Base
5
+ class Taxon < Spree.base_class
6
+ RULES_MATCH_POLICIES = %w[all any].freeze
7
+ SORT_ORDERS = %w[
8
+ manual
9
+ best-selling
10
+ name-a-z
11
+ name-z-a
12
+ price-high-to-low
13
+ price-low-to-high
14
+ newest-first
15
+ oldest-first
16
+ ]
17
+
6
18
  include Spree::TranslatableResource
7
19
  include Spree::TranslatableResourceSlug
8
20
  include Spree::Metadata
@@ -10,20 +22,21 @@ module Spree
10
22
  include Spree::Webhooks::HasWebhooks
11
23
  end
12
24
 
25
+ #
26
+ # Magic methods
27
+ #
13
28
  extend FriendlyId
14
29
  friendly_id :permalink, slug_column: :permalink, use: :history
15
- before_validation :set_permalink, on: :create, if: :name
16
-
17
30
  acts_as_nested_set dependent: :destroy
18
31
 
32
+ #
33
+ # Associations
34
+ #
19
35
  belongs_to :taxonomy, class_name: 'Spree::Taxonomy', inverse_of: :taxons
20
- has_many :classifications, -> { order(:position) }, dependent: :delete_all, inverse_of: :taxon
36
+ has_one :store, through: :taxonomy
37
+ has_many :classifications, -> { order(:position) }, dependent: :destroy_async, inverse_of: :taxon
21
38
  has_many :products, through: :classifications
22
-
23
- delegate :store, to: :taxonomy
24
-
25
- has_many :menu_items, as: :linked_resource
26
- has_many :cms_sections, as: :linked_resource
39
+ has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonImage' # TODO: remove this as this is deprecated
27
40
 
28
41
  has_many :prototype_taxons, class_name: 'Spree::PrototypeTaxon', dependent: :destroy
29
42
  has_many :prototypes, through: :prototype_taxons, class_name: 'Spree::Prototype'
@@ -31,6 +44,17 @@ module Spree
31
44
  has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', dependent: :destroy
32
45
  has_many :promotion_rules, through: :promotion_rule_taxons, class_name: 'Spree::PromotionRule'
33
46
 
47
+ has_many :page_links, as: :linkable, class_name: 'Spree::PageLink', dependent: :destroy
48
+
49
+ #
50
+ # Attachments
51
+ #
52
+ has_one_attached :image, service: Spree.public_storage_service_name
53
+ has_one_attached :square_image, service: Spree.public_storage_service_name
54
+
55
+ #
56
+ # Validations
57
+ #
34
58
  validates :name, presence: true, uniqueness: { scope: [:parent_id, :taxonomy_id], case_sensitive: false }
35
59
  validates :taxonomy, presence: true
36
60
  validates :permalink, uniqueness: { case_sensitive: false, scope: [:parent_id, :taxonomy_id] }
@@ -43,7 +67,12 @@ module Spree
43
67
  validates :meta_description
44
68
  validates :meta_title
45
69
  end
70
+ validates :image, :square_image, content_type: Rails.application.config.active_storage.web_image_content_types
46
71
 
72
+ #
73
+ # Callbacks
74
+ #
75
+ before_validation :set_permalink, on: :create, if: :name
47
76
  before_validation :copy_taxonomy_from_parent
48
77
  before_save :set_pretty_name
49
78
  before_save :set_permalink
@@ -52,21 +81,134 @@ module Spree
52
81
  after_touch :touch_ancestors_and_taxonomy
53
82
  after_commit :regenerate_pretty_name_and_permalink, on: :update, if: :should_regenerate_pretty_name_and_permalink?
54
83
  after_move :regenerate_pretty_name_and_permalink
84
+ after_move :regenerate_translations_pretty_name_and_permalink
55
85
 
56
- has_one :store, through: :taxonomy
57
-
58
- has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonImage'
86
+ after_commit :touch_featured_sections, on: [:update]
87
+ after_touch :touch_featured_sections
88
+ after_destroy :remove_featured_sections, if: -> { featured? }
59
89
 
90
+ #
91
+ # Scopes
92
+ #
60
93
  scope :for_store, ->(store) { joins(:taxonomy).where(spree_taxonomies: { store_id: store.id }) }
94
+ scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }
95
+ scope :for_taxonomy, lambda { |taxonomy_name|
96
+ if Spree.use_translations?
97
+ joins(:taxonomy).
98
+ join_translation_table(Taxonomy).
99
+ where(["LOWER(#{Taxonomy.translation_table_alias}.name) = ?", taxonomy_name.downcase.strip])
100
+ else
101
+ joins(:taxonomy).where(["LOWER(#{Spree::Taxonomy.table_name}.name) = ?", taxonomy_name.downcase.strip])
102
+ end
103
+ }
61
104
 
62
- self.whitelisted_ransackable_associations = %w[taxonomy]
63
- self.whitelisted_ransackable_attributes = %w[name permalink]
105
+ #
106
+ # Search
107
+ #
108
+ if defined?(PgSearch)
109
+ include PgSearch::Model
110
+ pg_search_scope :search_by_name, against: :name, using: { tsearch: { any_word: true, prefix: true } }
111
+ else
112
+ def self.search_by_name(query)
113
+ i18n { name.lower.matches("%#{query.downcase}%") }
114
+ end
115
+ end
64
116
 
65
- scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }
117
+ #
118
+ # Ransack
119
+ #
120
+ self.whitelisted_ransackable_associations = %w[taxonomy]
121
+ self.whitelisted_ransackable_attributes = %w[name permalink automatic]
66
122
 
123
+ #
124
+ # Translations
125
+ #
67
126
  TRANSLATABLE_FIELDS = %i[name pretty_name description permalink].freeze
68
127
  translates(*TRANSLATABLE_FIELDS, column_fallback: !Spree.always_use_translations?)
69
128
 
129
+ #
130
+ # Action Text
131
+ #
132
+ translates :description, backend: :action_text
133
+
134
+ # Automatic taxons
135
+ validates :rules_match_policy, inclusion: { in: RULES_MATCH_POLICIES }, presence: true
136
+ validates :sort_order, inclusion: { in: SORT_ORDERS }, presence: true
137
+
138
+ has_many :taxon_rules, class_name: 'Spree::TaxonRule', dependent: :destroy
139
+ accepts_nested_attributes_for :taxon_rules, allow_destroy: true, reject_if: proc { |attributes| attributes['value'].blank? }
140
+ alias rules taxon_rules
141
+
142
+ scope :manual, -> { where.not(automatic: true) }
143
+ scope :automatic, -> { where(automatic: true) }
144
+
145
+ after_commit :regenerate_taxon_products, on: [:update], if: -> { automatic? && saved_change_to_rules_match_policy? }
146
+ attribute :marked_for_regenerate_taxon_products, :boolean, default: true
147
+
148
+ def manual?
149
+ !automatic?
150
+ end
151
+
152
+ def manual_sort_order?
153
+ sort_order == 'manual'
154
+ end
155
+
156
+ def active_products_with_descendants
157
+ @active_products_with_descendants ||= store.products.
158
+ joins(:classifications).
159
+ active.
160
+ where(
161
+ Spree::Classification.table_name => {
162
+ taxon_id: descendants.ids + [id]
163
+ }
164
+ )
165
+ end
166
+
167
+ def products_matching_rules(opts = {})
168
+ return Spree::Product.none if manual? || rules.empty?
169
+
170
+ storefront = opts[:storefront] || false
171
+ currency = opts[:currency] || store.default_currency
172
+
173
+ all_products = store.products.not_archived
174
+
175
+ products_matcher_cache_key = [
176
+ 'products_matching_rules',
177
+ cache_key_with_version,
178
+ storefront,
179
+ currency,
180
+ all_products.cache_key_with_version
181
+ ]
182
+
183
+ all_products = all_products.active(currency: currency) if storefront
184
+
185
+ any_rules_match_policy = rules_match_policy == 'any'
186
+ products = any_rules_match_policy ? Spree::Product.none : all_products
187
+
188
+ rules.each do |rule|
189
+ if any_rules_match_policy
190
+ product_ids = rule.apply(all_products).ids
191
+ # it's safer to use this approach with ids as it will not break if the rule is not a simple where clause
192
+ # and we will avoid `ArgumentError (Relation passed to #or must be structurally compatible. Incompatible values: [:group, :order, :joins, :readonly])` error
193
+ products = products.or(all_products.where(id: product_ids)) if product_ids.any?
194
+ else
195
+ products = rule.apply(products)
196
+ end
197
+ end
198
+
199
+ products
200
+ end
201
+
202
+ # we need to create a new taxon product (classification) record for each product that matches the rules
203
+ # so we can later use them for product filtering and so on
204
+ # if we want to fire the service once during object lifecycle - pass only_once: true
205
+ def regenerate_taxon_products(only_once: false)
206
+ if marked_for_regenerate_taxon_products?
207
+ Spree::Taxons::RegenerateProducts.call(taxon: self)
208
+ self.marked_for_regenerate_taxon_products = false if !frozen? && only_once
209
+ end
210
+ end
211
+
70
212
  def slug
71
213
  permalink
72
214
  end
@@ -78,6 +220,7 @@ module Spree
78
220
  self::Translation.class_eval do
79
221
  before_save :set_permalink
80
222
  before_save :set_pretty_name
223
+ after_save :regenerate_pretty_name_and_permalink, if: :should_regenerate_pretty_name_and_permalink?
81
224
 
82
225
  def slug
83
226
  permalink
@@ -92,15 +235,23 @@ module Spree
92
235
  end
93
236
 
94
237
  def set_pretty_name
95
- self[:pretty_name] = generate_pretty_name
238
+ self.pretty_name = generate_pretty_name
96
239
  end
97
240
 
98
241
  def name_with_fallback
99
- name.blank? ? translated_model.name : name
242
+ name.blank? ? translated_model[:name] : name
100
243
  end
101
244
 
102
245
  def pretty_name_with_fallback
103
- pretty_name.blank? ? translated_model.pretty_name : pretty_name
246
+ pretty_name.blank? ? translated_model[:pretty_name] : pretty_name
247
+ end
248
+
249
+ def regenerate_pretty_name_and_permalink
250
+ Spree::Taxon::Translation.where(spree_taxon_id: translated_model.cached_self_and_descendants_ids).each(&:update_pretty_name_and_permalink)
251
+ end
252
+
253
+ def update_pretty_name_and_permalink
254
+ update_columns(pretty_name: generate_pretty_name, permalink: generate_slug, updated_at: Time.current)
104
255
  end
105
256
 
106
257
  private
@@ -119,7 +270,7 @@ module Spree
119
270
  if parent.present?
120
271
  generate_pretty_name_including_parent
121
272
  elsif pretty_name.blank?
122
- pretty_name_with_fallback
273
+ name_with_fallback
123
274
  else
124
275
  pretty_name
125
276
  end
@@ -130,7 +281,7 @@ module Spree
130
281
  end
131
282
 
132
283
  def generate_pretty_name_including_parent
133
- [parent_pretty_name_with_fallback, (pretty_name.blank? ? name_with_fallback : pretty_name)].compact.join(' -> ')
284
+ [parent_pretty_name_with_fallback, (name.blank? ? name_with_fallback : name)].compact.join(' -> ')
134
285
  end
135
286
 
136
287
  def parent
@@ -139,12 +290,16 @@ module Spree
139
290
 
140
291
  def parent_permalink_with_fallback
141
292
  localized_parent = parent.translations.find_by(locale: locale)
142
- localized_parent.present? ? localized_parent.permalink : parent.permalink
293
+ localized_parent.present? ? localized_parent.permalink : parent[:permalink]
143
294
  end
144
295
 
145
296
  def parent_pretty_name_with_fallback
146
297
  localized_parent = parent.translations.find_by(locale: locale)
147
- localized_parent.present? ? localized_parent.pretty_name : parent.pretty_name
298
+ localized_parent.present? ? localized_parent.pretty_name : parent[:pretty_name]
299
+ end
300
+
301
+ def should_regenerate_pretty_name_and_permalink?
302
+ saved_changes.key?(:name) || saved_changes.key?(:permalink)
148
303
  end
149
304
  end
150
305
 
@@ -166,7 +321,7 @@ module Spree
166
321
  end
167
322
 
168
323
  def set_pretty_name
169
- self[:pretty_name] = generate_pretty_name
324
+ self.pretty_name = generate_pretty_name
170
325
  end
171
326
 
172
327
  def generate_pretty_name
@@ -196,15 +351,17 @@ module Spree
196
351
  end
197
352
 
198
353
  def regenerate_pretty_name_and_permalink
199
- set_permalink
200
- update_columns(pretty_name: generate_pretty_name, updated_at: Time.current)
354
+ Mobility.with_locale(nil) do
355
+ update_columns(pretty_name: generate_pretty_name, permalink: generate_slug, updated_at: Time.current)
356
+ end
201
357
 
202
358
  children.reload.each(&:regenerate_pretty_name_and_permalink_as_child)
203
359
  end
204
360
 
205
361
  def regenerate_pretty_name_and_permalink_as_child
206
- set_permalink
207
- update_columns(pretty_name: generate_pretty_name, updated_at: Time.current)
362
+ Mobility.with_locale(nil) do
363
+ update_columns(pretty_name: generate_pretty_name, permalink: generate_slug, updated_at: Time.current)
364
+ end
208
365
 
209
366
  children.reload.each(&:regenerate_pretty_name_and_permalink_as_child)
210
367
  end
@@ -225,6 +382,20 @@ module Spree
225
382
  move_to_child_with_index(parent, idx.to_i) unless new_record?
226
383
  end
227
384
 
385
+ def page_builder_url
386
+ return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:nested_taxons_path)
387
+
388
+ Spree::Core::Engine.routes.url_helpers.nested_taxons_path(self)
389
+ end
390
+
391
+ def featured?
392
+ featured_sections.any?
393
+ end
394
+
395
+ def featured_sections
396
+ @featured_sections ||= Spree::PageSections::FeaturedTaxon.published.by_taxon_id(id)
397
+ end
398
+
228
399
  private
229
400
 
230
401
  def should_regenerate_pretty_name_and_permalink?
@@ -261,5 +432,17 @@ module Spree
261
432
  def copy_taxonomy_from_parent
262
433
  self.taxonomy = parent.taxonomy if parent.present? && taxonomy.blank?
263
434
  end
435
+
436
+ def regenerate_translations_pretty_name_and_permalink
437
+ translations.each(&:regenerate_pretty_name_and_permalink)
438
+ end
439
+
440
+ def touch_featured_sections
441
+ Spree::Taxons::TouchFeaturedSections.call(taxon_ids: [id])
442
+ end
443
+
444
+ def remove_featured_sections
445
+ featured_sections.destroy_all
446
+ end
264
447
  end
265
448
  end
@@ -5,7 +5,7 @@ module Spree
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- validates :attachment, content_type: /\Aimage\/.*\z/
8
+ validates :attachment, content_type: Rails.application.config.active_storage.web_image_content_types
9
9
 
10
10
  default_scope { includes(attachment_attachment: :blob) }
11
11
 
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ class TaxonRule < Spree.base_class
3
+ MATCH_POLICIES = %w[
4
+ is_equal_to
5
+ is_not_equal_to
6
+ contains
7
+ does_not_contain
8
+ ].freeze
9
+
10
+ belongs_to :taxon, class_name: 'Spree::Taxon', inverse_of: :taxon_rules, touch: true
11
+
12
+ validates :taxon, :type, :value, presence: true
13
+ validates :match_policy, inclusion: { in: MATCH_POLICIES }, presence: true
14
+
15
+ after_commit :regenerate_taxon_products, if: -> { saved_change_to_value? || destroyed? || saved_change_to_match_policy? }
16
+
17
+ delegate :store, to: :taxon
18
+
19
+ private
20
+
21
+ def regenerate_taxon_products
22
+ taxon.regenerate_taxon_products(only_once: true)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ module TaxonRules
3
+ class AvailableOn < Spree::TaxonRule
4
+ def apply(scope)
5
+ # value here is no of weeks
6
+ # eg. return products that become available in the last 2 weeks
7
+ timezone = taxon.store.preferred_timezone
8
+ date = value.to_i.days.ago.beginning_of_day.in_time_zone(timezone)
9
+
10
+ if match_policy == 'is_equal_to'
11
+ scope.where(['spree_products.created_at >= ?', date]).or(
12
+ scope.where(['spree_products.available_on >= ?', date])
13
+ )
14
+ else
15
+ scope
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ module TaxonRules
3
+ class Sale < Spree::TaxonRule
4
+ def apply(scope)
5
+ case match_policy
6
+ when 'is_equal_to' || 'contains'
7
+ scope.on_sale(store.default_currency)
8
+ when 'is_not_equal_to' || 'does_not_contain'
9
+ scope.where.not(id: scope.on_sale(store.default_currency))
10
+ else
11
+ scope
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module TaxonRules
3
+ class Tag < Spree::TaxonRule
4
+ def apply(scope)
5
+ if match_policy == 'is_equal_to'
6
+ scope.tagged_with(value)
7
+ elsif match_policy == 'is_not_equal_to'
8
+ scope.tagged_with(value, exclude: true)
9
+ elsif match_policy == 'contains'
10
+ scope.tagged_with(value, any: true)
11
+ elsif match_policy == 'does_not_contain'
12
+ scope.tagged_with(value, any: true, exclude: true)
13
+ else
14
+ scope
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class Taxonomy < Spree::Base
2
+ class Taxonomy < Spree.base_class
3
3
  include Spree::TranslatableResource
4
4
  include Spree::Metadata
5
5
  include Spree::SingleStoreResource
@@ -12,7 +12,7 @@ module Spree
12
12
 
13
13
  acts_as_list
14
14
 
15
- validates :name, presence: true, uniqueness: { case_sensitive: false, allow_blank: true, scope: :store_id }
15
+ validates :name, presence: true, uniqueness: { case_sensitive: false, scope: :store_id }
16
16
  validates :store, presence: true
17
17
 
18
18
  has_many :taxons, inverse_of: :taxonomy
@@ -24,6 +24,7 @@ module Spree
24
24
 
25
25
  default_scope { order("#{table_name}.position, #{table_name}.created_at") }
26
26
 
27
+ self.whitelisted_ransackable_attributes = %w[name]
27
28
  self.whitelisted_ransackable_associations = %w[root]
28
29
 
29
30
  private