spree_core 4.2.5 → 4.3.0

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 (263) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/concerns/spree/product_filterable.rb +9 -0
  3. data/app/finders/spree/cms_pages/find.rb +41 -0
  4. data/app/finders/spree/menus/find.rb +11 -0
  5. data/app/finders/spree/option_values/find_available.rb +20 -0
  6. data/app/finders/spree/orders/find_complete.rb +14 -2
  7. data/app/finders/spree/orders/find_current.rb +1 -13
  8. data/app/finders/spree/product_properties/find_available.rb +20 -0
  9. data/app/finders/spree/products/find.rb +80 -21
  10. data/app/finders/spree/stores/find_current.rb +24 -0
  11. data/app/finders/spree/taxons/find.rb +22 -6
  12. data/app/helpers/spree/base_helper.rb +69 -7
  13. data/app/helpers/spree/locale_helper.rb +5 -1
  14. data/app/helpers/spree/products_helper.rb +7 -3
  15. data/app/models/concerns/spree/display_link.rb +42 -0
  16. data/app/models/concerns/spree/display_money.rb +6 -2
  17. data/app/models/concerns/spree/filter_param.rb +21 -0
  18. data/app/models/concerns/spree/image_methods.rb +24 -0
  19. data/app/models/concerns/spree/memoized_data.rb +24 -0
  20. data/app/models/concerns/spree/multi_store_resource.rb +24 -0
  21. data/app/models/concerns/spree/product_scopes.rb +71 -8
  22. data/app/models/concerns/spree/single_store_resource.rb +19 -0
  23. data/app/models/concerns/spree/user_address.rb +19 -0
  24. data/app/models/concerns/spree/user_methods.rb +14 -12
  25. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  26. data/app/models/concerns/spree/user_reporting.rb +35 -10
  27. data/app/models/concerns/spree/user_roles.rb +25 -0
  28. data/app/models/spree/ability.rb +3 -1
  29. data/app/models/spree/address.rb +27 -6
  30. data/app/models/spree/app_configuration.rb +8 -0
  31. data/app/models/spree/app_dependencies.rb +20 -6
  32. data/app/models/spree/base.rb +18 -0
  33. data/app/models/spree/classification.rb +3 -0
  34. data/app/models/spree/cms/pages/feature_page.rb +7 -0
  35. data/app/models/spree/cms/pages/homepage.rb +20 -0
  36. data/app/models/spree/cms/pages/standard_page.rb +4 -0
  37. data/app/models/spree/cms/sections/featured_article.rb +22 -0
  38. data/app/models/spree/cms/sections/hero_image.rb +38 -0
  39. data/app/models/spree/cms/sections/image_gallery.rb +103 -0
  40. data/app/models/spree/cms/sections/product_carousel.rb +14 -0
  41. data/app/models/spree/cms/sections/rich_text_content.rb +13 -0
  42. data/app/models/spree/cms/sections/side_by_side_images.rb +74 -0
  43. data/app/models/spree/cms_page.rb +66 -0
  44. data/app/models/spree/cms_section.rb +69 -0
  45. data/app/models/spree/country.rb +14 -6
  46. data/app/models/spree/credit_card.rb +2 -8
  47. data/app/models/spree/customer_return.rb +8 -2
  48. data/app/models/spree/gateway/bogus.rb +6 -4
  49. data/app/models/spree/icon.rb +9 -0
  50. data/app/models/spree/image/configuration/active_storage.rb +2 -0
  51. data/app/models/spree/image.rb +5 -24
  52. data/app/models/spree/inventory_unit.rb +1 -1
  53. data/app/models/spree/line_item.rb +1 -1
  54. data/app/models/spree/menu.rb +63 -0
  55. data/app/models/spree/menu_item.rb +76 -0
  56. data/app/models/spree/option_type.rb +1 -1
  57. data/app/models/spree/option_value.rb +11 -0
  58. data/app/models/spree/option_value_variant.rb +1 -1
  59. data/app/models/spree/order/address_book.rb +3 -5
  60. data/app/models/spree/order/currency_updater.rb +1 -1
  61. data/app/models/spree/order/emails.rb +32 -0
  62. data/app/models/spree/order/payments.rb +1 -1
  63. data/app/models/spree/order/store_credit.rb +1 -1
  64. data/app/models/spree/order.rb +46 -45
  65. data/app/models/spree/payment.rb +7 -0
  66. data/app/models/spree/payment_method.rb +16 -0
  67. data/app/models/spree/preferences/preferable.rb +12 -0
  68. data/app/models/spree/preferences/preferable_class_methods.rb +10 -1
  69. data/app/models/spree/product.rb +37 -30
  70. data/app/models/spree/product_property.rb +19 -4
  71. data/app/models/spree/promotion/rules/country.rb +1 -1
  72. data/app/models/spree/promotion/rules/first_order.rb +4 -3
  73. data/app/models/spree/promotion/rules/taxon.rb +10 -7
  74. data/app/models/spree/promotion.rb +6 -15
  75. data/app/models/spree/promotion_handler/cart.rb +7 -2
  76. data/app/models/spree/promotion_handler/coupon.rb +5 -4
  77. data/app/models/spree/promotion_handler/free_shipping.rb +5 -6
  78. data/app/models/spree/promotion_handler/page.rb +3 -2
  79. data/app/models/spree/promotion_handler/promotion_duplicator.rb +1 -0
  80. data/app/models/spree/promotion_rule.rb +2 -0
  81. data/app/models/spree/property.rb +28 -1
  82. data/app/models/spree/reimbursement.rb +3 -1
  83. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +2 -1
  84. data/app/models/spree/shipment_handler.rb +4 -2
  85. data/app/models/spree/stock/quantifier.rb +6 -6
  86. data/app/models/spree/stock_location.rb +9 -0
  87. data/app/models/spree/store.rb +90 -7
  88. data/app/models/spree/store_credit.rb +7 -3
  89. data/app/models/spree/store_credit_event.rb +2 -2
  90. data/app/models/spree/store_payment_method.rb +11 -0
  91. data/app/models/spree/store_product.rb +11 -0
  92. data/app/models/spree/store_promotion.rb +11 -0
  93. data/app/models/spree/taxon.rb +22 -1
  94. data/app/models/spree/taxon_image/configuration/active_storage.rb +2 -0
  95. data/app/models/spree/taxon_image.rb +1 -0
  96. data/app/models/spree/taxonomy.rb +3 -1
  97. data/app/models/spree/variant.rb +21 -6
  98. data/app/presenters/spree/filters/options_presenter.rb +47 -0
  99. data/app/presenters/spree/filters/price_presenter.rb +22 -0
  100. data/app/presenters/spree/filters/price_range_presenter.rb +29 -0
  101. data/app/presenters/spree/filters/properties_presenter.rb +23 -0
  102. data/app/presenters/spree/filters/property_presenter.rb +42 -0
  103. data/app/presenters/spree/filters/quantified_price_range_presenter.rb +44 -0
  104. data/app/services/spree/account/addresses/create.rb +1 -0
  105. data/app/services/spree/account/addresses/helper.rb +6 -0
  106. data/app/services/spree/account/addresses/update.rb +9 -3
  107. data/app/services/spree/account/create.rb +17 -0
  108. data/app/services/spree/account/update.rb +15 -0
  109. data/app/services/spree/build_localized_redirect_url.rb +1 -1
  110. data/app/services/spree/cart/associate.rb +16 -0
  111. data/app/services/spree/cart/change_currency.rb +27 -0
  112. data/app/services/spree/cart/create.rb +5 -3
  113. data/app/services/spree/cart/destroy.rb +40 -0
  114. data/app/services/spree/cart/empty.rb +36 -0
  115. data/app/services/spree/cart/estimate_shipping_rates.rb +3 -3
  116. data/app/services/spree/checkout/add_store_credit.rb +1 -1
  117. data/app/services/spree/classifications/reposition.rb +18 -0
  118. data/app/services/spree/credit_cards/destroy.rb +41 -0
  119. data/app/sorters/spree/base_sorter.rb +23 -11
  120. data/app/sorters/spree/products/sort.rb +23 -7
  121. data/app/validators/db_maximum_length_validator.rb +5 -0
  122. data/app/validators/email_validator.rb +3 -1
  123. data/config/initializers/active_storage.rb +2 -0
  124. data/config/locales/en.yml +177 -3
  125. data/config/routes.rb +0 -5
  126. data/db/migrate/20201023152810_add_filterable_to_spree_properties.rb +8 -0
  127. data/db/migrate/20210407200948_create_spree_menus.rb +16 -0
  128. data/db/migrate/20210408092939_create_spree_menu_items.rb +31 -0
  129. data/db/migrate/20210504163720_add_filter_param_to_spree_product_properties.rb +8 -0
  130. data/db/migrate/20210505114659_add_filter_param_to_spree_properties.rb +8 -0
  131. data/db/migrate/20210512191732_create_spree_cms_pages.rb +24 -0
  132. data/db/migrate/20210514204251_create_spree_cms_sections.rb +22 -0
  133. data/db/migrate/20210527094055_create_spree_products_stores.rb +36 -0
  134. data/db/migrate/20210608045519_ensure_store_default_country_is_set.rb +5 -0
  135. data/db/migrate/20210702112334_add_missing_timestamp_columns.rb +46 -0
  136. data/db/migrate/20210713131614_add_unique_index_on_property_id_and_product_id_to_product_properties.rb +29 -0
  137. data/db/migrate/20210715091956_add_store_id_to_spree_store_credits.rb +10 -0
  138. data/db/migrate/20210716093151_add_store_id_to_spree_taxonomies.rb +11 -0
  139. data/db/migrate/20210716104141_add_index_on_name_parent_id_and_taxonomy_id_on_spree_taxons.rb +31 -0
  140. data/db/migrate/20210721120857_add_index_on_permalink_parent_id_and_taxonomy_id_on_spree_taxons.rb +31 -0
  141. data/db/migrate/20210721125657_create_spree_promotions_stores.rb +29 -0
  142. data/db/migrate/20210722090705_add_store_id_to_spree_customer_returns.rb +11 -0
  143. data/db/migrate/20210726065456_change_integer_id_columns_into_bigint.rb +305 -0
  144. data/db/migrate/20210730154425_fix_promotion_code_and_path_unique_indexes.rb +9 -0
  145. data/lib/generators/spree/dummy/dummy_generator.rb +11 -6
  146. data/lib/generators/spree/dummy/templates/rails/test.rb +2 -0
  147. data/lib/generators/spree/dummy_model/templates/model.rb.tt +1 -1
  148. data/lib/generators/spree/install/install_generator.rb +12 -24
  149. data/lib/spree/core/components.rb +8 -0
  150. data/lib/spree/core/controller_helpers/auth.rb +7 -1
  151. data/lib/spree/core/controller_helpers/common.rb +1 -1
  152. data/lib/spree/core/controller_helpers/order.rb +4 -4
  153. data/lib/spree/core/controller_helpers/search.rb +1 -0
  154. data/lib/spree/core/controller_helpers/store.rb +24 -1
  155. data/lib/spree/core/engine.rb +6 -0
  156. data/lib/spree/core/importer/product.rb +3 -1
  157. data/lib/spree/core/product_duplicator.rb +1 -0
  158. data/lib/spree/core/search/base.rb +17 -22
  159. data/lib/spree/core/version.rb +1 -1
  160. data/lib/spree/core.rb +8 -4
  161. data/lib/spree/i18n.rb +1 -1
  162. data/lib/spree/money.rb +2 -1
  163. data/lib/spree/permitted_attributes.rb +21 -3
  164. data/lib/spree/testing_support/authorization_helpers.rb +6 -3
  165. data/lib/spree/testing_support/capybara_config.rb +3 -1
  166. data/lib/spree/testing_support/common_rake.rb +17 -4
  167. data/lib/spree/testing_support/extension_rake.rb +2 -2
  168. data/lib/spree/testing_support/factories/address_factory.rb +1 -1
  169. data/lib/spree/testing_support/factories/classification_factory.rb +8 -0
  170. data/lib/spree/testing_support/factories/cms_page_factory.rb +20 -0
  171. data/lib/spree/testing_support/factories/cms_section_factory.rb +31 -0
  172. data/lib/spree/testing_support/factories/customer_return_factory.rb +24 -17
  173. data/lib/spree/testing_support/factories/icon_factory.rb +7 -0
  174. data/lib/spree/testing_support/factories/line_item_factory.rb +6 -2
  175. data/lib/spree/testing_support/factories/menu_factory.rb +16 -0
  176. data/lib/spree/testing_support/factories/menu_item_factory.rb +10 -0
  177. data/lib/spree/testing_support/factories/options_factory.rb +5 -0
  178. data/lib/spree/testing_support/factories/order_factory.rb +11 -2
  179. data/lib/spree/testing_support/factories/payment_method_factory.rb +6 -2
  180. data/lib/spree/testing_support/factories/product_factory.rb +17 -1
  181. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -1
  182. data/lib/spree/testing_support/factories/promotion_factory.rb +18 -9
  183. data/lib/spree/testing_support/factories/property_factory.rb +22 -0
  184. data/lib/spree/testing_support/factories/stock_location_factory.rb +5 -4
  185. data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -0
  186. data/lib/spree/testing_support/factories/store_factory.rb +11 -0
  187. data/lib/spree/testing_support/factories/taxon_factory.rb +3 -1
  188. data/lib/spree/testing_support/factories/taxon_image_factory.rb +7 -0
  189. data/lib/spree/testing_support/factories/taxonomy_factory.rb +1 -0
  190. data/lib/spree/testing_support/factories/user_factory.rb +7 -2
  191. data/lib/spree/testing_support/factories/variant_factory.rb +2 -2
  192. data/lib/spree/testing_support/flatpickr_capybara.rb +58 -35
  193. data/lib/spree/testing_support/locale_helpers.rb +1 -1
  194. data/lib/spree/testing_support/order_walkthrough.rb +9 -9
  195. data/lib/spree/testing_support/rspec_retry_config.rb +5 -0
  196. data/lib/tasks/core.rake +1 -1
  197. data/spec/fixtures/favicon.ico +0 -0
  198. data/spec/fixtures/files/icon_256x256.gif +0 -0
  199. data/spec/fixtures/files/icon_256x256.png +0 -0
  200. data/spec/fixtures/files/icon_512x512.png +0 -0
  201. data/spec/fixtures/files/img_256x128.png +0 -0
  202. data/spree_core.gemspec +12 -11
  203. metadata +211 -159
  204. data/app/assets/images/logo/spree_50.png +0 -0
  205. data/app/assets/images/noimage/large.png +0 -0
  206. data/app/assets/images/noimage/mini.png +0 -0
  207. data/app/assets/images/noimage/product.png +0 -0
  208. data/app/assets/images/noimage/small.png +0 -0
  209. data/app/assets/javascripts/spree.js +0 -78
  210. data/app/controllers/spree/errors_controller.rb +0 -11
  211. data/app/helpers/spree/mail_helper.rb +0 -29
  212. data/app/mailers/spree/base_mailer.rb +0 -46
  213. data/app/mailers/spree/order_mailer.rb +0 -26
  214. data/app/mailers/spree/reimbursement_mailer.rb +0 -12
  215. data/app/mailers/spree/shipment_mailer.rb +0 -12
  216. data/app/mailers/spree/test_mailer.rb +0 -8
  217. data/app/models/spree/validations/db_maximum_length_validator.rb +0 -22
  218. data/app/views/layouts/spree/base_mailer.html.erb +0 -46
  219. data/app/views/spree/errors/forbidden.html.erb +0 -0
  220. data/app/views/spree/errors/unauthorized.html.erb +0 -0
  221. data/app/views/spree/order_mailer/cancel_email.html.erb +0 -24
  222. data/app/views/spree/order_mailer/cancel_email.text.erb +0 -38
  223. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -23
  224. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -39
  225. data/app/views/spree/order_mailer/store_owner_notification_email.html.erb +0 -23
  226. data/app/views/spree/order_mailer/store_owner_notification_email.text.erb +0 -38
  227. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -56
  228. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -24
  229. data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -12
  230. data/app/views/spree/shared/_base_mailer_header.html.erb +0 -13
  231. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -456
  232. data/app/views/spree/shared/_error_messages.html.erb +0 -11
  233. data/app/views/spree/shared/_mailer_line_item.html.erb +0 -16
  234. data/app/views/spree/shared/_paths.html.erb +0 -8
  235. data/app/views/spree/shared/_purchased_items_table.html.erb +0 -69
  236. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +0 -13
  237. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +0 -27
  238. data/app/views/spree/shared/purchased_items_table/_subtotal.html.erb +0 -13
  239. data/app/views/spree/shared/purchased_items_table/_total.html.erb +0 -13
  240. data/app/views/spree/shipment_mailer/shipped_email.html.erb +0 -36
  241. data/app/views/spree/shipment_mailer/shipped_email.text.erb +0 -17
  242. data/app/views/spree/test_mailer/test_email.html.erb +0 -40
  243. data/app/views/spree/test_mailer/test_email.text.erb +0 -4
  244. data/config/initializers/assets.rb +0 -2
  245. data/config/initializers/premailer_assets.rb +0 -1
  246. data/config/initializers/premailer_rails.rb +0 -3
  247. data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +0 -10
  248. data/lib/generators/spree/dummy/templates/initializers/bullet.rb +0 -5
  249. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +0 -15
  250. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -16
  251. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/backend/all.css +0 -16
  252. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -16
  253. data/lib/generators/spree/mailers_preview/mailers_preview_generator.rb +0 -23
  254. data/lib/generators/spree/mailers_preview/templates/mailers/previews/order_preview.rb +0 -13
  255. data/lib/generators/spree/mailers_preview/templates/mailers/previews/reimbursement_preview.rb +0 -5
  256. data/lib/generators/spree/mailers_preview/templates/mailers/previews/shipment_preview.rb +0 -5
  257. data/lib/generators/spree/mailers_preview/templates/mailers/previews/user_preview.rb +0 -11
  258. data/lib/tasks/email.rake +0 -10
  259. data/vendor/assets/javascripts/cleave.js +0 -1669
  260. data/vendor/assets/javascripts/fetch.umd.js +0 -531
  261. data/vendor/assets/javascripts/jquery.payment.js +0 -652
  262. data/vendor/assets/javascripts/jsuri.js +0 -458
  263. data/vendor/assets/javascripts/polyfill.min.js +0 -1
