spree_core 5.1.3 → 5.1.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4653db5977ca32eb547d2b03e5813e9c8fbd20d554c4affed62c6b1eb264204c
4
- data.tar.gz: 07f9f6dda2fa6f0a25266d6255db293aa82c97007d0fec0248f006fdc6e29573
3
+ metadata.gz: 470b6197277a6eb437ae9844a0616136625b77b7902073032d08b86315b41dc5
4
+ data.tar.gz: 9795041b28ea8b1680df00d885128f58f8502ec7ddf279887900b87218f0e999
5
5
  SHA512:
6
- metadata.gz: 958124d59a881d5c5acf8e0ed3c0bff113fefeac06bf72cd6da310628122f15bae784f321709d2f7ab0d75fd655621c669e0c3fc97a1b53841d803106fcffe81
7
- data.tar.gz: 022d0ac938c12b166d69d4a3e83b3e472da68b5a752079249288b99bcd490cfe3608853d3cd1313453ab21e89b42f130e57f80beb03aad079a2294892e877df0
6
+ metadata.gz: a314d8e138aea8643c18b6f33ab3d993bc49149a64e17427a904909c77b4ca951e147c9f292dc85d902e5b608e2c51cfabf6429c1c396353ca869d38da737c18
7
+ data.tar.gz: c47e0a2eb39b6cf9acda5efc0c1a8e3994cb3a49106fe9b6ff3d342523e0eb3b9c9b2316049b3122e7f0acadf1c51406cf4f652d31bef937408d5855f71fbc75
@@ -9,7 +9,7 @@ module Spree
9
9
  end
10
10
 
11
11
  def payment_sources
12
- credit_cards.with_payment_profile.not_expired.where(payment_method: Spree::PaymentMethod.active).not_removed
12
+ credit_cards.capturable.not_expired.where(payment_method: Spree::PaymentMethod.active).not_removed
13
13
  end
14
14
 
15
15
  def drop_payment_source(source)
@@ -39,6 +39,7 @@ module Spree
39
39
  end
40
40
 
41
41
  scope :with_payment_profile, -> { where.not(gateway_customer_profile_id: nil) }
42
+ scope :capturable, -> { where.not(gateway_customer_profile_id: nil).or(where.not(gateway_payment_profile_id: nil)) }
42
43
  scope :default, -> { where(default: true) }
