spree_core 4.0.7 → 4.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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