spree_core 5.5.0.rc2 → 5.5.0.rc3

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: 7d9453fadded9ac66f0b64ac5af7ca30a78516088edafa569cd18bcf0b541e1f
4
- data.tar.gz: 72af3c8a53371f288c2084d2eed1e1f6635d0f2273bcb49f40adc626fe96d4c2
3
+ metadata.gz: 710ad2cd963e80f3cd21d2b481246a8a163dc2033fca523292f33ec17fd86525
4
+ data.tar.gz: c59f53d88277a62e55d327cd99c025e8808b30ecf1392ff3ccc9a98ff7cd9026
5
5
  SHA512:
6
- metadata.gz: 073262b0c991fdbae6c780957052d84ee297abfc6b438b6b1476bc301fc69af8c67ed9904a89e080a14309ac26b86352ad4627f934c0b972c649d520f105c936
7
- data.tar.gz: 4c581530147dac6e5b3ea79c655a9c739f40f6ce10b55adcdd5abe46d02073a96268e27a64790cc93c607dfe60cc3aef2c62ded74ad4daa3635135ce21847293
6
+ metadata.gz: 67c2f7227e1b20972b41b4dbfceeb2a00cb8c7c21cc0b295fe907d496a581b59b9eb919b41f6a89f6d7f4b19d5c5c6a405733b003915c8e8c6fb4811c15f43a6
7
+ data.tar.gz: 6355791ca10c5e668752f27b3ff7918b3f4ea27bad3b0308f597a310ec518cf139c93d6f562e2da55938524dc381f5a759b4618bc19ad8d684cb7a30841bf821
@@ -12,7 +12,10 @@ module Spree
12
12
 
13
13
  def set_default_store
14
14
  return if disable_store_presence_validation?
15
- return if stores.any?
15
+ # records built through a store's association (`store.payment_methods.build`)
16
+ # carry their link on the join association — `stores` can't see it until save
17
+ join_association = self.class.reflect_on_association(:stores).through_reflection.name
18
+ return if stores.any? || send(join_association).any?
16
19
 
17
20
  stores << Spree::Store.default
18
21
  end
@@ -54,7 +54,14 @@ module Spree
54
54
  def reconcile_typed_association(association, rows)
55
55
  collection = public_send(association)
56
56
  kept_ids = rows.filter_map { |row| save_typed_association_row(collection, row) }
57
- collection.where.not(id: kept_ids).destroy_all if kept_ids.any? || rows.empty?
57
+ if kept_ids.any? || rows.empty?
58
+ collection.where.not(id: kept_ids).destroy_all
59
+ # `where.not(...).destroy_all` deletes the dropped rows from the DB but
60
+ # leaves them in the already-loaded association target. Reset so a
61
+ # same-request read (e.g. the serializer rendering `rule_ids`) reflects
62
+ # the removal instead of echoing ghosts.
63
+ collection.reset
64
+ end
58
65
  end
59
66
 
60
67
  def save_typed_association_row(collection, row)
@@ -44,15 +44,18 @@ module Spree
44
44
  activate_permission_sets(permission_sets)
45
45
  end
46
46
 
47
- # Determines the role names for the current user.
47
+ # Determines the role names for the current user, scoped to the current
48
+ # store. A +Spree::RoleUser+ binds a role to a store via +resource+, so a
49
+ # role held on one store does not apply on another.
48
50
  #
49
51
  # @return [Array<Symbol>] the role names
50
52
  def determine_role_names
51
53
  return [:default] unless @user.persisted?
52
54
 
53
- # First, try to get roles from the spree_roles association
54
- if @user.respond_to?(:spree_roles)
55
- role_names = @user.spree_roles.pluck(:name).map(&:to_sym)
55
+ if @user.respond_to?(:role_users)
56
+ role_names = @user.role_users.where(resource: @store).
57
+ joins(:role).
58
+ pluck("#{Spree::Role.table_name}.name").map(&:to_sym)
56
59
  return role_names if role_names.any?
57
60
  end
58
61
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Spree
4
4
  class AllowedOrigin < Spree.base_class
5
+ # Loopback/development hosts that match any port, so storing `http://localhost`
6
+ # keeps matching `http://localhost:3000`, `:4000`, etc.
7
+ LOOPBACK_HOSTS = %w[localhost 127.0.0.1 ::1 0.0.0.0].freeze
8
+
5
9
  has_prefix_id :ao