43
44
  scope :not_expired, lambda {
44
45
  where('CAST(spree_credit_cards.year AS DECIMAL) > ?', Time.current.year).
@@ -50,6 +51,14 @@ module Spree
50
51
  # needed for some of the ActiveMerchant gateways (eg. SagePay)
51
52
  alias_attribute :brand, :cc_type
52
53
 
54
+ store_accessor :private_metadata, :wallet
55
+
56
+ # Returns the type of wallet the card is associated with, eg. "apple_pay", "google_pay", etc.
57
+ # @return [String]
58
+ def wallet_type
59
+ wallet&.[]('type')
60
+ end
61
+
53
62
  # ActiveMerchant::Billing::CreditCard added this accessor used by some gateways.
54
63
  # More info: https://github.com/spree/spree/issues/6209
55
64
  #
@@ -115,6 +124,8 @@ module Spree
115
124
  @verification_value = value.to_s.gsub(/\s/, '')
116
125
  end
117
126
 
127
+ # Returns the last 4 digits of the card number.
128
+ # @return [String], eg. "4338"
118
129
  def set_last_digits
119
130
  self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
120
131
  end
@@ -129,10 +140,13 @@ module Spree
129
140
  end
130
141
 
131
142
  # Show the card number, with all but last 4 numbers replace with "X". (XXXX-XXXX-XXXX-4338)
143
+ # @return [String]
132
144
  def display_number
133
145
  "XXXX-XXXX-XXXX-#{last_digits}"
134
146
  end
135
147
 
148
+ # Show the card brand, eg. "VISA", "MASTERCARD", etc.
149
+ # @return [String]
136
150
  def display_brand
137
151
  brand.present? ? brand.upcase : Spree.t(:no_cc_type)
138
152
  end
@@ -140,14 +154,21 @@ module Spree
140
154
  # ActiveMerchant needs first_name/last_name because we pass it a Spree::CreditCard and it calls those methods on it.
141
155
  # Looking at the ActiveMerchant source code we should probably be calling #to_active_merchant before passing
142
156
  # the object to ActiveMerchant but this should do for now.
157
+ #
158
+ # Returns the first name of the cardholder.
159
+ # @return [String]
143
160
  def first_name
144
161
  name.to_s.split(/[[:space:]]/, 2)[0]
145
162
  end
146
163
 
164
+ # Returns the last name of the cardholder.
165
+ # @return [String]
147
166
  def last_name
148
167
  name.to_s.split(/[[:space:]]/, 2)[1]
149
168
  end
150
169
 
170
+ # Returns an ActiveMerchant::Billing::CreditCard object.
171
+ # @return [ActiveMerchant::Billing::CreditCard]
151
172
  def to_active_merchant
152
173
  ActiveMerchant::Billing::CreditCard.new(
153
174
  number: number,
@@ -67,7 +67,7 @@ module Spree
67
67
 
68
68
  def sources_by_order(order)
69
69
  source_ids = order.payments.where(source_type: payment_source_class.to_s, payment_method_id: id).pluck(:source_id).uniq
70
- payment_source_class.where(id: source_ids).with_payment_profile
70
+ payment_source_class.where(id: source_ids).capturable
71
71
  end
72
72
 
73
73
  def reusable_sources(order)
@@ -75,7 +75,7 @@ module Spree
75
75
  sources_by_order order
76
76
  else
77
77
  if order.user_id
78
- credit_cards.where(user_id: order.user_id).with_payment_profile
78
+ credit_cards.where(user_id: order.user_id).capturable
79
79
  else
80
80
  []
81
81
  end
@@ -32,7 +32,7 @@ module Spree
32
32
  has_many :refunds, inverse_of: :payment
33
33
 
34
34
  validates :payment_method, presence: true
35
- validates :source, presence: true, if: -> { payment_method&.source_required? }
35
+ validates :source, presence: true, if: :source_required?
36
36
  validate :payment_method_available_for_order, on: :create
37
37
 
38
38
  before_validation :validate_source
@@ -50,6 +50,7 @@ module Spree
50
50
  after_destroy :update_order
51
51
 
52
52
  attr_accessor :source_attributes, :request_env, :capture_on_dispatch
53
+ attribute :skip_source_requirement, :boolean, default: false
53
54
 
54
55
  after_initialize :build_source
55
56
 
@@ -247,6 +248,12 @@ module Spree
247
248
  source_class.respond_to?(:display_name) ? source_class.display_name : source_class.name.demodulize.split(/(?=[A-Z])/).join(' ')
248
249
  end
249
250
 
251
+ def source_required?
252
+ return false if skip_source_requirement
253
+
254
+ payment_method&.source_required?
255
+ end
256
+
250
257
  private
251
258
 
252
259
  def set_amount
@@ -11,11 +11,15 @@ module Spree
11
11
  friendly_id :slug_candidates, use: [:history, :slugged, :scoped, :mobility], scope: spree_base_uniqueness_scope, slug_limit: 255
12
12
 
13
13
  Product::Translation.class_eval do
14
- before_save :set_slug
15
14
  acts_as_paranoid
16
15
  # deleted translation values also need to be accessible for index views listing deleted resources
17
16
  default_scope { unscope(where: :deleted_at) }
18
17
 
18
+ before_validation :set_slug
19
+ before_validation :ensure_slug_is_unique
20
+
21
+ validates :slug, presence: true, uniqueness: { allow_blank: true, case_sensitive: true, scope: [*::Spree.base_class.spree_base_uniqueness_scope, :locale] }
22
+
19
23
  private
20
24
 
21
25
  def set_slug
@@ -31,6 +35,11 @@ module Spree
31
35
  slug.to_url
32
36
  end
33
37
  end
38
+
39
+ def ensure_slug_is_unique
40
+ slug_exists = self.class.where(slug: slug, locale: locale).where.not(id: id).exists?
41
+ self.slug = [slug, SecureRandom.uuid].join('-') if slug_exists
42
+ end
34
43
  end
35
44
 
36
45
  before_validation :downcase_slug
@@ -57,8 +66,6 @@ module Spree
57
66
  def slug_candidates
58
67
  if defined?(:deleted_at) && deleted_at.present?
59
68
  [
60
- ['deleted', :name],
61
- ['deleted', :name, :sku],
62
69
  ['deleted', :name, :uuid_for_friendly_id]
63
70
  ]
64
71
  else
@@ -91,7 +98,7 @@ module Spree
91
98
  set_slug
92
99
  update_column(:slug, slug)
93
100
 
94
- new_slug = ->(rec) { "deleted-#{rec.id}_#{rec.slug}"[..254] }
101
+ new_slug = ->(rec) { "deleted-#{rec.slug}-#{uuid_for_friendly_id}"[..254] }
95
102
 
96
103
  translations.with_deleted.each { |rec| rec.update_columns(slug: new_slug.call(rec)) }
97
104
  slugs.with_deleted.each { |rec| rec.update_column(:slug, new_slug.call(rec)) }
@@ -33,6 +33,17 @@ module Spree
33
33
 
34
34
  self.whitelisted_ransackable_attributes = %w[amount zone_id tax_category_id included_in_price name]
35
35
 
36
+ # Virtual attribute for percentage display in admin forms
37
+ def amount_percentage
38
+ return nil if amount.nil?
39
+
40
+ (amount * 100).round(2)
41
+ end
42
+
43
+ def amount_percentage=(value)
44
+ self.amount = value.present? ? (value.to_f / 100) : nil
45
+ end
46
+
36
47
  # Gets the array of TaxRates appropriate for the specified tax zone
37
48
  def self.match(order_tax_zone)
38
49
  return [] unless order_tax_zone
@@ -10,7 +10,7 @@
10
10
 
11
11
  <% if spree.respond_to?(:admin_invitation_url) %>
12
12
  <p>
13
- <%= link_to Spree.t('invitation_mailer.invitation_email.link_text'), spree.admin_invitation_url(@invitation, token: @invitation.token, host: @invitation.store.formatted_url) %>
13
+ <%= link_to Spree.t(:accept), spree.admin_invitation_url(@invitation, token: @invitation.token, host: @invitation.store.formatted_url) %>
14
14
  </p>
15
15
  <% end %>
16
16
 
@@ -874,6 +874,7 @@ en:
874
874
  current: Current
875
875
  current_password: Current password
876
876
  custom_code: Custom code
877
+ custom_domains: Custom domains
877
878
  custom_font_code: Custom font code
878
879
  customer: Customer
879
880
  customer_details: Customer Details
@@ -1854,6 +1855,7 @@ en:
1854
1855
  select_stores: Select Store(s)
1855
1856
  select_user: Select customer
1856
1857
  selected_quantity_not_available: selected of %{item} is not available.
1858
+ send_consumer_transactional_emails: Send Customer Transactional Emails
1857
1859
  send_copy_of_all_mails_to: Send Copy of All Mails To
1858
1860
  send_mails_as: Send Mails As
1859
1861
  send_message: Send message
@@ -39,6 +39,13 @@ module Spree
39
39
  opts[:skip_test] = true
40
40
  opts[:skip_bootsnap] = true
41
41
  opts[:skip_asset_pipeline] = true # skip installing propshaft, we're still using sprockets as a dependency
42
+ opts[:skip_docker] = true
43
+ opts[:skip_rubocop] = true
44
+ opts[:skip_brakeman] = true
45
+ opts[:skip_ci] = true
46
+ opts[:skip_kamal] = true
47
+ opts[:skip_devcontainer] = true
48
+ opts[:skip_solid] = true
42
49
 
43
50
  puts 'Generating dummy Rails application...'
44
51
  invoke Rails::Generators::AppGenerator,
@@ -73,46 +80,11 @@ module Spree
73
80
  end
74
81
  end
75
82
 
76
- def test_dummy_clean
77
- inside dummy_path do
78
- remove_file '.gitignore'
79
- remove_file 'doc'
80
- remove_file 'Gemfile'
81
- remove_file 'lib/tasks'
82
- remove_file 'app/assets/images/rails.png'
83
- remove_file 'app/assets/javascripts/application.js'
84
- remove_file 'public/index.html'
85
- remove_file 'public/robots.txt'
86
- remove_file 'README'
87
- remove_file 'test'
88
- remove_file 'vendor'
89
- remove_file 'spec'
90
- end
91
- end
92
-
93
- def inject_content_security_policy
94
- inside dummy_path do
95
- inject_into_file 'config/initializers/content_security_policy.rb', %Q[
96
- p.script_src :self, :https, :unsafe_inline, :http, :unsafe_eval
97
- ], before: /^end/, verbose: true
98
- end
99
- end
100
-
101
83
  attr_reader :lib_name
102
84
  attr_reader :database
103
85
 
104
86
  protected
105
87
 
106
- def inject_require_for(requirement)
107
- inject_into_file 'config/application.rb', %Q[
108
- begin
109
- require '#{requirement}'
110
- rescue LoadError
111
- # #{requirement} is not available.
112
- end
113
- ], before: /require '#{@lib_name}'/, verbose: true
114
- end
115
-
116
88
  def inject_yaml_permitted_classes
117
89
  inside dummy_path do
118
90
  inject_into_file 'config/application.rb', %Q[
@@ -140,10 +112,6 @@ end
140
112
  end
141
113
  alias store_application_definition! application_definition
142
114
 
143
- def camelized
144
- @camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize
145
- end
146
-
147
115
  def remove_directory_if_exists(path)
148
116
  remove_dir(path) if File.directory?(path)
149
117
  end
@@ -46,24 +46,6 @@ module Spree
46
46
  template 'config/initializers/spree.rb', 'config/initializers/spree.rb'
47
47
  end
48
48
 
49
- def additional_tweaks
50
- return unless File.exist? 'public/robots.txt'
51
-
52
- append_file 'public/robots.txt', <<-ROBOTS.strip_heredoc
53
- User-agent: *
54
- Disallow: /checkout
55
- Disallow: /cart
56
- Disallow: /orders
57
- Disallow: /user
58
- Disallow: /account
59
- Disallow: /api
60
- Disallow: /password
61
- Disallow: /api_tokens
62
- Disallow: /cart_link
63
- Disallow: /account_link
64
- ROBOTS
65
- end
66
-
67
49
  # Currently we only support devise, in the future we will also add support for default Rails authentication
68
50
  def install_authentication
69
51
  if @authentication == 'devise'
@@ -121,7 +103,6 @@ module Spree
121
103
  say_status :copying, 'migrations'
122
104
  silence_stream(STDOUT) do
123
105
  silence_warnings { rake 'active_storage:install:migrations' }
124
- silence_warnings { rake 'action_mailbox:install:migrations' }
125
106
  silence_warnings { rake 'action_text:install:migrations' }
126
107
  silence_warnings { rake 'spree:install:migrations' }
127
108
  silence_warnings { rake 'spree_api:install:migrations' }
@@ -54,7 +54,7 @@ module Spree
54
54
  preference :require_master_price, :boolean, default: false
55
55
  preference :restock_inventory, :boolean, default: true # Determines if a return item is restocked automatically once it has been received
56
56
  preference :return_eligibility_number_of_days, :integer, default: 365
57
- preference :send_core_emails, :boolean, default: true # Default mail headers settings
57
+ preference :send_core_emails, :boolean, default: true, deprecated: true # Default mail headers settings
58
58
  preference :shipping_instructions, :boolean, deprecated: true
59
59
  preference :show_only_complete_orders_by_default, :boolean, deprecated: true
60
60
  preference :show_variant_full_price, :boolean, default: false # Displays variant full price or difference with product price. Default false to be compatible with older behavior
@@ -299,7 +299,7 @@ module Spree
299
299
  end
300
300
 
301
301
  initializer 'spree.core.checking_migrations' do
302
- Migrations.new(config, engine_name).check
302
+ Migrations.new(config, engine_name).check unless Rails.env.test?
303
303
  end
304
304
 
305
305
  initializer 'spree.core.assets' do |app|
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.1.3'.freeze
2
+ VERSION = '5.1.5'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/spree/core.rb CHANGED
@@ -91,7 +91,7 @@ module Spree
91
91
  end
92
92
 
93
93
  def self.root_domain
94
- @@root_domain ||= Rails.application.routes.default_url_options[:host] || 'lvh.me'
94
+ @@root_domain
95
95
  end
96
96
 
97
97
  def self.queues
@@ -257,11 +257,11 @@ module Spree
257
257
 
258
258
  @@store_credit_category_attributes = [:name]
259
259
 
260
- @@taxonomy_attributes = [:name]
260
+ @@taxonomy_attributes = [:name, :position]
261
261
 
262
262
  @@tax_category_attributes = [:name, :tax_code,:description, :is_default]
263
263
 
264
- @@tax_rate_attributes = [:name, :amount, :zone_id, :tax_category_id, :included_in_price, :show_rate_in_label, :calculator_type, calculator_attributes: {}]
264
+ @@tax_rate_attributes = [:name, :amount, :amount_percentage, :zone_id, :tax_category_id, :included_in_price, :show_rate_in_label, :calculator_type, calculator_attributes: {}]
265
265
 
266
266
  @@taxon_attributes = [
267
267
  :name, :parent_id, :position, :icon, :description, :permalink, :hide_from_nav,
@@ -21,16 +21,16 @@ namespace :common do
21
21
 
22
22
  skip_javascript = ['spree/api', 'spree/core', 'spree/sample', 'spree/emails'].include?(ENV['LIB_NAME'])
23
23
 
24
- Spree::DummyGenerator.start [
25
- "--lib_name=#{ENV['LIB_NAME']}",
26
- "--skip_javascript=#{skip_javascript}"
24
+ dummy_app_args = [
25
+ "--lib_name=#{ENV['LIB_NAME']}"
27
26
  ]
27
+ if skip_javascript
28
+ dummy_app_args << '--skip_javascript'
29
+ end
30
+ Spree::DummyGenerator.start dummy_app_args
28
31
 
29
- # install frontend libraries
30
32
  unless skip_javascript
31
- system('bin/rails importmap:install')
32
- system('bin/rails turbo:install')
33
- system('bin/rails stimulus:install')
33
+ system('bin/rails importmap:install turbo:install stimulus:install')
34
34
  end
35
35
 
36
36
  # install devise if it's not the legacy user, useful for testing storefront
@@ -52,21 +52,39 @@ namespace :common do
52
52
  "--authentication=#{args[:authentication]}"
53
53
  ]
54
54
 
55
- puts 'Setting up dummy database...'
56
- system('bin/rails db:environment:set RAILS_ENV=test > /dev/null 2>&1')
57
- system('bundle exec rake db:drop db:create > /dev/null 2>&1')
58
- Spree::DummyModelGenerator.start
59
- system('bundle exec rake db:migrate > /dev/null 2>&1')
55
+ if !skip_javascript || ENV['LIB_NAME'] == 'spree/emails'
56
+ puts 'Precompiling assets...'
57
+ system('bundle exec rake assets:precompile > /dev/null 2>&1')
58
+ end
59
+
60
+ unless ENV['NO_MIGRATE']
61
+ puts 'Setting up dummy database...'
62
+ system('bin/rails db:environment:set RAILS_ENV=test > /dev/null 2>&1')
63
+ system('bundle exec rake db:drop db:create > /dev/null 2>&1')
64
+ Spree::DummyModelGenerator.start
65
+ system('bundle exec rake db:migrate > /dev/null 2>&1')
66
+ end
60
67
 
61
68
  begin
62
69
  require "generators/#{ENV['LIB_NAME']}/install/install_generator"
63
70
  puts 'Running extension installation generator...'
64
- "#{ENV['LIB_NAME'].camelize}::Generators::InstallGenerator".constantize.start(['--auto-run-migrations'])
71
+
72
+ if ENV['NO_MIGRATE']
73
+ "#{ENV['LIB_NAME'].camelize}::Generators::InstallGenerator".constantize.start([])
74
+ else
75
+ "#{ENV['LIB_NAME'].camelize}::Generators::InstallGenerator".constantize.start(['--auto-run-migrations'])
76
+ end
65
77
  rescue LoadError
66
78
  puts 'Skipping installation no generator to run...'
67
79
  end
80
+ end
68
81
 
69
- system('bundle exec rake assets:precompile > /dev/null 2>&1') if !skip_javascript || ENV['LIB_NAME'] == 'spree/emails'
82
+ task :db_setup do |_t|
83
+ puts 'Setting up dummy database...'
84
+ system('bin/rails db:environment:set RAILS_ENV=test > /dev/null 2>&1')
85
+ system('bundle exec rake db:drop db:create > /dev/null 2>&1')
86
+ Spree::DummyModelGenerator.start
87
+ system('bundle exec rake db:migrate > /dev/null 2>&1')
70
88
  end
71
89
 
72
90
  task :seed do |_t|
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: 5.1.3
4
+ version: 5.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2025-07-20 00:00:00.000000000 Z
13
+ date: 2025-08-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n-tasks
@@ -1382,9 +1382,9 @@ licenses:
1382
1382
  - BSD-3-Clause
1383
1383
  metadata:
1384
1384
  bug_tracker_uri: https://github.com/spree/spree/issues
1385
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.1.3
1385
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.1.5
1386
1386
  documentation_uri: https://docs.spreecommerce.org/
1387
- source_code_uri: https://github.com/spree/spree/tree/v5.1.3
1387
+ source_code_uri: https://github.com/spree/spree/tree/v5.1.5
1388
1388
  post_install_message:
1389
1389
  rdoc_options: []
1390
1390
  require_paths: