solidus_core 4.7.2 → 4.8.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +3 -0
  3. data/app/helpers/spree/base_helper.rb +3 -3
  4. data/app/helpers/spree/core/controller_helpers/auth.rb +1 -1
  5. data/app/helpers/spree/core/controller_helpers/common.rb +2 -2
  6. data/app/helpers/spree/core/controller_helpers/timezone.rb +55 -0
  7. data/app/helpers/spree/products_helper.rb +2 -2
  8. data/app/jobs/spree/base_job.rb +1 -1
  9. data/app/mailers/spree/base_mailer.rb +2 -2
  10. data/app/models/spree/address.rb +1 -1
  11. data/app/models/spree/adjustment.rb +1 -1
  12. data/app/models/spree/calculator/flat_rate.rb +2 -2
  13. data/app/models/spree/calculator/shipping/flat_rate.rb +1 -1
  14. data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -1
  15. data/app/models/spree/calculator/shipping/per_item.rb +1 -1
  16. data/app/models/spree/calculator/shipping/price_sack.rb +1 -1
  17. data/app/models/spree/calculator.rb +1 -1
  18. data/app/models/spree/country.rb +1 -1
  19. data/app/models/spree/customer_return.rb +7 -0
  20. data/app/models/spree/exchange.rb +4 -1
  21. data/app/models/spree/image/active_storage_attachment.rb +15 -0
  22. data/app/models/spree/image/paperclip_attachment.rb +3 -0
  23. data/app/models/spree/image.rb +4 -0
  24. data/app/models/spree/log_entry.rb +26 -22
  25. data/app/models/spree/money.rb +2 -2
  26. data/app/models/spree/order/payments.rb +1 -1
  27. data/app/models/spree/order.rb +15 -17
  28. data/app/models/spree/order_cancellations.rb +1 -0
  29. data/app/models/spree/order_mutex.rb +1 -1
  30. data/app/models/spree/order_shipping.rb +17 -0
  31. data/app/models/spree/order_updater.rb +1 -1
  32. data/app/models/spree/payment_method/bogus_credit_card.rb +4 -0
  33. data/app/models/spree/payment_method/check.rb +4 -2
  34. data/app/models/spree/payment_method.rb +1 -1
  35. data/app/models/spree/price.rb +1 -1
  36. data/app/models/spree/product.rb +1 -1
  37. data/app/models/spree/product_option_type.rb +2 -0
  38. data/app/models/spree/refund.rb +17 -0
  39. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +1 -1
  40. data/app/models/spree/return_item.rb +1 -1
  41. data/app/models/spree/shipment.rb +5 -3
  42. data/app/models/spree/stock/inventory_unit_builder.rb +1 -0
  43. data/app/models/spree/stock/simple_coordinator.rb +9 -2
  44. data/app/models/spree/store.rb +8 -1
  45. data/app/models/spree/store_credit.rb +1 -1
  46. data/app/models/spree/tax_calculator/default.rb +2 -0
  47. data/app/models/spree/variant/pricing_options.rb +1 -1
  48. data/app/models/spree/variant.rb +3 -3
  49. data/config/locales/en.yml +13 -0
  50. data/db/default/spree/states.rb +1 -1
  51. data/db/migrate/20260924120000_add_address_to_spree_stores.rb +9 -0
  52. data/lib/generators/solidus/install/app_templates/authentication/existing.rb +0 -2
  53. data/lib/generators/solidus/install/app_templates/frontend/starter.rb +19 -1
  54. data/lib/generators/solidus/install/app_templates/payment_method/paypal.rb +2 -1
  55. data/lib/generators/solidus/install/install_generator.rb +7 -7
  56. data/lib/spree/app_configuration.rb +51 -0
  57. data/lib/spree/core/search/base.rb +3 -1
  58. data/lib/spree/core/version.rb +2 -2
  59. data/lib/spree/core.rb +0 -1
  60. data/lib/spree/permitted_attributes.rb +3 -2
  61. data/lib/spree/testing_support/capybara_ext.rb +0 -10
  62. data/lib/spree/testing_support/factories/calculator_factory.rb +6 -0
  63. data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -1
  64. data/lib/spree/testing_support/factories/store_factory.rb +4 -0
  65. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  66. data/solidus_core.gemspec +3 -3
  67. metadata +13 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3d8242467851026fff5d6e6c31639174974d747193e9bef00a77acbd7c6e912
4
- data.tar.gz: 7336f44aad6dc32d2d477acffe0de1af5acb1df5fa7bd399b3b754e3647269e3
3
+ metadata.gz: '01835b000500c6523be13d69e3b323203e5e5e5e8cdc0879c20b80aecbc4ab6e'
4
+ data.tar.gz: 81ad53d980d3d2c9fca7cce88e1b9fa0546114e6431d064ac53b46edfeb85e92
5
5
  SHA512:
6
- metadata.gz: e0521407fa49fac2011d849d4fa08f03a165c32751ffdda07656dd985c3aa73a3ea0b90e4d7b6dfc7157a310fc371d3195b9e7e3f1d1b1b517d3c927370d01c8
7
- data.tar.gz: 4619fdf8d485b61ba1610d4c579fd83f82e4f0710648719b07c4ec4e246915a6d2de458f351560c375b212c84a37db01d9b9463608f1f141e27e531e363a958f
6
+ metadata.gz: af199e02adbf3b4ee94bfb927bc2a6f7b5320737a65a7de7147700e3f5a4bec71147ae1b20881f27b71d522321833c9eaab60873bfb3cfe91eee48bcb40f9c53
7
+ data.tar.gz: 9ca9fbf1c2706cc69e11654026e6f64f2a623051c70dce63e57a9db46ba03542f98888371751e1c6fa19d2f731f35c839612f9423ca9d78ee968abf3cd93f0bf
data/LICENSE CHANGED
@@ -1,6 +1,9 @@
1
1
  Copyright (c) 2007-2014, Spree Commerce, Inc. and other contributors
2
2
  All rights reserved.
3
3
 
4
+ Copyright (c) 2015-2026, Solidus contributors
5
+ All rights reserved.
6
+
4
7
  Redistribution and use in source and binary forms, with or without modification,
5
8
  are permitted provided that the following conditions are met:
6
9
 
@@ -57,7 +57,7 @@ module Spree
57
57
  @body_class
58
58
  end
59
59
 
60
- def logo(image_path = Spree::Config[:logo])
60
+ def logo(image_path = Spree::Config.logo)
61
61
  link_to image_tag(image_path), spree.root_path
62
62
  end
63
63
 
@@ -93,7 +93,7 @@ module Spree
93
93
  end
94
94
  end
95
95
 
96
- content_tag(:nav, content_tag(:ol, raw(items.map(&:mb_chars).join), class: breadcrumb_class, itemscope: "", itemtype: "https://schema.org/BreadcrumbList"), id: "breadcrumbs", class: "sixteen columns")
96
+ content_tag(:nav, content_tag(:ol, safe_join(items), class: breadcrumb_class, itemscope: "", itemtype: "https://schema.org/BreadcrumbList"), id: "breadcrumbs", class: "sixteen columns")
97
97
  end
98
98
 
99
99
  def taxons_tree(root_taxon, current_taxon, max_level = 1)
@@ -110,7 +110,7 @@ module Spree
110
110
  end
111
111
  end
112
112
 
113
- def available_countries(restrict_to_zone: Spree::Config[:checkout_zone])
113
+ def available_countries(restrict_to_zone: Spree::Config.checkout_zone)
114
114
  countries = Spree::Country.available(restrict_to_zone:)
115
115
 
116
116
  country_names = Carmen::Country.all.map do |country|
@@ -37,7 +37,7 @@ module Spree
37
37
 
38
38
  def set_guest_token
39
39
  if cookies.signed[:guest_token].blank?
40
- cookies.permanent.signed[:guest_token] = Spree::Config[:guest_token_cookie_options].merge(
40
+ cookies.permanent.signed[:guest_token] = Spree::Config.guest_token_cookie_options.merge(
41
41
  value: SecureRandom.urlsafe_base64(nil, false),
42
42
  httponly: true
43
43
  )
@@ -27,7 +27,7 @@ module Spree
27
27
  def title
28
28
  title_string = @title.presence || accurate_title
29
29
  if title_string.present?
30
- if Spree::Config[:always_put_site_name_in_title]
30
+ if Spree::Config.always_put_site_name_in_title
31
31
  [title_string, default_title].join(" - ")
32
32
  else
33
33
  title_string
@@ -75,7 +75,7 @@ module Spree
75
75
  # Default layout is: +app/views/spree/layouts/spree_application+
76
76
  #
77
77
  def get_layout
78
- Spree::Config[:layout]
78
+ Spree::Config.layout
79
79
  end
80
80
  end
81
81
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Core
5
+ module ControllerHelpers
6
+ module Timezone
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ around_action :set_timezone
11
+ end
12
+
13
+ private
14
+
15
+ # Sets the timezone for the current request.
16
+ #
17
+ # Uses the most preferred timezone or falls back to the server default.
18
+ #
19
+ # It respects the server's configured timezone from +config/application.rb+.
20
+ #
21
+ def set_timezone(&action)
22
+ timezone = if timezone_change_needed?
23
+ resolved_timezone || Time.zone.name
24
+ else
25
+ session[:solidus_timezone]
26
+ end
27
+ session[:solidus_timezone] = timezone
28
+ Time.use_zone(timezone, &action)
29
+ end
30
+
31
+ # Checks if we need to change the timezone or not.
32
+ def timezone_change_needed?
33
+ params[:solidus_timezone].present? || session[:solidus_timezone].blank?
34
+ end
35
+
36
+ # Returns the first valid timezone from the priority chain, or nil.
37
+ #
38
+ # The priority order is:
39
+ #
40
+ # * the passed parameter: +params[:solidus_timezone]+
41
+ # * the user's timezone preference
42
+ #
43
+ def resolved_timezone
44
+ candidates = [params[:solidus_timezone], timezone_from_user].compact
45
+ candidates.detect { |tz| ActiveSupport::TimeZone[tz].present? }
46
+ end
47
+
48
+ # Try to get the timezone from user settings.
49
+ def timezone_from_user
50
+ spree_current_user.try(:timezone).presence
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -8,7 +8,7 @@ module Spree
8
8
  # @param variant [Spree::Variant] the variant
9
9
  # @return [Spree::Money] the price or price diff
10
10
  def variant_price(variant)
11
- if Spree::Config[:show_variant_full_price]
11
+ if Spree::Config.show_variant_full_price
12
12
  variant_full_price(variant)
13
13
  else
14
14
  variant_price_diff(variant)
@@ -46,7 +46,7 @@ module Spree
46
46
  # @param product [Spree::Product] the product whose description you want to filter
47
47
  # @return [String] the generated HTML
48
48
  def product_description(product)
49
- if Spree::Config[:show_raw_product_description]
49
+ if Spree::Config.show_raw_product_description
50
50
  raw(product.description)
51
51
  else
52
52
  sanitize(product.description.gsub(/(.*?)\r?\n\r?\n/m, '<p>\1</p>'))
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spree
4
4
  # Base class for all Solidus background jobs
5
- class BaseJob < ActiveJob::Base # rubocop:disable Rails/ApplicationJob
5
+ class BaseJob < ActiveJob::Base
6
6
  # Automatically retry jobs that encountered a deadlock
7
7
  retry_on ActiveRecord::Deadlocked
8
8
 
@@ -10,13 +10,13 @@ module Spree
10
10
  store.bcc_email
11
11
  end
12
12
 
13
- def money(amount, currency = Spree::Config[:currency])
13
+ def money(amount, currency = Spree::Config.currency)
14
14
  Spree::Money.new(amount, currency:).to_s
15
15
  end
16
16
  helper_method :money
17
17
 
18
18
  def mail(headers = {}, &block)
19
- super if Spree::Config[:send_core_emails]
19
+ super if Spree::Config.send_core_emails
20
20
  end
21
21
  end
22
22
  end
@@ -118,7 +118,7 @@ module Spree
118
118
  # @return [true] whether or not the address requires a phone number to be
119
119
  # present
120
120
  def require_phone?
121
- Spree::Config[:address_requires_phone]
121
+ Spree::Config.address_requires_phone
122
122
  end
123
123
 
124
124
  # @todo Remove this from the public API if possible.
@@ -65,7 +65,7 @@ module Spree
65
65
  end
66
66
 
67
67
  def currency
68
- adjustable ? adjustable.currency : Spree::Config[:currency]
68
+ adjustable ? adjustable.currency : Spree::Config.currency
69
69
  end
70
70
 
71
71
  # @return [Boolean] true when this is a promotion adjustment (Promotion adjustments have a {PromotionAction} source)
@@ -5,13 +5,13 @@ require_dependency "spree/calculator"
5
5
  module Spree
6
6
  class Calculator::FlatRate < Calculator
7
7
  preference :amount, :decimal, default: 0
8
- preference :currency, :string, default: -> { Spree::Config[:currency] }
8
+ preference :currency, :string, default: -> { Spree::Config.currency }
9
9
 
10
10
  def compute(object = nil)
11
11
  if object && preferred_currency.casecmp(object.currency).zero?
12
12
  preferred_amount
13
13
  else
14
- 0
14
+ Spree::ZERO
15
15
  end
16
16
  end
17
17
  end
@@ -7,7 +7,7 @@ module Spree
7
7
  module Calculator::Shipping
8
8
  class FlatRate < ShippingCalculator
9
9
  preference :amount, :decimal, default: 0
10
- preference :currency, :string, default: -> { Spree::Config[:currency] }
10
+ preference :currency, :string, default: -> { Spree::Config.currency }
11
11
 
12
12
  def compute_package(_package)
13
13
  preferred_amount
@@ -9,7 +9,7 @@ module Spree
9
9
  preference :first_item, :decimal, default: 0.0
10
10
  preference :additional_item, :decimal, default: 0.0
11
11
  preference :max_items, :integer, default: 0
12
- preference :currency, :string, default: -> { Spree::Config[:currency] }
12
+ preference :currency, :string, default: -> { Spree::Config.currency }
13
13
 
14
14
  def compute_package(package)
15
15
  compute_from_quantity(package.contents.sum(&:quantity))
@@ -7,7 +7,7 @@ module Spree
7
7
  module Calculator::Shipping
8
8
  class PerItem < ShippingCalculator
9
9
  preference :amount, :decimal, default: 0
10
- preference :currency, :string, default: -> { Spree::Config[:currency] }
10
+ preference :currency, :string, default: -> { Spree::Config.currency }
11
11
 
12
12
  def compute_package(package)
13
13
  compute_from_quantity(package.contents.sum(&:quantity))
@@ -9,7 +9,7 @@ module Spree
9
9
  preference :minimal_amount, :decimal, default: 0
10
10
  preference :normal_amount, :decimal, default: 0
11
11
  preference :discount_amount, :decimal, default: 0
12
- preference :currency, :string, default: -> { Spree::Config[:currency] }
12
+ preference :currency, :string, default: -> { Spree::Config.currency }
13
13
 
14
14
  def compute_package(package)
15
15
  compute_from_price(total(package.contents))
@@ -21,7 +21,7 @@ module Spree
21
21
  # Spree::ShippingRate)
22
22
  # @param ... [args, kwargs] Additional arguments passed to the specific compute method
23
23
  #
24
- # @return [BigDecimal, Numeric] The computed amount
24
+ # @return [BigDecimal] The computed amount
25
25
  #
26
26
  # @raise [NotImplementedError] If the calculator doesn't implement the required compute method
27
27
  #
@@ -25,7 +25,7 @@ module Spree
25
25
  find_by!(iso: Spree::Config.default_country_iso)
26
26
  end
27
27
 
28
- def self.available(restrict_to_zone: Spree::Config[:checkout_zone])
28
+ def self.available(restrict_to_zone: Spree::Config.checkout_zone)
29
29
  checkout_zone = Zone.find_by(name: restrict_to_zone)
30
30
 
31
31
  return checkout_zone.country_list if checkout_zone.try(:kind) == "country"
@@ -15,6 +15,7 @@ module Spree
15
15
 
16
16
  validates :return_items, presence: true
17
17
  validate :return_items_belong_to_same_order
18
+ validate :return_items_have_returnable_inventory_units, on: :create
18
19
 
19
20
  accepts_nested_attributes_for :return_items
20
21
 
@@ -73,6 +74,12 @@ module Spree
73
74
  end
74
75
  end
75
76
 
77
+ def return_items_have_returnable_inventory_units
78
+ return if inventory_units.compact.all?(&:can_return?)
79
+
80
+ errors.add(:base, I18n.t("spree.return_items_must_have_shipped_inventory_units"))
81
+ end
82
+
76
83
  def inventory_units
77
84
  return_items.flat_map(&:inventory_unit)
78
85
  end
@@ -22,7 +22,10 @@ module Spree
22
22
 
23
23
  def perform!
24
24
  begin
25
- shipments = Spree::Config.stock.coordinator_class.new(@order, @reimbursement_objects.map(&:build_exchange_inventory_unit)).shipments
25
+ shipments = Spree::Config.stock.coordinator_class.new(
26
+ @order,
27
+ inventory_units: @reimbursement_objects.map(&:build_exchange_inventory_unit)
28
+ ).shipments
26
29
  rescue Spree::Order::InsufficientStock
27
30
  raise UnableToCreateShipments.new("Could not generate shipments for all items. Out of stock?")
28
31
  end
@@ -21,4 +21,19 @@ module Spree::Image::ActiveStorageAttachment
21
21
  end
22
22
  end
23
23
  end
24
+
25
+ # Returns the preload tree needed to resolve image.url(style) and image.alt
26
+ # without N+1 queries. Used by callers to nest preloads under image-bearing
27
+ # associations on parent records, e.g.
28
+ # includes(variant_images: Spree::Image.attachment_preloads)
29
+ #
30
+ # We build this manually rather than delegating to with_attached_attachment
31
+ # because Rails' generated scope includes a preview_image_attachment branch
32
+ # that is only relevant for non-image files (PDFs, videos, etc.). Product
33
+ # images never have previews, so including that branch loads empty result
34
+ # sets from active_storage_attachments and active_storage_blobs on every
35
+ # request for no benefit.
36
+ def self.attachment_preloads = [
37
+ {attachment_attachment: {blob: {variant_records: {image_attachment: :blob}}}}
38
+ ]
24
39
  end
@@ -22,6 +22,9 @@ module Spree::Image::PaperclipAttachment
22
22
  after_post_process :find_dimensions, if: :valid?
23
23
  end
24
24
 
25
+ # When using the Paperclip adapter, attachment preloads are a no-op.
26
+ def self.attachment_preloads = []
27
+
25
28
  def url(size)
26
29
  attachment.url(size)
27
30
  end
@@ -3,5 +3,9 @@
3
3
  module Spree
4
4
  class Image < Asset
5
5
  include ::Spree::Config.image_attachment_module
6
+
7
+ def self.attachment_preloads
8
+ Spree::Config.image_attachment_module.attachment_preloads
9
+ end
6
10
  end
7
11
  end
@@ -11,8 +11,10 @@ module Spree
11
11
  CORE_PERMITTED_CLASSES = [
12
12
  ActiveMerchant::Billing::Response,
13
13
  ActiveSupport::TimeWithZone,
14
+ ActiveSupport::HashWithIndifferentAccess,
14
15
  Time,
15
- ActiveSupport::TimeZone
16
+ ActiveSupport::TimeZone,
17
+ Symbol
16
18
  ].freeze
17
19
 
18
20
  class SerializationError < RuntimeError
@@ -65,38 +67,40 @@ module Spree
65
67
  belongs_to :source, polymorphic: true, optional: true
66
68
 
67
69
  def parsed_details
68
- handle_psych_serialization_errors do
69
- @details ||= YAML.safe_load(
70
- details,
71
- permitted_classes: self.class.permitted_classes,
72
- aliases: Spree::Config.log_entry_allow_aliases
73
- )
74
- end
70
+ @details ||= deserialize_details
75
71
  end
76
72
 
77
73
  def parsed_details=(value)
78
- handle_psych_serialization_errors do
79
- self.details = YAML.safe_dump(
80
- value,
81
- permitted_classes: self.class.permitted_classes,
82
- aliases: Spree::Config.log_entry_allow_aliases
83
- )
84
- end
74
+ self.details = YAML.dump(value)
85
75
  end
86
76
 
87
77
  def parsed_payment_response_details_with_fallback=(response)
88
78
  self.parsed_details = response
89
- rescue SerializationError, YAML::Exception => e
90
- # Fall back on wrapping the response and signaling the error to the end user.
91
- self.parsed_details = ActiveMerchant::Billing::Response.new(
92
- response.success?,
93
- "[WARNING: An error occurred while trying to serialize the payment response] #{response.message}",
94
- {"data" => response.inspect, "error" => e.message.to_s}
95
- )
96
79
  end
97
80
 
98
81
  private
99
82
 
83
+ def deserialize_details
84
+ handle_psych_serialization_errors do
85
+ YAML.safe_load(
86
+ details,
87
+ permitted_classes: self.class.permitted_classes,
88
+ aliases: Spree::Config.log_entry_allow_aliases
89
+ )
90
+ end
91
+ rescue SerializationError, Psych::Exception => e
92
+ # The stored response could not be deserialized (a class that isn't
93
+ # permitted, a disabled alias, or malformed YAML). Keep the record readable
94
+ # by wrapping the raw details and surfacing the reason instead of raising,
95
+ # while reporting the error so it stays visible to developers.
96
+ Rails.error.report(e, handled: true, severity: :warning, context: {spree_log_entry_id: id})
97
+ ActiveMerchant::Billing::Response.new(
98
+ false,
99
+ "[WARNING: An error occurred while trying to deserialize the stored payment response] #{e.message}",
100
+ {"data" => details, "error" => e.message}
101
+ )
102
+ end
103
+
100
104
  def handle_psych_serialization_errors
101
105
  yield
102
106
  rescue Psych::DisallowedClass => e
@@ -11,7 +11,7 @@ module Spree
11
11
  class << self
12
12
  attr_accessor :default_formatting_rules
13
13
 
14
- def parse(amount, currency = Spree::Config[:currency])
14
+ def parse(amount, currency = Spree::Config.currency)
15
15
  new(parse_to_money(amount, currency))
16
16
  end
17
17
 
@@ -36,7 +36,7 @@ module Spree
36
36
  if amount.is_a?(::Money)
37
37
  @money = amount
38
38
  else
39
- currency = options[:currency] || Spree::Config[:currency]
39
+ currency = options[:currency] || Spree::Config.currency
40
40
 
41
41
  @money = Monetize.from_string(amount, currency)
42
42
  end
@@ -47,7 +47,7 @@ module Spree
47
47
  payment.public_send(method_name)
48
48
  end
49
49
  rescue Core::GatewayError => error
50
- result = !!Spree::Config[:allow_checkout_on_gateway_error]
50
+ result = !!Spree::Config.allow_checkout_on_gateway_error
51
51
  errors.add(:base, error.message) && (return result)
52
52
  end
53
53
  end
@@ -151,7 +151,8 @@ module Spree
151
151
  validates :email, presence: true, if: :email_required?
152
152
  validates :email, "spree/email" => true, :allow_blank => true
153
153
  validates :guest_token, presence: {allow_nil: true}
154
- validates :number, presence: true, uniqueness: {allow_blank: true, case_sensitive: true}
154
+ validates :number, presence: true
155
+ validates :number, uniqueness: {allow_blank: true, case_sensitive: true}, if: :number_changed?
155
156
  validates :store_id, presence: true
156
157
 
157
158
  def self.find_by_param(value)
@@ -237,7 +238,7 @@ module Spree
237
238
  end
238
239
 
239
240
  def currency
240
- self[:currency] || Spree::Config[:currency]
241
+ self[:currency] || Spree::Config.currency
241
242
  end
242
243
 
243
244
  def shipping_discount
@@ -271,7 +272,7 @@ module Spree
271
272
 
272
273
  # Returns the address for taxation based on configuration
273
274
  def tax_address
274
- if Spree::Config[:tax_using_ship_address]
275
+ if Spree::Config.tax_using_ship_address
275
276
  ship_address
276
277
  else
277
278
  bill_address
@@ -307,6 +308,14 @@ module Spree
307
308
  inventory_units.reload.all?(&:returned?)
308
309
  end
309
310
 
311
+ # Since this method is called from within the order state machine, we cannot
312
+ # simply delegate this to :shipping. The state_machines gem uses the arity
313
+ # of the method to determine if it should pass in the transition as an
314
+ # argument. The arity of the method is masked when using delegates.
315
+ def create_proposed_shipments
316
+ shipping.create_proposed_shipments
317
+ end
318
+
310
319
  def contents
311
320
  @contents ||= Spree::Config.order_contents_class.new(self)
312
321
  end
@@ -501,21 +510,10 @@ module Spree
501
510
  Spree::Config.billing_address_required
502
511
  end
503
512
 
504
- def create_proposed_shipments
505
- if completed?
506
- raise CannotRebuildShipments.new(I18n.t("spree.cannot_rebuild_shipments_order_completed"))
507
- elsif shipments.any? { |shipment| !shipment.pending? }
508
- raise CannotRebuildShipments.new(I18n.t("spree.cannot_rebuild_shipments_shipments_not_pending"))
509
- else
510
- shipments.destroy_all
511
- shipments.push(*Spree::Config.stock.coordinator_class.new(self).shipments)
512
- end
513
- end
514
-
515
513
  def create_shipments_for_line_item(line_item)
516
- units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)
514
+ inventory_units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)
517
515
 
518
- Spree::Config.stock.coordinator_class.new(self, units).shipments.each do |shipment|
516
+ Spree::Config.stock.coordinator_class.new(self, inventory_units:).shipments.each do |shipment|
519
517
  shipments << shipment
520
518
  end
521
519
  end
@@ -879,7 +877,7 @@ module Spree
879
877
  end
880
878
 
881
879
  def set_currency
882
- self.currency = Spree::Config[:currency] if self[:currency].nil?
880
+ self.currency = Spree::Config.currency if self[:currency].nil?
883
881
  end
884
882
 
885
883
  def create_token
@@ -53,6 +53,7 @@ class Spree::OrderCancellations
53
53
  end
54
54
  end
55
55
 
56
+ @order.refresh_shipment_rates
56
57
  @order.recalculate
57
58
 
58
59
  short_ship_tax_notifier&.call(unit_cancels)
@@ -6,7 +6,7 @@ module Spree
6
6
 
7
7
  belongs_to :order, class_name: "Spree::Order", optional: true
8
8
 
9
- scope :expired, -> { where(arel_table[:created_at].lteq(Spree::Config[:order_mutex_max_age].seconds.ago)) }
9
+ scope :expired, -> { where(arel_table[:created_at].lteq(Spree::Config.order_mutex_max_age.seconds.ago)) }
10
10
 
11
11
  class << self
12
12
  # Obtain a lock on an order, execute the supplied block and then release the lock.
@@ -8,6 +8,23 @@ class Spree::OrderShipping
8
8
  @order = order
9
9
  end
10
10
 
11
+ # A method that destroys the current shipments on an order, creates new
12
+ # shipments for the line items, and assigns them to the order.
13
+ #
14
+ # @return [Array<Spree::Shipment>] The created shipments
15
+ # @raise [Spree::Order::CannotRebuildShipments] raised if order is completed
16
+ # or there are any non-pending shipments.
17
+ def create_proposed_shipments
18
+ if @order.completed?
19
+ raise Spree::Order::CannotRebuildShipments.new(I18n.t("spree.cannot_rebuild_shipments_order_completed"))
20
+ elsif @order.shipments.any? { |shipment| !shipment.pending? }
21
+ raise Spree::Order::CannotRebuildShipments.new(I18n.t("spree.cannot_rebuild_shipments_shipments_not_pending"))
22
+ else
23
+ @order.shipments.destroy_all
24
+ @order.shipments.push(*Spree::Config.stock.coordinator_class.new(@order).shipments)
25
+ end
26
+ end
27
+
11
28
  # A shortcut method that ships *all* inventory units in a shipment in a single
12
29
  # carton. See also {#ship}.
13
30
  #
@@ -148,7 +148,7 @@ module Spree
148
148
  end
149
149
 
150
150
  def recalculate_payment_total
151
- order.payment_total = payments.completed.includes(:refunds).sum { |payment| payment.amount - payment.refunds.sum(:amount) }
151
+ order.payment_total = payments.completed.includes(:refunds).sum { |payment| payment.amount - payment.refunds.sum(&:amount) }
152
152
  end
153
153
  alias_method :update_payment_total, :recalculate_payment_total
154
154
  deprecate update_payment_total: :recalculate_payment_total, deprecator: Spree.deprecator
@@ -83,6 +83,10 @@ module Spree
83
83
  %w[capture void credit]
84
84
  end
85
85
 
86
+ def can_credit?(payment)
87
+ payment.completed? && payment.credit_allowed > 0
88
+ end
89
+
86
90
  private
87
91
 
88
92
  def generate_profile_id(success)
@@ -6,16 +6,18 @@ module Spree
6
6
  %w[capture void credit]
7
7
  end
8
8
 
9
- # Indicates whether its possible to capture the payment
10
9
  def can_capture?(payment)
11
10
  ["checkout", "pending"].include?(payment.state)
12
11
  end
13
12
 
14
- # Indicates whether its possible to void the payment.
15
13
  def can_void?(payment)
16
14
  payment.state != "void"
17
15
  end
18
16
 
17
+ def can_credit?(payment)
18
+ payment.completed? && payment.credit_allowed > 0
19
+ end
20
+
19
21
  def capture(*)
20
22
  simulated_successful_billing_response
21
23
  end
@@ -156,7 +156,7 @@ module Spree
156
156
  end
157
157
 
158
158
  def auto_capture?
159
- auto_capture.nil? ? Spree::Config[:auto_capture] : auto_capture
159
+ auto_capture.nil? ? Spree::Config.auto_capture : auto_capture
160
160
  end
161
161
 
162
162
  # Check if given source is supported by this payment method
@@ -74,7 +74,7 @@ module Spree
74
74
  end
75
75
 
76
76
  def check_price
77
- self.currency ||= Spree::Config[:currency]
77
+ self.currency ||= Spree::Config.currency
78
78
  end
79
79
 
80
80
  def pricing_options
@@ -124,7 +124,7 @@ module Spree
124
124
  validates :meta_keywords, length: {maximum: 255}
125
125
  validates :meta_title, length: {maximum: 255}
126
126
  validates :name, presence: true
127
- validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
127
+ validates :price, presence: true, if: proc { Spree::Config.require_master_price }
128
128
  validates :slug, presence: true, uniqueness: {allow_blank: true, case_sensitive: true}
129
129
 
130
130
  attr_accessor :option_values_hash