spree_core 4.3.0.rc1 → 4.3.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +26 -2
  3. data/app/finders/spree/taxons/find.rb +22 -6
  4. data/app/helpers/spree/base_helper.rb +7 -5
  5. data/app/models/concerns/spree/display_money.rb +6 -2
  6. data/app/models/concerns/spree/image_methods.rb +24 -0
  7. data/app/models/concerns/spree/product_scopes.rb +12 -0
  8. data/app/models/concerns/spree/single_store_resource.rb +10 -0
  9. data/app/models/concerns/spree/user_methods.rb +9 -8
  10. data/app/models/concerns/spree/user_reporting.rb +35 -10
  11. data/app/models/concerns/spree/user_roles.rb +25 -0
  12. data/app/models/spree/app_dependencies.rb +3 -1
  13. data/app/models/spree/cms/sections/featured_article.rb +0 -7
  14. data/app/models/spree/cms/sections/hero_image.rb +0 -9
  15. data/app/models/spree/cms/sections/image_gallery.rb +2 -2
  16. data/app/models/spree/cms/sections/side_by_side_images.rb +2 -2
  17. data/app/models/spree/cms_section.rb +12 -0
  18. data/app/models/spree/gateway/bogus.rb +6 -4
  19. data/app/models/spree/image.rb +5 -24
  20. data/app/models/spree/payment_method.rb +12 -0
  21. data/app/models/spree/product.rb +10 -8
  22. data/app/models/spree/property.rb +1 -1
  23. data/app/models/spree/stock_location.rb +9 -0
  24. data/app/models/spree/store.rb +2 -2
  25. data/app/models/spree/taxon.rb +1 -0
  26. data/app/models/spree/taxon_image.rb +1 -0
  27. data/app/models/spree/variant.rb +9 -3
  28. data/app/presenters/spree/filters/options_presenter.rb +20 -0
  29. data/app/presenters/spree/filters/property_presenter.rb +20 -0
  30. data/app/services/spree/cart/associate.rb +16 -0
  31. data/app/services/spree/cart/change_currency.rb +27 -0
  32. data/app/sorters/spree/base_sorter.rb +23 -11
  33. data/app/sorters/spree/products/sort.rb +23 -7
  34. data/config/locales/en.yml +11 -1
  35. data/db/migrate/20210527094055_create_spree_products_stores.rb +15 -8
  36. data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
  37. data/lib/generators/spree/dummy/templates/rails/test.rb +2 -0
  38. data/lib/spree/core/controller_helpers/store.rb +14 -0
  39. data/lib/spree/core/version.rb +1 -1
  40. data/lib/spree/permitted_attributes.rb +7 -0
  41. data/lib/spree/testing_support/factories/product_factory.rb +6 -0
  42. data/lib/spree/testing_support/locale_helpers.rb +1 -1
  43. data/lib/spree/testing_support/rspec_retry_config.rb +5 -0
  44. data/spree_core.gemspec +1 -1
  45. metadata +10 -7
  46. data/app/views/spree/shared/_purchased_items_table.text.erb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b031163e0c77da6776854068fb9769ed6cd44ff048e75a088ee6526c40db986
4
- data.tar.gz: 2cfbcbfb7a1c5221270e7b677222a94047b5965cb32e5144ca6540d927f2b51b
3
+ metadata.gz: b9362a52bbabd7b09ffc2bc0271bffc4a2d11bc311d7356b56db0271c79f0740
4
+ data.tar.gz: 41a19d295a5dad9f3b2dadfe07fe73210e5a57139cc82eeb170d9d2d6e143910
5
5
  SHA512:
6
- metadata.gz: 375bd44efe93e706a1bd58abcc9be1fb3f2af0b0a993fe223a1e093033193105012284e0a7f03413fad0099feb105fa4b1526b8d576d76e58e62e37967c67768
7
- data.tar.gz: c82f2406e121b6fff092677e91d0344e35c5b6e19aee5aca1916ee819d7909a42ef854653922413e215c67934cb73df2a1af002fbe3d48f53db99ff854d80f93
6
+ metadata.gz: b75ef727652e108dd4c34cf10bf2ab1d82abff4c9283ce2ee3c00078b328dbbcdcc38d729f7a1fa75d7a773470e48e5b3ce02d36d6cd33fbed5f8906dadfa944
7
+ data.tar.gz: eeaaf0d1aaa49aaaafd30b55ddc04e50ddcdc922ecd7779e9b98283ca5c8ac4e5fd899c73d770e8cbaa93474e25b3af40e0747612cb761e825afeb36988d2254
@@ -27,6 +27,9 @@ module Spree
27
27
  @deleted = params.dig(:filter, :show_deleted)
28
28
  @discontinued = params.dig(:filter, :show_discontinued)
29
29
  @properties = params.dig(:filter, :properties)
30
+ @in_stock = params.dig(:filter, :in_stock)
31
+ @backorderable = params.dig(:filter, :backorderable)
32
+ @purchasable = params.dig(:filter, :purchasable)
30
33
  end
31
34
 
32
35
  def execute
@@ -42,6 +45,9 @@ module Spree
42
45
  products = by_properties(products)
43
46
  products = include_deleted(products)
44
47
  products = include_discontinued(products)
48
+ products = show_only_stock(products)
49
+ products = show_only_backorderable(products)
50
+ products = show_only_purchasable(products)
45
51
  products = ordered(products)
46
52
 
47
53
  products.distinct
@@ -49,8 +55,8 @@ module Spree
49
55
 
50
56
  private
51
57
 
52
- attr_reader :ids, :skus, :price, :currency, :taxons, :concat_taxons, :name, :options,
53
- :option_value_ids, :scope, :sort_by, :deleted, :discontinued, :properties, :store
58
+ attr_reader :ids, :skus, :price, :currency, :taxons, :concat_taxons, :name, :options, :option_value_ids, :scope,
59
+ :sort_by, :deleted, :discontinued, :properties, :store, :in_stock, :backorderable, :purchasable
54
60
 
55
61
  def ids?
56
62
  ids.present?
@@ -231,6 +237,24 @@ module Spree
231
237
  discontinued ? products : products.active(currency)
232
238
  end
233
239
 