@@ -1,7 +1,41 @@
1
1
  module Spree
2
2
  class Store < Spree::Base
3
+ MAILER_LOGO_CONTENT_TYPES = ['image/png', 'image/jpg', 'image/jpeg'].freeze
4
+ FAVICON_CONTENT_TYPES = ['image/png', 'image/x-icon', 'image/vnd.microsoft.icon'].freeze
5
+
3
6
  has_many :orders, class_name: 'Spree::Order'
4
- has_many :payment_methods, class_name: 'Spree::PaymentMethod'
7
+ has_many :line_items, through: :orders, class_name: 'Spree::LineItem'
8
+ has_many :shipments, through: :orders, class_name: 'Spree::Shipment'
9
+ has_many :payments, through: :orders, class_name: 'Spree::Payment'
10
+ has_many :return_authorizations, through: :orders, class_name: 'Spree::ReturnAuthorization'
11
+ # has_many :reimbursements, through: :orders, class_name: 'Spree::Reimbursement' FIXME: we should fetch this via Customer Return
12
+
13
+ has_many :store_payment_methods, class_name: 'Spree::StorePaymentMethod'
14
+ has_many :payment_methods, through: :store_payment_methods, class_name: 'Spree::PaymentMethod'
15
+
16
+ has_many :cms_pages, class_name: 'Spree::CmsPage'
17
+ has_many :cms_sections, through: :cms_pages, class_name: 'Spree::CmsSection'
18
+
19
+ has_many :menus, class_name: 'Spree::Menu'
20
+ has_many :menu_items, through: :menus, class_name: 'Spree::MenuItem'
21
+
22
+ has_many :store_products, class_name: 'Spree::StoreProduct', dependent: :destroy
23
+ has_many :products, through: :store_products, class_name: 'Spree::Product'
24
+ has_many :product_properties, through: :products, class_name: 'Spree::ProductProperty'
25
+ has_many :variants, through: :products, class_name: 'Spree::Variant', source: :variants_including_master
26
+ has_many :stock_items, through: :variants, class_name: 'Spree::StockItem'
27
+ has_many :inventory_units, through: :variants, class_name: 'Spree::InventoryUnit'
28
+ has_many :customer_returns, class_name: 'Spree::CustomerReturn', inverse_of: :store
29
+
30
+ has_many :store_credits, class_name: 'Spree::StoreCredit'
31
+ has_many :store_credit_events, through: :store_credits, class_name: 'Spree::StoreCreditEvent'
32
+
33
+ has_many :taxonomies, class_name: 'Spree::Taxonomy'
34
+ has_many :taxons, through: :taxonomies, class_name: 'Spree::Taxon'
35
+
36
+ has_many :store_promotions, class_name: 'Spree::StorePromotion'
37
+ has_many :promotions, through: :store_promotions, class_name: 'Spree::Promotion'
38
+
5
39
  belongs_to :default_country, class_name: 'Spree::Country'
