spree_core 5.4.0.beta → 5.4.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +2 -42
  3. data/app/helpers/spree/base_helper.rb +1 -54
  4. data/app/mailers/spree/base_mailer.rb +4 -3
  5. data/app/models/concerns/spree/adjustment_source.rb +0 -8
  6. data/app/models/concerns/spree/admin_user_methods.rb +0 -2
  7. data/app/models/concerns/spree/image_methods.rb +4 -0
  8. data/app/models/concerns/spree/metadata.rb +10 -0
  9. data/app/models/concerns/spree/product_scopes.rb +21 -49
  10. data/app/models/concerns/spree/stores/markets.rb +7 -7
  11. data/app/models/concerns/spree/vat_price_calculation.rb +2 -2
  12. data/app/models/spree/ability.rb +5 -5
  13. data/app/models/spree/address.rb +3 -2
  14. data/app/models/spree/adjustable/promotion_accumulator.rb +1 -1
  15. data/app/models/spree/adjustment.rb +1 -20
  16. data/app/models/spree/api_key.rb +60 -2
  17. data/app/models/spree/country.rb +7 -3
  18. data/app/models/spree/credit_card.rb +0 -27
  19. data/app/models/spree/current.rb +38 -2
  20. data/app/models/spree/exports/products.rb +0 -6
  21. data/app/models/spree/gateway/bogus.rb +9 -9
  22. data/app/models/spree/gateway.rb +0 -3
  23. data/app/models/spree/gift_card_batch.rb +4 -0
  24. data/app/models/spree/image/configuration/active_storage.rb +0 -2
  25. data/app/models/spree/image.rb +7 -31
  26. data/app/models/spree/log_entry.rb +8 -5
  27. data/app/models/spree/market.rb +1 -2
  28. data/app/models/spree/market_country.rb +17 -0
  29. data/app/models/spree/newsletter_subscriber.rb +0 -3
  30. data/app/models/spree/order/checkout.rb +0 -18
  31. data/app/models/spree/order.rb +22 -16
  32. data/app/models/spree/payment/gateway_options.rb +2 -2
  33. data/app/models/spree/payment/processing.rb +5 -5
  34. data/app/models/spree/payment.rb +1 -1
  35. data/app/models/spree/payment_connection_error.rb +3 -0
  36. data/app/models/spree/payment_method/check.rb +1 -1
  37. data/app/models/spree/payment_method/store_credit.rb +5 -5
  38. data/app/models/spree/payment_response.rb +70 -0
  39. data/app/models/spree/permission_sets/default_customer.rb +0 -5
  40. data/app/models/spree/permission_sets/product_display.rb +0 -2
  41. data/app/models/spree/permission_sets/product_management.rb +0 -2
  42. data/app/models/spree/product.rb +5 -85
  43. data/app/models/spree/promotion.rb +2 -14
  44. data/app/models/spree/promotion_handler/coupon.rb +3 -3
  45. data/app/models/spree/prototype.rb +0 -3
  46. data/app/models/spree/refund.rb +6 -2
  47. data/app/models/spree/reimbursement.rb +0 -2
  48. data/app/models/spree/shipment.rb +0 -1
  49. data/app/models/spree/shipment_handler.rb +0 -2
  50. data/app/models/spree/shipping_category.rb +3 -3
  51. data/app/models/spree/store.rb +10 -49
  52. data/app/models/spree/store_credit.rb +4 -0
  53. data/app/models/spree/tax_category.rb +1 -11
  54. data/app/models/spree/taxon.rb +0 -16
  55. data/app/models/spree/variant.rb +2 -40
  56. data/app/models/spree/zone.rb +1 -4
  57. data/app/services/spree/cart/add_item.rb +8 -4
  58. data/app/services/spree/cart/create.rb +13 -2
  59. data/app/services/spree/newsletter/subscribe.rb +2 -2
  60. data/app/services/spree/products/duplicator.rb +0 -12
  61. data/app/services/spree/products/prepare_nested_attributes.rb +0 -12
  62. data/app/subscribers/spree/invitation_email_subscriber.rb +1 -1
  63. data/config/locales/en.yml +4 -20
  64. data/db/migrate/20210914000000_spree_four_three.rb +0 -38
  65. data/db/migrate/20210915064329_add_metadata_to_spree_multiple_tables.rb +0 -1
  66. data/db/migrate/20260226000000_add_locale_to_spree_orders.rb +5 -0
  67. data/db/migrate/20260226100000_add_token_digest_to_spree_api_keys.rb +21 -0
  68. data/lib/spree/core/configuration.rb +0 -3
  69. data/lib/spree/core/controller_helpers/strong_parameters.rb +1 -2
  70. data/lib/spree/core/dependencies.rb +2 -10
  71. data/lib/spree/core/engine.rb +0 -13
  72. data/lib/spree/core/pricing/resolver.rb +1 -9
  73. data/lib/spree/core/version.rb +1 -1
  74. data/lib/spree/core.rb +10 -26
  75. data/lib/spree/permitted_attributes.rb +2 -14
  76. data/lib/spree/testing_support/factories/order_factory.rb +1 -0
  77. data/lib/spree/testing_support/factories/payment_method_factory.rb +1 -6
  78. data/lib/spree/testing_support/factories/product_factory.rb +0 -7
  79. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -2
  80. data/lib/tasks/cli.rake +50 -0
  81. data/lib/tasks/core.rake +0 -265
  82. metadata +9 -111
  83. data/app/finders/spree/posts/find.rb +0 -137
  84. data/app/finders/spree/product_properties/find_available.rb +0 -20
  85. data/app/helpers/spree/mail_helper.rb +0 -27
  86. data/app/jobs/spree/api_key_touch_job.rb +0 -9
  87. data/app/mailers/spree/test_mailer.rb +0 -8
  88. data/app/models/action_text/video_embed.rb +0 -13
  89. data/app/models/concerns/spree/filter_param.rb +0 -21
  90. data/app/models/concerns/spree/has_one_link.rb +0 -42
  91. data/app/models/concerns/spree/linkable.rb +0 -9
  92. data/app/models/concerns/spree/previewable.rb +0 -17
  93. data/app/models/spree/data_feed/google.rb +0 -15
  94. data/app/models/spree/data_feed.rb +0 -42
  95. data/app/models/spree/gateway/bogus_simple.rb +0 -24
  96. data/app/models/spree/newsletter_subscriber/emails.rb +0 -12
  97. data/app/models/spree/order/emails.rb +0 -24
  98. data/app/models/spree/post.rb +0 -108
  99. data/app/models/spree/post_category.rb +0 -46
  100. data/app/models/spree/product_property.rb +0 -51
  101. data/app/models/spree/property.rb +0 -86
  102. data/app/models/spree/property_prototype.rb +0 -9
  103. data/app/models/spree/reimbursement/emails.rb +0 -11
  104. data/app/models/spree/shipment/emails.rb +0 -11
  105. data/app/models/spree/store_favicon_image.rb +0 -20
  106. data/app/models/spree/store_logo.rb +0 -4
  107. data/app/models/spree/store_mailer_logo.rb +0 -7
  108. data/app/models/spree/taxon_image/configuration/active_storage.rb +0 -26
  109. data/app/models/spree/taxon_image.rb +0 -28
  110. data/app/presenters/spree/filters/properties_presenter.rb +0 -23
  111. data/app/presenters/spree/filters/property_presenter.rb +0 -42
  112. data/app/services/spree/data_feeds/google/optional_attributes.rb +0 -23
  113. data/app/services/spree/data_feeds/google/optional_sub_attributes.rb +0 -21
  114. data/app/services/spree/data_feeds/google/products_list.rb +0 -14
  115. data/app/services/spree/data_feeds/google/required_attributes.rb +0 -68
  116. data/app/services/spree/data_feeds/google/rss.rb +0 -109
  117. data/app/sorters/spree/posts/sort.rb +0 -40
  118. data/app/views/action_text/video_embeds/_thumbnail.html.erb +0 -1
  119. data/app/views/action_text/video_embeds/_video_embed.html.erb +0 -3
  120. data/app/views/layouts/action_text/contents/_content.html.erb +0 -3
  121. data/app/views/spree/test_mailer/test_email.html.erb +0 -40
  122. data/app/views/spree/test_mailer/test_email.text.erb +0 -4
  123. data/config/initializers/oembed.rb +0 -1
  124. data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +0 -14
  125. data/db/migrate/20230109084253_create_product_property_translations.rb +0 -24
  126. data/db/migrate/20230109105943_create_property_translations.rb +0 -24
  127. data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +0 -5
  128. data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +0 -7
  129. data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +0 -5
  130. data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +0 -5
  131. data/db/migrate/20240914153106_add_display_on_to_spree_properties.rb +0 -5
  132. data/db/migrate/20240915144935_add_position_to_spree_properties.rb +0 -6
  133. data/db/migrate/20250121160028_create_spree_posts_and_spree_post_categories.rb +0 -33
  134. data/db/migrate/20250127083740_add_kind_to_spree_properties.rb +0 -5
  135. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
  136. data/db/migrate/20250305121657_remove_spree_posts_indices.rb +0 -7
  137. data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +0 -24
  138. data/db/sample_data/posts.rb +0 -7
  139. data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +0 -19
  140. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +0 -387
  141. data/lib/spree/core/controller_helpers/search.rb +0 -17
  142. data/lib/spree/core/importer/order.rb +0 -244
  143. data/lib/spree/core/importer/product.rb +0 -67
  144. data/lib/spree/core/importer.rb +0 -9
  145. data/lib/spree/core/product_filters.rb +0 -218
  146. data/lib/spree/core/query_filters/comparable.rb +0 -50
  147. data/lib/spree/core/query_filters/date.rb +0 -8
  148. data/lib/spree/core/query_filters/number.rb +0 -8
  149. data/lib/spree/core/query_filters/text.rb +0 -36
  150. data/lib/spree/core/query_filters.rb +0 -11
  151. data/lib/spree/core/search/base.rb +0 -144
  152. data/lib/spree/testing_support/factories/favicon_image_factory.rb +0 -9
  153. data/lib/spree/testing_support/factories/google_data_feed_factory.rb +0 -7
  154. data/lib/spree/testing_support/factories/post_category_factory.rb +0 -7
  155. data/lib/spree/testing_support/factories/post_factory.rb +0 -22
  156. data/lib/spree/testing_support/factories/product_property_factory.rb +0 -7
  157. data/lib/spree/testing_support/factories/property_factory.rb +0 -28
  158. data/lib/spree/testing_support/factories/taxon_image_factory.rb +0 -9
  159. data/lib/spree/testing_support/flash.rb +0 -25
  160. data/lib/spree/testing_support/flatpickr_capybara.rb +0 -124
  161. data/lib/tasks/exchanges.rake +0 -66
@@ -43,7 +43,7 @@ module Spree
43
43
  #
44
44
  # @return [String, nil] currency code (e.g., 'USD', 'EUR') or nil if no market found
45
45
  def market_currency
46
- Spree::Current.store&.market_for_country(self)&.currency
46
+ current_market&.currency
47
47
  end
48
48
 
49
49
  # Returns the default locale for this country from its market in the current store.
@@ -51,14 +51,18 @@ module Spree
51
51
  #
52
52
  # @return [String, nil] locale code (e.g., 'en', 'de') or nil if no market found
53
53
  def market_locale
54
- Spree::Current.store&.market_for_country(self)&.default_locale
54
+ current_market&.default_locale
55
55
  end
56
56
 
57
57
  # Returns the supported locales for this country from its market in the current store.
58
58
  #
59
59
  # @return [Array<String>] locale codes (e.g., ['en', 'fr']) or empty array if no market found
60
60
  def market_supported_locales
61
- Spree::Current.store&.market_for_country(self)&.supported_locales_list || []
61
+ current_market&.supported_locales_list || []
62
+ end
63
+
64
+ def current_market
65
+ @current_market ||= Spree::Current.store&.market_for_country(self)
62
66
  end
63
67
 
64
68
  def <=>(other)
@@ -2,7 +2,6 @@ module Spree
2
2
  class CreditCard < Spree.base_class
3
3
  has_prefix_id :card # Stripe: card_
4
4
 
5
- include ActiveMerchant::Billing::CreditCardMethods
6
5
  include Spree::Metafields
7
6
  include Spree::Metadata
8
7
  include Spree::PaymentSourceConcern
@@ -48,7 +47,6 @@ module Spree
48
47
  }
49
48
  scope :not_removed, -> { where(deleted_at: nil) }
50
49
 
51
- # needed for some of the ActiveMerchant gateways (eg. SagePay)
52
50
  alias_attribute :brand, :cc_type
53
51
 
54
52
  store_accessor :private_metadata, :wallet
@@ -59,14 +57,6 @@ module Spree
59
57
  wallet&.[]('type')
60
58
  end
61
59
 
62
- # ActiveMerchant::Billing::CreditCard added this accessor used by some gateways.
63
- # More info: https://github.com/spree/spree/issues/6209
64
- #
65
- # Returns or sets the track data for the card
66
- #
67
- # @return [String]
68
- attr_accessor :track_data
69
-
70
60
  CARD_TYPES = {
71
61
  visa: /^4\d{12}(\d{3})?(\d{3})?$/,
72
62
  master: /^(5[1-5]\d{4}|677189|222[1-9]\d{2}|22[3-9]\d{3}|2[3-6]\d{4}|27[01]\d{3}|2720\d{2})\d{10}$/,
@@ -151,10 +141,6 @@ module Spree
151
141
  brand.present? ? brand.upcase : Spree.t(:no_cc_type)
152
142
  end
153
143
 
154
- # ActiveMerchant needs first_name/last_name because we pass it a Spree::CreditCard and it calls those methods on it.
155
- # Looking at the ActiveMerchant source code we should probably be calling #to_active_merchant before passing
156
- # the object to ActiveMerchant but this should do for now.
157
- #
158
144
  # Returns the first name of the cardholder.
159
145
  # @return [String]
160
146
  def first_name
@@ -167,19 +153,6 @@ module Spree
167
153
  name.to_s.split(/[[:space:]]/, 2)[1]
168
154
  end
169
155
 
170
- # Returns an ActiveMerchant::Billing::CreditCard object.
171
- # @return [ActiveMerchant::Billing::CreditCard]
172
- def to_active_merchant
173
- ActiveMerchant::Billing::CreditCard.new(
174
- number: number,
175
- month: month,
176
- year: year,
177
- verification_value: verification_value,
178
- first_name: first_name,
179
- last_name: last_name
180
- )
181
- end
182
-
183
156
  def self.json_api_permitted_attributes
184
157
  [
185
158
  'number', 'month', 'year', 'expiry', 'verification_value', 'first_name', 'last_name',
@@ -1,23 +1,57 @@
1
1
  module Spree
2
+ # Thread-safe, per-request attributes for the current store context.
3
+ #
4
+ # All attributes are automatically reset between requests by Rails.
5
+ # Fallback chains ensure sensible defaults when attributes are not explicitly set.
2
6
  class Current < ::ActiveSupport::CurrentAttributes
3
- attribute :store, :market, :currency, :zone, :price_lists, :global_pricing_context
7
+ attribute :store, :market, :currency, :locale, :zone, :default_tax_zone, :price_lists, :global_pricing_context
4
8
 
9
+ resets { @default_tax_zone_loaded = false }
10
+
11
+ # Returns the current store, falling back to the default store.
12
+ # @return [Spree::Store]
5
13
  def store
6
14
  super || Spree::Store.default
7
15
  end
8
16
 
17
+ # Returns the current market, falling back to the store's default market.
18
+ # @return [Spree::Market, nil]
9
19
  def market
10
20
  super || store&.default_market
11
21
  end
12
22
 
23
+ # Returns the current currency.
24
+ # Fallback: market currency -> store default currency.
25
+ # @return [String] currency ISO code, e.g. +"USD"+
13
26
  def currency
14
27
  super || market&.currency || store&.default_currency
15
28
  end
16
29
 
30
+ # Returns the current locale.
31
+ # Fallback: market default locale -> store default locale.
32
+ # @return [String] locale code, e.g. +"en"+, +"de"+
33
+ def locale
34
+ super || market&.default_locale || store&.default_locale
35
+ end
36
+
37
+ # Returns the current tax zone, falling back to the default tax zone.
38
+ # @return [Spree::Zone, nil]
17
39
  def zone
18
- super || Spree::Zone.default_tax
40
+ super || default_tax_zone
41
+ end
42
+
43
+ # Returns the default tax zone (memoized per request).
44
+ # @return [Spree::Zone, nil]
45
+ def default_tax_zone
46
+ result = super
47
+ return result if result || @default_tax_zone_loaded
48
+
49
+ @default_tax_zone_loaded = true
50
+ self.default_tax_zone = Spree::Zone.find_by(default_tax: true)
19
51
  end
20
52
 
53
+ # Returns the current price lists for the global pricing context.
54
+ # @return [ActiveRecord::Relation<Spree::PriceList>]
21
55
  def price_lists
22
56
  super || begin
23
57
  context = global_pricing_context
@@ -25,6 +59,8 @@ module Spree
25
59
  end
26
60
  end
27
61
 
62
+ # Returns the current global pricing context, built from store, currency, zone, and market.
63
+ # @return [Spree::Pricing::Context]
28
64
  def global_pricing_context
29
65
  super || begin
30
66
  self.global_pricing_context = Spree::Pricing::Context.new(
@@ -4,7 +4,6 @@ module Spree
4
4
  # to avoid N+1 queries
5
5
  def scope_includes
6
6
  includes = [:tax_category, :master, :option_types, { taxons: :taxonomy }, { variants_including_master: variant_includes }]
7
- includes << { product_properties: [:property] } if Spree::Config[:product_properties_enabled]
8
7
  includes << { metafields: :metafield_definition }
9
8
  includes
10
9
  end
@@ -28,14 +27,9 @@ module Spree
28
27
 
29
28
  def csv_headers
30
29
  headers = Spree::CSV::ProductVariantPresenter::CSV_HEADERS.dup
31
- headers += properties_headers if Spree::Config[:product_properties_enabled]
32
30
  headers += metafields_headers
33
31
  @csv_headers ||= headers
34
32
  end
35
-
36
- def properties_headers
37
- @properties_headers ||= Spree::Property.order(:position).count.times.flat_map { |n| ["property#{n + 1}_name", "property#{n + 1}_value"] }
38
- end
39
33
  end
40
34
  end
41
35
  end
@@ -32,39 +32,39 @@ module Spree
32
32
  def authorize(_money, credit_card, _options = {})
33
33
  profile_id = credit_card.gateway_customer_profile_id
34
34
  if VALID_CCS.include?(credit_card.number) || (profile_id&.starts_with?('BGS-'))
35
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'D' })
35
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'D' })
36
36
  else
37
- ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
37
+ Spree::PaymentResponse.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
38
38
  end
39
39
  end
40
40
 
41
41
  def purchase(_money, credit_card, _options = {})
42
42
  profile_id = credit_card.gateway_customer_profile_id
43
43
  if VALID_CCS.include?(credit_card.number) || (profile_id&.starts_with?('BGS-'))
44
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'M' })
44
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'M' })
45
45
  else
46
- ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
46
+ Spree::PaymentResponse.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
47
47
  end
48
48
  end
49
49
 
50
50
  def credit(_money, _credit_card, _response_code, _options = {})
51
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
51
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
52
52
  end
53
53
 
54
54
  def capture(_money, authorization, _gateway_options)
55
55
  if authorization == '12345'
56
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true)
56
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true)
57
57
  else
58
- ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', error: 'Bogus Gateway: Forced failure', test: true)
58
+ Spree::PaymentResponse.new(false, 'Bogus Gateway: Forced failure', error: 'Bogus Gateway: Forced failure', test: true)
59
59
  end
60
60
  end
61
61
 
62
62
  def void(_response_code, _credit_card, _options = {})
63
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: 'void-12345')
63
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: 'void-12345')
64
64
  end
65
65
 
66
66
  def cancel(_response_code, _payment = nil)
67
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
67
+ Spree::PaymentResponse.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
68
68
  end
69
69
 
70
70
  def test?
@@ -20,9 +20,6 @@ module Spree
20
20
  def provider
21
21
  gateway_options = options
22
22
  gateway_options.delete :login if gateway_options.key?(:login) && gateway_options[:login].nil?
23
- if gateway_options[:server]
24
- ActiveMerchant::Billing::Base.mode = gateway_options[:server].to_sym
25
- end
26
23
  @provider ||= provider_class.new(gateway_options)
27
24
  end
28
25
 
@@ -32,6 +32,10 @@ module Spree
32
32
 
33
33
  money_methods :amount
34
34
 
35
+ def amount=(amount)
36
+ self[:amount] = Spree::LocalizedNumber.parse(amount)
37
+ end
38
+
35
39
  self.whitelisted_ransackable_attributes = %w[prefix]
36
40
 
37
41
  def generate_gift_cards
@@ -5,8 +5,6 @@ module Spree
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- validates :attachment, attached: true, content_type: Rails.application.config.active_storage.web_image_content_types
9
-
10
8
  # Returns image styles derived from Spree::Config.product_image_variant_sizes
11
9
  # Format: { variant_name: 'WxH>' } for API compatibility
12
10
  def self.styles
@@ -1,8 +1,10 @@
1
1
  module Spree
2
2
  class Image < Asset
3
- include Spree::Image::Configuration::ActiveStorage
3
+ include Spree::Image::Configuration::ActiveStorage # legacy to be removed in Spree 6
4
4
  include Rails.application.routes.url_helpers
5
- include Spree::ImageMethods
5
+ include Spree::ImageMethods # legacy, will be removed in Spree 6
6
+
7
+ validates :attachment, attached: true, content_type: Rails.application.config.active_storage.web_image_content_types
6
8
 
7
9
  after_commit :touch_product_variants, if: :should_touch_product_variants?, on: :update
8
10
  after_commit :update_variant_thumbnail, on: [:create, :destroy]
@@ -19,7 +21,10 @@ module Spree
19
21
  # The line below prevents the error.
20
22
  self.inheritance_column = nil
21
23
 
24
+ # @deprecated
22
25
  def styles
26
+ Spree::Deprecation.warn("Image#styles is deprecated and will be removed in Spree 6.0. Please use active storage variants with cdn_image_url")
27
+
23
28
  self.class.styles.map do |_, size|
24
29
  width, height = size.chop.split('x').map(&:to_i)
25
30
 
@@ -32,35 +37,6 @@ module Spree
32
37
  end
33
38
  end
34
39
 
35
- def style(name)
36
- size = self.class.styles[name]
37
- return unless size
38
-
39
- width, height = size.chop.split('x').map(&:to_i)
40
-
41
- {
42
- url: generate_url(size: size),
43
- size: size,
44
- width: width,
45
- height: height
46
- }
47
- end
48
-
49
- def style_dimensions(name)
50
- size = self.class.styles[name]
51
- width, height = size.chop.split('x').map(&:to_i)
52
-
53
- {
54
- width: width,
55
- height: height
56
- }
57
- end
58
-
59
- def plp_url
60
- Spree::Deprecation.warn "Image#plp_url is deprecated. Use variant(:large) instead."
61
- generate_url(size: self.class.styles[:large])
62
- end
63
-
64
40
  private
65
41
 
66
42
  def touch_product_variants
@@ -17,11 +17,14 @@ module Spree
17
17
  end
18
18
 
19
19
  def parsed_details
20
- @details ||= if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0')
21
- YAML.safe_load(details, permitted_classes: [ActiveMerchant::Billing::Response])
22
- else
23
- YAML.safe_load(details, [ActiveMerchant::Billing::Response])
24
- end
20
+ @details ||= YAML.safe_load(
21
+ details,
22
+ permitted_classes: [
23
+ Spree::PaymentResponse,
24
+ ActiveSupport::HashWithIndifferentAccess,
25
+ (ActiveMerchant::Billing::Response if defined?(ActiveMerchant::Billing::Response))
26
+ ].compact
27
+ )
25
28
  end
26
29
  end
27
30
  end
@@ -44,8 +44,7 @@ module Spree
44
44
  joins(:market_countries)
45
45
  .where(store_id: store.id)
46
46
  .where(spree_market_countries: { country_id: country.id })
47
- .order(:position)
48
- .first
47
+ .take
49
48
  end
50
49
 
51
50
  # Returns the default market for a store, or falls back to the first by position
@@ -8,6 +8,7 @@ module Spree
8
8
  validates :market, :country, presence: true
9
9
  validates :country_id, uniqueness: { scope: :market_id }
10
10
  validate :country_covered_by_shipping_zone
11
+ validate :country_unique_per_store
11
12
 
12
13
  private
13
14
 
@@ -21,5 +22,21 @@ module Spree
21
22
  errors.add(:country, :not_in_shipping_zone)
22
23
  end
23
24
  end
25
+
26
+ def country_unique_per_store
27
+ return if market.blank? || country.blank?
28
+
29
+ store = market.store
30
+ return if store.blank?
31
+
32
+ existing = self.class.joins(:market)
33
+ .where(country_id: country_id)
34
+ .where(spree_markets: { store_id: store.id, deleted_at: nil })
35
+ .where.not(id: id)
36
+
37
+ if existing.exists?
38
+ errors.add(:country, :already_in_market)
39
+ end
40
+ end
24
41
  end
25
42
  end
@@ -1,10 +1,7 @@
1
- require_dependency 'spree/newsletter_subscriber/emails'
2
-
3
1
  module Spree
4
2
  class NewsletterSubscriber < Spree.base_class
5
3
  has_prefix_id :sub
6
4
 
7
- include Spree::NewsletterSubscriber::Emails
8
5
  include Spree::Metafields
9
6
 
10
7
  publishes_lifecycle_events
@@ -89,7 +89,6 @@ module Spree
89
89
  end
90
90
 
91
91
  after_transition to: :complete, do: :create_user_record
92
- after_transition to: :complete, do: :persist_user_credit_card
93
92
  before_transition to: :payment, do: :set_shipments_cost
94
93
  before_transition to: :payment, do: :create_tax_charge!
95
94
  before_transition to: :payment, do: :recalculate_store_credit_payment
@@ -293,23 +292,6 @@ module Spree
293
292
  Spree::Orders::CreateUserAccount.call(order: self, accepts_email_marketing: accept_marketing?)
294
293
  end
295
294
 
296
- def persist_user_credit_card
297
- return unless !temporary_credit_card && user_id && valid_credit_cards.present?
298
-
299
- valid_credit_cards.first.update(user_id: user_id, default: true)
300
- end
301
-
302
- def assign_default_credit_card
303
- return unless payments.from_credit_card.size.empty? && user_has_valid_default_card? && payment_required?
304
-
305
- cc = user.default_credit_card
306
- payments.create!(payment_method_id: cc.payment_method_id, source: cc, amount: total)
307
- end
308
-
309
- def user_has_valid_default_card?
310
- user && user.default_credit_card.try(:valid?)
311
- end
312
-
313
295
  def move_to_next_step_if_address_not_required
314
296
  return if requires_ship_address?
315
297
 
@@ -3,7 +3,6 @@ require_dependency 'spree/order/currency_updater'
3
3
  require_dependency 'spree/order/digital'
4
4
  require_dependency 'spree/order/payments'
5
5
  require_dependency 'spree/order/store_credit'
6
- require_dependency 'spree/order/emails'
7
6
  require_dependency 'spree/order/gift_card'
8
7
 
9
8
  module Spree
@@ -22,7 +21,6 @@ module Spree
22
21
  include Spree::Order::Payments
23
22
  include Spree::Order::StoreCredit
24
23
  include Spree::Order::AddressBook
25
- include Spree::Order::Emails
26
24
  include Spree::Order::Webhooks
27
25
  include Spree::Core::NumberGenerator.new(prefix: 'R')
28
26
  include Spree::Order::GiftCard
@@ -92,7 +90,7 @@ module Spree
92
90
  self.whitelisted_ransackable_scopes = %w[refunded partially_refunded multi_search]
93
91
 
94
92
  attr_reader :coupon_code
95
- attr_accessor :temporary_address, :temporary_credit_card
93
+ attr_accessor :temporary_address
96
94
 
97
95
  attribute :state_machine_resumed, :boolean
98
96
 
@@ -159,6 +157,7 @@ module Spree
159
157
  # Needs to happen before save_permalink is called
160
158
  before_validation :ensure_store_presence
161
159
  before_validation :ensure_currency_presence
160
+ before_validation :ensure_locale_presence
162
161
 
163
162
  before_validation :clone_billing_address, if: :use_billing?
164
163
  before_validation :clone_shipping_address, if: :use_shipping?
@@ -174,6 +173,7 @@ module Spree
174
173
  validates :item_count, numericality: { greater_than_or_equal_to: 0, less_than: 2**31, only_integer: true, allow_blank: true }
175
174
  validates :store
176
175
  validates :currency
176
+ validates :locale
177
177
  end
178
178
  validates :payment_state, inclusion: { in: PAYMENT_STATES, allow_blank: true }
179
179
  validates :shipment_state, inclusion: { in: SHIPMENT_STATES, allow_blank: true }
@@ -186,6 +186,7 @@ module Spree
186
186
  validates :promo_total, NEGATIVE_MONEY_VALIDATION
187
187
  validates :total, MONEY_VALIDATION
188
188
  validate :currency_must_be_supported_by_store
189
+ validate :locale_must_be_supported_by_store
189
190
 
190
191
  delegate :update_totals, :persist_totals, to: :updater
191
192
  delegate :merge!, to: :merger
@@ -308,9 +309,9 @@ module Spree
308
309
  # @return [Boolean]
309
310
  def order_refunded?
310
311
  return false if item_count.zero?
312
+ return false if refunds_total.zero?
311
313
 
312
- (payment_state.in?(%w[void failed]) && refunds_total.positive?) ||
313
- refunds_total == total_minus_store_credits - additional_tax_total.abs
314
+ payment_state.in?(%w[void failed]) || refunds_total == total_minus_store_credits - additional_tax_total.abs
314
315
  end
315
316
 
316
317
  def refunds_total
@@ -529,16 +530,6 @@ module Spree
529
530
  complete? || canceled? || returned?
530
531
  end
531
532
 
532
- def credit_cards
533
- credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
534
- CreditCard.where(id: credit_card_ids)
535
- end
536
-
537
- def valid_credit_cards
538
- credit_card_ids = payments.from_credit_card.valid.pluck(:source_id).uniq
539
- CreditCard.where(id: credit_card_ids)
540
- end
541
-
542
533
  # Finalizes an in progress order after checkout is complete.
543
534
  # Called after transition to complete state when payments will have been processed
544
535
  def finalize!
@@ -972,7 +963,6 @@ module Spree
972
963
  payments.store_credits.pending.each(&:void!)
973
964
  end
974
965
 
975
- send_cancel_email
976
966
  update_with_updater!
977
967
  send_order_canceled_webhook
978
968
  publish_order_canceled_event
@@ -997,6 +987,12 @@ module Spree
997
987
  self.currency ||= store&.default_currency
998
988
  end
999
989
 
990
+ # Sets the locale from Spree::Current.locale when not already set.
991
+ # Called as a before_validation callback, mirroring ensure_currency_presence.
992
+ def ensure_locale_presence
993
+ self.locale ||= Spree::Current.locale
994
+ end
995
+
1000
996
  def currency_must_be_supported_by_store
1001
997
  return if currency.blank? || store.blank?
1002
998
 
@@ -1006,6 +1002,16 @@ module Spree
1006
1002
  end
1007
1003
  end
1008
1004
 
1005
+ # Validates that the order's locale is within the store's supported locales.
1006
+ # Mirrors currency_must_be_supported_by_store.
1007
+ def locale_must_be_supported_by_store
1008
+ return if locale.blank? || store.blank?
1009
+
1010
+ unless store.supported_locales_list.include?(locale)
1011
+ errors.add(:locale, Spree.t(:locale_not_supported_by_store))
1012
+ end
1013
+ end
1014
+
1009
1015
  def collect_payment_methods
1010
1016
  Spree::Deprecation.warn('`Order#collect_payment_methods` is deprecated and will be removed in Spree 5.5. Use `collect_frontend_payment_methods` instead.')
1011
1017
 
@@ -51,11 +51,11 @@ module Spree
51
51
  end
52
52
 
53
53
  def billing_address
54
- order.bill_address.try(:active_merchant_hash)
54
+ order.bill_address.try(:gateway_hash)
55
55
  end
56
56
 
57
57
  def shipping_address
58
- order.ship_address.try(:active_merchant_hash)
58
+ order.ship_address.try(:gateway_hash)
59
59
  end
60
60
 
61
61
  def hash_methods
@@ -48,7 +48,7 @@ module Spree
48
48
  amount ||= money.amount_in_cents
49
49
  started_processing!
50
50
  protect_from_connection_error do
51
- # Standard ActiveMerchant capture usage
51
+ # Capture the payment amount
52
52
  response = payment_method.capture(
53
53
  amount,
54
54
  response_code,
@@ -71,7 +71,7 @@ module Spree
71
71
  # so supply the authorization itself as well as the credit card, rather than just the authorization code
72
72
  response = payment_method.void(response_code, source, gateway_options)
73
73
  else
74
- # Standard ActiveMerchant void usage
74
+ # Standard void usage
75
75
  response = payment_method.void(response_code, gateway_options)
76
76
  end
77
77
  record_response(response)
@@ -165,15 +165,15 @@ module Spree
165
165
 
166
166
  def protect_from_connection_error
167
167
  yield
168
- rescue ActiveMerchant::ConnectionError => e
168
+ rescue Spree::PaymentConnectionError => e
169
169
  failure!
170
170
  gateway_error(e)
171
171
  end
172
172
 
173
173
  def gateway_error(error)
174
- text = if error.is_a? ActiveMerchant::Billing::Response
174
+ text = if error.is_a? Spree::PaymentResponse
175
175
  error.params['message'] || error.params['response_reason_text'] || error.message
176
- elsif error.is_a? ActiveMerchant::ConnectionError
176
+ elsif error.is_a? Spree::PaymentConnectionError
177
177
  Spree.t(:unable_to_connect_to_gateway)
178
178
  else
179
179
  error.to_s
@@ -348,7 +348,7 @@ module Spree
348
348
  return if source.respond_to?(:imported) && source.imported
349
349
 
350
350
  payment_method.create_profile(self)
351
- rescue ActiveMerchant::ConnectionError => e
351
+ rescue Spree::PaymentConnectionError => e
352
352
  gateway_error e
353
353
  end
354
354
 
@@ -0,0 +1,3 @@
1
+ module Spree
2
+ class PaymentConnectionError < StandardError; end
3
+ end
@@ -37,7 +37,7 @@ module Spree
37
37
  private
38
38
 
39
39
  def simulated_successful_billing_response
40
- ActiveMerchant::Billing::Response.new(true, '', {}, {})
40
+ Spree::PaymentResponse.new(true, '', {}, {})
41
41
  end
42
42
  end
43
43
  end
@@ -22,7 +22,7 @@ module Spree
22
22
 
23
23
  def authorize(amount_in_cents, store_credit, gateway_options = {})
24
24
  if store_credit.nil?
25
- ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
25
+ Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
26
26
  else
27
27
  action = lambda do |store_credit|
28
28
  store_credit.authorize(
@@ -58,7 +58,7 @@ module Spree
58
58
  end
59
59
 
60
60
  if event.blank?
61
- ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
61
+ Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
62
62
  else
63
63
  capture(amount_in_cents, event.authorization_code, gateway_options)
64
64
  end
@@ -111,14 +111,14 @@ module Spree
111
111
  store_credit.with_lock do
112
112
  if response = action.call(store_credit)
113
113
  # note that we only need to return the auth code on an 'auth', but it's innocuous to always return
114
- ActiveMerchant::Billing::Response.new(
114
+ Spree::PaymentResponse.new(
115
115
  true,
116
116
  Spree.t('store_credit_payment_method.successful_action', action: action_name),
117
117
  {},
118
118
  authorization: auth_code || response
119
119
  )
120
120
  else
121
- ActiveMerchant::Billing::Response.new(false, store_credit.errors.full_messages.to_sentence, {}, {})
121
+ Spree::PaymentResponse.new(false, store_credit.errors.full_messages.to_sentence, {}, {})
122
122
  end
123
123
  end
124
124
  end
@@ -128,7 +128,7 @@ module Spree
128
128
  store_credit = StoreCreditEvent.find_by(authorization_code: auth_code).try(:store_credit)
129
129
 
130
130
  if store_credit.nil?
131
- ActiveMerchant::Billing::Response.new(
131
+ Spree::PaymentResponse.new(
132
132
  false,
133
133
  Spree.t('store_credit_payment_method.unable_to_find_for_action', auth_code: auth_code, action: action_name),
134
134
  {},