From cdc8e0793f16a61c014a5cfce987ea4415a8496b Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Tue, 23 Dec 2025 10:22:25 +0100 Subject: [PATCH 1/3] Add Spree::LineItem#recalculate_price Allows a line item to recalculate its price. When prices change, that's a useful thing to have. --- core/app/models/spree/line_item.rb | 9 +++++++++ core/spec/models/spree/line_item_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/app/models/spree/line_item.rb b/core/app/models/spree/line_item.rb index f6cb4edc19d..a994db08005 100644 --- a/core/app/models/spree/line_item.rb +++ b/core/app/models/spree/line_item.rb @@ -127,6 +127,15 @@ def options=(options = {}) end end + # Recalculate the price using the pricing options for this line item. + # Useful for making sure carts are up-to-date when prices change. + # Will not make changes to completed orders. + # + def recalculate_price + return if order.completed? + self.money_price = variant.price_for_options(pricing_options)&.money + end + def pricing_options Spree::Config.pricing_options_class.from_line_item(self) end diff --git a/core/spec/models/spree/line_item_spec.rb b/core/spec/models/spree/line_item_spec.rb index 7dc4b4ae2f9..645941ab8b9 100644 --- a/core/spec/models/spree/line_item_spec.rb +++ b/core/spec/models/spree/line_item_spec.rb @@ -184,5 +184,25 @@ end end + describe "#recalculate_price" do + let(:order) { create(:order) } + let(:variant) { create(:variant, price: 14.99) } + let(:line_item) { build(:line_item, order:, variant:, price: 13.39) } + + subject { line_item.recalculate_price } + + it "updates the price of the line item" do + expect { subject }.to change { line_item.price }.from(13.39).to(14.99) + end + + context "if order is completed" do + let(:order) { create(:order, completed_at: 1.hour.ago) } + + it "does not update line item prices" do + expect { subject }.not_to change { line_item.price }.from(13.39) + end + end + end + it_behaves_like "customer and admin metadata fields: storage and validation", :line_item end From 629d5c26a81a831a77cad69225c6e43bb4e72792 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Tue, 23 Dec 2025 10:44:43 +0100 Subject: [PATCH 2/3] Add versioned preference :recalculate_cart_prices From version 5, we should recalculate cart prices when updating orders. --- .../templates/config/initializers/spree.rb.tt | 3 +++ core/lib/spree/app_configuration.rb | 4 ++++ core/spec/lib/spree/app_configuration_spec.rb | 14 ++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/core/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt b/core/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt index ea8a56aab94..8512588b178 100644 --- a/core/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +++ b/core/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt @@ -21,6 +21,9 @@ Spree.config do |config| config.image_attachment_module = 'Spree::Image::ActiveStorageAttachment' config.taxon_attachment_module = 'Spree::Taxon::ActiveStorageAttachment' + # Uncomment to recalculate cart prices when the cart changes + # config.recalculate_cart_prices = true + # Defaults # Permission Sets: diff --git a/core/lib/spree/app_configuration.rb b/core/lib/spree/app_configuration.rb index 287d96eb5d7..4b285f79c50 100644 --- a/core/lib/spree/app_configuration.rb +++ b/core/lib/spree/app_configuration.rb @@ -245,6 +245,10 @@ class AppConfiguration < Preferences::Configuration # @return [Integer] Products to show per-page in the frontend (default: +12+) preference :products_per_page, :integer, default: 12 + # @!attribute [rw] recalculate_cart_prices + # @return [Boolean] Whether to recalculate cart prices when recalculating (default: +false+) + versioned_preference :recalculate_cart_prices, :boolean, initial_value: false, boundaries: { "5.0.0.alpha" => true } + # @!attribute [rw] require_master_price # @return [Boolean] Require a price on the master variant of a product (default: +true+) preference :require_master_price, :boolean, default: true diff --git a/core/spec/lib/spree/app_configuration_spec.rb b/core/spec/lib/spree/app_configuration_spec.rb index df308b757ea..e699c195e4e 100644 --- a/core/spec/lib/spree/app_configuration_spec.rb +++ b/core/spec/lib/spree/app_configuration_spec.rb @@ -248,4 +248,18 @@ class DummyClass; end; expect(prefs[:meta_data_max_value_length]).to eq(256) end end + + describe "#recalculate_cart_prices" do + subject { prefs[:recalculate_cart_prices] } + + it { is_expected.to be false } + + context "if solidus version is 5.0" do + before do + prefs.load_defaults "5.0" + end + + it { is_expected.to be true } + end + end end From b3b5e566d92218733488f9a8e63333b0747b8db5 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Tue, 23 Dec 2025 10:50:43 +0100 Subject: [PATCH 3/3] Order updater: Recalculate cart prices if preferred This enhances the order updater to recalculate cart prices if preferred. --- core/app/models/spree/order_updater.rb | 7 +++++ core/spec/models/spree/order_updater_spec.rb | 33 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/app/models/spree/order_updater.rb b/core/app/models/spree/order_updater.rb index 2a5ba57803f..ac58e0773ee 100644 --- a/core/app/models/spree/order_updater.rb +++ b/core/app/models/spree/order_updater.rb @@ -18,6 +18,7 @@ def initialize(order) # associations try to save and then in turn try to call +update!+ again.) def recalculate order.transaction do + recalculate_line_item_prices recalculate_item_count update_shipment_amounts update_totals @@ -240,5 +241,11 @@ def recalculate_item_totals ) end end + + def recalculate_line_item_prices + if Spree::Config.recalculate_cart_prices + line_items.each(&:recalculate_price) + end + end end end diff --git a/core/spec/models/spree/order_updater_spec.rb b/core/spec/models/spree/order_updater_spec.rb index e79a0da63b7..215c5126a6d 100644 --- a/core/spec/models/spree/order_updater_spec.rb +++ b/core/spec/models/spree/order_updater_spec.rb @@ -267,6 +267,39 @@ module Spree end end + describe "price recalculation" do + let(:variant) { create(:variant, price: 98) } + let!(:order) { create(:order_with_line_items, line_items_attributes: [{ variant:, price: 98 }]) } + + subject { updater.recalculate } + + before do + variant.price = 100 + variant.save! + order.reload + end + + context "when recalculate_cart_prices is true" do + before do + stub_spree_preferences(recalculate_cart_prices: true) + end + + it "sets prices to the currently active price" do + expect { subject }.to change { order.line_items.first.price }.from(98).to(100) + end + end + + context "when recalculate_cart_prices is false" do + before do + stub_spree_preferences(recalculate_cart_prices: false) + end + + it "does not recalculate prices" do + expect { subject }.not_to change { order.line_items.first.price }.from(98) + end + end + end + context "updating shipment state" do let(:order) { create :order_with_line_items }