6
40
  belongs_to :checkout_zone, class_name: 'Spree::Zone'
7
41
 
@@ -18,13 +52,20 @@ module Spree
18
52
  validates :new_order_notifications_email, email: { allow_blank: true }
19
53
  end
20
54
 
55
+ default_scope { order(created_at: :asc) }
56
+
21
57
  has_one_attached :logo
22
58
  has_one_attached :mailer_logo
59
+ has_one_attached :favicon_image
23
60
 
24
- validates :mailer_logo, content_type: ['image/png', 'image/jpg', 'image/jpeg']
61
+ validates :mailer_logo, content_type: MAILER_LOGO_CONTENT_TYPES
62
+ validates :favicon_image, content_type: FAVICON_CONTENT_TYPES,
63
+ dimension: { max: 256..256 },
64
+ aspect_ratio: :square,
65
+ size: { less_than_or_equal_to: 1.megabyte }
25
66
 
26
67
  before_save :ensure_default_exists_and_is_unique
27
- before_save :ensure_supported_currencies, :ensure_supported_locales
68
+ before_save :ensure_supported_currencies, :ensure_supported_locales, :ensure_default_country
28
69
  before_destroy :validate_not_default
29
70
 
30
71
  scope :by_url, ->(url) { where('url like ?', "%#{url}%") }
