spree_core 4.0.8 → 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/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