spree_core 4.0.7 → 4.1.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js +0 -1
  3. data/app/finders/spree/products/find.rb +19 -6
  4. data/app/finders/spree/variants/option_types_finder.rb +21 -0
  5. data/app/finders/spree/variants/visible_finder.rb +22 -0
  6. data/app/helpers/spree/base_helper.rb +48 -18
  7. data/app/helpers/spree/products_helper.rb +76 -8
  8. data/app/models/concerns/spree/user_methods.rb +2 -2
  9. data/app/models/spree/app_dependencies.rb +1 -7
  10. data/app/models/spree/credit_card.rb +4 -5
  11. data/app/models/spree/image.rb +52 -3
  12. data/app/models/spree/image/configuration/active_storage.rb +9 -1
  13. data/app/models/spree/line_item.rb +1 -2
  14. data/app/models/spree/option_type.rb +4 -0
  15. data/app/models/spree/order.rb +12 -12
  16. data/app/models/spree/order/address_book.rb +20 -7
  17. data/app/models/spree/payment_method.rb +8 -0
  18. data/app/models/spree/preferences/preferable.rb +1 -1
  19. data/app/models/spree/product.rb +7 -6
  20. data/app/models/spree/promotion/actions/create_item_adjustments.rb +1 -1
  21. data/app/models/spree/promotion_handler/coupon.rb +2 -1
  22. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +1 -1
  23. data/app/models/spree/store.rb +2 -1
  24. data/app/models/spree/taxon.rb +2 -6
  25. data/app/models/spree/variant.rb +2 -14
  26. data/app/models/spree/zone.rb +6 -3
  27. data/app/presenters/spree/product_summary_presenter.rb +26 -0
  28. data/app/presenters/spree/variant_presenter.rb +69 -0
  29. data/app/presenters/spree/variants/option_types_presenter.rb +74 -0
  30. data/app/presenters/spree/variants/options_presenter.rb +49 -0
  31. data/app/services/spree/checkout/get_shipping_rates.rb +10 -7
  32. data/app/services/spree/checkout/update.rb +2 -13
  33. data/config/locales/en.yml +156 -14
  34. data/db/default/spree/stores.rb +8 -3
  35. data/db/migrate/20140309033438_create_store_from_preferences.rb +8 -4
  36. data/db/migrate/20191005121504_add_store_id_to_payment_methods.rb +7 -0
  37. data/db/migrate/20191016134113_add_deafult_value_for_store_default_currency.rb +5 -0
  38. data/db/migrate/20200102141311_add_social_to_spree_stores.rb +7 -0
  39. data/lib/generators/spree/dummy/dummy_generator.rb +2 -1
  40. data/lib/generators/spree/dummy/templates/initializers/bullet.rb +5 -0
  41. data/lib/generators/spree/install/install_generator.rb +13 -5
  42. data/lib/generators/spree/install/templates/config/initializers/spree.rb +0 -1
  43. data/lib/generators/spree/install/templates/config/initializers/spree_storefront.rb +1 -0
  44. data/lib/generators/spree/install/templates/config/spree_storefront.yml +67 -0
  45. data/lib/spree/core.rb +0 -1
  46. data/lib/spree/core/controller_helpers/order.rb +12 -6
  47. data/lib/spree/core/controller_helpers/store.rb +2 -2
  48. data/lib/spree/core/search/base.rb +59 -22
  49. data/lib/spree/core/version.rb +1 -3
  50. data/lib/spree/money.rb +8 -1
  51. data/lib/spree/permitted_attributes.rb +3 -2
  52. data/lib/spree/testing_support/capybara_ext.rb +0 -45
  53. data/lib/spree/testing_support/common_rake.rb +1 -1
  54. data/lib/spree/testing_support/factories/store_factory.rb +3 -0
  55. data/lib/spree/testing_support/order_walkthrough.rb +7 -3
  56. data/spree_core.gemspec +8 -14
  57. metadata +45 -24
  58. data/app/finders/spree/addresses/find.rb +0 -17
  59. data/app/services/spree/account/addresses/base.rb +0 -39
  60. data/app/services/spree/account/addresses/create.rb +0 -18
  61. data/app/services/spree/account/addresses/update.rb +0 -18
  62. data/lib/spree/database_type_utilities.rb +0 -12
