Another promotion question! I see that spree has t...
# support
g
Another promotion question! I see that spree has the ability to have a promotion automatically add an item to the cart when applied (https://guides.spreecommerce.org/user/promotions/creating_promotions.html#create-line-items), but it that functionality doesn't appear to be in solidus (or I completely missed it). Is that something that I could (hopefully) easily add in if it's not there?
z
this is a proof of concept that has not been tested, but basically you need to create: 1. Add a custom promotion action 2. Add a partial view to Admin to store the config
Copy code
# frozen_string_literal: true
module Spree
  class Promotion < Spree::Base
    module Actions
      class GiftPromotion < Spree::PromotionAction
    
        preference :group_size, :integer, default: 1
        preference :product_id, :integer


        include Spree::AdjustmentSource

        has_many :adjustments, as: :source

        delegate :eligible?, to: :promotion        

        def perform(payload = {})
          order = payload[:order]
          promotion = payload[:promotion]
          promotion_code = payload[:promotion_code]
          remove_from(order)
          results = line_items_to_adjust(promotion, order).map do |line_item|
            create_adjustment(line_item, order, promotion_code)
          end
          order.recalculate
          results.any?
        end

        def remove_from(order)
          variant  = Spree::Variant.find(self.preferences[:product_id])
          order.line_items.where(variant: variant, price: 0).destroy_all
        end

        def create_adjustment(adjustable, order, promotion_code)
          variant  = Spree::Variant.find(self.preferences[:product_id])
          line_item = order.line_items.create(variant: variant, quantity: self.preferences[:group_size], price: 0)
          line_item.adjustments.create!(
            source: self,
            amount: compute_amount(0),
            order: order,
            promotion_code: promotion_code,
            label: I18n.t('spree.adjustment_labels.line_item', promotion: Spree::Promotion.model_name.human, promotion_name: promotion.name)
          )
          true

        end
        
        def compute_amount(adjustable)
           0.00
        end

        def line_items_to_adjust(promotion, order)
          excluded_ids = adjustments.
            where(adjustable_id: order.line_items.all.pluck(:id), adjustable_type: 'Spree::LineItem').
            pluck(:adjustable_id).
            to_set
          order.line_items.where.not(price: 0).select do |line_item|
            !excluded_ids.include?(line_item.id) &&
              promotion.line_item_actionable?(order, line_item)
          end
        end 
      end
    end
  end
end
g
Oh dang, that's awesome, I'll give that a try! Thanks!
šŸ‘ 1
z
Copy code
<!-- app/views/spree/admin/promotions/action/_gift_promotion.html.erb  -->
<div class="field">
  <% field_name = "#{param_prefix}[preferred_group_size]" %>
  <%= label_tag field_name, t('spree.group_size') %>
  <%= number_field_tag field_name, promotion_action.preferred_group_size, class: "fullwidth", min: 1 %>
</div>

<div class="row">
  <div class="col-12">
    <div class="field products_rule_products">
      <%= label_tag "#{param_prefix}_preferred_product_id", t('spree.product_rule.choose_products') %>
      <% field_name = "#{param_prefix}[preferred_product_id]" %>
      <%= hidden_field_tag field_name, promotion_action.preferred_product_id, class: "product_picker fullwidth" %> 
    </div>
  </div>
</div>
and this is the partial for admin šŸ™‚
g
Did you start with one of the existing actions and then tweak it? Or is there an example somewhere that I haven't found yet?
z
g
Gotcha, somehow I hadn't found that page yet, apparently my google-fu is weak today
z
and finally you need to add the promo in the spree.rb initializer
Rails.application.config.spree.promotions.actions << Spree::Promotion::Actions::GiftPromotion
once my team has completed and tested this, I will share the final result
g
So far this works great, I can add things to the cart based on the promotion code, but I can't seem to figure out how to remove it if the qualifying product is removed from the cart. I don't want people to get something for free without buying the thing they're supposed to šŸ˜›
z
Yep, remove_from method is still a WIP, but so far this should work (once full implementation is done, I will post the complete solution= As you can see in the method create_adjustment the line item containing the git product has a reference to line_item.adjustments , so from method remove_from you can delete all the order line items with adjustment equals to the promotion_code or something like that kinda the reverse of create_adjustment
g
Had disabled this functionality for a bit while the client was reviewing their requirements for it, finally coming back around on it today. I've looked at finishing up the functionality on it, but I keep finding scenarios that I can't sort out how to sort out. • When I add a product to the cart and add the gift promo code, I can add more of the $0 gift item. • If I remove the paid item, the free gift item stays in the cart. • I can't pick a variant to add, it only adds the master. I don't know for sure that the client will need that, but I can see it being a possible sticking point. Don't suppose you've had a chance to look a bit more into it on your side? šŸ˜„