240
+ def show_only_stock(products)
241
+ return products unless in_stock.to_s == 'true'
242
+
243
+ products.in_stock
244
+ end
245
+
246
+ def show_only_backorderable(products)
247
+ return products unless backorderable.to_s == 'true'
248
+
249
+ products.backorderable
250
+ end
251
+
252
+ def show_only_purchasable(products)
253
+ return products unless purchasable.to_s == 'true'
254
+
255
+ products.in_stock_or_backorderable
256
+ end
257
+
234
258
  def map_prices(prices)
235
259
  prices.map do |price|
236
260
  price == 'Infinity' ? Float::INFINITY : price.to_f
@@ -3,16 +3,18 @@ module Spree
3
3
  class Find
4
4
  def initialize(scope:, params:)
5
5
  @scope = scope
6
- @ids = String(params.dig(:filter, :ids)).split(',')
7
- @parent = params.dig(:filter, :parent_id)
8
- @taxonomy = params.dig(:filter, :taxonomy_id)
9
- @name = params.dig(:filter, :name)
10
- @roots = params.dig(:filter, :roots)
6
+ @ids = String(params.dig(:filter, :ids)).split(',')
7
+ @parent = params.dig(:filter, :parent_id)
8
+ @parent_permalink = params.dig(:filter, :parent_permalink)
9
+ @taxonomy = params.dig(:filter, :taxonomy_id)
10
+ @name = params.dig(:filter, :name)
11
+ @roots = params.dig(:filter, :roots)
11
12
  end
12
13
 
13
14
  def execute
14
15
  taxons = by_ids(scope)
15
16
  taxons = by_parent(taxons)
17
+ taxons = by_parent_permalink(taxons)
16
18
  taxons = by_taxonomy(taxons)
17
19
  taxons = by_roots(taxons)
18
20
  taxons = by_name(taxons)
@@ -22,7 +24,7 @@ module Spree
22
24
 
23
25
  private
24
26
 
25
- attr_reader :ids, :parent, :taxonomy, :roots, :name, :scope
27
+ attr_reader :ids, :parent, :parent_permalink, :taxonomy, :roots, :name, :scope
26
28
 
27
29
  def ids?
28
30
  ids.present?
@@ -32,6 +34,10 @@ module Spree
32
34
  parent.present?
33
35
  end
34
36
 
37
+ def parent_permalink?
38
+ parent_permalink.present?
39
+ end
40
+
35
41
  def taxonomy?
36
42
  taxonomy.present?
37
43
  end
@@ -60,6 +66,16 @@ module Spree
60
66
  taxons.where(parent_id: parent)
61
67
  end
62
68
 
69
+ def by_parent_permalink(taxons)
70
+ return taxons unless parent_permalink?
71
+
72
+ if Rails::VERSION::STRING >= '6.1'
73
+ taxons.joins(:parent).where(parent: { permalink: parent_permalink })
74
+ else
75
+ taxons.joins("INNER JOIN #{Spree::Taxon.table_name} AS parent_taxon ON parent_taxon.id = #{Spree::Taxon.table_name}.parent_id").where(["parent_taxon.permalink = ?", parent_permalink])
76
+ end
77
+ end
78
+
63
79
  def by_taxonomy(taxons)
64
80
  return taxons unless taxonomy?
65
81
 
@@ -246,11 +246,13 @@ module Spree
246
246
  options = options.first || {}
247
247
  options[:alt] ||= product.name
248
248
  image_path = default_image_for_product_or_variant(product)
249
- if image_path.present?
250
- create_product_image_tag image_path, product, options, style
251
- else
252
- image_tag "noimage/#{style}.png", options
253
- end
249
+ img = if image_path.present?
250
+ create_product_image_tag image_path, product, options, style
251
+ else
252
+ inline_svg_tag "noimage/backend-missing-image.svg", class: "noimage", size: "60%*60%"
253
+ end
254
+
255
+ content_tag(:div, img, class: "admin-product-image-container #{style}-img")
254
256
  end
255
257
  end
256
258
 
@@ -17,13 +17,17 @@ module Spree
17
17
  # Decorate a method, but with some additional options
18
18
  # extend Spree::DisplayMoney
19
19
  # money_methods tax_amount: { currency: "CAD", no_cents: true }, :price
20
+ # Use generated method with the same arguments as in wrapped method
21
+ # def tax_amount(order, country)
22
+ # display_tax_amount(order, country)
20
23
  def money_methods(*args)
21
24
  args.each do |money_method|
22
25
  money_method = { money_method => {} } unless money_method.is_a? Hash
23
26
  money_method.each do |method_name, opts|
24
- define_method("display_#{method_name}") do
27
+ define_method("display_#{method_name}") do |**args_list|
25
28
  default_opts = respond_to?(:currency) ? { currency: currency } : {}
26
- Spree::Money.new(send(method_name), default_opts.merge(opts))
29
+
30
+ Spree::Money.new(send(method_name, **args_list), default_opts.merge(opts).merge(args_list.slice(:currency)))
27
31
  end
28
32
  end
29
33
  end
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module ImageMethods
3
+ extend ActiveSupport::Concern
4
+
5
+ def generate_url(size:, gravity: 'center', quality: 80, background: 'show')
6
+ return if size.blank?
7
+ size = size.gsub(/\s+/, '')
8
+
9
+ return unless size.match(/(\d+)x(\d+)/)
10
+
11
+ polymorphic_path(attachment.variant(
12
+ gravity: gravity,
13
+ resize: size,
14
+ extent: size,
15
+ background: background,
16
+ quality: quality.to_i
17
+ ), only_path: true)
18
+ end
19
+
20
+ def original_url
21
+ polymorphic_path(attachment, only_path: true)
22
+ end
23
+ end
24
+ end
@@ -62,6 +62,18 @@ module Spree
62
62
  where("#{price_table_name}.amount >= ?", price)
63
63
  end
64
64
 
65
+ add_search_scope :in_stock do
66
+ joins(:variants_including_master).merge(Spree::Variant.in_stock)
67
+ end
68
+
69
+ add_search_scope :backorderable do
70
+ joins(:variants_including_master).merge(Spree::Variant.backorderable)
71
+ end
72
+
73
+ add_search_scope :in_stock_or_backorderable do
74
+ joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable)
75
+ end
76
+
65
77
  # This scope selects products in taxon AND all its descendants
66
78
  # If you need products only within one taxon use
67
79
  #