@@ -33,9 +74,12 @@ module Spree
33
74
 
34
75
  alias_attribute :contact_email, :customer_support_email
35
76
 
36
- def self.current(domain = nil)
37
- current_store = domain ? Store.by_url(domain).first : nil
38
- current_store || Store.default
77
+ def self.current(url = nil)
78
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
79
+ `Spree::Store.current` is deprecated and will be removed in Spree 5.0
80
+ Please use `Spree::Stores::FindCurrent.new(url: "https://example.com").execute` instead
81
+ DEPRECATION
82
+ Stores::FindCurrent.new(url: url).execute
39
83
  end
40
84
 
41
85
  def self.default
@@ -50,12 +94,34 @@ module Spree
50
94
  end
51
95
  end
52
96
 
97
+ def default_menu(location)
98
+ menu = menus.find_by(location: location, locale: default_locale) || menus.find_by(location: location)
99
+
100
+ menu.root if menu.present?
101
+ end
102
+
53
103
  def supported_currencies_list
54
104
  @supported_currencies_list ||= (read_attribute(:supported_currencies).to_s.split(',') << default_currency).sort.map(&:to_s).map do |code|
55
105
  ::Money::Currency.find(code.strip)
56
106
  end.uniq.compact
57
107
  end
58
108
 
109
+ def homepage(requested_locale)
110
+ cms_pages.by_locale(requested_locale).find_by(type: 'Spree::Cms::Pages::Homepage') ||
111
+ cms_pages.by_locale(default_locale).find_by(type: 'Spree::Cms::Pages::Homepage') ||
112
+ cms_pages.find_by(type: 'Spree::Cms::Pages::Homepage')
113
+ end
114
+
115
+ def seo_meta_description
116
+ if meta_description.present?
117
+ meta_description
118
+ elsif seo_title.present?
119
+ seo_title
120
+ else
121
+ name
122
+ end
123
+ end
124
+
59
125
  def supported_locales_list