6
10
 
7
11
  include Spree::SingleStoreResource
@@ -12,6 +16,42 @@ module Spree
12
16
  validates :origin, uniqueness: { scope: [:store_id, *spree_base_uniqueness_scope] }
13
17
  validate :origin_must_be_valid_http_url
14
18
 
19
+ # Parses a URL into its comparable origin components, or nil when the URL is
20
+ # invalid or not http(s). The host is downcased and has a single trailing dot
21
+ # stripped, and the port is the URI default (80/443) when not explicitly given.
22
+ #
23
+ # @param url [String] the URL to parse
24
+ # @return [Hash, nil] `{ scheme:, host:, port: }` or nil
25
+ def self.parse_origin(url)
26
+ uri = URI.parse(url.to_s)
27
+ return nil unless uri.is_a?(URI::HTTP)
28
+ return nil if uri.host.blank?
29
+
30
+ { scheme: uri.scheme.downcase, host: uri.host.downcase.chomp('.'), port: uri.port }
31
+ rescue URI::InvalidURIError
32
+ nil
33
+ end
34
+
35
+ # Returns true if the given URL's origin matches this stored origin.
36
+ #
37
+ # Scheme and host must match exactly (host comparison is case- and trailing-dot-
38
+ # insensitive). Port must also match, with the scheme default applied, so storing
39
+ # `https://shop.com` matches `https://shop.com:443`. Loopback/development hosts
40
+ # ({LOOPBACK_HOSTS}) are exempt from the port check, so `http://localhost` still
41
+ # matches `http://localhost:3000`, `:4000`, etc.
42
+ #
43
+ # @param url [String] the candidate URL to check
44
+ # @return [Boolean]
45
+ def matches?(url)
46
+ candidate = self.class.parse_origin(url)
47
+ allowed = self.class.parse_origin(origin)
48
+ return false if candidate.nil? || allowed.nil?
49
+ return false unless allowed[:scheme] == candidate[:scheme]
50
+ return false unless allowed[:host] == candidate[:host]
51
+
52
+ LOOPBACK_HOSTS.include?(allowed[:host]) || allowed[:port] == candidate[:port]
53
+ end
54
+
15
55
  private
16
56
 
17
57
  def origin_must_be_valid_http_url
@@ -19,12 +59,7 @@ module Spree
19
59
 
20
60
  uri = URI.parse(origin)
21
61
 
22
- unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
23
- errors.add(:origin, :invalid)
24
- return
25
- end
26
-
27
- if uri.host.blank?
62
+ if self.class.parse_origin(origin).nil?
28
63
  errors.add(:origin, :invalid)
29
64
  return
30
65
  end
@@ -57,6 +57,13 @@ module Spree
57
57
  validates :store, presence: true
58
58
  validates :scopes, presence: true, if: -> { secret? && scopes_enforceable? }
59
59
  validate :validate_known_scopes, if: -> { secret? && scopes_enforceable? }
60
+ # Scopes are fixed for the life of the secret — mirroring Stripe/GitHub/AWS:
61
+ # authority is changed by issuing a new key and revoking the old one, never by
62
+ # re-scoping an existing secret in place. This keeps the audit boundary clean
63
+ # and prevents a shared/leaked key from silently gaining write access. The
64
+ # guard is at the model so every entry point (API, legacy admin, rake tasks)
65
+ # is covered uniformly.
66
+ validate :scopes_immutable, if: -> { secret? && persisted? && scopes_changed? }
60
67
 
61
68
  before_validation :generate_token, on: :create
62
69
 
@@ -145,6 +152,10 @@ module Spree
145
152
  errors.add(:scopes, "contains unknown scopes: #{invalid.join(', ')}") if invalid.any?
146
153
  end
147
154
 
155
+ def scopes_immutable
156
+ errors.add(:scopes, Spree.t(:api_key_scopes_immutable))
157
+ end
158
+
148
159
  # Generates the token on creation. For publishable keys, stores the raw token
149
160
  # in the +token+ column. For secret keys, computes an HMAC-SHA256 digest stored
150
161
  # in +token_digest+, saves the first 12 characters as +token_prefix+ for display,
@@ -22,7 +22,7 @@ module Spree
22
22
  validates :name, presence: true
23
23
  normalizes :name, with: ->(value) { value&.to_s&.squish&.presence }
24
24
 
