spree_core 4.3.0.rc1 → 4.3.1

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