60
126
  # TODO: add support of multiple supported languages to a single Store
61
127
  @supported_locales_list ||= (read_attribute(:supported_locales).to_s.split(',') << default_locale).compact.uniq.sort
@@ -71,7 +137,7 @@ module Spree
71
137
  @formatted_url ||= if url.match(/http:\/\/|https:\/\//)
72
138
  url
73
139
  else
74
- Rails.env.development? ? "http://#{url}" : "https://#{url}"
140
+ Rails.env.development? || Rails.env.test? ? "http://#{url}" : "https://#{url}"
75
141
  end
76
142
  end
77
143
 
@@ -87,6 +153,12 @@ module Spree
87
153
  @checkout_zone_or_default ||= checkout_zone || Spree::Zone.default_checkout_zone
88
154
  end
89
155
 
156
+ def favicon
157
+ return unless favicon_image.attached?
158
+
159
+ favicon_image.variant(resize: '32x32')
160
+ end
161
+
90
162
  private
91
163
 
92
164
  def ensure_default_exists_and_is_unique
@@ -124,5 +196,16 @@ module Spree
124
196
  Rails.cache.delete('default_store')
125
197
  Rails.cache.delete('stores_available_locales')
126
198
  end
199
+
200
+ def ensure_default_country
201
+ return unless has_attribute?(:default_country_id)
202
+ return if default_country.present? && (checkout_zone.blank? || checkout_zone.country_list.blank? || checkout_zone.country_list.include?(default_country))
203
+
204
+ self.default_country = if checkout_zone.present? && checkout_zone.country_list.any?
205
+ checkout_zone.country_list.first
206
+ else
207
+ Country.find_by(iso: 'US') || Country.first
208
+ end
209
+ end
127
210
  end
128
211
  end
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class StoreCredit < Spree::Base
3
+ include SingleStoreResource
4
+
3
5
  acts_as_paranoid
4
6
 
5
7
  VOID_ACTION = 'void'.freeze
@@ -16,9 +18,10 @@ module Spree
16
18
  belongs_to :category, class_name: 'Spree::StoreCreditCategory'
17
19
  belongs_to :created_by, class_name: Spree.user_class.to_s, foreign_key: 'created_by_id'
18
20
  belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id'
19
- has_many :store_credit_events
21
+ belongs_to :store, class_name: 'Spree::Store'
22
+ has_many :store_credit_events, class_name: 'Spree::StoreCreditEvent'
20
23
 
21
- validates :user, :category, :credit_type, :created_by, :currency, presence: true
24
+ validates :user, :category, :credit_type, :created_by, :currency, :store, presence: true
22
25
  validates :amount, numericality: { greater_than: 0 }
23
26
  validates :amount_used, numericality: { greater_than_or_equal_to: 0 }
24
27
  validate :amount_used_less_than_or_equal_to_amount
@@ -188,7 +191,8 @@ module Spree
188
191
  created_by_id: created_by_id,
189
192
  currency: currency,
190
193
  type_id: type_id,
191
- memo: credit_allocation_memo
194
+ memo: credit_allocation_memo,
195
+ store: store
192
196
  }
193
197
  end
194
198
 
@@ -8,7 +8,7 @@ module Spree
8
8
  scope :exposed_events, -> { where.not(action: [Spree::StoreCredit::ELIGIBLE_ACTION, Spree::StoreCredit::AUTHORIZE_ACTION]) }
9
9
  scope :reverse_chronological, -> { order(created_at: :desc) }
10
10
 
11
- delegate :currency, to: :store_credit
11
+ delegate :currency, :store, to: :store_credit
12
12
 
13
13
  def display_amount
14
14
  Spree::Money.new(amount, currency: currency)
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def order
35
- Spree::Payment.find_by(response_code: authorization_code).try(:order)
35
+ store.payments.find_by(response_code: authorization_code).try(:order)
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class StorePaymentMethod < Spree::Base
3
+ self.table_name = 'spree_payment_methods_stores'
4
+
5
+ belongs_to :store, class_name: 'Spree::Store', touch: true
6
+ belongs_to :payment_method, class_name: 'Spree::PaymentMethod', touch: true
7
+
8
+ validates :store, :payment_method, presence: true
9
+ validates :store_id, uniqueness: { scope: :payment_method_id }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class StoreProduct < Spree::Base
3
+ self.table_name = 'spree_products_stores'
4
+
5
+ belongs_to :store, class_name: 'Spree::Store', touch: true
6
+ belongs_to :product, class_name: 'Spree::Product', touch: true
7
+
8
+ validates :store, :product, presence: true
9
+ validates :store_id, uniqueness: { scope: :product_id }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class StorePromotion < Spree::Base
3
+ self.table_name = 'spree_promotions_stores'
4
+
5
+ belongs_to :store, class_name: 'Spree::Store', touch: true
6
+ belongs_to :promotion, class_name: 'Spree::Promotion', touch: true
7
+
8
+ validates :store, :promotion, presence: true
9
+ validates :store_id, uniqueness: { scope: :promotion_id }
10
+ end
11
+ end
@@ -13,6 +13,9 @@ module Spree
13
13
  has_many :classifications, -> { order(:position) }, dependent: :delete_all, inverse_of: :taxon