25
- has_many :store_payment_methods, class_name: 'Spree::StorePaymentMethod'
25
+ has_many :store_payment_methods, class_name: 'Spree::StorePaymentMethod', inverse_of: :payment_method
26
26
  has_many :stores, class_name: 'Spree::Store', through: :store_payment_methods
27
27
 
28
28
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :payment_method, dependent: :nullify
@@ -303,8 +303,18 @@ module Spree
303
303
  to_remove = current - desired
304
304
  to_add = desired - current
305
305
 
306
+ return unless to_remove.any? || to_add.any?
307
+
306
308
  remove_products(to_remove) if to_remove.any?
307
309
  add_products(to_add) if to_add.any?
310
+
311
+ # Membership is changed via raw `upsert_all`/`delete_all` on `prices`,
312
+ # which bypasses the `variants`/`products` association caches (and the
313
+ # `product_ids` ids-reader memo `current` populated above). Reset them so
314
+ # a same-request read — e.g. the serializer's `product_ids` — reflects the
315
+ # new membership instead of the pre-change set.
316
+ association(:variants).reset
317
+ association(:products).reset
308
318
  end
309
319
 
310
320
  def apply_pending_prices
@@ -96,6 +96,13 @@ module Spree
96
96
  self.store ||= Spree::Current.store || Spree::Store.default
97
97
  end
98
98
 
99
+ # The store a product's associations (categories, channels) must belong
100
+ # to. Falls back to the request/default store for not-yet-persisted
101
+ # products whose `store` hasn't been assigned.
102
+ def assignable_store
103
+ store || Spree::Current.store || Spree::Store.default
104
+ end
105
+
99
106
  def pending_publications?
100
107
  @pending_publications_params.present?
101
108
  end
@@ -126,9 +133,14 @@ module Spree
126
133
  publication.update!(publication_data.slice(:published_at, :unpublished_at))
127
134
  publication_ids_in_payload << publication.id
128
135
  elsif channel_id.present?
136
+ # Only publish to channels owned by the product's store; ignore
137
+ # ids referencing another store's channel.
138
+ channel = publishable_channels.find_by(id: channel_id)
139
+ next unless channel
140
+
129
141
  # Upsert by channel_id so repeat submissions are idempotent
130
142
  # against the unique (product_id, channel_id) index.
131
- publication = product_publications.find_or_initialize_by(channel_id: channel_id)
143
+ publication = product_publications.find_or_initialize_by(channel_id: channel.id)
132
144
  publication.assign_attributes(publication_data.slice(:published_at, :unpublished_at))
133
145
  publication.save!
134
146
  publication_ids_in_payload << publication.id
@@ -144,6 +156,11 @@ module Spree
144
156
 
145
157
  Spree::PrefixedId.decode_prefixed_id(value) || value
146
158
  end
159
+
160
+ # Channels the product may be published to — those owned by its store.
161
+ def publishable_channels
162
+ assignable_store.channels
163
+ end
147
164
  end
148
165
  end
149
166
  end
@@ -211,11 +211,14 @@ module Spree
211
211
  end
212
212
 
213
213
  # Maps 6.0 API name (category_ids) to model column (taxon_ids).
214
- # Accepts both prefixed IDs and raw integer IDs.
214
+ # Accepts both prefixed IDs and raw integer IDs. Only taxons belonging to
215
+ # the product's own store are assigned — ids from another store's
216
+ # taxonomies are dropped, preventing cross-store category attachment.
215
217
  def category_ids=(ids)
216
- self.taxon_ids = Array(ids).filter_map do |id|
218
+ decoded_ids = Array(ids).filter_map do |id|
217
219
  id.to_s.include?('_') ? Spree::Taxon.decode_prefixed_id(id) : id
218
220
  end
221
+ self.taxon_ids = Spree::Taxon.for_store(assignable_store).where(id: decoded_ids).ids
219
222
  end
220
223
 
221
224
  # Sync media inline. Entries with `id` patch the existing asset
@@ -757,6 +760,7 @@ module Spree
757
760
  def apply_variants(variants_params)
758
761
  variant_ids_in_payload = []
759
762
  master_touched = false
763
+ mutated = false
760
764
 
761
765
  variants_params.each do |variant_data|
762
766
  variant_data = variant_data.to_h.with_indifferent_access
@@ -767,6 +771,7 @@ module Spree
767
771
  variant = variants_including_master.find_by_param!(variant_id)
