spree_core 4.2.7 → 4.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (249) 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 +55 -20
  10. data/app/finders/spree/stores/find_current.rb +24 -0
  11. data/app/helpers/spree/base_helper.rb +62 -2
  12. data/app/helpers/spree/locale_helper.rb +5 -1
  13. data/app/helpers/spree/products_helper.rb +7 -3
  14. data/app/models/concerns/spree/display_link.rb +42 -0
  15. data/app/models/concerns/spree/filter_param.rb +21 -0
  16. data/app/models/concerns/spree/memoized_data.rb +24 -0
  17. data/app/models/concerns/spree/multi_store_resource.rb +24 -0
  18. data/app/models/concerns/spree/product_scopes.rb +59 -8
  19. data/app/models/concerns/spree/single_store_resource.rb +9 -0
  20. data/app/models/concerns/spree/user_address.rb +19 -0
  21. data/app/models/concerns/spree/user_methods.rb +5 -4
  22. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  23. data/app/models/spree/ability.rb +3 -1
  24. data/app/models/spree/address.rb +27 -6
  25. data/app/models/spree/app_configuration.rb +8 -0
  26. data/app/models/spree/app_dependencies.rb +18 -6
  27. data/app/models/spree/base.rb +18 -0
  28. data/app/models/spree/classification.rb +3 -0
  29. data/app/models/spree/cms/pages/feature_page.rb +7 -0
  30. data/app/models/spree/cms/pages/homepage.rb +20 -0
  31. data/app/models/spree/cms/pages/standard_page.rb +4 -0
  32. data/app/models/spree/cms/sections/featured_article.rb +29 -0
  33. data/app/models/spree/cms/sections/hero_image.rb +47 -0
  34. data/app/models/spree/cms/sections/image_gallery.rb +103 -0
  35. data/app/models/spree/cms/sections/product_carousel.rb +14 -0
  36. data/app/models/spree/cms/sections/rich_text_content.rb +13 -0
  37. data/app/models/spree/cms/sections/side_by_side_images.rb +74 -0
  38. data/app/models/spree/cms_page.rb +66 -0
  39. data/app/models/spree/cms_section.rb +57 -0
  40. data/app/models/spree/country.rb +14 -6
  41. data/app/models/spree/credit_card.rb +2 -8
  42. data/app/models/spree/customer_return.rb +8 -2
  43. data/app/models/spree/icon.rb +9 -0
  44. data/app/models/spree/image/configuration/active_storage.rb +2 -0
  45. data/app/models/spree/inventory_unit.rb +1 -1
  46. data/app/models/spree/line_item.rb +1 -1
  47. data/app/models/spree/menu.rb +63 -0
  48. data/app/models/spree/menu_item.rb +76 -0
  49. data/app/models/spree/option_type.rb +1 -1
  50. data/app/models/spree/option_value.rb +11 -0
  51. data/app/models/spree/option_value_variant.rb +1 -1
  52. data/app/models/spree/order/address_book.rb +3 -5
  53. data/app/models/spree/order/currency_updater.rb +1 -1
  54. data/app/models/spree/order/emails.rb +32 -0
  55. data/app/models/spree/order/payments.rb +1 -1
  56. data/app/models/spree/order/store_credit.rb +1 -9
  57. data/app/models/spree/order.rb +46 -45
  58. data/app/models/spree/payment.rb +7 -0
  59. data/app/models/spree/payment_method.rb +4 -0
  60. data/app/models/spree/preferences/preferable.rb +12 -0
  61. data/app/models/spree/preferences/preferable_class_methods.rb +10 -1
  62. data/app/models/spree/product.rb +27 -22
  63. data/app/models/spree/product_property.rb +19 -4
  64. data/app/models/spree/promotion/rules/country.rb +1 -1
  65. data/app/models/spree/promotion/rules/first_order.rb +4 -3
  66. data/app/models/spree/promotion/rules/taxon.rb +10 -7
  67. data/app/models/spree/promotion.rb +6 -15
  68. data/app/models/spree/promotion_handler/cart.rb +7 -2
  69. data/app/models/spree/promotion_handler/coupon.rb +5 -4
  70. data/app/models/spree/promotion_handler/free_shipping.rb +5 -6
  71. data/app/models/spree/promotion_handler/page.rb +3 -2
  72. data/app/models/spree/promotion_handler/promotion_duplicator.rb +1 -0
  73. data/app/models/spree/promotion_rule.rb +2 -0
  74. data/app/models/spree/property.rb +27 -0
  75. data/app/models/spree/reimbursement.rb +3 -1
  76. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +2 -1
  77. data/app/models/spree/shipment_handler.rb +4 -2
  78. data/app/models/spree/stock/quantifier.rb +6 -6
  79. data/app/models/spree/store.rb +90 -7
  80. data/app/models/spree/store_credit.rb +7 -3
  81. data/app/models/spree/store_credit_event.rb +2 -2
  82. data/app/models/spree/store_payment_method.rb +11 -0
  83. data/app/models/spree/store_product.rb +11 -0
  84. data/app/models/spree/store_promotion.rb +11 -0
  85. data/app/models/spree/taxon.rb +21 -1
  86. data/app/models/spree/taxon_image/configuration/active_storage.rb +2 -0
  87. data/app/models/spree/taxonomy.rb +3 -1
  88. data/app/models/spree/variant.rb +13 -4
  89. data/app/presenters/spree/filters/options_presenter.rb +27 -0
  90. data/app/presenters/spree/filters/price_presenter.rb +22 -0
  91. data/app/presenters/spree/filters/price_range_presenter.rb +29 -0
  92. data/app/presenters/spree/filters/properties_presenter.rb +23 -0
  93. data/app/presenters/spree/filters/property_presenter.rb +22 -0
  94. data/app/presenters/spree/filters/quantified_price_range_presenter.rb +44 -0
  95. data/app/services/spree/account/addresses/create.rb +1 -0
  96. data/app/services/spree/account/addresses/helper.rb +6 -0
  97. data/app/services/spree/account/addresses/update.rb +9 -3
  98. data/app/services/spree/account/create.rb +17 -0
  99. data/app/services/spree/account/update.rb +15 -0
  100. data/app/services/spree/build_localized_redirect_url.rb +1 -1
  101. data/app/services/spree/cart/create.rb +5 -3
  102. data/app/services/spree/cart/destroy.rb +40 -0
  103. data/app/services/spree/cart/empty.rb +36 -0
  104. data/app/services/spree/cart/estimate_shipping_rates.rb +3 -3
  105. data/app/services/spree/checkout/add_store_credit.rb +1 -1
  106. data/app/services/spree/classifications/reposition.rb +18 -0
  107. data/app/services/spree/credit_cards/destroy.rb +41 -0
  108. data/app/validators/db_maximum_length_validator.rb +5 -0
  109. data/app/validators/email_validator.rb +3 -1
  110. data/app/views/spree/shared/_purchased_items_table.text.erb +25 -0
  111. data/config/initializers/active_storage.rb +2 -0
  112. data/config/locales/en.yml +166 -2
  113. data/config/routes.rb +0 -5
  114. data/db/migrate/20201023152810_add_filterable_to_spree_properties.rb +8 -0
  115. data/db/migrate/20210407200948_create_spree_menus.rb +16 -0
  116. data/db/migrate/20210408092939_create_spree_menu_items.rb +31 -0
  117. data/db/migrate/20210504163720_add_filter_param_to_spree_product_properties.rb +8 -0
  118. data/db/migrate/20210505114659_add_filter_param_to_spree_properties.rb +8 -0
  119. data/db/migrate/20210512191732_create_spree_cms_pages.rb +24 -0
  120. data/db/migrate/20210514204251_create_spree_cms_sections.rb +22 -0
  121. data/db/migrate/20210527094055_create_spree_products_stores.rb +29 -0
  122. data/db/migrate/20210608045519_ensure_store_default_country_is_set.rb +5 -0
  123. data/db/migrate/20210702112334_add_missing_timestamp_columns.rb +46 -0
  124. data/db/migrate/20210713131614_add_unique_index_on_property_id_and_product_id_to_product_properties.rb +29 -0
  125. data/db/migrate/20210715091956_add_store_id_to_spree_store_credits.rb +10 -0
  126. data/db/migrate/20210716093151_add_store_id_to_spree_taxonomies.rb +11 -0
  127. data/db/migrate/20210716104141_add_index_on_name_parent_id_and_taxonomy_id_on_spree_taxons.rb +31 -0
  128. data/db/migrate/20210721120857_add_index_on_permalink_parent_id_and_taxonomy_id_on_spree_taxons.rb +31 -0
  129. data/db/migrate/20210721125657_create_spree_promotions_stores.rb +29 -0
  130. data/db/migrate/20210722090705_add_store_id_to_spree_customer_returns.rb +11 -0
  131. data/db/migrate/20210726065456_change_integer_id_columns_into_bigint.rb +305 -0
  132. data/db/migrate/20210730154425_fix_promotion_code_and_path_unique_indexes.rb +9 -0
  133. data/lib/generators/spree/dummy/dummy_generator.rb +11 -6
  134. data/lib/generators/spree/dummy_model/templates/model.rb.tt +1 -1
  135. data/lib/generators/spree/install/install_generator.rb +12 -24
  136. data/lib/spree/core/components.rb +8 -0
  137. data/lib/spree/core/controller_helpers/auth.rb +7 -1
  138. data/lib/spree/core/controller_helpers/common.rb +1 -1
  139. data/lib/spree/core/controller_helpers/order.rb +4 -4
  140. data/lib/spree/core/controller_helpers/search.rb +1 -0
  141. data/lib/spree/core/controller_helpers/store.rb +10 -1
  142. data/lib/spree/core/engine.rb +6 -0
  143. data/lib/spree/core/importer/product.rb +3 -1
  144. data/lib/spree/core/product_duplicator.rb +1 -0
  145. data/lib/spree/core/search/base.rb +17 -22
  146. data/lib/spree/core/version.rb +1 -1
  147. data/lib/spree/core.rb +8 -4
  148. data/lib/spree/i18n.rb +1 -1
  149. data/lib/spree/money.rb +2 -1
  150. data/lib/spree/permitted_attributes.rb +14 -3
  151. data/lib/spree/testing_support/authorization_helpers.rb +6 -3
  152. data/lib/spree/testing_support/capybara_config.rb +3 -1
  153. data/lib/spree/testing_support/common_rake.rb +17 -4
  154. data/lib/spree/testing_support/extension_rake.rb +2 -2
  155. data/lib/spree/testing_support/factories/address_factory.rb +1 -1
  156. data/lib/spree/testing_support/factories/classification_factory.rb +8 -0
  157. data/lib/spree/testing_support/factories/cms_page_factory.rb +20 -0
  158. data/lib/spree/testing_support/factories/cms_section_factory.rb +31 -0
  159. data/lib/spree/testing_support/factories/customer_return_factory.rb +24 -17
  160. data/lib/spree/testing_support/factories/icon_factory.rb +7 -0
  161. data/lib/spree/testing_support/factories/line_item_factory.rb +6 -2
  162. data/lib/spree/testing_support/factories/menu_factory.rb +16 -0
  163. data/lib/spree/testing_support/factories/menu_item_factory.rb +10 -0
  164. data/lib/spree/testing_support/factories/options_factory.rb +5 -0
  165. data/lib/spree/testing_support/factories/order_factory.rb +11 -2
  166. data/lib/spree/testing_support/factories/payment_method_factory.rb +6 -2
  167. data/lib/spree/testing_support/factories/product_factory.rb +11 -1
  168. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -1
  169. data/lib/spree/testing_support/factories/promotion_factory.rb +18 -9
  170. data/lib/spree/testing_support/factories/property_factory.rb +22 -0
  171. data/lib/spree/testing_support/factories/stock_location_factory.rb +5 -4
  172. data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -0
  173. data/lib/spree/testing_support/factories/store_factory.rb +11 -0
  174. data/lib/spree/testing_support/factories/taxon_factory.rb +3 -1
  175. data/lib/spree/testing_support/factories/taxon_image_factory.rb +7 -0
  176. data/lib/spree/testing_support/factories/taxonomy_factory.rb +1 -0
  177. data/lib/spree/testing_support/factories/user_factory.rb +7 -2
  178. data/lib/spree/testing_support/factories/variant_factory.rb +2 -2
  179. data/lib/spree/testing_support/flatpickr_capybara.rb +58 -35
  180. data/lib/spree/testing_support/order_walkthrough.rb +9 -9
  181. data/lib/tasks/core.rake +1 -1
  182. data/spec/fixtures/favicon.ico +0 -0
  183. data/spec/fixtures/files/icon_256x256.gif +0 -0
  184. data/spec/fixtures/files/icon_256x256.png +0 -0
  185. data/spec/fixtures/files/icon_512x512.png +0 -0
  186. data/spec/fixtures/files/img_256x128.png +0 -0
  187. data/spree_core.gemspec +11 -10
  188. metadata +207 -159
  189. data/app/assets/images/logo/spree_50.png +0 -0
  190. data/app/assets/images/noimage/large.png +0 -0
  191. data/app/assets/images/noimage/mini.png +0 -0
  192. data/app/assets/images/noimage/product.png +0 -0
  193. data/app/assets/images/noimage/small.png +0 -0
  194. data/app/assets/javascripts/spree.js +0 -78
  195. data/app/controllers/spree/errors_controller.rb +0 -11
  196. data/app/helpers/spree/mail_helper.rb +0 -29
  197. data/app/mailers/spree/base_mailer.rb +0 -46
  198. data/app/mailers/spree/order_mailer.rb +0 -26
  199. data/app/mailers/spree/reimbursement_mailer.rb +0 -12
  200. data/app/mailers/spree/shipment_mailer.rb +0 -12
  201. data/app/mailers/spree/test_mailer.rb +0 -8
  202. data/app/models/spree/order_contents.rb +0 -31
  203. data/app/models/spree/validations/db_maximum_length_validator.rb +0 -22
  204. data/app/views/layouts/spree/base_mailer.html.erb +0 -46
  205. data/app/views/spree/errors/forbidden.html.erb +0 -0
  206. data/app/views/spree/errors/unauthorized.html.erb +0 -0
  207. data/app/views/spree/order_mailer/cancel_email.html.erb +0 -24
  208. data/app/views/spree/order_mailer/cancel_email.text.erb +0 -38
  209. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -23
  210. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -39
  211. data/app/views/spree/order_mailer/store_owner_notification_email.html.erb +0 -23
  212. data/app/views/spree/order_mailer/store_owner_notification_email.text.erb +0 -38
  213. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -56
  214. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -24
  215. data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -12
  216. data/app/views/spree/shared/_base_mailer_header.html.erb +0 -13
  217. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -456
  218. data/app/views/spree/shared/_error_messages.html.erb +0 -11
  219. data/app/views/spree/shared/_mailer_line_item.html.erb +0 -16
  220. data/app/views/spree/shared/_paths.html.erb +0 -8
  221. data/app/views/spree/shared/_purchased_items_table.html.erb +0 -69
  222. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +0 -13
  223. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +0 -27
  224. data/app/views/spree/shared/purchased_items_table/_subtotal.html.erb +0 -13
  225. data/app/views/spree/shared/purchased_items_table/_total.html.erb +0 -13
  226. data/app/views/spree/shipment_mailer/shipped_email.html.erb +0 -36
  227. data/app/views/spree/shipment_mailer/shipped_email.text.erb +0 -17
  228. data/app/views/spree/test_mailer/test_email.html.erb +0 -40
  229. data/app/views/spree/test_mailer/test_email.text.erb +0 -4
  230. data/config/initializers/assets.rb +0 -2
  231. data/config/initializers/premailer_assets.rb +0 -1
  232. data/config/initializers/premailer_rails.rb +0 -3
  233. data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +0 -10
  234. data/lib/generators/spree/dummy/templates/initializers/bullet.rb +0 -5
  235. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +0 -15
  236. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -16
  237. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/backend/all.css +0 -16
  238. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -16
  239. data/lib/generators/spree/mailers_preview/mailers_preview_generator.rb +0 -23
  240. data/lib/generators/spree/mailers_preview/templates/mailers/previews/order_preview.rb +0 -13
  241. data/lib/generators/spree/mailers_preview/templates/mailers/previews/reimbursement_preview.rb +0 -5
  242. data/lib/generators/spree/mailers_preview/templates/mailers/previews/shipment_preview.rb +0 -5
  243. data/lib/generators/spree/mailers_preview/templates/mailers/previews/user_preview.rb +0 -11
  244. data/lib/tasks/email.rake +0 -10
  245. data/vendor/assets/javascripts/cleave.js +0 -1669
  246. data/vendor/assets/javascripts/fetch.umd.js +0 -531
  247. data/vendor/assets/javascripts/jquery.payment.js +0 -652
  248. data/vendor/assets/javascripts/jsuri.js +0 -458
  249. data/vendor/assets/javascripts/polyfill.min.js +0 -1
