spree_core 5.4.0.beta3 → 5.4.0.beta4

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: ea9fcfc9e4ac5937296c789f151d5d431b60a2fadb131e5a7bca034cae47b2ab
4
- data.tar.gz: e89e3fd21bf14b0bb0bda0de42c0ecc49d173add3d9ce58c29b3c781eafb55fa
3
+ metadata.gz: 29b95e3e3222214cdeef8094a1a9553636c875d88857244241431196d06399d2
4
+ data.tar.gz: 93c9f4d387fce7ba5e2e9f4b6454f9c46f1b8a15cfe180a16739c17bf76a5125
5
5
  SHA512:
6
- metadata.gz: b9bd748be9f9b4949d9b71887990fe3ac2377a6f4e7ed3d3060b0be073d6c199bd92a633bb2c62e7b8ee13dc0d7dae07e1bedd3d4d9dc5edebf3e4fc3839cae7
7
- data.tar.gz: 8f8639ca080722372b01f91df71e54e0dc3f8f39d82cdbd1b3ad72103d995dbaf9bc50ef0f2a6032b0d400c2e94387edd2e06f64a52f3932391ed49ee5650014
6
+ metadata.gz: 5c8a23c3edd35dacb0493c5435e28d114fc2611b4b25a0d85b4b5e4579f38402fb61a3a65a9f3b361fae8f67d5f58ac73984b6ab2cd4bcbb0326813589a18330
7
+ data.tar.gz: 1fabd1160061e412e2e9a4a1e92083686938a5313c3b29193e410bc07d6118d60af4bd7c2951794273c20f8b053f1dfc5acba8dfe748a722f5895aacc17d33d9
@@ -4,7 +4,7 @@ module Spree
4
4
  queue_as Spree.queues.exports
5
5
 
6
6
  def perform(export_id)
7
- export = Spree::Export.find(export_id)
7
+ export = Spree::Export.find_by_prefix_id!(export_id)
8
8
  export.generate
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Spree
4
4
  queue_as Spree.queues.reports
5
5
 
6
6
  def perform(report_id)
7
- report = Spree::Report.find(report_id)
7
+ report = Spree::Report.find_by_prefix_id!(report_id)
8
8
  report.generate
9
9
  end
10
10
  end
@@ -91,6 +91,14 @@ module Spree
91
91
  joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable)
92
92
  end
93
93
  end
94
+
95
+ add_search_scope :price_lte do |price|
96
+ where(Price.table_name => { amount: ..price })
97
+ end
98
+
99
+ add_search_scope :price_gte do |price|
100
+ where(Price.table_name => { amount: price.. })
101
+ end
94
102
  search_scopes << :in_stock
95
103
 
96
104
  def self.out_of_stock(out_of_stock = true)
@@ -260,7 +268,7 @@ module Spree
260
268
 
261
269
  def self.not_discontinued(only_not_discontinued = true)
262
270
  if only_not_discontinued != '0' && only_not_discontinued
263
- where(discontinue_on: [nil, Time.current..])
271
+ where(discontinue_on: [nil, Time.current.beginning_of_minute..])
264
272
  else
265
273
  all
266
274
  end
@@ -278,7 +286,10 @@ module Spree
278
286
  # Can't use add_search_scope for this as it needs a default argument
279
287
  def self.available(available_on = nil, currency = nil)
280
288
  scope = not_discontinued.where(status: 'active')
281
- scope = scope.where("#{Product.quoted_table_name}.available_on <= ?", available_on) if available_on
289
+ if available_on
290
+ available_on = available_on.beginning_of_minute if available_on.respond_to?(:beginning_of_minute)
291
+ scope = scope.where("#{Product.quoted_table_name}.available_on <= ?", available_on)
292
+ end
282
293
 
283
294
  unless Spree::Config.show_products_without_price
284
295
  currency ||= Spree::Store.default.default_currency
@@ -27,8 +27,6 @@ module Spree
27
27
  # those attributes depending of the logic of their applications
28
28
  ADDRESS_FIELDS = %w(firstname lastname company address1 address2 city state zipcode country phone)
29
29
  EXCLUDED_KEYS_FOR_COMPARISON = %w(id updated_at created_at deleted_at label user_id public_metadata private_metadata)
30
- FIELDS_TO_NORMALIZE = %w(firstname lastname phone alternative_phone company address1 address2 city zipcode)
31
-
32
30
  if defined?(Spree::Security::Addresses)
33
31
  include Spree::Security::Addresses