768
772
  variant.update!(variant_data)
769
773
  variant_ids_in_payload << variant.id
774
+ mutated = true
770
775
  elsif options.blank? || (options.is_a?(Array) && options.empty?)
771
776
  # An entry with no options addresses the master variant. Building a
772
777
  # non-master here would create a phantom duplicate (the auto-built
@@ -778,18 +783,46 @@ module Spree
778
783
  target.assign_attributes(variant_data)
779
784
  target.save!
780
785
  master_touched = true
786
+ mutated = true
781
787
  else
782
788
  variant = variants.build
783
789
  variant.assign_attributes(variant_data)
784
790
  variant.save!
785
791
  variant_ids_in_payload << variant.id
792
+ mutated = true
786
793
  end
787
794
  end
788
795
 
789
796
  # Remove variants not in the payload (only non-master). If only the
790
797
  # master was touched (simple product), leave existing non-master
791
798
  # variants alone — the payload is partial, not a full replacement.
792
- variants.where.not(id: variant_ids_in_payload).destroy_all if variant_ids_in_payload.any? && !master_touched
799
+ if variant_ids_in_payload.any? && !master_touched
800
+ removed = variants.where.not(id: variant_ids_in_payload).destroy_all
801
+ mutated ||= removed.any?
802
+ end
803
+
804
+ sync_variant_state! if mutated
805
+ end
806
+
807
+ # Re-syncs the in-memory derived variant state after `apply_variants`
808
+ # mutates variants out-of-band (the variant counter is bumped via
809
+ # `Spree::Product.increment_counter` in a Variant callback, and
810
+ # `default_variant` is memoized). Without this, a freshly built/updated
811
+ # product serialized in the same request returns a stale `variant_count`
812
+ # and a stale `price` (delegated to the memoized `default_variant`).
813
+ #
814
+ # The `default_variant` memo reset here changes shape once master is
815
+ # removed (it becomes a `belongs_to` FK). See
816
+ # docs/plans/6.0-remove-master-variant.md (Phase 3).
817
+ # @return [void]
818
+ def sync_variant_state!
819
+ # `variant_count` is maintained by a direct counter update in a Variant
820
+ # callback, so the in-memory attribute is stale here — re-read it from the
821
+ # row without reloading the whole record.
822
+ self[:variant_count] = self.class.where(id: id).pick(:variant_count)
823
+ variants.reset
824
+ @default_variant = nil
825
+ @default_variant_id = nil
793
826
  end
794
827
 
795
828
  def add_associations_from_prototype
@@ -811,6 +844,7 @@ module Spree
811
844
 
812
845
  # Builds variants from a hash of option types & values
813
846
  def build_variants_from_option_values_hash
847
+ Spree::Deprecation.warn('Spree::Product#build_variants_from_option_values_hash is deprecated and will be removed in Spree 6.0.')
814
848
  ensure_option_types_exist_for_values_hash
815
849
  values = option_values_hash.values
816
850
  values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
@@ -925,12 +959,14 @@ module Spree
925
959
  end
926
960
 
927
961
  def discontinue_on_must_be_later_than_make_active_at
962
+ Spree::Deprecation.warn('Spree::Product#discontinue_on_must_be_later_than_make_active_at is deprecated and will be removed in Spree 6.0.')
928
963
  if discontinue_on < make_active_at
929
964
  errors.add(:discontinue_on, :invalid_date_range)
930
965
  end
931
966
  end
932
967
 
933
968
  def requires_price?
969
+ Spree::Deprecation.warn('Spree::Product#requires_price? is deprecated and will be removed in Spree 6.0.')
934
970
  Spree::Config[:require_master_price]
935
971
  end
936
972
 
@@ -86,7 +86,9 @@ module Spree
86
86
  #
87
87
  # Ransack
88
88
  #
89
- self.whitelisted_ransackable_attributes = ['path', 'promotion_category_id', 'code', 'starts_at', 'expires_at']
89
+ # `name` is whitelisted so the admin global search / command palette can
90
+ # filter via the `name_or_code_cont` predicate without a dedicated scope.
91
+ self.whitelisted_ransackable_attributes = ['name', 'path', 'promotion_category_id', 'code', 'starts_at', 'expires_at']
90
92
  self.whitelisted_ransackable_associations = %w[coupon_codes]
91
93
 
92
94
  def self.with_coupon_code(coupon_code)