@@ -2,6 +2,7 @@ require_dependency 'spree/order/checkout'
2
2
  require_dependency 'spree/order/currency_updater'
3
3
  require_dependency 'spree/order/payments'
4
4
  require_dependency 'spree/order/store_credit'
5
+ require_dependency 'spree/order/emails'
5
6
 
6
7
  module Spree
7
8
  class Order < Spree::Base
@@ -13,10 +14,15 @@ module Spree
13
14
  include Spree::Order::Payments
14
15
  include Spree::Order::StoreCredit
15
16
  include Spree::Order::AddressBook
17
+ include Spree::Order::Emails
16
18
  include Spree::Core::NumberGenerator.new(prefix: 'R')
17
19
  include Spree::Core::TokenGenerator
18
20
 
19
21
  include NumberAsParam
22
+ include SingleStoreResource
23
+ include MemoizedData
24
+
25
+ MEMOIZED_METHODS = %w(tax_zone)
20
26
 
21
27
  extend Spree::DisplayMoney
22
28
  money_methods :outstanding_balance, :item_total, :adjustment_total,
@@ -93,6 +99,7 @@ module Spree
93
99
  has_many :reimbursements, inverse_of: :order, class_name: 'Spree::Reimbursement'
94
100
  has_many :line_item_adjustments, through: :line_items, source: :adjustments