14
14
  has_many :products, through: :classifications
15
15
 
16
+ has_many :menu_items, as: :linked_resource
17
+ has_many :cms_sections, as: :linked_resource
18
+
16
19
  has_many :prototype_taxons, class_name: 'Spree::PrototypeTaxon', dependent: :destroy
17
20
  has_many :prototypes, through: :prototype_taxons, class_name: 'Spree::Prototype'
18
21
 
@@ -20,22 +23,30 @@ module Spree
20
23
  has_many :promotion_rules, through: :promotion_rule_taxons, class_name: 'Spree::PromotionRule'
21
24
 
22
25
  validates :name, presence: true, uniqueness: { scope: [:parent_id, :taxonomy_id], allow_blank: true, case_sensitive: false }
23
- validates :permalink, uniqueness: { case_sensitive: false }
26
+ validates :taxonomy, presence: true
27
+ validates :permalink, uniqueness: { case_sensitive: false, scope: [:parent_id, :taxonomy_id] }
24
28
  validates :hide_from_nav, inclusion: { in: [true, false] }
25
29
  validates_associated :icon
26
30
  validate :check_for_root, on: :create
31
+ validate :parent_belongs_to_same_taxonomy
27
32
  with_options length: { maximum: 255 }, allow_blank: true do
28
33
  validates :meta_keywords
29
34
  validates :meta_description
30
35
  validates :meta_title
31
36
  end
32
37
 
38
+ before_validation :copy_taxonomy_from_parent
33
39
  after_save :touch_ancestors_and_taxonomy
34
40
  after_touch :touch_ancestors_and_taxonomy
35
41
 
36
42
  has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonImage'
37
43
 
44
+ scope :for_store, ->(store) { joins(:taxonomy).where(spree_taxonomies: { store_id: store.id }) }
45
+
38
46
  self.whitelisted_ransackable_associations = %w[taxonomy]
47
+ self.whitelisted_ransackable_attributes = %w[name permalink]
48
+
49
+ scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }
39
50
 
40
51
  # indicate which filters should be used for a taxon
41
52
  # this method should be customized to your own site
@@ -104,5 +115,15 @@ module Spree
104
115
  errors.add(:root_conflict, 'this taxonomy already has a root taxon')
105
116
  end
106
117
  end
118
+
119
+ def parent_belongs_to_same_taxonomy
120
+ if parent.present? && parent.taxonomy_id != taxonomy_id
121
+ errors.add(:parent, 'must belong to the same taxonomy')
122
+ end
123
+ end
124
+
125
+ def copy_taxonomy_from_parent
126
+ self.taxonomy = parent.taxonomy if parent.present? && taxonomy.blank?
127
+ end
107
128
  end
108
129
  end
@@ -9,6 +9,8 @@ module Spree
9
9
 
10
10
  has_one_attached :attachment
11
11
 
12
+ default_scope { includes(attachment_attachment: :blob) }
13
+
12
14
  def self.styles