@@ -3,7 +3,17 @@ module Spree
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
+ validate :ensure_store_association_is_not_changed
7
+
6
8
  scope :for_store, ->(store) { where(store_id: store.id) }
7
9
  end
10
+
11
+ protected
12
+
13
+ def ensure_store_association_is_not_changed
14
+ if store_id_changed? && persisted?
15
+ errors.add(:store, Spree.t('errors.messages.store_association_can_not_be_changed'))
16
+ end
17
+ end
8
18
  end
9
19
  end
@@ -4,6 +4,7 @@ module Spree
4
4
 
5
5
  include Spree::UserPaymentSource
6
6
  include Spree::UserReporting
7
+ include Spree::UserRoles
7
8
  include Spree::RansackableAttributes
8
9
 
9
10
  included do
@@ -15,9 +16,6 @@ module Spree
15
16
 
16
17
  attr_accessor :use_billing
17
18
 
18
- has_many :role_users, class_name: 'Spree::RoleUser', foreign_key: :user_id, dependent: :destroy
19
- has_many :spree_roles, through: :role_users, class_name: 'Spree::Role', source: :role
20
-
21
19
  has_many :promotion_rule_users, class_name: 'Spree::PromotionRuleUser', foreign_key: :user_id, dependent: :destroy
22
20
  has_many :promotion_rules, through: :promotion_rule_users, class_name: 'Spree::PromotionRule'
23
21
 
@@ -47,11 +45,6 @@ module Spree
47
45
  end
48
46
  end
49
47
 
50
- # has_spree_role? simply needs to return true or false whether a user has a role or not.
51
- def has_spree_role?(role_in_question)
52
- spree_roles.any? { |role| role.name == role_in_question.to_s }
53
- end
54
-
55
48
  def last_incomplete_spree_order(store, options = {})
56
49
  orders.where(store: store).incomplete.
57
50
  includes(options[:includes]).
@@ -65,6 +58,14 @@ module Spree
65
58
  store_credits.for_store(store).where(currency: currency).reload.to_a.sum(&:amount_remaining)
66
59
  end
67
60
 
61
+ def available_store_credits(store)
62
+ store ||= Store.default
63
+
64
+ store_credits.for_store(store).pluck(:currency).uniq.each_with_object([]) do |currency, arr|
65
+ arr << Spree::Money.new(total_available_store_credit(currency, store), currency: currency)
66
+ end
67
+ end
68
+
68
69
  private
69
70
 
70
71
  def check_completed_orders
@@ -3,20 +3,45 @@ module Spree
3
3
  extend DisplayMoney
4
4
  money_methods :lifetime_value, :average_order_value
5
5
 
6
- def lifetime_value
7
- orders.complete.sum(:total)
6
+ def report_values_for(report_name, store)
7
+ store ||= Store.default
8
+
9
+ completed_orders(store).pluck(:currency).uniq.each_with_object([]) do |currency, arr|
10
+ arr << send("display_#{report_name}", store: store, currency: currency)
11
+ end
8
12
  end
9
13
 
10
- def order_count
11
- orders.complete.size
14
+ def lifetime_value(**args)
15
+ order_calculate(operation: :sum,
16
+ column: :total,
17
+ **args)
12
18
  end
13
19
 
14
- def average_order_value
15
- if order_count > 0
16
- lifetime_value / order_count
17
- else
18
- BigDecimal('0.00')
19
- end
20
+ def average_order_value(**args)
21
+ order_calculate(operation: :average,
22
+ column: :total,
23
+ **args)
24
+ end
25
+
26
+ def order_count(store = nil)
27
+ store ||= Store.default
28
+ order_calculate(store: store,
29
+ currency: store.supported_currencies.split(','),
30
+ operation: :count,
31
+ column: :all)
32
+ end
33
+
34
+ private
35
+
36
+ def order_calculate(operation:, column:, store: nil, currency: nil)
37
+ store ||= Store.default
38
+ currency ||= store.default_currency
39
+
40
+ completed_orders(store).where(currency: currency).calculate(operation, column) || BigDecimal('0.00')
41
+ end
42
+
43
+ def completed_orders(store)
44
+ orders.for_store(store).complete
20
45
  end
21
46
  end
22
47
  end
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module UserRoles
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :role_users, class_name: 'Spree::RoleUser', foreign_key: :user_id, dependent: :destroy
7
+ has_many :spree_roles, through: :role_users, class_name: 'Spree::Role', source: :role
8
+
9
+ scope :admin, -> { joins(:spree_roles).where(Spree::Role.table_name => { name: 'admin' }) }
10
+
11
+ # has_spree_role? simply needs to return true or false whether a user has a role or not.
12
+ def has_spree_role?(role_name)
13
+ spree_roles.exists?(name: role_name)
14
+ end
15
+
16
+ def self.admin_created?
17
+ admin.exists?
18
+ end
19
+
20
+ def admin?
21
+ has_spree_role?('admin')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -13,7 +13,7 @@ module Spree
13
13
  :products_finder, :taxon_finder, :line_item_by_variant_finder, :cart_estimate_shipping_rates_service,
14
14
  :account_create_address_service, :account_update_address_service, :account_create_service, :account_update_service,
15
15
  :address_finder, :collection_sorter, :error_handler, :current_store_finder, :cart_empty_service, :cart_destroy_service,
16
- :classification_reposition_service, :credit_cards_destroy_service
16
+ :classification_reposition_service, :credit_cards_destroy_service, :cart_associate_service, :cart_change_currency_service
17
17
  ].freeze
18
18
 
19
19
  attr_accessor *INJECTION_POINTS
@@ -43,6 +43,8 @@ module Spree
43
43
  @cart_estimate_shipping_rates_service = 'Spree::Cart::EstimateShippingRates'
44
44
  @cart_empty_service = 'Spree::Cart::Empty'
45
45
  @cart_destroy_service = 'Spree::Cart::Destroy'
46
+ @cart_associate_service = 'Spree::Cart::Associate'
47
+ @cart_change_currency_service = 'Spree::Cart::ChangeCurrency'
46
48
 
47
49
  # checkout
48
50
  @checkout_next_service = 'Spree::Checkout::Next'
@@ -1,6 +1,5 @@
1
1
  module Spree::Cms::Sections
2
2
  class FeaturedArticle < Spree::CmsSection
3
- before_save :reset_link_attributes
4
3
  after_initialize :default_values