95
101
  has_many :inventory_units, inverse_of: :order, class_name: 'Spree::InventoryUnit'
102
+ has_many :return_items, through: :inventory_units, class_name: 'Spree::ReturnItem'
96
103
  has_many :variants, through: :line_items
97
104
  has_many :products, through: :variants
98
105
  has_many :refunds, through: :payments
@@ -121,6 +128,8 @@ module Spree
121
128
  # Needs to happen before save_permalink is called
122
129
  before_validation :ensure_store_presence
123
130
  before_validation :ensure_currency_presence
131
+ before_validation :uppercase_number
132
+
124
133
  before_validation :clone_billing_address, if: :use_billing?
125
134
  attr_accessor :use_billing
126
135
 
@@ -129,7 +138,9 @@ module Spree
129
138
  before_update :homogenize_line_item_currencies, if: :currency_changed?
130
139
 
131
140
  with_options presence: true do
132
- validates :number, length: { maximum: 32, allow_blank: true }, uniqueness: { allow_blank: true, case_sensitive: false }
141
+ # we want to have this case_sentive: true as changing it to false causes all SQL to use LOWER(slug)
142
+ # which is very costly and slow on large set of records
143
+ validates :number, length: { maximum: 32, allow_blank: true }, uniqueness: { allow_blank: true, case_sensitive: true }
133
144
  validates :email, length: { maximum: 254, allow_blank: true }, email: { allow_blank: true }, if: :require_email