13
15
  @styles ||= {
14
16
  mini: '32x32>',
@@ -2,6 +2,7 @@ module Spree
2
2
  class TaxonImage < Asset
3
3
  include Configuration::ActiveStorage
4
4
  include Rails.application.routes.url_helpers
5
+ include ::Spree::ImageMethods
5
6
 
6
7
  def styles
7
8
  self.class.styles.map do |_, size|
@@ -2,10 +2,12 @@ module Spree
2
2
  class Taxonomy < Spree::Base
3
3
  acts_as_list
4
4
 
5
- validates :name, presence: true, uniqueness: { case_sensitive: false, allow_blank: true }
5
+ validates :name, presence: true, uniqueness: { case_sensitive: false, allow_blank: true, scope: :store_id }
6
+ validates :store, presence: true
6
7
 
7
8
  has_many :taxons, inverse_of: :taxonomy
8
9
  has_one :root, -> { where parent_id: nil }, class_name: 'Spree::Taxon', dependent: :destroy
10
+ belongs_to :store, class_name: 'Spree::Store'
9
11
 
10
12
  after_create :set_root
11
13
  after_save :set_root_taxon_name
@@ -3,6 +3,10 @@ module Spree
3
3
  acts_as_paranoid
4
4
  acts_as_list scope: :product
5
5
 
6
+ include MemoizedData
7
+
8
+ MEMOIZED_METHODS = %w(purchasable in_stock backorderable tax_category options_text compare_at_price)
9
+
6
10
  belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product', inverse_of: :variants
7
11
  belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
8
12
 
@@ -29,7 +33,7 @@ module Spree
29
33
  end
30
34
 
31
35
  has_many :option_value_variants, class_name: 'Spree::OptionValueVariant'
32
- has_many :option_values, through: :option_value_variants, class_name: 'Spree::OptionValue'
36
+ has_many :option_values, through: :option_value_variants, dependent: :destroy, class_name: 'Spree::OptionValue'
33
37
 
34
38
  has_many :images, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Image'
35
39
 
@@ -56,7 +60,7 @@ module Spree
56
60
 
57
61
  after_touch :clear_in_stock_cache
58
62
 
59
- scope :in_stock, -> { joins(:stock_items).where('count_on_hand > ? OR track_inventory = ?', 0, false) }
63
+ scope :in_stock, -> { joins(:stock_items).where("#{Spree::StockItem.table_name}.count_on_hand > ? OR #{Spree::Variant.table_name}.track_inventory = ?", 0, false) }
60
64
  scope :backorderable, -> { joins(:stock_items).where(spree_stock_items: { backorderable: true }) }
61
65
  scope :in_stock_or_backorderable, -> { in_stock.or(backorderable) }
62
66
 
@@ -93,6 +97,7 @@ module Spree
93
97
  not_discontinued.not_deleted.
94
98
  for_currency_and_available_price_amount(currency)
95
99
  end
100
+ # FIXME: cost price should be represented with DisplayMoney class
96
101
  LOCALIZED_NUMBERS = %w(cost_price weight depth width height)
97
102
 
98
103
  LOCALIZED_NUMBERS.each do |m|
@@ -199,6 +204,10 @@ module Spree
199
204
  price_in(currency).try(:amount)
200
205
  end
201
206
 
207
+ def compare_at_amount_in(currency)
208
+ price_in(currency).try(:compare_at_amount)
209
+ end
210
+
202
211
  def price_modifier_amount_in(currency, options = {})
203
212
  return 0 unless options.present?
204
213
 
@@ -242,17 +251,23 @@ module Spree
242
251
  # Check if model responds to cache version and fall back to updated_at for older rails versions
243
252
  # This makes sure a version is supplied when recyclable cache keys are disabled.
244
253
  version = respond_to?(:cache_version) ? cache_version : updated_at.to_i
245
- Rails.cache.fetch(in_stock_cache_key, version: version) do
254
+ @in_stock ||= Rails.cache.fetch(in_stock_cache_key, version: version) do
246
255
  total_on_hand > 0
247
256
  end
248
257
  end
249
258
 
250
- delegate :total_on_hand, :can_supply?, :backorderable?, to: :quantifier
259
+ def backorderable?
260
+ @backorderable ||= Rails.cache.fetch(['variant-backorderable', cache_key_with_version]) do
261
+ quantifier.backorderable?
262
+ end
263
+ end
264
+
265
+ delegate :total_on_hand, :can_supply?, to: :quantifier
251
266
 
252
267
  alias is_backorderable? backorderable?
253
268
 
254
269
  def purchasable?
255
- in_stock? || backorderable?
270
+ @purchasable ||= in_stock? || backorderable?
256
271
  end
257
272
 
258
273
  # Shortcut method to determine if inventory tracking is enabled for this variant
@@ -282,7 +297,7 @@ module Spree
282
297
  end
283
298
 
284
299
  def backordered?
285
- total_on_hand <= 0 && stock_items.exists?(backorderable: true)
300
+ @backordered ||= !in_stock? && stock_items.exists?(backorderable: true)
286
301
  end
287
302
 
288
303
  private
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ module Filters
3
+ class OptionsPresenter
4
+ FilterableOptionType = Struct.new(:option_type, :option_values, keyword_init: true) do
5
+ delegate_missing_to :option_type
6
+
7
+ def to_h
8
+ {
9
+ id: option_type.id,
10
+ name: option_type.name,
11
+ presentation: option_type.presentation,
12
+ option_values: option_values.map { |e| option_value_hash(e) }
13
+ }
14
+ end
15
+
16
+ private
17
+
18
+ def option_value_hash(option_value)
19
+ {
20
+ id: option_value.id,
21
+ name: option_value.name,
22
+ presentation: option_value.presentation,
23
+ position: option_value.position
24
+ }
25
+ end
26
+ end
27
+
28
+ def initialize(option_values_scope:)
29
+ @option_values = option_values_scope.includes(:option_type)
30
+ end
31
+
32
+ def to_a
33
+ grouped_options.map do |option_type, option_values|
34
+ FilterableOptionType.new(option_type: option_type, option_values: option_values)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :option_values
41
+
42
+ def grouped_options
43
+ option_values.group_by(&:option_type)
44
+ end
45
+ end
46
+ end
47
+ end