From e9c26dd0601c53ae5ad835da9dbf856f18fe448c Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Thu, 27 Aug 2026 10:10:50 +0000 Subject: [PATCH 1/2] Load discarded promotions in benefit association Benefits need access to their promotion even when it's discarded to properly handle cleanup and checks. This matches the pattern used in OrderPromotion and PromotionCode associations. (cherry picked from commit d57b042ea9b4825d1381ada62d44c650a0dc51ca) --- .../app/models/solidus_promotions/benefit.rb | 2 +- .../app/models/solidus_promotions/promotion.rb | 2 +- .../spec/models/solidus_promotions/benefit_spec.rb | 10 ++++++++++ promotions/spec/models/spree/line_item_spec.rb | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/promotions/app/models/solidus_promotions/benefit.rb b/promotions/app/models/solidus_promotions/benefit.rb index cbe7c60509c..7a7aa2c5047 100644 --- a/promotions/app/models/solidus_promotions/benefit.rb +++ b/promotions/app/models/solidus_promotions/benefit.rb @@ -40,7 +40,7 @@ class Benefit < Spree::Base # @!attribute [rw] promotion # The owning promotion. # @return [SolidusPromotions::Promotion] - belongs_to :promotion, inverse_of: :benefits + belongs_to :promotion, -> { with_discarded }, inverse_of: :benefits # @!attribute [rw] original_promotion_action # Back-reference to the original Solidus (Spree) promotion action, when migrated. # @return [Spree::PromotionAction, nil] diff --git a/promotions/app/models/solidus_promotions/promotion.rb b/promotions/app/models/solidus_promotions/promotion.rb index 36a019b4ab6..53ff26c6d98 100644 --- a/promotions/app/models/solidus_promotions/promotion.rb +++ b/promotions/app/models/solidus_promotions/promotion.rb @@ -12,7 +12,7 @@ class Promotion < Spree::Base optional: true, inverse_of: :promotions belongs_to :original_promotion, class_name: "Spree::Promotion", optional: true - has_many :benefits, class_name: "SolidusPromotions::Benefit", dependent: :destroy + has_many :benefits, class_name: "SolidusPromotions::Benefit", dependent: :destroy, inverse_of: :promotion has_many :conditions, through: :benefits has_many :codes, class_name: "SolidusPromotions::PromotionCode", dependent: :destroy, inverse_of: :promotion has_many :code_batches, class_name: "SolidusPromotions::PromotionCodeBatch", dependent: :destroy diff --git a/promotions/spec/models/solidus_promotions/benefit_spec.rb b/promotions/spec/models/solidus_promotions/benefit_spec.rb index 96a0b09027d..4f584f6b4c0 100644 --- a/promotions/spec/models/solidus_promotions/benefit_spec.rb +++ b/promotions/spec/models/solidus_promotions/benefit_spec.rb @@ -11,6 +11,16 @@ it { is_expected.to respond_to :discount } it { is_expected.to respond_to :can_discount? } + describe "promotion association" do + let(:promotion) { create(:solidus_promotion, :with_adjustable_benefit) } + let(:benefit) { promotion.benefits.first } + + it "loads discarded promotions" do + promotion.discard! + expect(benefit.reload.promotion).to eq(promotion) + end + end + describe "#can_adjust?" do let(:adjustable) { Spree::LineItem.new } let(:benefit_class) do diff --git a/promotions/spec/models/spree/line_item_spec.rb b/promotions/spec/models/spree/line_item_spec.rb index 7438be8955c..6a885a1b890 100644 --- a/promotions/spec/models/spree/line_item_spec.rb +++ b/promotions/spec/models/spree/line_item_spec.rb @@ -94,6 +94,20 @@ it { is_expected.to eq(26) } end + + context "with an adjustment from a discarded promotion" do + before do + pre_lane_promotion.discard! + end + + it "does not crash when accessing promotion.lane" do + expect { subject }.not_to raise_error + end + + it "still calculates the discounted amount correctly" do + is_expected.to eq(23) + end + end end describe "#current_lane_discounts" do From 275d4449728c31174e1be698ca53189d89526466 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Thu, 27 Aug 2026 10:10:54 +0000 Subject: [PATCH 2/2] Remove adjustments from cart orders when promotion is discarded (cherry picked from commit 8c3c0334b00e6e1ecf935e038d19cc0af7f5ccca) --- promotions/app/models/solidus_promotions/promotion.rb | 1 + .../spec/models/solidus_promotions/promotion_spec.rb | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/promotions/app/models/solidus_promotions/promotion.rb b/promotions/app/models/solidus_promotions/promotion.rb index 53ff26c6d98..c62a47afb46 100644 --- a/promotions/app/models/solidus_promotions/promotion.rb +++ b/promotions/app/models/solidus_promotions/promotion.rb @@ -173,6 +173,7 @@ def apply_automatically_disallowed_with_promotion_codes def delete_cart_connections order_promotions.where(order: Spree::Order.incomplete).destroy_all + benefits.each(&:remove_adjustments_from_incomplete_orders) end end end diff --git a/promotions/spec/models/solidus_promotions/promotion_spec.rb b/promotions/spec/models/solidus_promotions/promotion_spec.rb index 8b706915f97..a68098c675d 100644 --- a/promotions/spec/models/solidus_promotions/promotion_spec.rb +++ b/promotions/spec/models/solidus_promotions/promotion_spec.rb @@ -93,6 +93,17 @@ it "destroys the connection" do expect { subject }.to change(SolidusPromotions::OrderPromotion, :count).by(-1) end + + it "removes adjustments from incomplete orders" do + benefit = promotion.benefits.first + order.adjustments.create!( + source: benefit, + order: order, + amount: -10.0, + label: "Test Adjustment" + ) + expect { subject }.to change { order.adjustments.reload.count }.from(1).to(0) + end end context "when the promotion has been added to a complete order" do