134
145
  validates :item_count, numericality: { greater_than_or_equal_to: 0, less_than: 2**31, only_integer: true, allow_blank: true }
135
146
  validates :store
@@ -158,6 +169,8 @@ module Spree
158
169
  scope :complete, -> { where.not(completed_at: nil) }
159
170
  scope :incomplete, -> { where(completed_at: nil) }
160
171
  scope :not_canceled, -> { where.not(state: 'canceled') }
172
+ scope :with_deleted_bill_address, -> { joins(:bill_address).where.not(Address.table_name => { deleted_at: nil }) }
173
+ scope :with_deleted_ship_address, -> { joins(:ship_address).where.not(Address.table_name => { deleted_at: nil }) }
161
174
 
162
175
  # shows completed orders first, by their completed_at date, then uncompleted orders by their created_at
163
176
  scope :reverse_chronological, -> { order(Arel.sql('spree_orders.completed_at IS NULL'), completed_at: :desc, created_at: :desc) }
@@ -326,6 +339,10 @@ module Spree
326
339
  complete? || resumed? || awaiting_return? || returned?
327
340
  end
328
341
 
342
+ def uneditable?
343
+ complete? || canceled? || returned?
344
+ end
345
+
329
346
  def credit_cards
330
347
  credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
331
348
  CreditCard.where(id: credit_card_ids)
@@ -356,7 +373,6 @@ module Spree
356
373
  touch :completed_at
357
374
 
358
375
  deliver_order_confirmation_email unless confirmation_delivered?
359
-
360
376
  deliver_store_owner_order_notification_email if deliver_store_owner_order_notification_email?
361
377
 
362
378
  consider_risk
@@ -368,17 +384,16 @@ module Spree
368
384
  save!
369
385
  end
370
386
 
371
- def deliver_order_confirmation_email
372
- OrderMailer.confirm_email(id).deliver_later
373
- update_column(:confirmation_delivered, true)
374
- end
375
-
376
387
  # Helper methods for checkout steps
377
388
  def paid?
378
389
  payment_state == 'paid' || payment_state == 'credit_owed'
379
390
  end
380
391
 
381
392
  def available_payment_methods(store = nil)
393
+ if store.present?
394
+ ActiveSupport::Deprecation.warn('The `store` parameter is deprecated and will be removed in Spree 5. Order is already associated with Store')
395
+ end
396
+
382
397
  @available_payment_methods ||= collect_payment_methods(store)
383
398
  end
384
399
 
@@ -410,21 +425,15 @@ module Spree
410
425
  end
411
426
 
412
427
  def empty!
413
- if completed?
414
- raise Spree.t(:cannot_empty_completed_order)
415
- else
416
- line_items.destroy_all
417
- updater.update_item_count
418
- adjustments.destroy_all
419
- shipments.destroy_all
420
- state_changes.destroy_all
421
- order_promotions.destroy_all
428
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
429
+ `Order#empty!` is deprecated and will be removed in Spree 5.0.
430
+ Please use `Spree::Cart::Empty.call(order: order)` instead.
431
+ DEPRECATION
422
432
 
423
- update_totals
424
- persist_totals
425
- restart_checkout_flow
426
- self
427
- end
433
+ raise Spree.t(:cannot_empty_completed_order) if completed?
434
+
435
+ result = Spree::Dependencies.cart_empty_service.constantize.call(order: self)
436
+ result.value
428
437
  end
429
438
 
430
439
  def has_step?(step)
@@ -562,6 +571,10 @@ module Spree
562
571
  !approved?
563
572
  end
564
573
 
574
+ def can_be_destroyed?
575
+ !completed? && payments.completed.empty?
576
+ end
577
+
565
578
  def consider_risk
566
579
  considered_risky! if is_risky? && !approved?
567
580
  end
@@ -574,11 +587,6 @@ module Spree
574
587
  update_column(:considered_risky, false)
575
588
  end
576
589
 
577
- def reload(options = nil)
578
- remove_instance_variable(:@tax_zone) if defined?(@tax_zone)
579
- super
580
- end
581
-
582
590
  def tax_total
583
591
  included_tax_total + additional_tax_total
584
592
  end
@@ -617,12 +625,12 @@ module Spree
617
625
 
618
626
  def validate_payments_attributes(attributes)
619
627
  # Ensure the payment methods specified are allowed for this user
620
- payment_methods = Spree::PaymentMethod.where(id: available_payment_methods.map(&:id))
628
+ payment_method_ids = available_payment_methods.map(&:id)
629
+
621
630
  attributes.each do |payment_attributes|
622
- payment_method_id = payment_attributes[:payment_method_id]
631
+ payment_method_id = payment_attributes[:payment_method_id].to_i
623
632
 
624
- # raise RecordNotFound unless it is an allowed payment method
625
- payment_methods.find(payment_method_id) if payment_method_id
633
+ raise ActiveRecord::RecordNotFound unless payment_method_ids.include?(payment_method_id)
626
634
  end
627
635
  end
628
636
 
@@ -693,10 +701,6 @@ module Spree
693
701
  update_with_updater!
694
702
  end
695
703
 
696
- def send_cancel_email
697
- OrderMailer.cancel_email(id).deliver_later
698
- end
699
-
700
704
  def after_resume
701
705
  shipments.each(&:resume!)
702
706
  consider_risk
@@ -715,23 +719,20 @@ module Spree
715
719
  end
716
720
 
717
721
  def collect_payment_methods(store = nil)
718
- PaymentMethod.available_on_front_end.select { |pm| pm.available_for_order?(self) && pm.available_for_store?(store) }
722
+ if store.present?
723
+ ActiveSupport::Deprecation.warn('The `store` parameter is deprecated and will be removed in Spree 5. Order is already associated with Store')
724
+ end
725
+ store ||= self.store
726
+
727
+ store.payment_methods.available_on_front_end.select { |pm| pm.available_for_order?(self) }
719
728
  end
720
729
 
721
730
  def credit_card_nil_payment?(attributes)
722
731
  payments.store_credits.present? && attributes[:amount].to_f.zero?
723
732
  end
724
733
 
725
- # Returns true if:
726
- # 1. an email address is set for new order notifications AND
727
- # 2. no notification for this order has been sent yet.
728
- def deliver_store_owner_order_notification_email?
729
- store.new_order_notifications_email.present? && !store_owner_notification_delivered?
730
- end
731
-
732
- def deliver_store_owner_order_notification_email
733
- OrderMailer.store_owner_notification_email(id).deliver_later
734
- update_column(:store_owner_notification_delivered, true)
734
+ def uppercase_number
735
+ number&.upcase!
735
736
  end
736
737
  end
737
738
  end
@@ -112,6 +112,13 @@ module Spree
112
112
  end
113
113
  end
114
114
 
115
+ def source
116
+ return super if payment_method.nil?
117
+ return super unless payment_method.source_required?
118
+
119
+ payment_method.payment_source_class.unscoped { super }
120
+ end
121
+
115
122
  def money
116
123
  Spree::Money.new(amount, currency: currency)
117
124
  end
@@ -3,6 +3,8 @@ module Spree
3
3
  acts_as_paranoid
4
4
  acts_as_list
5
5
 
6
+ include MultiStoreResource
7
+
6
8
  DISPLAY = [:both, :front_end, :back_end].freeze
7
9
 
8
10
  scope :active, -> { where(active: true).order(position: :asc) }
@@ -31,6 +33,8 @@ module Spree
31
33
  # e.g. CreditCard in the case of a the Gateway payment type
32
34
  # nil means the payment method doesn't require a source e.g. check
33
35
  def payment_source_class
36
+ return unless source_required?
37
+
34
38
  raise ::NotImplementedError, 'You must implement payment_source_class method for this gateway.'
35
39
  end
36
40
 
@@ -58,6 +58,11 @@ module Spree::Preferences::Preferable
58
58
  send self.class.preference_default_getter_method(name)
59
59
  end
60
60
 
61
+ def preference_deprecated(name)
62
+ has_preference! name
63
+ send(self.class.preference_deprecated_getter_method(name))
64
+ end
65
+
61
66
  def has_preference!(name)
62
67
  raise NoMethodError, "#{name} preference not defined" unless has_preference? name
63
68
  end
@@ -72,6 +77,13 @@ module Spree::Preferences::Preferable
72
77
  end
73
78
  end
74
79
 
80
+ def deprecated_preferences
81
+ defined_preferences.each_with_object([]) do |pref_name, array|
82
+ deprecated_message = preference_deprecated(pref_name)
83
+ array << { name: pref_name, message: deprecated_message } unless deprecated_message.nil?
84
+ end
85
+ end
86
+
75
87
  def default_preferences
76
88
  Hash[
77
89
  defined_preferences.map do |preference|
@@ -2,9 +2,10 @@ module Spree::Preferences
2
2
  module PreferableClassMethods
3
3
  def preference(name, type, *args)
4
4
  options = args.extract_options!
5
- options.assert_valid_keys(:default)
5
+ options.assert_valid_keys(:default, :deprecated)
6
6
  default = options[:default]
7
7
  default = -> { options[:default] } unless default.is_a?(Proc)
8
+ deprecated = options[:deprecated]
8
9
 
9
10
  # cache_key will be nil for new objects, then if we check if there
10
11
  # is a pending preference before going to default
@@ -29,6 +30,10 @@ module Spree::Preferences
29
30
  define_method preference_type_getter_method(name) do
30
31
  type
31
32
  end
33
+
34
+ define_method preference_deprecated_getter_method(name) do
35
+ deprecated
36
+ end
32
37
  end
33
38
 
34
39
  def preference_getter_method(name)
@@ -43,6 +48,10 @@ module Spree::Preferences
43
48
  "preferred_#{name}_default".to_sym
44
49
  end
45
50
 
51
+ def preference_deprecated_getter_method(name)
52
+ "preferred_#{name}_deprecated".to_sym
53
+ end
54
+
46
55
  def preference_type_getter_method(name)
47
56
  "preferred_#{name}_type".to_sym
48
57
  end
@@ -21,7 +21,13 @@
21
21
  module Spree
22
22
  class Product < Spree::Base
23
23
  extend FriendlyId
24
- include Spree::ProductScopes
24
+ include ProductScopes
25
+ include MultiStoreResource
26
+ include MemoizedData
27
+
28
+ MEMOIZED_METHODS = %w(total_on_hand taxonomy_ids taxon_and_ancestors category
29
+ default_variant_id tax_category default_variant
30
+ purchasable? in_stock? backorderable?)
25
31
 
26
32
  friendly_id :slug_candidates, use: :history
27
33
 
@@ -36,6 +42,8 @@ module Spree
36
42
  has_many :product_properties, dependent: :destroy, inverse_of: :product
37
43
  has_many :properties, through: :product_properties
38
44
 
45
+ has_many :menu_items, as: :linked_resource
46
+
39
47
  has_many :classifications, dependent: :delete_all, inverse_of: :product
40
48
  has_many :taxons, through: :classifications, before_remove: :remove_taxon
41
49
 
@@ -77,6 +85,9 @@ module Spree
77
85
  has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
78
86
  has_many :variant_images_without_master, -> { order(:position) }, source: :images, through: :variants
79
87
 
88
+ has_many :store_products, class_name: 'Spree::StoreProduct'
89
+ has_many :stores, through: :store_products, class_name: 'Spree::Store'
90
+
80
91
  after_create :add_associations_from_prototype
81
92
  after_create :build_variants_from_option_values_hash, if: :option_values_hash
82
93
 
@@ -90,10 +101,7 @@ module Spree
90
101
  after_save :reset_nested_changes
91
102
  after_touch :touch_taxons
92
103
 
93
- # reset cache on save inside trasaction and transaction commit
94
- after_save :reset_memoized_data
95
- after_commit :reset_memoized_data
96
-
104
+ before_validation :downcase_slug
97
105
  before_validation :normalize_slug, on: :update
98
106
  before_validation :validate_master
99
107
 
@@ -107,7 +115,7 @@ module Spree
107
115
  validates :price, if: :requires_price?
108
116
  end
109
117
 
110
- validates :slug, presence: true, uniqueness: { allow_blank: true, case_sensitive: false }
118
+ validates :slug, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
111
119
  validate :discontinue_on_must_be_later_than_available_on, if: -> { available_on && discontinue_on }
112
120
 
113
121
  attr_accessor :option_values_hash
@@ -122,7 +130,7 @@ module Spree
122
130
 
123
131
  [
124
132
  :sku, :price, :currency, :weight, :height, :width, :depth, :is_master,
125
- :cost_currency, :price_in, :amount_in, :cost_price, :compare_at_price
133
+ :cost_currency, :price_in, :amount_in, :cost_price, :compare_at_price, :compare_at_amount_in
126
134
  ].each do |method_name|
127
135
  delegate method_name, :"#{method_name}=", to: :find_or_build_master
128
136
  end
@@ -132,13 +140,6 @@ module Spree
132
140
 
133
141
  alias master_images images
134
142
 
135
- def reload
136
- %w(total_on_hand taxonomy_ids taxon_and_ancestors category category default_variant_id tax_category default_variant).each do |v|
137
- instance_variable_set(:"@#{v}", nil)
138
- end
139
- super
140
- end
141
-
142
143
  # Cant use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
143
144
  def purchasable?
144
145
  variants_including_master.any?(&:purchasable?)
@@ -173,8 +174,8 @@ module Spree
173
174
  # @return [Spree::Variant]
174
175
  def default_variant
175
176
  @default_variant ||= Rails.cache.fetch(default_variant_cache_key) do
176
- if Spree::Config[:track_inventory_levels] && variants.in_stock_or_backorderable.any?
177
- variants.in_stock_or_backorderable.first
177
+ if Spree::Config[:track_inventory_levels] && available_variant = variants.detect(&:purchasable?)
178
+ available_variant
178
179
  else
179
180
  has_variants? ? variants.first : master
180
181
  end
@@ -322,6 +323,12 @@ module Spree
322
323
  @category ||= taxons.joins(:taxonomy).order(depth: :desc).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) })
323
324
  end