34
32
  end
@@ -53,7 +51,6 @@ module Spree
53
51
  before_validation :normalize_country
54
52
  before_validation :normalize_state
55
53
  before_validation :clear_invalid_state_entities, if: -> { country.present? }, on: :update
56
- before_validation :remove_emoji_and_normalize
57
54
 
58
55
  after_create :set_user_attributes, if: -> { user.present? }
59
56
 
@@ -294,17 +291,6 @@ module Spree
294
291
  end
295
292
  end
296
293
 
297
- def remove_emoji_and_normalize
298
- attributes_to_normalize = attributes.slice(*FIELDS_TO_NORMALIZE)
299
- normalized_attributes = attributes_to_normalize.compact_blank.deep_transform_values do |value|
300
- NormalizeString.remove_emoji_and_normalize(value.to_s).strip
301
- end
302
-
303
- normalized_attributes.transform_keys! { |key| key.gsub('original_', '') } if defined?(Spree::Security::Addresses)
304
-
305
- assign_attributes(normalized_attributes)
306
- end
307
-
308
294
  def set_user_attributes
309
295
  if user.name.blank?
310
296
  user.first_name = firstname
@@ -38,29 +38,8 @@ module Spree
38
38
  iso.upcase.chars.map { |c| (c.ord + 127397).chr(Encoding::UTF_8) }.join
39
39
  end
40
40
 
41
- # Returns the currency for this country from its market in the current store.
42
- # Looks up which market contains this country and returns that market's currency.
43
- #
44
- # @return [String, nil] currency code (e.g., 'USD', 'EUR') or nil if no market found
45
- def market_currency
46
- current_market&.currency
47
- end
48
-
49
- # Returns the default locale for this country from its market in the current store.
50
- # Looks up which market contains this country and returns that market's default locale.
51
- #
52
- # @return [String, nil] locale code (e.g., 'en', 'de') or nil if no market found
53
- def market_locale
54
- current_market&.default_locale
55
- end
56
-
57
- # Returns the supported locales for this country from its market in the current store.
58
- #
59
- # @return [Array<String>] locale codes (e.g., ['en', 'fr']) or empty array if no market found
60
- def market_supported_locales
61
- current_market&.supported_locales_list || []
62
- end
63
-
41
+ # Lookups Market for Country for the current Store
42
+ # @return [Spree::Market, nil]
64
43
  def current_market
65
44
  @current_market ||= Spree::Current.store&.market_for_country(self)
66
45
  end
@@ -19,7 +19,8 @@ module Spree
19
19
  validates :code, presence: true, uniqueness: { scope: spree_base_uniqueness_scope, conditions: -> { where(deleted_at: nil) } }
20
20
  validates :state, :promotion, presence: true
21
21
 
22
- self.whitelisted_ransackable_attributes = %w[state code]
22
+ self.whitelisted_ransackable_attributes = %w[state code promotion_id]
23
+ self.whitelisted_ransackable_associations = %w[promotion]
23
24
 
24
25
  def self.used?(code)
25
26
  used_with_code(code).any?
@@ -36,5 +37,9 @@ module Spree
36
37
  def display_code
37
38
  code.upcase
38
39
  end
40
+
41
+ def to_csv(_store = nil)
42
+ Spree::CSV::CouponCodePresenter.new(self).call
43
+ end
39
44
  end
40
45
  end
@@ -34,10 +34,11 @@ module Spree
34
34
  super || market&.default_locale || store&.default_locale
35
35
  end
36
36
 
37
- # Returns the current tax zone, falling back to the default tax zone.
37
+ # Returns the current tax zone.
38
+ # Fallback: market's tax zone (from default country) -> global default tax zone.
38
39
  # @return [Spree::Zone, nil]
39
40
  def zone
40
- super || default_tax_zone
41
+ super || market&.tax_zone || default_tax_zone
41
42
  end
42
43
 
43
44
  # Returns the default tax zone (memoized per request).
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module Exports
3
+ class CouponCodes < Spree::Export
4
+ def csv_headers
5
+ Spree::CSV::CouponCodePresenter::HEADERS
6
+ end
7
+
8
+ def scope_includes
9
+ [:promotion, :order]
10
+ end
11
+
12
+ def scope
13
+ model_class.joins(promotion: :stores).where(spree_stores: { id: store.id })
14
+ .accessible_by(current_ability)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -64,6 +64,14 @@ module Spree
64
64
  countries.order(:name).first