@@ -15,7 +15,15 @@ module Spree
15
15
  mini: '48x48>',
16
16
  small: '100x100>',
17
17
  product: '240x240>',
18
- large: '600x600>'
18
+ pdp_thumbnail: '160x200>',
19
+ plp_and_carousel: '448x600>',
20
+ plp_and_carousel_xs: '254x340>',
21
+ plp_and_carousel_sm: '350x468>',
22
+ plp_and_carousel_md: '222x297>',
23
+ plp_and_carousel_lg: '278x371>',
24
+ large: '600x600>',
25
+ plp: '278x371>',
26
+ zoomed: '650x870>'
19
27
  }
20
28
  end
21
29
 
@@ -59,8 +59,7 @@ module Spree
59
59
 
60
60
  extend DisplayMoney
61
61
  money_methods :amount, :subtotal, :discounted_amount, :final_amount, :total, :price,
62
- :adjustment_total, :additional_tax_total, :promo_total, :included_tax_total,
63
- :pre_tax_amount
62
+ :adjustment_total, :additional_tax_total, :promo_total, :included_tax_total
64
63
 
65
64
  alias single_money display_price
66
65
  alias single_display_amount display_price
@@ -23,6 +23,10 @@ module Spree
23
23
 
24
24
  after_touch :touch_all_products
25
25
 
26
+ def filter_param
27
+ presentation.titleize.delete(' ').downcase
28
+ end
29
+
26
30
  private
27
31
 
28
32
  def touch_all_products
@@ -21,8 +21,7 @@ module Spree
21
21
  extend Spree::DisplayMoney
22
22
  money_methods :outstanding_balance, :item_total, :adjustment_total,
23
23
  :included_tax_total, :additional_tax_total, :tax_total,
24
- :shipment_total, :promo_total, :total,
25
- :cart_promo_total, :pre_tax_item_amount, :pre_tax_total
24
+ :shipment_total, :promo_total, :total
26
25
 
27
26
  alias display_ship_total display_shipment_total
28
27
  alias_attribute :ship_total, :shipment_total
@@ -174,12 +173,7 @@ module Spree
174
173
 
175
174
  # Sum of all line item amounts pre-tax
176
175
  def pre_tax_item_amount
177
- line_items.sum(:pre_tax_amount)
178
- end
179
-
180
- # Sum of all line item and shipment pre-tax
181
- def pre_tax_total
182
- pre_tax_item_amount + shipments.sum(:pre_tax_amount)
176
+ line_items.to_a.sum(&:pre_tax_amount)
183
177
  end
184
178
 
185
179
  def shipping_discount
@@ -375,8 +369,8 @@ module Spree
375
369
  payment_state == 'paid' || payment_state == 'credit_owed'
376
370
  end
377
371
 
378
- def available_payment_methods
379
- @available_payment_methods ||= collect_payment_methods
372
+ def available_payment_methods(store = nil)
373
+ @available_payment_methods ||= collect_payment_methods(store)
380
374
  end
381
375
 
382
376
  def insufficient_stock_lines
@@ -631,6 +625,12 @@ module Spree
631
625
  all_adjustments.eligible.nonzero.promotion.map { |a| a.source.promotion_id }.uniq
632
626
  end
633
627
 
628
+ def valid_coupon_promotions
629
+ promotions.
630
+ where(id: valid_promotion_ids).
631
+ coupons
632
+ end
633
+
634
634
  private
635
635
 
636
636
  def link_by_email
@@ -689,8 +689,8 @@ module Spree
689
689
  self.token ||= generate_token
690
690
  end
691
691
 
692
- def collect_payment_methods
693
- PaymentMethod.available_on_front_end.select { |pm| pm.available_for_order?(self) }
692
+ def collect_payment_methods(store = nil)
693
+ PaymentMethod.available_on_front_end.select { |pm| pm.available_for_order?(self) && pm.available_for_store?(store) }
694
694
  end
695
695
 
696
696
  def credit_card_nil_payment?(attributes)
@@ -55,18 +55,31 @@ module Spree
55
55
  def update_or_create_address(attributes = {})
56
56
  return if attributes.blank?
57
57
 
58
- attributes.transform_values! { |v| v == '' ? nil : v }
58
+ attributes = attributes.select { |_k, v| v.present? }
59
59
 
60
- default_address_scope = user ? user.addresses : ::Spree::Address
61
- default_address = default_address_scope.find_by(id: attributes[:id])
60
+ if user
61
+ address = user.addresses.build(attributes.except(:id)).check
62
+ return address if address.id
63
+ end
64
+
65
+ if attributes[:id]
66
+ address = Spree::Address.find(attributes[:id])
67
+ attributes.delete(:id)
62
68
 
63
- if default_address&.editable?
64
- default_address.update(attributes)
69
+ if address&.editable?
70
+ address.update(attributes)
71
+ return address
72
+ else
73
+ attributes.delete(:id)
74
+ end
75
+ end
65
76
 
66
- return default_address
77
+ unless attributes[:id]
78
+ address = Spree::Address.new(attributes)
79
+ address.save
67
80
  end
68
81
 
69
- ::Spree::Address.find_or_create_by(attributes.except(:id, :updated_at, :created_at))
82
+ address
70
83
  end
71
84
  end
72
85
  end
@@ -12,6 +12,8 @@ module Spree
12
12
 
13
13
  validates :name, presence: true
14
14
 
15
+ belongs_to :store
16
+
15
17
  with_options dependent: :restrict_with_error do
16
18
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :payment_method
17
19
  has_many :credit_cards, class_name: 'Spree::CreditCard'
@@ -75,5 +77,11 @@ module Spree
75
77
  def available_for_order?(_order)
76
78
  true
77
79
  end
80
+
81
+ def available_for_store?(store)
82
+ return true if store.blank? || store_id.blank?
83
+
84
+ store_id == store.id
85
+ end
78
86
  end
79
87
  end
@@ -100,7 +100,7 @@ module Spree::Preferences::Preferable
100
100
  if value.is_a?(FalseClass) ||
101
101
  value.nil? ||
102
102
  value == 0 ||
103
- value&.to_s =~ /^(f|false|0)$/i ||
103
+ value =~ /^(f|false|0)$/i ||
104
104
  (value.respond_to?(:empty?) && value.empty?)
105
105
  false
106
106
  else
@@ -110,7 +110,7 @@ module Spree
110
110
 
111
111
  alias options product_option_types
112
112
 
113
- self.whitelisted_ransackable_associations = %w[taxons stores variants_including_master master variants]
113
+ self.whitelisted_ransackable_associations = %w[stores variants_including_master master variants]
114
114
  self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on]
115
115
  self.whitelisted_ransackable_scopes = %w[not_discontinued]
116
116
 
@@ -239,9 +239,7 @@ module Spree
239
239
  # values should not be displayed to frontend users. Otherwise it breaks the
240
240
  # idea of having variants
241
241
  def variants_and_option_values(current_currency = nil)
242
- variants.includes(:option_values).active(current_currency).select do |variant|
243
- variant.option_values.any?
244
- end
242
+ variants.active(current_currency).joins(:option_value_variants)
245
243
  end
246
244
 
247
245
  def empty_option_values?
@@ -284,7 +282,10 @@ module Spree
284
282
  end
285
283
 
286
284
  def category
287
- taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) })
285
+ taxons.joins(:taxonomy).
286
+ where(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) }).
287
+ order(depth: :desc).
288
+ first
288
289
  end
289
290
 
290
291
  private
@@ -321,7 +322,7 @@ module Spree
321
322
  price: master.price
322
323
  )
323
324
  end
324
- save
325
+ throw(:abort) unless save
325
326
  end
326
327
 
327
328
  def ensure_master
@@ -23,7 +23,7 @@ module Spree
23
23
  order = line_item.order
24
24
 
25
25
  # Prevent negative order totals
26
- amounts << order.amount - order.adjustments.eligible.sum(:amount).abs if order.adjustments.eligible.any?
26
+ amounts << order.amount - order.adjustments.sum(:amount).abs if order.adjustments.any?
27
27
 
28
28
  amounts.min * -1
29
29
  end
@@ -25,6 +25,7 @@ module Spree
25
25
 
26
26
  def remove(coupon_code)
27
27
  promotion = order.promotions.with_coupon_code(coupon_code)
28
+
28
29
  if promotion.present?
29
30
  # Order promotion has to be destroyed before line item removing
30
31
  order.order_promotions.find_by!(promotion_id: promotion.id).destroy
@@ -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(order: order, item: item.variant, quantity: item.quantity)
79
80
  end
80
81
  end
81
82
 
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class Spree::ReturnItem::EligibilityValidator::BaseValidator
2
+ class ReturnItem::EligibilityValidator::BaseValidator
3
3
  attr_reader :errors
4
4
 
5
5
  def initialize(return_item)
@@ -1,10 +1,11 @@
1
1
  module Spree
2
2
  class Store < Spree::Base
3
3
  has_many :orders, class_name: 'Spree::Order'
4
+ has_many :payment_methods, class_name: 'Spree::PaymentMethod'
4
5
 
5
6
  with_options presence: true do
6
- validates :code, uniqueness: { case_sensitive: false, allow_blank: true }
7
7
  validates :name, :url, :mail_from_address
8
+ validates :default_currency
8
9
  end
9
10
 
10
11
  before_save :ensure_default_exists_and_is_unique
@@ -48,13 +48,9 @@ module Spree
48
48
  fs
49
49
  end
50
50
 
51
- # Return meta_title if set otherwise generates from root name and/or taxon name
51
+ # Return meta_title if set otherwise generates from taxon name
52
52
  def seo_title
53
- if meta_title.blank?
54
- root? ? name : "#{root.name} - #{name}"
55
- else
56
- meta_title
57
- end
53
+ meta_title.blank? ? name : meta_title
58
54
  end
59
55
 
60
56
  # Creates permalink base for friendly_id
@@ -116,15 +116,7 @@ module Spree
116
116
  end
117
117
 
118
118
  def options_text
119
- values = option_values.sort do |a, b|
120
- a.option_type.position <=> b.option_type.position
121
- end
122
-
123
- values.to_a.map! do |ov|
124
- "#{ov.option_type.presentation}: #{ov.presentation}"
125
- end
126
-
127
- values.to_sentence(words_connector: ', ', two_words_connector: ', ')
119
+ Spree::Variants::OptionsPresenter.new(self).to_sentence
128
120
  end
129
121
 
130
122
  # Default to master name
@@ -234,11 +226,7 @@ module Spree
234
226
  end
235
227
 
236
228
  def in_stock?
237
- # Issue 10280
238
- # Check if model responds to cache version and fall back to updated_at for older rails versions
239
- # This makes sure a version is supplied when recyclable cache keys are disabled.
240
- version = respond_to?(:cache_version) ? cache_version : updated_at.to_i
241
- Rails.cache.fetch(in_stock_cache_key, version: version) do
229
+ Rails.cache.fetch(in_stock_cache_key) do
242
230
  total_on_hand > 0
243
231
  end
244
232
  end
@@ -25,7 +25,9 @@ module Spree
25
25
  self.whitelisted_ransackable_attributes = ['description']
26
26
 
27
27
  def self.default_tax
28
- find_by(default_tax: true)
28
+ Rails.cache.fetch('default_tax') do
29
+ find_by(default_tax: true)
30
+ end
29
31
  end
30
32
 
31
33
  def self.potential_matching_zones(zone)
@@ -69,8 +71,8 @@ module Spree
69
71
  end
70
72
 
71
73
  def kind
72
- if self[:kind].present?
73
- self[:kind]
74
+ if kind?
75
+ super
74
76
  else
75
77
  not_nil_scope = members.where.not(zoneable_type: nil)
76
78
  zone_type = not_nil_scope.order('created_at ASC').pluck(:zoneable_type).last
@@ -173,6 +175,7 @@ module Spree
173
175
 
174
176
  def remove_previous_default
175
177
  Spree::Zone.with_default_tax.where.not(id: id).update_all(default_tax: false)
178
+ Rails.cache.delete('default_zone')
176
179
  end
177
180
 
178
181
  def set_zone_members(ids, type)
@@ -0,0 +1,26 @@
1
+ module Spree
2
+ class ProductSummaryPresenter
3
+ include Rails.application.routes.url_helpers
4
+
5
+ def initialize(product)
6
+ @product = product
7
+ end
8
+
9
+ def call
10
+ {
11
+ name: @product.name,
12
+ images: images
13
+ }
14
+ end
15
+
16
+ private
17
+
18
+ def images
19
+ @product.images.map do |image|
20
+ {
21
+ url_product: rails_representation_url(image.url(:product), only_path: true)
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,69 @@
1
+ module Spree
2
+ class VariantPresenter
3
+ include Rails.application.routes.url_helpers
4
+ include Spree::BaseHelper
5
+
6
+ attr_reader :current_currency, :current_price_options
7
+
8
+ def initialize(opts = {})
9
+ @variants = opts[:variants]
10
+ @is_product_available_in_currency = opts[:is_product_available_in_currency]
11
+ @current_currency = opts[:current_currency]
12
+ @current_price_options = opts[:current_price_options]
13
+ end
14
+
15
+ def call
16
+ @variants.map do |variant|
17
+ {
18
+ display_price: display_price(variant),
19
+ is_product_available_in_currency: @is_product_available_in_currency,
20
+ backorderable: backorderable?(variant),
21
+ images: images(variant),
22
+ option_values: option_values(variant),
23
+ }.merge(
24
+ variant_attributes(variant)
25
+ )
26
+ end
27
+ end
28
+
29
+ def images(variant)
30
+ variant.images.map do |image|
31
+ {
32
+ alt: image.alt,
33
+ url_product: rails_representation_url(image.url(:product), only_path: true)
34
+ }
35
+ end
36
+ end
37
+
38
+ def option_values(variant)
39
+ variant.option_values.map do |option_value|
40
+ {
41
+ id: option_value.id,
42
+ name: option_value.name,
43
+ presentation: option_value.presentation,
44
+ }
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def backorderable?(variant)
51
+ backorderable_variant_ids.include?(variant.id)
52
+ end
53
+
54
+ def backorderable_variant_ids
55
+ @backorderable_variant_ids ||= Spree::Variant.joins(:stock_items).where(id: @variants.map(&:id)).
56
+ where(spree_stock_items: { backorderable: true }).merge(Spree::StockItem.with_active_stock_location).distinct.ids
57
+ end
58
+
59
+ def variant_attributes(variant)
60
+ {
61
+ id: variant.id,
62
+ sku: variant.sku,
63
+ price: variant.price,
64
+ in_stock: variant.in_stock?,
65
+ purchasable: variant.purchasable?
66
+ }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,74 @@
1
+ module Spree
2
+ module Variants
3
+ class OptionTypesPresenter
4
+ def initialize(option_types)
5
+ @option_types = option_types
6
+ end
7
+
8
+ def default_variant
9
+ default_variant_data[:variant]
10
+ end
11
+
12
+ def options
13
+ option_types.map do |option_type|
14
+ {
15
+ id: option_type.id,
16
+ name: option_type.name,
17
+ presentation: option_type.presentation,
18
+ position: option_type.position,
19
+ option_values: option_values_options(option_type.option_values)
20
+ }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :option_types
27
+
28
+ def default_variant_data
29
+ return {} if option_types.empty?
30
+
31
+ @default_variant_data ||= begin
32
+ find_in_stock_variant_data || find_backorderable_variant_data || find_first_variant_data
33
+ end
34
+ end
35
+
36
+ def find_in_stock_variant_data
37
+ find_variant_data(&:in_stock?)
38
+ end
39
+
40
+ def find_backorderable_variant_data
41
+ find_variant_data(&:backorderable?)
42
+ end
43
+
44
+ def find_variant_data(&block)
45
+ option_types.first.option_values.each do |option_value|
46
+ variant = option_value.variants.find(&block)
47
+
48
+ return { variant: variant, option_value: option_value } if variant
49
+ end
50
+
51
+ nil
52
+ end
53
+
54
+ def find_first_variant_data
55
+ option_value = option_types.first.option_values.first
56
+ variant = option_value.variants.first
57
+
58
+ { variant: variant, option_value: option_value }
59
+ end
60
+
61
+ def option_values_options(option_values)
62
+ option_values.map do |option_value|
63
+ {
64
+ id: option_value.id,
65
+ position: option_value.position,
66
+ presentation: option_value.presentation,
67
+ variant_id: option_value.variants.first.id,
68
+ is_default: option_value == default_variant_data[:option_value]
69
+ }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end