324
325
 
326
+ def taxons_for_store(store)
327
+ Rails.cache.fetch("#{cache_key_with_version}/taxons-per-store/#{store.id}") do
328
+ taxons.for_store(store)
329
+ end
330
+ end
331
+
325
332
  private
326
333
 
327
334
  def add_associations_from_prototype
@@ -481,12 +488,6 @@ module Spree
481
488
  end
482
489
  end
483
490
 
484
- def reset_memoized_data
485
- %w(total_on_hand taxonomy_ids taxon_and_ancestors category default_variant_id tax_category default_variant).each do |v|
486
- instance_variable_set(:"@#{v}", nil)
487
- end
488
- end
489
-
490
491
  def requires_price?
491
492
  Spree::Config[:require_master_price]
492
493
  end
@@ -494,5 +495,9 @@ module Spree
494
495
  def requires_shipping_category?
495
496
  true
496
497
  end
498
+
499
+ def downcase_slug
500
+ slug&.downcase!
501
+ end
497
502
  end
498
503
  end
@@ -1,29 +1,44 @@
1
1
  module Spree
2
2
  class ProductProperty < Spree::Base
3
+ include Spree::FilterParam
4
+
5
+ auto_strip_attributes :value
6
+
3
7
  acts_as_list scope: :product
4
8
 
5
9
  with_options inverse_of: :product_properties do
6
10
  belongs_to :product, touch: true, class_name: 'Spree::Product'
7
- belongs_to :property, class_name: 'Spree::Property'
11
+ belongs_to :property, touch: true, class_name: 'Spree::Property'
8
12
  end
9
13
 
10
14
  validates :property, presence: true
11
-
12
- validates :value, db_maximum_length: true
15
+ validates :property_id, uniqueness: { scope: :product_id }
13
16
 
14
17
  default_scope { order(:position) }
15
18
 
16
- self.whitelisted_ransackable_attributes = ['value']
19
+ scope :filterable, -> { joins(:property).where(Property.table_name => { filterable: true }) }
20
+ scope :for_products, ->(products) { joins(:product).merge(products) }
21
+
22
+ self.whitelisted_ransackable_attributes = ['value', 'filter_param']
17
23
  self.whitelisted_ransackable_associations = ['property']
18
24
 
19
25
  # virtual attributes for use with AJAX completion stuff
20
26
  delegate :name, :presentation, to: :property, prefix: true, allow_nil: true
21
27
 
22
28
  def property_name=(name)
29
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
30
+ `ProductProperty#property_name=` is deprecated and will be removed in Spree 5.0.
31
+ DEPRECATION
23
32
  if name.present?
24
33
  # don't use `find_by :name` to workaround globalize/globalize#423 bug
25
34
  self.property = Property.where(name: name).first_or_create(presentation: name)
26
35
  end
27
36
  end
37
+
38
+ protected
39
+
40
+ def param_candidate
41
+ value
42
+ end
28
43
  end
29
44
  end
@@ -11,7 +11,7 @@ module Spree
11
11
 
12
12
  def eligible?(order, options = {})
13
13
  country_id = options[:country_id] || order.ship_address.try(:country_id)
14
- unless country_id.eql?(preferred_country_id || Spree::Country.default)
14
+ unless country_id.eql?(preferred_country_id || order.store.default_country_id)
15
15
  eligibility_errors.add(:base, eligibility_error_message(:wrong_country))
16
16
  end
17
17
 
@@ -2,7 +2,7 @@ module Spree
2
2
  class Promotion
3
3
  module Rules
4
4
  class FirstOrder < PromotionRule
5
- attr_reader :user, :email
5
+ attr_reader :user, :email, :store
6
6
 
7
7
  def applicable?(promotable)
8
8
  promotable.is_a?(Spree::Order)
@@ -11,6 +11,7 @@ module Spree
11
11
  def eligible?(order, options = {})
12
12
  @user = order.try(:user) || options[:user]
13
13
  @email = order.email
14
+ @store = order.store
14
15
 
15
16
  if user || email
16
17
  if !completed_orders.blank? && completed_orders.first != order
@@ -26,11 +27,11 @@ module Spree
26
27
  private
27
28
 
28
29
  def completed_orders
29
- user ? user.orders.complete : orders_by_email
30
+ user ? user.orders.for_store(store).complete : orders_by_email
30
31
  end
31
32
 
32
33
  def orders_by_email
33
- Spree::Order.where(email: email).complete
34
+ store.orders.where(email: email).complete
34
35
  end
35
36
  end
36
37
  end
@@ -30,7 +30,12 @@ module Spree
30
30
  end
31
31
 
32
32
  def actionable?(line_item)
33
- taxon_product_ids.include? line_item.variant.product_id
33
+ store = line_item.order.store
34
+
35
+ store.products.
36
+ joins(:classifications).
37
+ where(Spree::Classification.table_name => { taxon_id: taxon_ids, product_id: line_item.product_id }).
38
+ exists?
34
39
  end
35
40
 
36
41
  def taxon_ids_string
@@ -39,14 +44,16 @@ module Spree
39
44
 
40
45
  def taxon_ids_string=(s)
41
46
  ids = s.to_s.split(',').map(&:strip)
42
- self.taxons = Spree::Taxon.find(ids)
47
+ self.taxons = Spree::Taxon.for_stores(stores).find(ids)
43
48
  end
44
49
 
45
50
  private
46
51
 
47
52
  # All taxons in an order
48
53
  def order_taxons(order)
49
- Spree::Taxon.joins(products: { variants_including_master: :line_items }).where(spree_line_items: { order_id: order.id }).distinct
54
+ taxon_ids = Spree::Classification.where(product_id: order.product_ids).pluck(:taxon_id).uniq
55
+
56
+ order.store.taxons.where(id: taxon_ids)
50
57
  end
