Advertisement
dkrat

Untitled

Oct 7th, 2022
204
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 13.85 KB | None | 0 0
  1. # GENERATED BY THE SHOPIFY SCRIPT CREATOR APP
  2. class Campaign
  3.   def initialize(condition, *qualifiers)
  4.     @condition = (condition.to_s + '?').to_sym
  5.     @qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
  6.     @line_item_selector = qualifiers.last unless @line_item_selector
  7.     qualifiers.compact.each do |qualifier|
  8.       is_multi_select = qualifier.instance_variable_get(:@conditions).is_a?(Array)
  9.       if is_multi_select
  10.         qualifier.instance_variable_get(:@conditions).each do |nested_q|
  11.           @post_amount_qualifier = nested_q if nested_q.is_a?(PostCartAmountQualifier)
  12.           @qualifiers << qualifier
  13.         end
  14.       else
  15.         @post_amount_qualifier = qualifier if qualifier.is_a?(PostCartAmountQualifier)
  16.         @qualifiers << qualifier
  17.       end
  18.     end if @qualifiers.empty?
  19.   end
  20.  
  21.   def qualifies?(cart)
  22.     return true if @qualifiers.empty?
  23.     @unmodified_line_items = cart.line_items.map do |item|
  24.       new_item = item.dup
  25.       new_item.instance_variables.each do |var|
  26.         val = item.instance_variable_get(var)
  27.         new_item.instance_variable_set(var, val.dup) if val.respond_to?(:dup)
  28.       end
  29.       new_item
  30.     end if @post_amount_qualifier
  31.     @qualifiers.send(@condition) do |qualifier|
  32.       is_selector = false
  33.       if qualifier.is_a?(Selector) || qualifier.instance_variable_get(:@conditions).any? { |q| q.is_a?(Selector) }
  34.         is_selector = true
  35.       end rescue nil
  36.       if is_selector
  37.         raise "Missing line item match type" if @li_match_type.nil?
  38.         cart.line_items.send(@li_match_type) { |item| qualifier.match?(item) }
  39.       else
  40.         qualifier.match?(cart, @line_item_selector)
  41.       end
  42.     end
  43.   end
  44.  
  45.   def run_with_hooks(cart)
  46.     before_run(cart) if respond_to?(:before_run)
  47.     run(cart)
  48.     after_run(cart)
  49.   end
  50.  
  51.   def after_run(cart)
  52.     @discount.apply_final_discount if @discount && @discount.respond_to?(:apply_final_discount)
  53.     revert_changes(cart) unless @post_amount_qualifier.nil? || @post_amount_qualifier.match?(cart)
  54.   end
  55.  
  56.   def revert_changes(cart)
  57.     cart.instance_variable_set(:@line_items, @unmodified_line_items)
  58.   end
  59. end
  60.  
  61. class TieredDiscount < Campaign
  62.   def initialize(condition, customer_qualifier, cart_qualifier, line_item_selector, discount_type, tier_type, discount_tiers)
  63.     super(condition, customer_qualifier, cart_qualifier)
  64.     @line_item_selector = line_item_selector
  65.     @discount_type = discount_type
  66.     @tier_type = tier_type
  67.     @discount_tiers = discount_tiers.sort_by {|tier| tier[:discount].to_f }
  68.   end
  69.  
  70.   def init_discount(amount, message)
  71.     case @discount_type
  72.       when :fixed
  73.         return FixedTotalDiscount.new(amount, message, :split)
  74.       when :percent
  75.         return PercentageDiscount.new(amount, message)
  76.       when :per_item
  77.         return FixedItemDiscount.new(amount, message)
  78.     end
  79.   end
  80.  
  81.   def run(cart)
  82.     return unless qualifies?(cart)
  83.  
  84.     applicable_items = cart.line_items.select { |item| @line_item_selector.nil? || @line_item_selector.match?(item) }
  85.     case @tier_type
  86.       when :customer_tag
  87.         return if cart.customer.nil?
  88.         customer_tags = cart.customer.tags.map(&:downcase)
  89.         qualified_tiers = @discount_tiers.select { |tier| customer_tags.include?(tier[:tier].downcase) }
  90.       when :cart_subtotal
  91.         cart_total = cart.subtotal_price
  92.         qualified_tiers = @discount_tiers.select { |tier| cart_total >= Money.new(cents: tier[:tier].to_i * 100) }
  93.       when :discountable_total
  94.         discountable_total = applicable_items.reduce(Money.zero) { |total, item| total + item.line_price }
  95.         qualified_tiers = @discount_tiers.select { |tier| discountable_total >= Money.new(cents: tier[:tier].to_i * 100) }
  96.       when :discountable_total_items
  97.         discountable_quantity = applicable_items.reduce(0) { |total, item| total + item.quantity }
  98.         qualified_tiers = @discount_tiers.select { |tier| discountable_quantity >= tier[:tier].to_i }
  99.       when :cart_items
  100.         cart_quantity = cart.line_items.reduce(0) { |total, item| total + item.quantity }
  101.         qualified_tiers = @discount_tiers.select { |tier| cart_quantity >= tier[:tier].to_i }
  102.     end
  103.  
  104.     if @tier_type == :line_quantity
  105.       applicable_items.each do |item|
  106.         qualified_tiers = @discount_tiers.select { |tier| item.quantity >= tier[:tier].to_i }
  107.         next if qualified_tiers.empty?
  108.  
  109.         discount_amount = qualified_tiers.last[:discount].to_f
  110.         discount_message = qualified_tiers.last[:message]
  111.         discount = init_discount(discount_amount, discount_message)
  112.         discount.apply(item)
  113.         discount.apply_final_discount if discount.respond_to?(:apply_final_discount)
  114.       end
  115.     else
  116.       return if qualified_tiers.empty?
  117.       discount_amount = qualified_tiers.last[:discount].to_f
  118.       discount_message = qualified_tiers.last[:message]
  119.  
  120.       @discount = init_discount(discount_amount, discount_message)
  121.       applicable_items.each { |item| @discount.apply(item) }
  122.     end
  123.   end
  124. end
  125.  
  126. class PercentageDiscount
  127.   def initialize(percent, message)
  128.     @discount = (100 - percent) / 100.0
  129.     @message = message
  130.   end
  131.  
  132.   def apply(line_item)
  133.     line_item.change_line_price(line_item.line_price * @discount, message: @message)
  134.   end
  135. end
  136.  
  137. class FixedTotalDiscount
  138.   def initialize(amount, message, behaviour = :to_zero)
  139.     @amount = Money.new(cents: amount * 100)
  140.     @message = message
  141.     @discount_applied = Money.zero
  142.     @all_items = []
  143.     @is_split = behaviour == :split
  144.   end
  145.  
  146.   def apply(line_item)
  147.     if @is_split
  148.       @all_items << line_item
  149.     else
  150.       return unless @discount_applied < @amount
  151.       discount_to_apply = [(@amount - @discount_applied), line_item.line_price].min
  152.       line_item.change_line_price(line_item.line_price - discount_to_apply, {message: @message})
  153.       @discount_applied += discount_to_apply
  154.     end
  155.   end
  156.  
  157.   def apply_final_discount
  158.     return if @all_items.length == 0
  159.     total_items = @all_items.length
  160.     total_quantity = 0
  161.     total_cost = Money.zero
  162.     @all_items.each do |item|
  163.       total_quantity += item.quantity
  164.       total_cost += item.line_price
  165.     end
  166.     @all_items.each_with_index do |item, index|
  167.       discount_percent = item.line_price.cents / total_cost.cents
  168.       if total_items == index + 1
  169.         discount_to_apply = Money.new(cents: @amount.cents - @discount_applied.cents.floor)
  170.       else
  171.         discount_to_apply = Money.new(cents: @amount.cents * discount_percent)
  172.       end
  173.       item.change_line_price(item.line_price - discount_to_apply, {message: @message})
  174.       @discount_applied += discount_to_apply
  175.     end
  176.   end
  177. end
  178.  
  179. class FixedItemDiscount
  180.   def initialize(amount, message)
  181.     @amount = Money.new(cents: amount * 100)
  182.     @message = message
  183.   end
  184.  
  185.   def apply(line_item)
  186.     per_item_price = line_item.variant.price
  187.     per_item_discount = [(@amount - per_item_price), @amount].max
  188.     discount_to_apply = [(per_item_discount * line_item.quantity), line_item.line_price].min
  189.     line_item.change_line_price(line_item.line_price - discount_to_apply, {message: @message})
  190.   end
  191. end
  192.  
  193. class Qualifier
  194.   def partial_match(match_type, item_info, possible_matches)
  195.     match_type = (match_type.to_s + '?').to_sym
  196.     if item_info.kind_of?(Array)
  197.       possible_matches.any? do |possibility|
  198.         item_info.any? do |search|
  199.           search.send(match_type, possibility)
  200.         end
  201.       end
  202.     else
  203.       possible_matches.any? do |possibility|
  204.         item_info.send(match_type, possibility)
  205.       end
  206.     end
  207.   end
  208.  
  209.   def compare_amounts(compare, comparison_type, compare_to)
  210.     case comparison_type
  211.       when :greater_than
  212.         return compare > compare_to
  213.       when :greater_than_or_equal
  214.         return compare >= compare_to
  215.       when :less_than
  216.         return compare < compare_to
  217.       when :less_than_or_equal
  218.         return compare <= compare_to
  219.       when :equal_to
  220.         return compare == compare_to
  221.       else
  222.         raise "Invalid comparison type"
  223.     end
  224.   end
  225. end
  226.  
  227. class CustomerTagQualifier < Qualifier
  228.   def initialize(match_type, match_condition, tags)
  229.     @match_condition = match_condition
  230.     @invert = match_type == :does_not
  231.     @tags = tags.map(&:downcase)
  232.   end
  233.  
  234.   def match?(cart, selector = nil)
  235.     return true if cart.customer.nil? && @invert
  236.     return false if cart.customer.nil?
  237.     customer_tags = cart.customer.tags.to_a.map(&:downcase)
  238.     case @match_condition
  239.       when :match
  240.         return @invert ^ ((@tags & customer_tags).length > 0)
  241.       else
  242.         return @invert ^ partial_match(@match_condition, customer_tags, @tags)
  243.     end
  244.   end
  245. end
  246.  
  247. class CartAmountQualifier < Qualifier
  248.   def initialize(behaviour, comparison_type, amount)
  249.     @behaviour = behaviour
  250.     @comparison_type = comparison_type
  251.     @amount = Money.new(cents: amount * 100)
  252.   end
  253.  
  254.   def match?(cart, selector = nil)
  255.     total = cart.subtotal_price
  256.     if @behaviour == :item || @behaviour == :diff_item
  257.       total = cart.line_items.reduce(Money.zero) do |total, item|
  258.         total + (selector&.match?(item) ? item.line_price : Money.zero)
  259.       end
  260.     end
  261.     case @behaviour
  262.       when :cart, :item
  263.         compare_amounts(total, @comparison_type, @amount)
  264.       when :diff_cart
  265.         compare_amounts(cart.subtotal_price_was - @amount, @comparison_type, total)
  266.       when :diff_item
  267.         original_line_total = cart.line_items.reduce(Money.zero) do |total, item|
  268.           total + (selector&.match?(item) ? item.original_line_price : Money.zero)
  269.         end
  270.         compare_amounts(original_line_total - @amount, @comparison_type, total)
  271.     end
  272.   end
  273. end
  274.  
  275. class Selector
  276.   def partial_match(match_type, item_info, possible_matches)
  277.     match_type = (match_type.to_s + '?').to_sym
  278.     if item_info.kind_of?(Array)
  279.       possible_matches.any? do |possibility|
  280.         item_info.any? do |search|
  281.           search.send(match_type, possibility)
  282.         end
  283.       end
  284.     else
  285.       possible_matches.any? do |possibility|
  286.         item_info.send(match_type, possibility)
  287.       end
  288.     end
  289.   end
  290. end
  291.  
  292. class ProductTypeSelector < Selector
  293.   def initialize(match_type, product_types)
  294.     @invert = match_type == :not_one
  295.     @product_types = product_types.map(&:downcase)
  296.   end
  297.  
  298.   def match?(line_item)
  299.     @invert ^ @product_types.include?(line_item.variant.product.product_type.downcase)
  300.   end
  301. end
  302.  
  303. CAMPAIGNS = [
  304.   TieredDiscount.new(
  305.     :all,
  306.     CustomerTagQualifier.new(
  307.       :does,
  308.       :match,
  309.       ["flexdealer"]
  310.     ),
  311.     CartAmountQualifier.new(
  312.       :cart,
  313.       :greater_than_or_equal,
  314.       400
  315.     ),
  316.     ProductTypeSelector.new(
  317.       :is_one,
  318.       ["ratcat"]
  319.     ),
  320.     :percent,
  321.     :cart_subtotal,
  322.     [{:tier => "ratcat", :discount => "29", :message => "$400 column pricing -RatCAT"}]
  323.   ),
  324.   TieredDiscount.new(
  325.     :all,
  326.     CustomerTagQualifier.new(
  327.       :does,
  328.       :match,
  329.       ["flexdealer"]
  330.     ),
  331.     CartAmountQualifier.new(
  332.       :cart,
  333.       :greater_than_or_equal,
  334.       400
  335.     ),
  336.     ProductTypeSelector.new(
  337.       :is_one,
  338.       ["cabletester"]
  339.     ),
  340.     :percent,
  341.     :cart_subtotal,
  342.     [{:tier => "cabletesters", :discount => "29", :message => "$400 column pricing - Testers"}]
  343.   ),
  344.   TieredDiscount.new(
  345.     :all,
  346.     CustomerTagQualifier.new(
  347.       :does,
  348.       :match,
  349.       ["flexdealer"]
  350.     ),
  351.     CartAmountQualifier.new(
  352.       :cart,
  353.       :greater_than_or_equal,
  354.       400
  355.     ),
  356.     ProductTypeSelector.new(
  357.       :is_one,
  358.       ["cable"]
  359.     ),
  360.     :percent,
  361.     :cart_subtotal,
  362.     [{:tier => "cable", :discount => "15", :message => "$400 column pricing - cable"}]
  363.   ),
  364.   TieredDiscount.new(
  365.     :all,
  366.     CustomerTagQualifier.new(
  367.       :does,
  368.       :match,
  369.       ["flexdealer"]
  370.     ),
  371.     CartAmountQualifier.new(
  372.       :cart,
  373.       :greater_than_or_equal,
  374.       400
  375.     ),
  376.     ProductTypeSelector.new(
  377.       :is_one,
  378.       ["essentials"]
  379.     ),
  380.     :percent,
  381.     :cart_subtotal,
  382.     [{:tier => "essentials", :discount => "25", :message => "$400 column pricing - essentials"}]
  383.   ),
  384.   TieredDiscount.new(
  385.     :all,
  386.     CustomerTagQualifier.new(
  387.       :does,
  388.       :match,
  389.       ["flexdealer"]
  390.     ),
  391.     CartAmountQualifier.new(
  392.       :cart,
  393.       :greater_than_or_equal,
  394.       2500
  395.     ),
  396.     ProductTypeSelector.new(
  397.       :is_one,
  398.       ["ratcat"]
  399.     ),
  400.     :percent,
  401.     :cart_subtotal,
  402.     [{:tier => "ratcat", :discount => "39", :message => "$2500 column pricing -RatCAT"}]
  403.   ),
  404.   TieredDiscount.new(
  405.     :all,
  406.     CustomerTagQualifier.new(
  407.       :does,
  408.       :match,
  409.       ["flexdealer"]
  410.     ),
  411.     CartAmountQualifier.new(
  412.       :cart,
  413.       :greater_than_or_equal,
  414.       2500
  415.     ),
  416.     ProductTypeSelector.new(
  417.       :is_one,
  418.       ["cabletester"]
  419.     ),
  420.     :percent,
  421.     :cart_subtotal,
  422.     [{:tier => "cabletesters", :discount => "39", :message => "$2500 column pricing - Testers"}]
  423.   ),
  424.   TieredDiscount.new(
  425.     :all,
  426.     CustomerTagQualifier.new(
  427.       :does,
  428.       :match,
  429.       ["flexdealer"]
  430.     ),
  431.     CartAmountQualifier.new(
  432.       :cart,
  433.       :greater_than_or_equal,
  434.       2500
  435.     ),
  436.     ProductTypeSelector.new(
  437.       :is_one,
  438.       ["cable"]
  439.     ),
  440.     :percent,
  441.     :cart_subtotal,
  442.     [{:tier => "cable", :discount => "25", :message => "$2500 column pricing - cable"}]
  443.   ),
  444.   TieredDiscount.new(
  445.     :all,
  446.     CustomerTagQualifier.new(
  447.       :does,
  448.       :match,
  449.       ["flexdealer"]
  450.     ),
  451.     CartAmountQualifier.new(
  452.       :cart,
  453.       :greater_than_or_equal,
  454.       2500
  455.     ),
  456.     ProductTypeSelector.new(
  457.       :is_one,
  458.       ["essentials"]
  459.     ),
  460.     :percent,
  461.     :cart_subtotal,
  462.     [{:tier => "essentials", :discount => "25", :message => "$2500 column pricing - essentials"}]
  463.   )
  464. ].freeze
  465.  
  466. CAMPAIGNS.each do |campaign|
  467.   campaign.run_with_hooks(Input.cart)
  468. end
  469.  
  470. Output.cart = Input.cart
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement