spree_core 5.4.0.beta → 5.4.0.beta2

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +1 -28
  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 +2 -47
  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 +57 -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/order/checkout.rb +0 -18
  28. data/app/models/spree/order.rb +20 -11
  29. data/app/models/spree/payment/gateway_options.rb +2 -2
  30. data/app/models/spree/payment/processing.rb +5 -5
  31. data/app/models/spree/payment.rb +1 -1
  32. data/app/models/spree/payment_connection_error.rb +3 -0
  33. data/app/models/spree/payment_method/check.rb +1 -1
  34. data/app/models/spree/payment_method/store_credit.rb +5 -5
  35. data/app/models/spree/payment_response.rb +70 -0
  36. data/app/models/spree/permission_sets/default_customer.rb +0 -5
  37. data/app/models/spree/permission_sets/product_display.rb +0 -2
  38. data/app/models/spree/permission_sets/product_management.rb +0 -2
  39. data/app/models/spree/product.rb +4 -72
  40. data/app/models/spree/promotion.rb +2 -14
  41. data/app/models/spree/promotion_handler/coupon.rb +3 -3
  42. data/app/models/spree/prototype.rb +0 -3
  43. data/app/models/spree/refund.rb +6 -2
  44. data/app/models/spree/shipment/emails.rb +1 -0
  45. data/app/models/spree/shipping_category.rb +3 -3
  46. data/app/models/spree/store.rb +10 -49
  47. data/app/models/spree/store_credit.rb +4 -0
  48. data/app/models/spree/tax_category.rb +1 -11
  49. data/app/models/spree/taxon.rb +0 -16
  50. data/app/models/spree/variant.rb +2 -40
  51. data/app/models/spree/zone.rb +1 -4
  52. data/app/services/spree/cart/add_item.rb +8 -4
  53. data/app/services/spree/cart/create.rb +13 -2
  54. data/app/services/spree/products/duplicator.rb +0 -12
  55. data/app/services/spree/products/prepare_nested_attributes.rb +0 -12
  56. data/config/locales/en.yml +3 -20
  57. data/db/migrate/20210914000000_spree_four_three.rb +0 -38
  58. data/db/migrate/20210915064329_add_metadata_to_spree_multiple_tables.rb +0 -1
  59. data/db/migrate/20260226000000_add_locale_to_spree_orders.rb +5 -0
  60. data/db/migrate/20260226100000_add_token_digest_to_spree_api_keys.rb +21 -0
  61. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +1 -3
  62. data/lib/spree/core/configuration.rb +0 -3
  63. data/lib/spree/core/controller_helpers/strong_parameters.rb +1 -2
  64. data/lib/spree/core/dependencies.rb +2 -10
  65. data/lib/spree/core/engine.rb +0 -13
  66. data/lib/spree/core/pricing/resolver.rb +1 -9
  67. data/lib/spree/core/version.rb +1 -1
  68. data/lib/spree/core.rb +10 -25
  69. data/lib/spree/permitted_attributes.rb +2 -14
  70. data/lib/spree/testing_support/factories/order_factory.rb +1 -0
  71. data/lib/spree/testing_support/factories/payment_method_factory.rb +1 -6
  72. data/lib/spree/testing_support/factories/product_factory.rb +0 -7
  73. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -2
  74. data/lib/tasks/core.rake +0 -265
  75. metadata +8 -101
  76. data/app/finders/spree/posts/find.rb +0 -137
  77. data/app/finders/spree/product_properties/find_available.rb +0 -20
  78. data/app/helpers/spree/mail_helper.rb +0 -27
  79. data/app/jobs/spree/api_key_touch_job.rb +0 -9
  80. data/app/mailers/spree/test_mailer.rb +0 -8
  81. data/app/models/action_text/video_embed.rb +0 -13
  82. data/app/models/concerns/spree/has_one_link.rb +0 -42
  83. data/app/models/concerns/spree/linkable.rb +0 -9
  84. data/app/models/concerns/spree/previewable.rb +0 -17
  85. data/app/models/spree/data_feed/google.rb +0 -15
  86. data/app/models/spree/data_feed.rb +0 -42
  87. data/app/models/spree/gateway/bogus_simple.rb +0 -24
  88. data/app/models/spree/post.rb +0 -108
  89. data/app/models/spree/post_category.rb +0 -46
  90. data/app/models/spree/product_property.rb +0 -51
  91. data/app/models/spree/property.rb +0 -86
  92. data/app/models/spree/property_prototype.rb +0 -9
  93. data/app/models/spree/store_favicon_image.rb +0 -20
  94. data/app/models/spree/store_logo.rb +0 -4
  95. data/app/models/spree/store_mailer_logo.rb +0 -7
  96. data/app/models/spree/taxon_image/configuration/active_storage.rb +0 -26
  97. data/app/models/spree/taxon_image.rb +0 -28
  98. data/app/presenters/spree/filters/properties_presenter.rb +0 -23
  99. data/app/presenters/spree/filters/property_presenter.rb +0 -42
  100. data/app/services/spree/data_feeds/google/optional_attributes.rb +0 -23
  101. data/app/services/spree/data_feeds/google/optional_sub_attributes.rb +0 -21
  102. data/app/services/spree/data_feeds/google/products_list.rb +0 -14
  103. data/app/services/spree/data_feeds/google/required_attributes.rb +0 -68
  104. data/app/services/spree/data_feeds/google/rss.rb +0 -109
  105. data/app/sorters/spree/posts/sort.rb +0 -40
  106. data/app/views/action_text/video_embeds/_thumbnail.html.erb +0 -1
  107. data/app/views/action_text/video_embeds/_video_embed.html.erb +0 -3
  108. data/app/views/layouts/action_text/contents/_content.html.erb +0 -3
  109. data/app/views/spree/test_mailer/test_email.html.erb +0 -40
  110. data/app/views/spree/test_mailer/test_email.text.erb +0 -4
  111. data/config/initializers/oembed.rb +0 -1
  112. data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +0 -14
  113. data/db/migrate/20230109084253_create_product_property_translations.rb +0 -24
  114. data/db/migrate/20230109105943_create_property_translations.rb +0 -24
  115. data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +0 -5
  116. data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +0 -7
  117. data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +0 -5
  118. data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +0 -5
  119. data/db/migrate/20240914153106_add_display_on_to_spree_properties.rb +0 -5
  120. data/db/migrate/20240915144935_add_position_to_spree_properties.rb +0 -6
  121. data/db/migrate/20250121160028_create_spree_posts_and_spree_post_categories.rb +0 -33
  122. data/db/migrate/20250127083740_add_kind_to_spree_properties.rb +0 -5
  123. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
  124. data/db/migrate/20250305121657_remove_spree_posts_indices.rb +0 -7
  125. data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +0 -24
  126. data/db/sample_data/posts.rb +0 -7
  127. data/lib/spree/core/controller_helpers/search.rb +0 -17
  128. data/lib/spree/core/product_filters.rb +0 -218
  129. data/lib/spree/core/query_filters/comparable.rb +0 -50
  130. data/lib/spree/core/query_filters/date.rb +0 -8
  131. data/lib/spree/core/query_filters/number.rb +0 -8
  132. data/lib/spree/core/query_filters/text.rb +0 -36
  133. data/lib/spree/core/query_filters.rb +0 -11
  134. data/lib/spree/core/search/base.rb +0 -144
  135. data/lib/spree/testing_support/factories/favicon_image_factory.rb +0 -9
  136. data/lib/spree/testing_support/factories/google_data_feed_factory.rb +0 -7
  137. data/lib/spree/testing_support/factories/post_category_factory.rb +0 -7
  138. data/lib/spree/testing_support/factories/post_factory.rb +0 -22
  139. data/lib/spree/testing_support/factories/product_property_factory.rb +0 -7
  140. data/lib/spree/testing_support/factories/property_factory.rb +0 -28
  141. data/lib/spree/testing_support/factories/taxon_image_factory.rb +0 -9
  142. data/lib/spree/testing_support/flash.rb +0 -25
  143. data/lib/spree/testing_support/flatpickr_capybara.rb +0 -124
  144. data/lib/tasks/exchanges.rake +0 -66
@@ -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
@@ -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
 
@@ -92,7 +92,7 @@ module Spree
92
92
  self.whitelisted_ransackable_scopes = %w[refunded partially_refunded multi_search]
93
93
 
94
94
  attr_reader :coupon_code
95
- attr_accessor :temporary_address, :temporary_credit_card
95
+ attr_accessor :temporary_address
96
96
 
97
97
  attribute :state_machine_resumed, :boolean
98
98
 
@@ -159,6 +159,7 @@ module Spree
159
159
  # Needs to happen before save_permalink is called
160
160
  before_validation :ensure_store_presence
161
161
  before_validation :ensure_currency_presence
162
+ before_validation :ensure_locale_presence
162
163
 
163
164
  before_validation :clone_billing_address, if: :use_billing?
164
165
  before_validation :clone_shipping_address, if: :use_shipping?
@@ -174,6 +175,7 @@ module Spree
174
175
  validates :item_count, numericality: { greater_than_or_equal_to: 0, less_than: 2**31, only_integer: true, allow_blank: true }
175
176
  validates :store
176
177
  validates :currency
178
+ validates :locale
177
179
  end
178
180
  validates :payment_state, inclusion: { in: PAYMENT_STATES, allow_blank: true }
179
181
  validates :shipment_state, inclusion: { in: SHIPMENT_STATES, allow_blank: true }
@@ -186,6 +188,7 @@ module Spree
186
188
  validates :promo_total, NEGATIVE_MONEY_VALIDATION
187
189
  validates :total, MONEY_VALIDATION
188
190
  validate :currency_must_be_supported_by_store
191
+ validate :locale_must_be_supported_by_store
189
192
 
190
193
  delegate :update_totals, :persist_totals, to: :updater
191
194
  delegate :merge!, to: :merger
@@ -529,16 +532,6 @@ module Spree
529
532
  complete? || canceled? || returned?
530
533
  end
531
534
 
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
535
  # Finalizes an in progress order after checkout is complete.
543
536
  # Called after transition to complete state when payments will have been processed
544
537
  def finalize!
@@ -997,6 +990,12 @@ module Spree
997
990
  self.currency ||= store&.default_currency
998
991
  end
999
992
 
993
+ # Sets the locale from Spree::Current.locale when not already set.
994
+ # Called as a before_validation callback, mirroring ensure_currency_presence.
995
+ def ensure_locale_presence
996
+ self.locale ||= Spree::Current.locale
997
+ end
998
+
1000
999
  def currency_must_be_supported_by_store
1001
1000
  return if currency.blank? || store.blank?
1002
1001
 
@@ -1006,6 +1005,16 @@ module Spree
1006
1005
  end
1007
1006
  end
1008
1007
 
1008
+ # Validates that the order's locale is within the store's supported locales.
1009
+ # Mirrors currency_must_be_supported_by_store.
1010
+ def locale_must_be_supported_by_store
1011
+ return if locale.blank? || store.blank?
1012
+
1013
+ unless store.supported_locales_list.include?(locale)
1014
+ errors.add(:locale, Spree.t(:locale_not_supported_by_store))
1015
+ end
1016
+ end
1017
+
1009
1018
  def collect_payment_methods
1010
1019
  Spree::Deprecation.warn('`Order#collect_payment_methods` is deprecated and will be removed in Spree 5.5. Use `collect_frontend_payment_methods` instead.')
1011
1020
 
@@ -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
  {},
@@ -0,0 +1,70 @@
1
+ module Spree
2
+ # Lightweight value object representing a payment gateway response.
3
+ #
4
+ # This is Spree's native replacement for +ActiveMerchant::Billing::Response+.
5
+ # It carries the same interface so existing payment method implementations
6
+ # (Bogus, Check, StoreCredit, and third-party gateways) can return it without
7
+ # any callers needing to change.
8
+ #
9
+ # @example Successful authorization
10
+ # Spree::PaymentResponse.new(true, 'Transaction approved', {},
11
+ # authorization: 'ch_abc123',
12
+ # avs_result: { code: 'D' },
13
+ # cvv_result: { code: 'M', message: 'Match' },
14
+ # test: true)
15
+ #
16
+ # @example Failed charge
17
+ # Spree::PaymentResponse.new(false, 'Card declined', { message: 'Insufficient funds' })
18
+ #
19
+ class PaymentResponse
20
+ # @return [HashWithIndifferentAccess] raw gateway params
21
+ attr_reader :params
22
+
23
+ # @return [String] human-readable result message
24
+ attr_reader :message
25
+
26
+ # @return [Boolean] whether the response came from a test/sandbox environment
27
+ attr_reader :test
28
+
29
+ # @return [String, nil] gateway authorization / transaction reference
30
+ attr_reader :authorization
31
+
32
+ # @return [Hash] AVS (Address Verification System) result with +"code"+ key
33
+ attr_reader :avs_result
34
+
35
+ # @return [Hash] CVV result with +"code"+ and +"message"+ keys
36
+ attr_reader :cvv_result
37
+
38
+ # @param success [Boolean] whether the gateway action succeeded
39
+ # @param message [String] human-readable result message
40
+ # @param params [Hash] raw key/value pairs returned by the gateway
41
+ # @param options [Hash] additional metadata
42
+ # @option options [String] :authorization gateway transaction reference
43
+ # @option options [Hash] :avs_result must contain +:code+
44
+ # @option options [Hash] :cvv_result may contain +:code+ and +:message+
45
+ # @option options [Boolean] :test sandbox/test flag
46
+ def initialize(success, message, params = {}, options = {})
47
+ @success = success
48
+ @message = message
49
+ @params = params.with_indifferent_access
50
+ @test = options[:test] || false
51
+ @authorization = options[:authorization]
52
+ @avs_result = options[:avs_result] ? { 'code' => options[:avs_result][:code] } : { 'code' => nil }
53
+ @cvv_result = if options[:cvv_result]
54
+ { 'code' => options[:cvv_result][:code], 'message' => options[:cvv_result][:message] }
55
+ else
56
+ { 'code' => nil, 'message' => nil }
57
+ end
58
+ end
59
+
60
+ # @return [Boolean] whether the gateway action succeeded
61
+ def success?
62
+ @success
63
+ end
64
+
65
+ # @return [Boolean] whether the response came from a test/sandbox environment
66
+ def test?
67
+ @test
68
+ end
69
+ end
70
+ end
@@ -15,8 +15,6 @@ module Spree
15
15
  can :read, Spree::OptionType
16
16
  can :read, Spree::OptionValue
17
17
  can :read, Spree::Product
18
- can :read, Spree::ProductProperty
19
- can :read, Spree::Property
20
18
  can :read, Spree::State
21
19
  can :read, Spree::Store
22
20
  can :read, Spree::Taxon
@@ -26,9 +24,6 @@ module Spree
26
24
 
27
25
  # Content pages
28
26
  can :read, Spree::Policy
29
- can :read, Spree::Page if defined?(Spree::Page)
30
- can :read, Spree::Post if defined?(Spree::Post)
31
- can :read, Spree::PostCategory if defined?(Spree::PostCategory)
32
27
 
33
28
  # Order management for the user's own orders
34
29
  can :create, Spree::Order
@@ -14,8 +14,6 @@ module Spree
14
14
  can [:read, :admin], Spree::Variant
15
15
  can [:read, :admin], Spree::OptionType
16
16
  can [:read, :admin], Spree::OptionValue
17
- can [:read, :admin], Spree::Property
18
- can [:read, :admin], Spree::ProductProperty
19
17
  can [:read, :admin], Spree::Metafield
20
18
  can [:read, :admin], Spree::Taxon
21
19
  can [:read, :admin], Spree::Taxonomy
@@ -14,8 +14,6 @@ module Spree
14
14
  can :manage, Spree::Variant
15
15
  can :manage, Spree::OptionType
16
16
  can :manage, Spree::OptionValue
17
- can :manage, Spree::Property
18
- can :manage, Spree::ProductProperty
19
17
  can :manage, Spree::Taxon
20
18
  can :manage, Spree::Taxonomy
21
19
  can :manage, Spree::Classification