@@ -82,6 +82,7 @@ module Spree
82
82
  has_many :variants, through: :products, class_name: 'Spree::Variant', source: :variants_including_master
83
83
  has_many :stock_items, through: :variants, class_name: 'Spree::StockItem'
84
84
  has_many :prices, through: :variants, class_name: 'Spree::Price'
85
+ has_many :price_lists, class_name: 'Spree::PriceList', inverse_of: :store
85
86
  has_many :inventory_units, through: :variants, class_name: 'Spree::InventoryUnit'
86
87
  has_many :option_value_variants, through: :variants, class_name: 'Spree::OptionValueVariant'
87
88
  has_many :customer_returns, class_name: 'Spree::CustomerReturn', inverse_of: :store
@@ -278,25 +279,14 @@ module Spree
278
279
  end
279
280
 
280
281
  # Returns true if the given URL's origin matches one of the store's allowed origins.
281
- # Comparison is port-less: only scheme + host are matched, so storing
282
- # `http://localhost` will match `http://localhost:3000`, `http://localhost:4000`, etc.
282
+ # See {Spree::AllowedOrigin#matches?} for the matching rules (scheme/host/port).
283
283
  #
284
284
  # @param url [String] the full URL to check
285
285
  # @return [Boolean]
286
286
  def allowed_origin?(url)
287
287
  return false if url.blank?
288
288
 
289
- uri = URI.parse(url)
290
- request_origin = "#{uri.scheme}://#{uri.host}"
291
-
292
- allowed_origins.pluck(:origin).any? do |stored|
293
- stored_uri = URI.parse(stored)
294
- "#{stored_uri.scheme}://#{stored_uri.host}" == request_origin
295
- rescue URI::InvalidURIError
296
- false
297
- end
298
- rescue URI::InvalidURIError
299
- false
289
+ allowed_origins.any? { |allowed_origin| allowed_origin.matches?(url) }
300
290
  end
301
291
 
302
292
  # Returns the states available for checkout for the store
@@ -3,7 +3,7 @@ module Spree
3
3
  self.table_name = 'spree_payment_methods_stores'
4
4
 
5
5
  belongs_to :store, class_name: 'Spree::Store', touch: true
6
- belongs_to :payment_method, class_name: 'Spree::PaymentMethod', touch: true
6
+ belongs_to :payment_method, class_name: 'Spree::PaymentMethod', touch: true, inverse_of: :store_payment_methods
7
7
 
8
8
  validates :store, :payment_method, presence: true
9
9
  validates :store_id, uniqueness: { scope: :payment_method_id }
@@ -539,7 +539,20 @@ module Spree
539
539
  # @param compare_at_amount [BigDecimal] the compare at amount to set
540
540
  # @return [void]
541
541
  def set_price(currency, amount, compare_at_amount = nil)
542
- price = prices.base_prices.find_or_initialize_by(currency: currency)
542
+ # When the prices association is already loaded (eager-loaded for
543
+ # serialization), reuse the cached base-price instance so readers that
544
+ # branch on `prices.loaded?` (Pricing::Resolver, #price_in, serializers)
545
+ # observe the write without a reload. `base_prices.find_or_initialize_by`
546
+ # would issue a fresh query and return a detached object, leaving the
547
+ # loaded collection — and the serialized response — stale.
548
+ price =
549
+ if prices.loaded?
550
+ prices.detect { |p| p.price_list_id.nil? && p.currency == currency } ||
551
+ prices.build(currency: currency)
552
+ else
553
+ prices.base_prices.find_or_initialize_by(currency: currency)
554
+ end
555
+
543
556
  price.amount = amount
544
557
  price.compare_at_amount = compare_at_amount
545
558
  price.save! if persisted?
@@ -69,10 +69,7 @@ module Spree
69
69
  payment_method.name ||= Spree.t(:store_credit_name)
70
70
  payment_method.active = true
71
71
 
72
- if payment_method.new_record?
73
- payment_method.stores << store unless payment_method.stores.include?(store)
74
- payment_method.save!
75
- end
72
+ payment_method.save! if payment_method.new_record?
76
73
 
77
74
  payment_method
78
75
  end
@@ -99,6 +99,9 @@ module Spree
99
99
  product.slug = attributes['slug']
100
100
  product.sku = attributes['sku'] if attributes['sku'].present? && options.empty?
101
101
  product.store = store
