diff --git a/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb b/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb index ddaa7292..832981d7 100644 --- a/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb +++ b/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb @@ -4,7 +4,17 @@ module SolidusFriendlyPromotions module LineItemDecorator def self.prepended(base) base.belongs_to :managed_by_order_action, class_name: "SolidusFriendlyPromotions::PromotionAction", optional: true + base.validate :validate_managed_quantity_same, on: :update end + + private + + def validate_managed_quantity_same + if managed_by_order_action && quantity_changed? + errors.add(:quantity, :cannot_be_changed_for_automated_items) + end + end + Spree::LineItem.prepend self Spree::LineItem.prepend SolidusFriendlyPromotions::DiscountableAmount end diff --git a/app/decorators/models/solidus_friendly_promotions/order_decorator.rb b/app/decorators/models/solidus_friendly_promotions/order_decorator.rb index 87e69f4f..53128bae 100644 --- a/app/decorators/models/solidus_friendly_promotions/order_decorator.rb +++ b/app/decorators/models/solidus_friendly_promotions/order_decorator.rb @@ -29,6 +29,13 @@ def reset_current_discounts shipments.each(&:reset_current_discounts) end + # This helper method excludes line items that are managed by an order action for the benefit + # of calculators and actions that discount normal line items. Line items that are managed by an + # order actions handle their discounts themselves. + def discountable_line_items + line_items.reject(&:managed_by_order_action) + end + def apply_shipping_promotions if Spree::Config.promotion_adjuster_class <= SolidusFriendlyPromotions::FriendlyPromotionAdjuster recalculate diff --git a/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb b/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb index 7dc86e5e..777bfd5e 100644 --- a/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb +++ b/app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb @@ -79,7 +79,7 @@ def compute_amount(line_item) private def actionable_line_items(order) - order.line_items.select do |item| + order.discountable_line_items.select do |item| promotion.rules.select do |rule| rule.applicable?(item) end.all? { |rule| rule.eligible?(item) } diff --git a/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb b/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb index 5bb5f80a..15451ee2 100644 --- a/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb +++ b/app/models/solidus_friendly_promotions/calculators/distributed_amount.rb @@ -28,7 +28,7 @@ def compute_line_item(line_item) private def eligible_line_items(order) - order.line_items.select do |line_item| + order.discountable_line_items.select do |line_item| calculable.promotion.eligible_by_applicable_rules?(line_item) end end diff --git a/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/discount_order.rb b/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/discount_order.rb index e8cdbed5..d96daa18 100644 --- a/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/discount_order.rb +++ b/app/models/solidus_friendly_promotions/friendly_promotion_adjuster/discount_order.rb @@ -48,8 +48,8 @@ def perform_order_actions(lane_promotions, lane) end def adjust_line_items(promotions) - order.line_items.select do |line_item| - line_item.variant.product.promotionable? && !line_item.managed_by_order_action + order.discountable_line_items.select do |line_item| + line_item.variant.product.promotionable? end.map do |line_item| discounts = generate_discounts(promotions, line_item) chosen_item_discounts = SolidusFriendlyPromotions.config.discount_chooser_class.new(discounts).call diff --git a/config/locales/en.yml b/config/locales/en.yml index 8235ab1b..b6312a99 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -257,3 +257,7 @@ en: attributes: base: disallowed_with_apply_automatically: Could not create promotion code on promotion that apply automatically + spree/line_item: + attributes: + quantity: + cannot_be_changed_for_automated_items: cannot be changed on a line item managed by a promotion action diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index d2d50e04..8b7d2a06 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -36,4 +36,25 @@ expect { subject }.to change { line_item.current_discounts.length }.from(1).to(0) end end + + describe "changing quantities" do + context "when line item is managed by an automation" do + let(:order) { create(:order) } + let(:variant) { create(:variant) } + let(:promotion) { create(:friendly_promotion, apply_automatically: true) } + let(:promotion_action) { SolidusFriendlyPromotions::Actions::CreateDiscountedItem.create!(calculator: hundred_percent, preferred_variant_id: variant.id, promotion: promotion) } + let(:hundred_percent) { SolidusFriendlyPromotions::Calculators::Percent.new(preferred_percent: 100) } + + before do + order.line_items.create!(variant: variant, managed_by_order_action: promotion_action, quantity: 1) + end + + it "makes the line item invalid" do + line_item = order.line_items.first + line_item.quantity = 2 + expect { line_item.save! }.to raise_exception(ActiveRecord::RecordInvalid) + expect(line_item.errors.full_messages.first).to eq("Quantity cannot be changed on a line item managed by a promotion action") + end + end + end end