65
65
  end
66
66
 
67
+ # Returns the tax zone matching this market's default country.
68
+ # Used by Spree::Current to determine the browsing tax zone before a customer enters an address.
69
+ #
70
+ # @return [Spree::Zone, nil]
71
+ def tax_zone
72
+ @tax_zone ||= Spree::Zone.match(default_country)
73
+ end
74
+
67
75
  # Returns supported locales as an array, always including default_locale
68
76
  #
69
77
  # @return [Array<String>]
@@ -220,7 +220,9 @@ module Spree
220
220
  self.whitelisted_ransackable_associations = %w[taxons stores variants_including_master master variants tags labels
221
221
  shipping_category classifications option_types]
222
222
  self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon price_between
223
+ price_lte price_gte
223
224
  multi_search in_stock out_of_stock with_option_value_ids
225
+
224
226
  ascend_by_price descend_by_price]
225
227
 
226
228
  [
@@ -5,16 +5,16 @@ module Spree
5
5
  has_prefix_id :txn # Spree-specific: taxon
6
6
 
7
7
  RULES_MATCH_POLICIES = %w[all any].freeze
8
- SORT_ORDERS = %w[
9
- manual
10
- best-selling
11
- name-a-z
12
- name-z-a
13
- price-high-to-low
14
- price-low-to-high
15
- newest-first
16
- oldest-first
17
- ]
8
+ SORT_ORDERS = [
9
+ 'manual',
10
+ 'best_selling',
11
+ 'price asc',
12
+ 'price desc',
13
+ 'available_on desc',
14
+ 'available_on asc',
15
+ 'name asc',
16
+ 'name desc'
17
+ ].freeze
18
18
 
19
19
  include Spree::TranslatableResource
20
20
  include Spree::TranslatableResourceSlug
@@ -103,7 +103,7 @@ module Spree
103
103
  scope :not_discontinued, lambda {
104
104
  where(
105
105
  arel_table[:discontinue_on].eq(nil).or(
106
- arel_table[:discontinue_on].gteq(Time.current)
106
+ arel_table[:discontinue_on].gteq(Time.current.beginning_of_minute)
107
107
  )
108
108
  )
109
109
  }
@@ -52,15 +52,27 @@ module Spree
52
52
 
53
53
  # Returns the matching zone with the highest priority zone type (State, Country, Zone.)
54
54
  # Returns nil in the case of no matches.
55
- def self.match(address)
56
- return unless address &&
57
- matches = includes(:zone_members).
58
- order('spree_zones.zone_members_count', 'spree_zones.created_at').
59
- where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " \
60
- 'spree_zone_members.zoneable_id = ?) OR ' \
61
- "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
62
- 'spree_zone_members.zoneable_id = ?)', address.country_id, address.state_id).
63
- references(:zones)
55
+ # Accepts either an address (with country_id/state_id) or a Spree::Country directly.
56
+ def self.match(address_or_country)
57
+ return unless address_or_country
58
+
59
+ if address_or_country.is_a?(Spree::Country)
60
+ country_id = address_or_country.id
61
+ state_id = nil
62
+ else
63
+ country_id = address_or_country.country_id
64
+ state_id = address_or_country.state_id
65
+ end
66
+
67
+ matches = includes(:zone_members).
68
+ order('spree_zones.zone_members_count', 'spree_zones.created_at').
69
+ where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " \
70
+ 'spree_zone_members.zoneable_id = ?) OR ' \
71
+ "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
72
+ 'spree_zone_members.zoneable_id = ?)', country_id, state_id).
73
+ references(:zones)
74
+
75
+ return if matches.empty?
64
76
 
65
77
  %w[state country].each do |zone_kind|
66
78
  if match = matches.detect { |zone| zone_kind == zone.kind }
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module CSV
3
+ class CouponCodePresenter
4
+ HEADERS = [
5
+ 'Code',
6
+ 'State',
7
+ 'Promotion Name',
8
+ 'Order Number',
9
+ 'Created At',
10
+ 'Updated At'
11
+ ].freeze
12
+
13
+ def initialize(coupon_code)
14
+ @coupon_code = coupon_code
15
+ end
16
+
17
+ attr_accessor :coupon_code
18
+
19
+ def call
20
+ [
21
+ coupon_code.display_code,
22
+ coupon_code.state,
23
+ coupon_code.promotion&.name,
24
+ coupon_code.order&.number,
25
+ coupon_code.created_at&.strftime('%Y-%m-%d %H:%M:%S'),
26
+ coupon_code.updated_at&.strftime('%Y-%m-%d %H:%M:%S')
27
+ ]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -180,7 +180,8 @@ module Spree
180
180
  Spree::Exports::Orders,
181
181
  Spree::Exports::Customers,
182
182
  Spree::Exports::GiftCards,
183
- Spree::Exports::NewsletterSubscribers
183
+ Spree::Exports::NewsletterSubscribers,
184
+ Spree::Exports::CouponCodes
184
185
  ]
185
186
 
186
187
  Rails.application.config.spree.import_types = [
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.4.0.beta3'.freeze
2
+ VERSION = '5.4.0.beta4'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/spree_core.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'friendly_id/paranoia'
2
2
  require 'friendly_id/history_decorator'
3
3
  require 'mobility/plugins/store_based_fallbacks'
4
- require 'normalize_string'
5
4
 
6
5
  require 'spree/core'
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.4.0.beta3
4
+ version: 5.4.0.beta4
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: 2026-03-02 00:00:00.000000000 Z
13
+ date: 2026-03-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n-tasks
@@ -490,20 +490,6 @@ dependencies:
490
490
  - - ">="
491
491
  - !ruby/object:Gem::Version
492
492
  version: '0'
493
- - !ruby/object:Gem::Dependency
494
- name: any_ascii
495
- requirement: !ruby/object:Gem::Requirement
496
- requirements:
497
- - - "~>"
498
- - !ruby/object:Gem::Version
499
- version: 0.3.2
500
- type: :runtime
501
- prerelease: false
502
- version_requirements: !ruby/object:Gem::Requirement
503
- requirements:
504
- - - "~>"
505
- - !ruby/object:Gem::Version
506
- version: 0.3.2
507
493
  - !ruby/object:Gem::Dependency
508
494
  name: safely_block
509
495
  requirement: !ruby/object:Gem::Requirement
@@ -935,6 +921,7 @@ files:
935
921
  - app/models/spree/event.rb
936
922
  - app/models/spree/exchange.rb
937
923
  - app/models/spree/export.rb
924
+ - app/models/spree/exports/coupon_codes.rb
938
925
  - app/models/spree/exports/customers.rb
939
926
  - app/models/spree/exports/gift_cards.rb
940
927
  - app/models/spree/exports/newsletter_subscribers.rb
@@ -1165,6 +1152,7 @@ files:
1165
1152
  - app/models/spree/zone.rb
1166
1153
  - app/models/spree/zone_member.rb
1167
1154
  - app/paginators/spree/shared/paginate.rb
1155
+ - app/presenters/spree/csv/coupon_code_presenter.rb
1168
1156
  - app/presenters/spree/csv/customer_presenter.rb
1169
1157
  - app/presenters/spree/csv/gift_card_presenter.rb
1170
1158
  - app/presenters/spree/csv/metafields_helper.rb
@@ -1463,7 +1451,6 @@ files:
1463
1451
  - lib/generators/spree/model_decorator/model_decorator_generator.rb
1464
1452
  - lib/generators/spree/model_decorator/templates/model_decorator.rb.tt
1465
1453
  - lib/mobility/plugins/store_based_fallbacks.rb
1466
- - lib/normalize_string.rb
1467
1454
  - lib/spree/analytics.rb
1468
1455
  - lib/spree/core.rb
1469
1456
  - lib/spree/core/components.rb
@@ -1653,9 +1640,9 @@ licenses:
1653
1640
  - BSD-3-Clause
1654
1641
  metadata:
1655
1642
  bug_tracker_uri: https://github.com/spree/spree/issues
1656
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta3
1643
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta4
1657
1644
  documentation_uri: https://docs.spreecommerce.org/
1658
- source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta3
1645
+ source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta4
1659
1646
  post_install_message:
1660
1647
  rdoc_options: []
1661
1648
  require_paths:
@@ -1,18 +0,0 @@
1
- require 'any_ascii'
2
-
3
- module NormalizeString
4
- def self.normalize(string)
5
- return unless string.present?
6
-
7
- AnyAscii.transliterate(string)
8
- end
9
-
10
- def self.remove_emoji_and_normalize(string, keep_emoji_when_empty: false)
11
- return unless string.present?
12
-
13
- result = AnyAscii.transliterate(string.gsub(/\p{So}/, ''))
14
- return result if result.present? || !keep_emoji_when_empty
15
-
16
- normalize(string)
17
- end
18
- end