5
4
 
6
5
  store :content, accessors: [:title, :subtitle, :button_text, :rte_content], coder: JSON
@@ -14,12 +13,6 @@ module Spree::Cms::Sections
14
13
 
15
14
  private
16
15
 
17
- def reset_link_attributes
18
- if linked_resource_type_changed?
19
- self.linked_resource_id = nil
20
- end
21
- end
22
-
23
16
  def default_values
24
17
  self.gutters ||= 'No Gutters'
25
18
  self.fit ||= 'Screen'
@@ -1,6 +1,5 @@
1
1
  module Spree::Cms::Sections
2
2
  class HeroImage < Spree::CmsSection
3
- before_save :reset_link_attributes
4
3
  after_initialize :default_values
5
4
 
6
5
  store :content, accessors: [:title, :button_text], coder: JSON
@@ -30,14 +29,6 @@ module Spree::Cms::Sections
30
29
 
31
30
  private
32
31
 
33
- def reset_link_attributes
34
- if linked_resource_type_changed?
35
- return if linked_resource_id_was.nil?
36
-
37
- self.linked_resource_id = nil
38
- end
39
- end
40
-
41
32
  def default_values
42
33
  self.gutters ||= 'No Gutters'
43
34
  self.fit ||= 'Screen'
@@ -1,7 +1,7 @@
1
1
  module Spree::Cms::Sections
2
2
  class ImageGallery < Spree::CmsSection
3
3
  after_initialize :default_values
4
- before_save :reset_link_attributes
4
+ validate :reset_multiple_link_attributes
5
5
 
6
6
  LINKED_RESOURCE_TYPE = if Rails::VERSION::STRING < '6.0'
7
7
  ['Spree::Taxon'].freeze
@@ -70,7 +70,7 @@ module Spree::Cms::Sections
70
70
 
71
71
  private
72
72
 
73
- def reset_link_attributes
73
+ def reset_multiple_link_attributes
74
74
  return if Rails::VERSION::STRING < '6.0'
75
75
 
76
76
  if link_type_one_changed?
@@ -1,7 +1,7 @@
1
1
  module Spree::Cms::Sections
2
2
  class SideBySideImages < Spree::CmsSection
3
3
  after_initialize :default_values
4
- before_save :reset_link_attributes
4
+ validate :reset_multiple_link_attributes
5
5
 
6
6
  LINKED_RESOURCE_TYPE = if Rails::VERSION::STRING < '6.0'
7
7
  ['Spree::Taxon'].freeze
@@ -48,7 +48,7 @@ module Spree::Cms::Sections
48
48
 
49
49
  private
50
50
 
51
- def reset_link_attributes
51
+ def reset_multiple_link_attributes
52
52
  return if Rails::VERSION::STRING < '6.0'
53
53
 
54
54
  if link_type_one_changed?
@@ -5,6 +5,8 @@ module Spree
5
5
  acts_as_list scope: :cms_page
6
6
  belongs_to :cms_page, touch: true
7
7
 
8
+ validate :reset_link_attributes
9
+
8
10
  IMAGE_COUNT = ['one', 'two', 'three']
9
11
  IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].freeze
10
12
  IMAGE_SIZE = ['sm', 'md', 'lg', 'xl']
@@ -53,5 +55,15 @@ module Spree
53
55
  def fullscreen?
54
56
  fit == 'Screen'
55
57
  end
58
+
59
+ private
60
+
61
+ def reset_link_attributes
62
+ if linked_resource_type_changed?
63
+ return if linked_resource_id_was.nil?
64
+
65
+ self.linked_resource_id = nil
66
+ end
67
+ end
56
68
  end
57
69
  end
@@ -9,14 +9,12 @@ module Spree
9
9
 
10
10
  attr_accessor :test
11
11
 
12
+ preference :dummy_key, :string, default: 'PUBLICKEY123'
13
+
12
14
  def provider_class
13
15
  self.class
14
16
  end
15
17
 
16
- def preferences
17
- {}
18
- end
19
-
20
18
  def create_profile(payment)
21
19
  return if payment.source.has_payment_profile?
22
20
 
@@ -88,5 +86,9 @@ module Spree
88
86
  end
89
87
  random
90
88
  end
89
+
90
+ def public_preference_keys
91
+ [:dummy_key]
92
+ end
91
93
  end
92
94
  end
@@ -2,6 +2,7 @@ module Spree
2
2
  class Image < Asset
3
3
  include Configuration::ActiveStorage
4
4
  include Rails.application.routes.url_helpers
5
+ include ::Spree::ImageMethods
5
6
 
6
7
  # In Rails 5.x class constants are being undefined/redefined during the code reloading process
7
8
  # in a rails development environment, after which the actual ruby objects stored in those class constants
@@ -15,13 +16,8 @@ module Spree
15
16
  width, height = size.chop.split('x')
16
17
 
17
18
  {
18
- url: polymorphic_path(attachment.variant(
19
- gravity: 'center',
20
- resize: size,
21
- extent: size,
22
- background: 'snow2',
23
- quality: 80
24
- ), only_path: true),
19
+ url: generate_url(size: size),
20
+ size: size,
25
21
  width: width,
26
22
  height: height
27
23
  }
@@ -35,13 +31,7 @@ module Spree
35
31
  width, height = size.chop.split('x')
36
32
 
37
33
  {
38
- url: polymorphic_path(attachment.variant(
39
- gravity: 'center',
40
- resize: size,
41
- extent: size,
42
- background: 'snow2',
43
- quality: 80
44
- ), only_path: true),
34
+ url: generate_url(size: size),
45
35
  size: size,
46
36
  width: width,
47
37
  height: height
@@ -59,16 +49,7 @@ module Spree
59
49
  end
60
50
 
61
51
  def plp_url
62
- size = self.class.styles[:plp_and_carousel]
63
- variant = attachment.variant(
64
- gravity: 'center',
65
- resize: size,
66
- extent: size,
67
- background: 'snow2',
68
- quality: 80
69
- )
70
-
71
- polymorphic_path(variant, only_path: true)
52
+ generate_url(size: self.class.styles[:plp_and_carousel])
72
53
  end
73
54
  end
74
55
  end
@@ -87,5 +87,17 @@ module Spree
87
87
 
88
88
  store_ids.include?(store.id)
89
89
  end
90
+
91
+ def public_preferences
92
+ public_preference_keys.each_with_object({}) do |key, hash|
93
+ hash[key] = preferences[key]
94
+ end
95
+ end
96
+
97
+ protected
98
+
99
+ def public_preference_keys
100
+ []
101
+ end
90
102
  end
91
103
  end
@@ -142,17 +142,17 @@ module Spree
142
142
 
143
143
  # Cant use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
144
144
  def purchasable?
145
- variants_including_master.any?(&:purchasable?)
145
+ default_variant.purchasable? || variants.any?(&:purchasable?)
146
146
  end
147
147
 
148
148
  # Cant use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
149
149
  def in_stock?
150
- variants_including_master.any?(&:in_stock?)
150
+ default_variant.in_stock? || variants.any?(&:in_stock?)
151
151
  end
152
152
 
153
153
  # Cant use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
154
154
  def backorderable?
155
- variants_including_master.any?(&:backorderable?)
155
+ default_variant.backorderable? || variants.any?(&:backorderable?)
156
156
  end
157
157
 
158
158
  def find_or_build_master
@@ -301,11 +301,13 @@ module Spree
301
301
  end
302
302
 
303
303
  def total_on_hand
304
- @total_on_hand ||= if any_variants_not_track_inventory?
305
- Float::INFINITY
306
- else
307
- stock_items.sum(:count_on_hand)
308
- end
304
+ @total_on_hand ||= Rails.cache.fetch(['product-total-on-hand', cache_key_with_version]) do
305
+ if any_variants_not_track_inventory?
306
+ Float::INFINITY
307
+ else
308
+ stock_items.sum(:count_on_hand)
309
+ end
310
+ end
309
311
  end
310
312
 
311
313
  # Master variant may be deleted (i.e. when the product is deleted)
@@ -18,7 +18,7 @@ module Spree
18
18
  after_touch :touch_all_products
19
19
  after_save :ensure_product_properties_have_filter_params
20
20
 
21
- self.whitelisted_ransackable_attributes = ['presentation']
21
+ self.whitelisted_ransackable_attributes = ['presentation', 'filterable']
22
22
 
23
23
  def uniq_values(product_properties_scope: nil)
24
24
  with_uniq_values_cache_key(product_properties_scope) do
@@ -2,6 +2,7 @@ module Spree
2
2
  class StockLocation < Spree::Base
3
3
  has_many :shipments
4
4
  has_many :stock_items, dependent: :delete_all, inverse_of: :stock_location
5
+ has_many :variants, through: :stock_items
5
6
  has_many :stock_movements, through: :stock_items
6
7
 
7
8
  belongs_to :state, class_name: 'Spree::State', optional: true
@@ -14,6 +15,7 @@ module Spree
14
15
 
15
16
  after_create :create_stock_items, if: :propagate_all_variants?
16
17
  after_save :ensure_one_default
18
+ after_update :conditional_touch_records
17
19
 
18
20
  def state_text
19
21
  state.try(:abbr) || state.try(:name) || state_name
@@ -134,5 +136,12 @@ module Spree
134
136
  StockLocation.where(default: true).where.not(id: id).update_all(default: false)
135
137
  end
136
138
  end
139
+
140
+ def conditional_touch_records
141
+ return unless active_changed?
142
+
143
+ stock_items.update_all(updated_at: Time.current)
144
+ variants.update_all(updated_at: Time.current)
145
+ end
137
146
  end
138
147
  end
@@ -76,8 +76,8 @@ module Spree
76
76
 
77
77
  def self.current(url = nil)
78
78
  ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
79
- `Spree::Store.current` is deprecated and will be removed in Spree 5
80
- Spree::Stores::FindCurrent.new(url: "http://example.com") instead
79
+ `Spree::Store.current` is deprecated and will be removed in Spree 5.0
80
+ Please use `Spree::Stores::FindCurrent.new(url: "https://example.com").execute` instead
81
81
  DEPRECATION
82
82
  Stores::FindCurrent.new(url: url).execute
83
83
  end
@@ -44,6 +44,7 @@ module Spree
44
44
  scope :for_store, ->(store) { joins(:taxonomy).where(spree_taxonomies: { store_id: store.id }) }
45
45
 
46
46
  self.whitelisted_ransackable_associations = %w[taxonomy]
47
+ self.whitelisted_ransackable_attributes = %w[name permalink]
47
48
 
48
49
  scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }
49
50
 
@@ -2,6 +2,7 @@ module Spree
2
2
  class TaxonImage < Asset
3
3
  include Configuration::ActiveStorage
4
4
  include Rails.application.routes.url_helpers
5
+ include ::Spree::ImageMethods
5
6
 
6
7
  def styles
7
8
  self.class.styles.map do |_, size|
@@ -60,7 +60,7 @@ module Spree
60
60
 
61
61
  after_touch :clear_in_stock_cache
62
62
 
63
- scope :in_stock, -> { joins(:stock_items).where('count_on_hand > ? OR track_inventory = ?', 0, false) }
63
+ scope :in_stock, -> { joins(:stock_items).where("#{Spree::StockItem.table_name}.count_on_hand > ? OR #{Spree::Variant.table_name}.track_inventory = ?", 0, false) }
64
64
  scope :backorderable, -> { joins(:stock_items).where(spree_stock_items: { backorderable: true }) }
65
65
  scope :in_stock_or_backorderable, -> { in_stock.or(backorderable) }
66
66
 
@@ -256,7 +256,13 @@ module Spree
256
256
  end
257
257
  end
258
258
 
259
- delegate :total_on_hand, :can_supply?, :backorderable?, to: :quantifier
259
+ def backorderable?
260
+ @backorderable ||= Rails.cache.fetch(['variant-backorderable', cache_key_with_version]) do
261
+ quantifier.backorderable?
262
+ end
263
+ end
264
+
265
+ delegate :total_on_hand, :can_supply?, to: :quantifier
260
266
 
261
267
  alias is_backorderable? backorderable?
262
268
 
@@ -291,7 +297,7 @@ module Spree
291
297
  end
292
298
 
293
299
  def backordered?
294
- @backordered ||= total_on_hand <= 0 && stock_items.exists?(backorderable: true)
300
+ @backordered ||= !in_stock? && stock_items.exists?(backorderable: true)
295
301
  end
296
302
 
297
303
  private