51
58
 
52
59
  # ids of taxons rules and taxons rules children
@@ -62,10 +69,6 @@ module Spree
62
69
  def taxons_in_order_including_parents(order)
63
70
  order_taxons_in_taxons_and_children(order).inject([]) { |taxons, taxon| taxons << taxon.self_and_ancestors }.flatten.uniq
64
71
  end
65
-
66
- def taxon_product_ids
67
- Spree::Product.joins(:taxons).where(spree_taxons: { id: taxons.pluck(:id) }).pluck(:id).uniq
68
- end
69
72
  end
70
73
  end
71
74
  end
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class Promotion < Spree::Base
3
+ include MultiStoreResource
4
+
3
5
  MATCH_POLICIES = %w(all any)
4
6
  UNACTIVATABLE_ORDER_STATES = ['complete', 'awaiting_return', 'returned']
5
7
 
@@ -16,19 +18,18 @@ module Spree
16
18
  has_many :order_promotions, class_name: 'Spree::OrderPromotion'
17
19
  has_many :orders, through: :order_promotions, class_name: 'Spree::Order'
18
20
 
21
+ has_and_belongs_to_many :stores, class_name: 'Spree::Store', join_table: 'spree_promotions_stores'
22
+
19
23
  accepts_nested_attributes_for :promotion_actions, :promotion_rules
20
24
 
21
25
  validates_associated :rules
22
26
 
23
27
  validates :name, presence: true
24
- validates :path, :code, uniqueness: { case_sensitive: false, allow_blank: true }
25
28
  validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
26
29
  validates :description, length: { maximum: 255 }, allow_blank: true
27
30
  validate :expires_at_must_be_later_than_starts_at, if: -> { starts_at && expires_at }
28
31
 
29
- before_save :normalize_blank_values
30
-
31
- before_validation :normalize_code
32
+ auto_strip_attributes :code, :path, :name
32
33
 
33
34
  scope :coupons, -> { where.not(code: nil) }
34
35
  scope :advertised, -> { where(advertise: true) }
@@ -44,7 +45,7 @@ module Spree
44
45
  def self.with_coupon_code(coupon_code)
45
46
  where("lower(#{table_name}.code) = ?", coupon_code.strip.downcase).
46
47
  includes(:promotion_actions).where.not(spree_promotion_actions: { id: nil }).
47
- first
48
+ last
48
49
  end
49
50
 
50
51
  def self.active
@@ -212,16 +213,6 @@ module Spree
212
213
  end
213
214
  end
214
215
 
215
- def normalize_blank_values
216
- [:code, :path].each do |column|
217
- self[column] = nil if self[column].blank?
218
- end
219
- end
220
-
221
- def normalize_code
222
- self.code = code.strip if code.present?
223
- end
224
-
225
216
  def match_all?
226
217
  match_policy == 'all'
227
218
  end
@@ -13,12 +13,13 @@ module Spree
13
13
  # the promotions system accurate and performant. Here they can plug custom
14
14
  # handler to activate promos as they wish once an item is added to cart
15
15
  class Cart
16
- attr_reader :line_item, :order
16
+ attr_reader :line_item, :order, :store
17
17
  attr_accessor :error, :success
18
18
 
19
19
  def initialize(order, line_item = nil)
20
20
  @order = order
21
21
  @line_item = line_item
22
+ @store = order.store
22
23
  end
23
24
 
24
25
  def activate
@@ -34,7 +35,11 @@ module Spree
34
35
  private
35
36
 
36
37
  def promotions
37
- Promotion.find_by_sql("#{order.promotions.active.to_sql} UNION #{Promotion.active.where(code: nil, path: nil).to_sql}")
38
+ promotion_scope.find_by_sql("#{order.promotions.active.to_sql} UNION #{promotion_scope.active.where(code: nil, path: nil).to_sql}")
39
+ end
40
+
41
+ def promotion_scope
42
+ ::Spree::Promotion.for_store(store)
38
43
  end
39
44
  end
40
45
  end
@@ -1,18 +1,19 @@
1
1
  module Spree
2
2
  module PromotionHandler
3
3
  class Coupon
4
- attr_reader :order
4
+ attr_reader :order, :store
5
5
  attr_accessor :error, :success, :status_code
6
6
 
7
7
  def initialize(order)
8
8
  @order = order
9
+ @store = order.store
9
10
  end
10
11
 
11
12
  def apply
12
13
  if order.coupon_code.present?
13
14
  if promotion.present? && promotion.actions.exists?
14
15
  handle_present_promotion
15
- elsif Promotion.with_coupon_code(order.coupon_code).try(:expired?)
16
+ elsif store.promotions.with_coupon_code(order.coupon_code).try(:expired?)
16
17
  set_error_code :coupon_code_expired
17
18
  else
18
19
  set_error_code :coupon_code_not_found
@@ -51,7 +52,7 @@ module Spree
51
52
  end
52
53
 
53
54
  def promotion
54
- @promotion ||= Promotion.active.includes(
55
+ @promotion ||= store.promotions.active.includes(
55
56
  :promotion_rules, :promotion_actions
56
57
  ).with_coupon_code(order.coupon_code)
57
58
  end
@@ -75,7 +76,7 @@ module Spree
75
76
  line_item = order.find_line_item_by_variant(item.variant)
76
77
  next if line_item.blank?
77
78
 
78
- Spree::Dependencies.cart_remove_item_service.constantize.call(order: order, item: item.variant, quantity: item.quantity)
79
+ Spree::Dependencies.cart_remove_item_service.constantize.call(order: order, variant: item.variant, quantity: item.quantity)
79
80
  end
80
81
  end
81
82