diff --git a/promotions/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb b/promotions/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb index 832981d7..e20801e3 100644 --- a/promotions/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb +++ b/promotions/app/decorators/models/solidus_friendly_promotions/line_item_decorator.rb @@ -3,18 +3,24 @@ module SolidusFriendlyPromotions module LineItemDecorator def self.prepended(base) + base.attr_accessor :quantity_setter base.belongs_to :managed_by_order_action, class_name: "SolidusFriendlyPromotions::PromotionAction", optional: true base.validate :validate_managed_quantity_same, on: :update + base.after_save :reset_quantity_setter end private def validate_managed_quantity_same - if managed_by_order_action && quantity_changed? + if managed_by_order_action && quantity_changed? && quantity_setter != managed_by_order_action errors.add(:quantity, :cannot_be_changed_for_automated_items) end end + def reset_quantity_setter + @quantity_setter = nil + end + Spree::LineItem.prepend self Spree::LineItem.prepend SolidusFriendlyPromotions::DiscountableAmount end diff --git a/promotions/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb b/promotions/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb index 0f22842a..58096dff 100644 --- a/promotions/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb +++ b/promotions/app/models/solidus_friendly_promotions/actions/create_discounted_item.rb @@ -6,9 +6,11 @@ class CreateDiscountedItem < PromotionAction include OrderLevelAction preference :variant_id, :integer preference :quantity, :integer, default: 1 + preference :necessary_quantity, :integer, default: 1 def perform(order) line_item = find_item(order) || create_item(order) + set_quantity(line_item, determine_item_quantity(order)) line_item.current_discounts << discount(line_item) end @@ -24,7 +26,20 @@ def find_item(order) end def create_item(order) - order.line_items.create!(quantity: preferred_quantity, variant: variant, managed_by_order_action: self) + order.line_items.create!(quantity: determine_item_quantity(order), variant: variant, managed_by_order_action: self) + end + + def determine_item_quantity(order) + applicable_line_items = promotion.applicable_line_items(order) + # Integer division will floor automatically, which is what we want here: + # 1 Item, 2 needed: 1 * 1 / 2 => 0 + # 5 items, 2 preferred, 2 needed: 5 / 2 * 2 => 4 + applicable_line_items.sum(&:quantity) / preferred_necessary_quantity * preferred_quantity + end + + def set_quantity(line_item, quantity) + line_item.quantity_setter = self + line_item.quantity = quantity end def variant diff --git a/promotions/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb b/promotions/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb index 87da02ec..ae6abb19 100644 --- a/promotions/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb +++ b/promotions/app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb @@ -5,6 +5,10 @@ <%= form.label :preferred_quantity %> <%= form.number_field :preferred_quantity, class: "fullwidth" %> +