@@ -3,6 +3,26 @@ module Spree
3
3
  class OptionsPresenter
4
4
  FilterableOptionType = Struct.new(:option_type, :option_values, keyword_init: true) do
5
5
  delegate_missing_to :option_type
6
+
7
+ def to_h
8
+ {
9
+ id: option_type.id,
10
+ name: option_type.name,
11
+ presentation: option_type.presentation,
12
+ option_values: option_values.map { |e| option_value_hash(e) }
13
+ }
14
+ end
15
+
16
+ private
17
+
18
+ def option_value_hash(option_value)
19
+ {
20
+ id: option_value.id,
21
+ name: option_value.name,
22
+ presentation: option_value.presentation,
23
+ position: option_value.position
24
+ }
25
+ end
6
26
  end
7
27
 
8
28
  def initialize(option_values_scope:)
@@ -14,9 +14,29 @@ module Spree
14
14
  property.uniq_values(product_properties_scope: product_properties)
15
15
  end
16
16
 
17
+ def to_h
18
+ {
19
+ id: property.id,
20
+ name: property.name,
21
+ presentation: property.presentation,
22
+ values: values_hash
23
+ }
24
+ end
25
+
17
26
  private
18
27
 
19
28
  attr_reader :property
29
+
30
+ def values_hash
31
+ value_hashes = uniq_values.map do |filter_param, value|
32
+ {
33
+ value: value,
34
+ filter_param: filter_param
35
+ }
36
+ end
37
+
38
+ value_hashes.sort_by { |e| e[:value] }
39
+ end
20
40
  end
21
41
  end
22
42
  end
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ module Cart
3
+ class Associate
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(guest_order:, user:)
7
+ if guest_order.user.nil?
8
+ guest_order.associate_user!(user)
9
+ success(guest_order)
10
+ else
11
+ failure(guest_order, 'Already assigned to a user')
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module Cart
3
+ class ChangeCurrency
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(order:, new_currency:)
7
+ return failure('Currency not supported') unless supported_currency?(order, new_currency)
8
+
9
+ result = order.update!(currency: new_currency) rescue false
10
+
11
+ if result
12
+ success(order)
13
+ else
14
+ failure('Failed to update order')
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def supported_currency?(order, currency)
21
+ store = order.store
22
+ supported_currencies = store.supported_currencies_list
23
+ supported_currencies.map(&:iso_code).include?(currency.upcase)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,34 +2,46 @@ module Spree
2
2
  class BaseSorter
3
3
  def initialize(scope, params = {}, allowed_sort_attributes = [])
4
4
  @scope = scope
5
- @sort = params[:sort]
6
5
  @allowed_sort_attributes = allowed_sort_attributes
6
+ @sort = sort_fields(params[:sort])
7
7
  end
8
8
 
9
9
  def call
10
- by_param_attribute(scope)
10
+ by_param_attributes(scope)
11
11
  end
12
12
 
13
13
  protected
14
14
 
15
15
  attr_reader :scope, :collection, :sort, :allowed_sort_attributes
16
16
 
17
- def by_param_attribute(scope)
18
- return scope if sort_field.blank? || !allowed_sort_attributes.include?(sort_field.to_sym)
17
+ def by_param_attributes(scope)
18
+ return scope if sort.empty?
19
19
 
20
- scope.order("#{sort_field}": order_direction)
20
+ sort.each do |value, order|
21
+ next if value.blank? || allowed_sort_attributes.exclude?(value.to_sym)
22
+
23
+ scope = scope.order("#{value}": order)
24
+ end
25
+
26
+ scope
27
+ end
28
+
29
+ def sort_fields(sort)
30
+ return [] if sort.nil?
31
+
32
+ sort.split(',').map { |field| [sort_field(field), order_direction(field)] }
21
33
  end
22
34
 
23
- def desc_order
24
- @desc_order ||= String(sort)[0] == '-'
35
+ def desc_order(field)
36
+ String(field)[0] == '-'
25
37
  end
26
38
 
27
- def sort_field
28
- @sort_field ||= desc_order ? sort[1..-1] : sort
39
+ def sort_field(field)
40
+ desc_order(field) ? field[1..-1] : field
29
41
  end
30
42
 
31
- def order_direction
32
- desc_order ? :desc : :asc
43
+ def order_direction(field)
44
+ desc_order(field) ? :desc : :asc
33
45
  end
34
46
  end
35
47
  end
@@ -7,8 +7,9 @@ module Spree
7
7
  end
8
8
 
9
9
  def call
10
- products = by_param_attribute(scope)
10
+ products = by_param_attributes(scope)
11
11
  products = by_price(products)
12
+ products = by_sku(products)
12
13
 
13
14
  products.distinct
14
15
  end
@@ -17,18 +18,33 @@ module Spree
17
18
 
18
19
  attr_reader :sort, :scope, :currency, :allowed_sort_attributes
19
20
 
20
- def price?
21
- sort_field == 'price'
22
- end
23
-
24
21
  def by_price(scope)
25
- return scope unless price?
22
+ return scope unless (value = sort_by?('price'))
26
23
 
27
24
  scope.joins(master: :prices).
28
25
  select("#{Spree::Product.table_name}.*, #{Spree::Price.table_name}.amount").
29
26
  distinct.
30
27
  where(spree_prices: { currency: currency }).
31
- order("#{Spree::Price.table_name}.amount #{order_direction}")
28
+ order("#{Spree::Price.table_name}.amount #{value[1]}")
29
+ end
30
+
31
+ def by_sku(scope)
32
+ return scope unless (value = sort_by?('sku'))
33
+
34
+ select_product_attributes = if scope.to_sql.include?("#{Spree::Product.table_name}.*")
35
+ ''
36
+ else
37
+ "#{Spree::Product.table_name}.*, "
38
+ end
39
+
40
+ scope.joins(:master).
41
+ select("#{select_product_attributes}#{Spree::Variant.table_name}.sku").
42
+ where(Spree::Variant.table_name.to_s => { is_master: true }).
43
+ order("#{Spree::Variant.table_name}.sku #{value[1]}")
44
+ end
45
+
46
+ def sort_by?(field)
47
+ sort.detect { |s| s[0] == field }
32
48
  end
33
49
  end
34
50
  end
@@ -184,6 +184,12 @@ en:
184
184
  spree/address:
185
185
  one: Address
186
186
  other: Addresses
187
+ spree/cms_page:
188
+ one: Page
189
+ other: Pages
190
+ spree/cms_section:
191
+ one: Section
192
+ other: Sections
187
193
  spree/country:
188
194
  one: Country
189
195
  other: Countries
@@ -558,6 +564,7 @@ en:
558
564
  draft_mode: Draft Mode
559
565
  full_width: Full width
560
566
  link_to_taxon: Link to Taxon
567
+ link_to_product: Link to Product
561
568
  title: Title
562
569
  fit: Fit To
563
570
  info_hero_image_body: "<p>The Hero Image section adds a large image with a button and tagline text to your page.</p>
@@ -964,6 +971,8 @@ en:
964
971
  error: error
965
972
  errors:
966
973
  messages:
974
+ store_association_can_not_be_changed: The store association can not be changed
975
+ store_is_already_set: Store is already set
967
976
  blank: can't be blank
968
977
  could_not_create_taxon: Could not create taxon
969
978
  no_shipping_methods_available: No shipping methods available for selected location, please change your address and try again.
@@ -1132,7 +1141,7 @@ en:
1132
1141
  log_in_to_continue: Log in to continue
1133
1142
  logs: "Logs"
1134
1143
  logged_in_as: Logged in as
1135
- logged_in_succesfully: Logged in successfully
1144
+ logged_in_successfully: Logged in successfully
1136
1145
  logged_out: You have been logged out.
1137
1146
  login: Login
1138
1147
  login_as_existing: Login as Existing Customer
@@ -1341,6 +1350,7 @@ en:
1341
1350
  backordered_confirm_info: Selected item is backordered so expect delays. Are you sure you want to order it?
1342
1351
  overview: Overview
1343
1352
  package_from: package from
1353
+ page: Page
1344
1354
  page_not_found: Sorry! Page you are looking can’t be found.
1345
1355
  pagination:
1346
1356
  next_page: next page &raquo;
@@ -1,10 +1,17 @@
1
1
  class CreateSpreeProductsStores < ActiveRecord::Migration[5.2]
2
2
  def up
3
- unless table_exists?(:spree_products_stores)
3
+ if table_exists?(:spree_products_stores)
4
+ unless index_exists?(:spree_products_stores, [:product_id, :store_id], unique: true)
5
+ add_index :spree_products_stores, [:product_id, :store_id], unique: true
6
+ end
7
+ unless column_exists?(:spree_products_stores, :created_at)
8
+ add_timestamps :spree_products_stores, default: Time.current
9
+ end
10
+ else
4
11
  create_table :spree_products_stores do |t|
5
12
  t.references :product, index: true
6
13
  t.references :store, index: true
7
- t.timestamps
14
+ t.timestamps default: Time.current
8
15
 
9
16
  t.index [:product_id, :store_id], unique: true
10
17
  end
@@ -12,13 +19,13 @@ class CreateSpreeProductsStores < ActiveRecord::Migration[5.2]
12
19
  stores = Spree::Store.all
13
20
  product_ids = Spree::Product.with_deleted.order(:id).ids
14
21
 
15
- stores.find_each do |store|
16
- prepared_values = product_ids.map { |id| "(#{id}, #{store.id}, '#{Time.current.to_s(:db)}', '#{Time.current.to_s(:db)}')" }.join(', ')
17
- next if prepared_values.empty?
22
+ if product_ids.any? && Spree::StoreProduct.respond_to?(:insert_all)
23
+ stores.find_each do |store|
24
+ records = product_ids.map { |product_id| { product_id: product_id, store_id: store.id } }
18
25
 
19
- begin
20
- execute "INSERT INTO spree_products_stores (product_id, store_id, created_at, updated_at) VALUES #{prepared_values};"
21
- rescue ActiveRecord::RecordNotUnique; end
26
+ # Rails 5 does not have insert_all
27
+ Spree::StoreProduct.insert_all(records)
28
+ end
22
29
  end
23
30
  end
24
31
  end
@@ -68,7 +68,7 @@ module Spree
68
68
  inside dummy_path do
69
69
  inject_require_for(gem)
70
70
  end
71
- rescue
71
+ rescue StandardError, LoadError
72
72
  end
73
73
  end
74
74
  end
@@ -35,4 +35,6 @@ Dummy::Application.configure do
35
35
  config.active_support.deprecation = :stderr
36
36
 
37
37
  config.active_job.queue_adapter = :test
38
+
39
+ config.cache_store = :redis_cache_store
38
40
  end
@@ -22,6 +22,20 @@ module Spree
22
22
  current_store.default_locale
23
23
  end
24
24
 
25
+ def ensure_current_store(object)
26
+ return if object.nil?
27
+
28
+ if object.has_attribute?(:store_id)
29
+ if object.store.present? && object.store != current_store
30
+ raise Spree.t('errors.messages.store_is_already_set')
31
+ else
32
+ object.store = current_store
33
+ end
34
+ elsif object.class.method_defined?(:stores) && object.stores.exclude?(current_store)
35
+ object.stores << current_store
36
+ end
37
+ end
38
+
25
39
  # Return a Hash of things that influence the prices displayed in your shop.
26
40
  #
27
41
  # By default, the only thing that influences prices that is the current order's +tax_zone+
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '4.3.0.rc1'.freeze
2
+ VERSION = '4.3.1'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -8,6 +8,8 @@ module Spree
8
8
  :image_attributes,
9
9
  :inventory_unit_attributes,
10
10
  :line_item_attributes,
11
+ :menu_attributes,
12
+ :menu_item_attributes,
11
13
  :option_type_attributes,
12
14
  :option_value_attributes,
13
15
  :payment_attributes,
@@ -62,6 +64,11 @@ module Spree
62
64
 
63
65
  @@line_item_attributes = [:id, :variant_id, :quantity]
64
66
 
67
+ @@menu_attributes = [:name, :locale, :location]
68
+
69
+ @@menu_item_attributes = [:name, :subtite, :destination, :new_window, :item_type,
70
+ :linked_resource_type, :linked_resource_id, :code, :menu_id]
71
+
65
72
  @@option_type_attributes = [:name, :presentation, :option_values_attributes]
66
73
 
67
74
  @@option_value_attributes = [:name, :presentation]
@@ -36,6 +36,12 @@ FactoryBot.define do
36
36
  after :create do |product|
37
37
  product.master.stock_items.first.adjust_count_on_hand(10)
38
38
  end
39
+
40
+ trait :without_backorder do
41
+ after :create do |product|
42
+ product.master.stock_items.update_all(backorderable: false)
43
+ end
44
+ end
39
45
  end
40
46
 
41
47
  factory :product_with_option_types do
@@ -48,7 +48,7 @@ module Spree
48
48
  remember_me: 'Se souvenir de moi',
49
49
  my_account: 'Mon compte',
50
50
  my_orders: 'Mes commandes',
51
- logged_in_succesfully: 'Connexion réussie'
51
+ logged_in_successfully: 'Connexion réussie'
52
52
  })
53
53
  end
54
54
  # rubocop:enable Layout/ArgumentAlignment
@@ -7,4 +7,9 @@ RSpec.configure do |config|
7
7
  config.around :each, type: :feature do |ex|
8
8
  ex.run_with_retry retry: ENV.fetch('RSPEC_RETRY_RETRY_COUNT', 3).to_i
9
9
  end
10
+
11
+ # callback to be run between retries
12
+ config.retry_callback = proc do |ex|
13
+ Rails.cache.clear
14
+ end
10
15
  end
data/spree_core.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.metadata = {
17
17
  "bug_tracker_uri" => "https://github.com/spree/spree/issues",
18
18
  "changelog_uri" => "https://github.com/spree/spree/releases/tag/v#{s.version}",
19
- "documentation_uri" => "https://guides.spreecommerce.org/",
19
+ "documentation_uri" => "https://dev-docs.spreecommerce.org/",
20
20
  "source_code_uri" => "https://github.com/spree/spree/tree/v#{s.version}",
21
21
  }
22
22
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.0.rc1
4
+ version: 4.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-11 00:00:00.000000000 Z
12
+ date: 2022-01-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -472,6 +472,7 @@ files:
472
472
  - app/models/concerns/spree/display_link.rb
473
473
  - app/models/concerns/spree/display_money.rb
474
474
  - app/models/concerns/spree/filter_param.rb
475
+ - app/models/concerns/spree/image_methods.rb
475
476
  - app/models/concerns/spree/memoized_data.rb
476
477
  - app/models/concerns/spree/multi_store_resource.rb
477
478
  - app/models/concerns/spree/named_type.rb
@@ -483,6 +484,7 @@ files:
483
484
  - app/models/concerns/spree/user_methods.rb
484
485
  - app/models/concerns/spree/user_payment_source.rb
485
486
  - app/models/concerns/spree/user_reporting.rb
487
+ - app/models/concerns/spree/user_roles.rb
486
488
  - app/models/concerns/spree/vat_price_calculation.rb
487
489
  - app/models/friendly_id/slug_decorator.rb
488
490
  - app/models/spree/ability.rb
@@ -697,6 +699,8 @@ files:
697
699
  - app/services/spree/account/update.rb
698
700
  - app/services/spree/build_localized_redirect_url.rb
699
701
  - app/services/spree/cart/add_item.rb
702
+ - app/services/spree/cart/associate.rb
703
+ - app/services/spree/cart/change_currency.rb
700
704
  - app/services/spree/cart/create.rb
701
705
  - app/services/spree/cart/destroy.rb
702
706
  - app/services/spree/cart/empty.rb
@@ -722,7 +726,6 @@ files:
722
726
  - app/sorters/spree/products/sort.rb
723
727
  - app/validators/db_maximum_length_validator.rb
724
728
  - app/validators/email_validator.rb
725
- - app/views/spree/shared/_purchased_items_table.text.erb
726
729
  - config/initializers/active_storage.rb
727
730
  - config/initializers/friendly_id.rb
728
731
  - config/initializers/inflections.rb
@@ -1192,9 +1195,9 @@ licenses:
1192
1195
  - BSD-3-Clause
1193
1196
  metadata:
1194
1197
  bug_tracker_uri: https://github.com/spree/spree/issues
1195
- changelog_uri: https://github.com/spree/spree/releases/tag/v4.3.0.rc1
1196
- documentation_uri: https://guides.spreecommerce.org/
1197
- source_code_uri: https://github.com/spree/spree/tree/v4.3.0.rc1
1198
+ changelog_uri: https://github.com/spree/spree/releases/tag/v4.3.1
1199
+ documentation_uri: https://dev-docs.spreecommerce.org/
1200
+ source_code_uri: https://github.com/spree/spree/tree/v4.3.1
1198
1201
  post_install_message:
1199
1202
  rdoc_options: []
1200
1203
  require_paths:
@@ -1210,7 +1213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1210
1213
  - !ruby/object:Gem::Version
1211
1214
  version: 1.8.23
1212
1215
  requirements: []
1213
- rubygems_version: 3.2.3
1216
+ rubygems_version: 3.2.22
1214
1217
  signing_key:
1215
1218
  specification_version: 4
1216
1219
  summary: The bare bones necessary for Spree
@@ -1,25 +0,0 @@
1
- <% line_items.each do |item| %>
2
- <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) <%= Spree.t('at_symbol') %> <%= item.single_money %> = <%= item.display_amount %>
3
- <% end %>
4
- ============================================================
5
- <%= Spree.t('order_mailer.subtotal') %> <%= order.display_item_total %>
6
- <% if order.line_item_adjustments.exists? %>
7
- <% if order.all_adjustments.promotion.eligible.exists? %>
8
- <% order.all_adjustments.promotion.eligible.group_by(&:label).each do |label, adjustments| %>
9
- <%= Spree.t(:promotion) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: order.currency) %>
10
- <% end %>
11
- <% end %>
12
- <% end %>
13
-
14
- <% order.shipments.group_by { |s| s.selected_shipping_rate.try(:name) }.each do |name, shipments| %>
15
- <%= Spree.t(:shipping) %>: <%= name %> <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: order.currency) %>
16
- <% end %>
17
-
18
- <% if order.additional_tax_total != 0 %>
19
- <%= Spree.t(:tax) %>: <%= order.display_additional_tax_total.to_html %>
20
- <% end %>
21
-
22
- <% order.adjustments.eligible.each do |adjustment| %>
23
- <% next if (adjustment.source_type == 'Spree::TaxRate') and (adjustment.amount == 0) %>
24
- <%= adjustment.label %> <%= adjustment.display_amount %>
25
- <% end %>