spree_core 4.0.8 → 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/configuration/active_storage.rb +9 -1
  12. data/app/models/spree/image.rb +52 -3
  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/address_book.rb +20 -7
  16. data/app/models/spree/order.rb +12 -12
  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 +3 -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/controller_helpers/order.rb +12 -6
  46. data/lib/spree/core/controller_helpers/store.rb +2 -2
  47. data/lib/spree/core/search/base.rb +59 -22
  48. data/lib/spree/core/version.rb +1 -3
  49. data/lib/spree/core.rb +0 -1
  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 -52
  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 +9 -15
  57. metadata +49 -34
  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
@@ -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
@@ -0,0 +1,49 @@
1
+ module Spree
2
+ module Variants
3
+ class OptionsPresenter
4
+ WORDS_CONNECTOR = ', '.freeze
5
+
6
+ attr_reader :variant
7
+
8
+ delegate :option_values, to: :variant
9
+
10
+ def initialize(variant)
11
+ @variant = variant
12
+ end
13
+
14
+ def to_sentence
15
+ options = option_values
16
+ options = sort_options(options)
17
+ options = present_options(options)
18
+
19
+ join_options(options)
20
+ end
21
+
22
+ private
23
+
24
+ def sort_options(options)
25
+ options.sort_by { |o| o.option_type.position }
26
+ end
27
+
28
+ def present_options(options)
29
+ options.map do |ov|
30
+ method = "present_#{ov.option_type.name}_option"
31
+
32
+ respond_to?(method, true) ? send(method, ov) : present_option(ov)
33
+ end
34
+ end
35
+
36
+ def present_color_option(option)
37
+ "#{option.option_type.presentation}: #{option.name}"
38
+ end
39
+
40
+ def present_option(option)
41
+ "#{option.option_type.presentation}: #{option.presentation}"
42
+ end
43
+
44
+ def join_options(options)
45
+ options.to_sentence(words_connector: WORDS_CONNECTOR, two_words_connector: WORDS_CONNECTOR)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -7,7 +7,9 @@ module Spree
7
7
  run :reload_order
8
8
  run :ensure_shipping_address
9
9
  run :ensure_line_items_present
10
- run :generate_or_return_shipping_rates
10
+ run :move_order_to_delivery_state
11
+ run :generate_shipping_rates
12
+ run :return_shipments
11
13
  end
12
14
 
13
15
  private
@@ -29,11 +31,6 @@ module Spree
29
31
  success(order: order)
30
32
  end
31
33
 
32
- def generate_or_return_shipping_rates(order:)
33
- generate_shipping_rates(order: order) if order.shipments.empty?
34
- return_shipments(order: order)
35
- end
36
-
37
34
  def generate_shipping_rates(order:)
38
35
  ApplicationRecord.transaction do
39
36
  order.create_proposed_shipments
@@ -45,7 +42,13 @@ module Spree
45
42
  end
46
43
 
47
44
  def return_shipments(order:)
48
- success(order.reload.shipments.includes([shipping_rates: :shipping_method]))
45
+ success(order.shipments.includes([shipping_rates: :shipping_method]))
46
+ end
47
+
48
+ def move_order_to_delivery_state(order:)
49
+ Spree::Dependencies.checkout_next_service.constantize.call(order: order) until order.state == 'delivery'
50
+
51
+ success(order: order)
49
52
  end
50
53
  end
51
54
  end
@@ -4,12 +4,8 @@ module Spree
4
4
  prepend Spree::ServiceModule::Base
5
5
 
6
6
  def call(order:, params:, permitted_attributes:, request_env:)
7
- ship_changed = address_with_country_iso_present?(params, 'ship')
8
- bill_changed = address_with_country_iso_present?(params, 'bill')
9
- params = replace_country_iso_with_id(params, 'ship') if ship_changed
10
- params = replace_country_iso_with_id(params, 'bill') if bill_changed
11
- order.state = 'address' if (ship_changed || bill_changed) && order.has_checkout_step?('address')
12
- order.state = 'delivery' if selected_shipping_rate_present?(params) && order.has_checkout_step?('delivery')
7
+ params = replace_country_iso_with_id(params, 'ship') if address_with_country_iso_present?(params, 'ship')
8
+ params = replace_country_iso_with_id(params, 'bill') if address_with_country_iso_present?(params, 'bill')
13
9
  return success(order) if order.update_from_params(params, permitted_attributes, request_env)
14
10
 
15
11
  failure(order)
@@ -24,13 +20,6 @@ module Spree
24
20
  true
25
21
  end
26
22
 
27
- def selected_shipping_rate_present?(params)
28
- shipments_attributes = params.dig(:order, :shipments_attributes)
29
- return false unless shipments_attributes
30
-
31
- shipments_attributes.any? { |s| s.dig(:selected_shipping_rate_id) }
32
- end
33
-
34
23
  def replace_country_iso_with_id(params, address_kind = 'ship')
35
24
  country_id = Spree::Country.by_iso(params[:order]["#{address_kind}_address_attributes"].fetch(:country_iso))&.id
36
25