102
+ # Publish to the store's default channel so imported products surface
103
+ # in the storefront.
104
+ product.channels << store.default_channel if store.default_channel && product.channels.empty?
102
105
  end
103
106
 
104
107
  product.name = attributes['name'] if attributes['name'].present?
@@ -4,14 +4,17 @@ module Spree
4
4
  prepend Spree::ServiceModule::Base
5
5
 
6
6
  def call
7
- payment_method = Spree::PaymentMethod::StoreCredit.find_or_initialize_by(
8
- name: Spree.t(:store_credit_name),
9
- description: Spree.t(:store_credit_name),
10
- active: true
11
- )
7
+ Spree::Store.all.find_each do |store|
8
+ payment_method = store.payment_methods.find_or_initialize_by(
9
+ type: 'Spree::PaymentMethod::StoreCredit'
10
+ )
11
+ next if payment_method.persisted?
12
12
 
13
- payment_method.stores = Spree::Store.all if payment_method.new_record?
14
- payment_method.save!
13
+ payment_method.name = Spree.t(:store_credit_name)
14
+ payment_method.description = Spree.t(:store_credit_name)
15
+ payment_method.active = true
16
+ payment_method.save!
17
+ end
15
18
  end
16
19
  end
17
20
  end
@@ -774,6 +774,8 @@ en:
774
774
  one: "...and %{count} more"
775
775
  other: "...and %{count} more"
776
776
  api_key: API Key
777
+ api_key_no_current_key: No API key authenticated this request
778
+ api_key_scopes_immutable: cannot be changed after the key is created; create a new key with the scopes you need and revoke this one
777
779
  api_keys: API Keys
778
780
  apis:
779
781
  admin: Admin API
@@ -1425,6 +1427,7 @@ en:
1425
1427
  max: Max
1426
1428
  max_items: Max Items
1427
1429
  max_quantity: Max Quantity
1430
+ me_no_current_user: No admin user authenticated this request
1428
1431
  media: Media
1429
1432
  memo: Memo
1430
1433
  meta_description: Meta Description
@@ -1,19 +1,19 @@
1
- cc_payment_method = Spree::Gateway::Bogus.where(
2
- name: 'Credit Card',
3
- description: 'Bogus payment gateway.',
4
- active: true
5
- ).first_or_initialize
1
+ Spree::Store.all.find_each do |store|
2
+ cc_payment_method = store.payment_methods.find_or_initialize_by(
3
+ type: 'Spree::Gateway::Bogus',
4
+ name: 'Credit Card',
5
+ description: 'Bogus payment gateway.',
6
+ active: true
7
+ )
8
+ cc_payment_method.display_on = 'back_end'
9
+ cc_payment_method.save!
6
10
 
7
- cc_payment_method.display_on = 'back_end'
8
- cc_payment_method.stores = Spree::Store.all
9
- cc_payment_method.save!
10
-
11
- check_payment_method = Spree::PaymentMethod::Check.where(
12
- name: 'Check',
13
- description: 'Pay by check.',
14
- active: true
15
- ).first_or_initialize
16
-
17
- check_payment_method.display_on = 'back_end'
18
- check_payment_method.stores = Spree::Store.all
19
- check_payment_method.save!
11
+ check_payment_method = store.payment_methods.find_or_initialize_by(
12
+ type: 'Spree::PaymentMethod::Check',
13
+ name: 'Check',
14
+ description: 'Pay by check.',
15
+ active: true
16
+ )
17
+ check_payment_method.display_on = 'back_end'
18
+ check_payment_method.save!
19
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.5.0.rc2'.freeze
2
+ VERSION = '5.5.0.rc3'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
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.5.0.rc2
4
+ version: 5.5.0.rc3
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-06-15 00:00:00.000000000 Z
13
+ date: 2026-06-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n-tasks
@@ -1816,9 +1816,9 @@ licenses:
1816
1816
  - BSD-3-Clause
1817
1817
  metadata:
1818
1818
  bug_tracker_uri: https://github.com/spree/spree/issues
1819
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.0.rc2
1819
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.0.rc3
1820
1820
  documentation_uri: https://docs.spreecommerce.org/
1821
- source_code_uri: https://github.com/spree/spree/tree/v5.5.0.rc2
1821
+ source_code_uri: https://github.com/spree/spree/tree/v5.5.0.rc3
1822
1822
  post_install_message:
1823
1823
  rdoc_options: []
1824
1824
  require_paths: