Line item script examples
Line item scripts affect items in a cart, and can change their properties and prices. These scripts run each time that a cart attribute is changed, such as adding or removing an item, changing the shipping address, or adding a discount code.
Shopify Plus
Shopify Scripts and the Script Editor app are available toShopify Plus merchants only. The Script Editor app is no longer available for download from the Shopify App Store.
On August 28, 2025, Shopify Scripts will be removed and will no longer work. Migrate your existing Scripts toShopify Functions before this date.
To use the templates on this page, create a new script with a blank template.
Steps:
From your Shopify admin, go toApps >Script Editor .
ClickCreate script .
ClickLine items .
SelectBlank template , then clickCreate script .
In theRuby source code section, delete the default line of code:Output.cart = Input.cart
Copy a script from this page and paste it into theRuby source code section.
Edit theCustomizable Settings section of the script to function in your store.
Test your script. For more information, refer toTesting and debugging Shopify Scripts .
After testing:
clickSave draft to save an unpublished draft of the script, or
clickSave and publish to create and publish the script.
Tiered cart discount by spend
Use this script to offer a dollar amount discount that increases according to the total value of the items in their cart. This discount is spread as evenly as possible across the individual items in the cart.
For example, offer customers $25 off their total if they spend more than $150, $50 off if they spend more than $300, or $75 off if they spend more than $400.
# ================================ Customizable Settings ================================ # ================================================================ # Tiered Cart Discounts by Spend Threshold # # If the cart total is greater than (or equal to) an entered # threshold, the associated discount is applied to the cart. The # discount will be spread, as evenly as possible, across all items. # # - 'threshold' is the spend amount needed to qualify # - 'discount_amount' is the dollar discount to apply to the # cart # - 'discount_message' is the message to show when a discount # is applied # ================================================================ SPENDING_THRESHOLDS = [ { threshold: 150 , discount_amount: 25 , discount_message: 'Spend $150 and get $25 off!' , }, { threshold: 300 , discount_amount: 50 , discount_message: 'Spend $300 and get $50 off!' , }, { threshold: 400 , discount_amount: 75 , discount_message: 'Spend $400 and get $75 off!' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # DollarDiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DollarDiscountApplicator def initialize ( discount_message ) @discount_message = discount_message end def apply ( line_item , discount_amount ) new_line_price = line_item . line_price - discount_amount line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # TieredCartDiscountBySpendCampaign # # If the cart total is greater than (or equal to) an entered # threshold, the associated discount is applied to the cart. The # discount will be spread, as evenly as possible, across all items. # ================================================================ class TieredCartDiscountBySpendCampaign def initialize ( tiers ) @tiers = tiers . sort_by { | tier | tier [ :threshold ] }. reverse end def run ( cart ) applicable_tier = @tiers . find { | tier | cart . subtotal_price >= ( Money . new ( cents: 100 ) * tier [ :threshold ]) } return if applicable_tier . nil? discount_applicator = DollarDiscountApplicator . new ( applicable_tier [ :discount_message ]) discount_amount = applicable_tier [ :discount_amount ] items = cart . line_items . sort_by { | line_item | line_item . variant . price } self . loop_items ( cart , items , discount_amount , discount_applicator ) end def loop_items ( cart , line_items , discount_amount , discount_applicator ) avg_discount = ( discount_amount . to_f * ( 1 / line_items . map ( & :quantity ). reduce ( 0 , : + ))). round ( 2 ) avg_discount = Money . new ( cents: 100 ) * avg_discount discount_amount = Money . new ( cents: 100 ) * discount_amount line_items . each_with_index do | line_item , index | break if discount_amount <= Money . zero line_discount = avg_discount * line_item . quantity if discount_amount < line_discount || index == ( line_items . size - 1 ) discount_update = line_item . line_price > discount_amount ? discount_amount : line_item . line_price else discount_update = line_item . line_price > line_discount ? line_discount : line_item . line_price end discount_amount -= discount_update discount_applicator . apply ( line_item , discount_update ) end end end CAMPAIGNS = [ TieredCartDiscountBySpendCampaign . new ( SPENDING_THRESHOLDS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Tiered discount by spend
Use this script to offer a percentage discount that increases according to the total value of the items in their cart.
For example, offer customers 10% off if they spend $30 or more, 15% off if they spend $50 or more, or 20% off if they spend $100 or more.
# ================================ Customizable Settings ================================ # ================================================================ # Tiered Discounts by Spend Threshold # # If the cart total is greater than (or equal to) an entered # threshold, the associated discount is applied to each item. # # - 'threshold' is the spend amount needed to qualify # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ SPENDING_THRESHOLDS = [ { threshold: 30 , discount_type: :percent , discount_amount: 10 , discount_message: 'Spend $30 and get 10% off!' , }, { threshold: 50 , discount_type: :percent , discount_amount: 15 , discount_message: 'Spend $50 and get 15% off!' , }, { threshold: 100 , discount_type: :percent , discount_amount: 20 , discount_message: 'Spend $100 and get 20% off!' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # TieredDiscountBySpendCampaign # # If the cart total is greater than (or equal to) an entered # threshold, the associated discount is applied to each item. # ================================================================ class TieredDiscountBySpendCampaign def initialize ( tiers ) @tiers = tiers . sort_by { | tier | tier [ :threshold ] }. reverse end def run ( cart ) applicable_tier = @tiers . find { | tier | cart . subtotal_price >= ( Money . new ( cents: 100 ) * tier [ :threshold ]) } return if applicable_tier . nil? discount_applicator = DiscountApplicator . new ( applicable_tier [ :discount_type ], applicable_tier [ :discount_amount ], applicable_tier [ :discount_message ] ) cart . line_items . each do | line_item | next if line_item . variant . product . gift_card? discount_applicator . apply ( line_item ) end end end CAMPAIGNS = [ TieredDiscountBySpendCampaign . new ( SPENDING_THRESHOLDS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Tiered product discount by quantity
Use this script to offer a bulk discount for a particular product, with a percentage discount that increases according to the number of those products in the cart.
For example, offer customers 10% off each hat if they purchase 2 hats or more, or 15% off if they purchase 5 hats or more.
# ================================ Customizable Settings ================================ # ================================================================ # Tiered Product Discount by Quantity # # If the total quantity of matching items is greater than (or # equal to) an entered threshold, the associated discount is # applied to each matching item. # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) for #合格ing products. Product/Variant ID lists should only # contain numbers (ie. no quotes). If ':all' is used, this # can also be 'nil'. # - 'tiers' is a list of tiers where: # - 'quantity' is the minimum quantity you need to buy to #合格 # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNT_TIERS = [ { product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "your_tag" ], tiers: [ { quantity: 2 , discount_type: :percent , discount_amount: 10 , discount_message: '10% off for 2+' , }, { quantity: 5 , discount_type: :percent , discount_amount: 15 , discount_message: '15% off for 5+' , }, ], }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # TieredProductDiscountByQuantityCampaign # # If the total quantity of matching items is greater than (or # equal to) an entered threshold, the associated discount is # applied to each matching item. # ================================================================ class TieredProductDiscountByQuantityCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) applicable_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if applicable_items . nil? total_applicable_quantity = applicable_items . map ( & :quantity ). reduce ( 0 , : + ) tiers = campaign [ :tiers ]. sort_by { | tier | tier [ :quantity ] }. reverse applicable_tier = tiers . find { | tier | tier [ :quantity ] <= total_applicable_quantity } next if applicable_tier . nil? discount_applicator = DiscountApplicator . new ( applicable_tier [ :discount_type ], applicable_tier [ :discount_amount ], applicable_tier [ :discount_message ] ) applicable_items . each do | line_item | discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ TieredProductDiscountByQuantityCampaign . new ( PRODUCT_DISCOUNT_TIERS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Tiered product discount by product spend
Use this script to offer a percentage discount that increases according to the total value of specific items in the cart.
For example, offer customers 10% off if they spend $30 or more, 15% off if they spend $50 or more, and 20% off if they spend $100 or more, but only for items that match a specific tag.
# ================================ Customizable Settings ================================ # ================================================================ # Tiered Product Discount by Product Spend Threhsold # # If the total amount spent on matching items is greather than (or # equal to) an entered threshold, the associated discount is # applied to each matching item. # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'tiers' is a list of tiers where: # - 'threshold' is the minimum dollar amount needed to #合格 # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNT_TIERS = [ { product_selector_match_type: :exclude , product_selector_type: :tag , product_selectors: [ "your_tag" , "another_tag" ], tiers: [ { threshold: 100 , discount_type: :percent , discount_amount: 10 , discount_message: 'Spend $100 or more, and get 10% off!' , }, ], }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # TieredProductDiscountByProductSpendCampaign # # If the total amount spent on matching items is greather than (or # equal to) an entered threshold, the associated discount is # applied to each matching item. # ================================================================ class TieredProductDiscountByProductSpendCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | if campaign [ :product_selector_type ] == :all total_applicable_cost = cart . subtotal_price applicable_items = cart . line_items else product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) applicable_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if applicable_items . nil? total_applicable_cost = applicable_items . map ( & :line_price ). reduce ( Money . zero , : + ) end tiers = campaign [ :tiers ]. sort_by { | tier | tier [ :threshold ] }. reverse applicable_tier = tiers . find { | tier | total_applicable_cost >= ( Money . new ( cents: 100 ) * tier [ :threshold ]) } next if applicable_tier . nil? discount_applicator = DiscountApplicator . new ( applicable_tier [ :discount_type ], applicable_tier [ :discount_amount ], applicable_tier [ :discount_message ] ) applicable_items . each do | line_item | discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ TieredProductDiscountByProductSpendCampaign . new ( PRODUCT_DISCOUNT_TIERS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Product discount
Use this script to offer a discount on specific items.
For example, offer customers 10% off on items taggeddiscounted
.
# ================================ Customizable Settings ================================ # ================================================================ # Discount by Product # #任何匹配项将打折的烤鸭red amount. # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNTS = [ { product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "your_tag" ], discount_type: :percent , discount_amount: 10 , discount_message: '10% off tagged products!' } ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # ProductDiscountCampaign # #任何匹配项将打折的烤鸭red amount. # ================================================================ class ProductDiscountCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ ProductDiscountCampaign . new ( PRODUCT_DISCOUNTS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Product discount by discount code
Use this script to offer a discount on specific items if a specific discount code is used.
For example, offer customers 10% off on items taggeddiscounted
if they use the discount codeDISCOUNT_10
.
# ================================ Customizable Settings ================================ # ================================================================ # Product Discount by Discount Code # # If any matching discount codes are used, any matching items # will be discounted by the entered amount. # # - 'discount_code_match_type' determines whether the below # strings should be an exact or partial match. Can be: # - ':exact' for an exact match # - ':partial' for a partial match # - 'discount_codes' is a list of strings to identify discount # codes # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNTS_BY_DISCOUNT_CODE = [ { discount_code_match_type: :exact , discount_codes: [ "TESTCODE1" , "TESTCODE2" ], product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "your_tag" ], discount_type: :percent , discount_amount: 10 , discount_message: '10% off tagged products!' } ] # ================================ Script Code (do not edit) ================================ # ================================================================ # DiscountCodeSelector # # Finds whether the supplied discount code matches any of the # entered codes. # ================================================================ class DiscountCodeSelector def initialize ( match_type , discount_codes ) @comparator = match_type == :exact ? '==' : 'include?' @discount_codes = discount_codes . map { | discount_code | discount_code . upcase . strip } end def match? ( discount_code ) @discount_codes . any? { | code | discount_code . code . upcase . send ( @comparator , code ) } end end # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # ProductDiscountByCodeCampaign # # If any matching discount codes are used, any matching items # will be discounted by the entered amount. # ================================================================ class ProductDiscountByCodeCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) return if cart . discount_code . nil? @campaigns . each do | campaign | discount_code_selector = DiscountCodeSelector . new ( campaign [ :discount_code_match_type ], campaign [ :discount_codes ] ) next unless discount_code_selector . match? ( cart . discount_code ) product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ ProductDiscountByCodeCampaign . new ( PRODUCT_DISCOUNTS_BY_DISCOUNT_CODE ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Buy a specific amount of a product, get a second amount at a discount
Use this script to offer a discount on a specific item if more than a certain quantity is purchased.
For example, offer customers to buy three items taggeddiscount
and receive 50% off a fourth item taggeddiscount
.
Note
This script will not add the 'get' items to the cart. It can only discount items that are already in the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Buy X, Get Y For Z Discount # # Buy a certain number of matching items, get a certain number # of the same matching items with the entered discount applied. For # example: # # "Buy 2 products tagged with 'tag', get another product # tagged with 'tag' for 10% off" # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) for #合格ing products. Product/Variant ID lists should only # contain numbers (ie. no quotes). If ':all' is used, this # can also be 'nil'. # - 'quantity_to_buy' is the number of products needed to #合格 # - 'quantity_to_discount' is the number of products to discount # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # # Something to note for the case of running multiple offers is # that there shouldn't be any overlap between product selection # as this can lead to extra discounting. For example, you should # NOT offer "Buy 1 Product X, get 1 50% off", as well as "Buy 2 # Product X, get 1 free" # ================================================================ BUY_X_GET_Y_FOR_Z = [ { product_selector_match_type: :include , product_selector_type: :all , product_selectors: nil , quantity_to_buy: 1 , quantity_to_discount: 1 , discount_type: :percent , discount_amount: 50 , discount_message: 'Buy one item, get the second 50% off!' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # BuyXGetYForZCampaign # # Buy a certain number of matching items, get a certain number # of the same matching items with the entered discount applied. # ================================================================ class BuyXGetYForZCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) eligible_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if eligible_items . nil? eligible_items = eligible_items . sort_by { | line_item | - line_item . variant . price } quantity_to_buy = campaign [ :quantity_to_buy ] quantity_to_discount = campaign [ :quantity_to_discount ] bundle_size = quantity_to_buy + quantity_to_discount number_of_bundles = ( eligible_items . map ( & :quantity ). reduce ( 0 , : + ) / bundle_size ). floor number_of_discountable_items = number_of_bundles * quantity_to_discount next unless number_of_discountable_items > 0 discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) self . loop_items ( discount_applicator , cart , eligible_items , number_of_discountable_items , quantity_to_buy , quantity_to_discount ) end end def loop_items ( discount_applicator , cart , line_items , num_to_discount , quantity_to_buy , quantity_to_discount ) surplus = 0 bundle_size = quantity_to_buy + quantity_to_discount line_items . each do | line_item | line_quantity = line_item . quantity + surplus if line_quantity > quantity_to_buy bundles_per_line = ( line_quantity / bundle_size ). floor take_quantity = bundles_per_line * quantity_to_discount surplus += ( line_quantity - ( bundle_size * bundles_per_line )) if line_item . quantity > take_quantity discount_item = line_item . split ( take: take_quantity ) discount_applicator . apply ( discount_item ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , discount_item ) num_to_discount -= take_quantity else discount_applicator . apply ( line_item ) num_to_discount -= line_item . quantity end else surplus += line_quantity end break if num_to_discount <= 0 end end end CAMPAIGNS = [ BuyXGetYForZCampaign . new ( BUY_X_GET_Y_FOR_Z ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Buy a bundle, get an additional product at a discount
Use this script to offer a discount on a specific item if they have purchased a number of other items.
For example, offer customers a free pair of socks if they also purchase a hat, a t-shirt, and a pair of sunglasses.
Note
This script will not add the 'get' item(s) to the cart. It can only discount items that are already in the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Buy Products VWX, get Product Y for Z Discount # # Buy a specific bundle of items, get another item at a discount. # For example: # # "Buy a t-shirt, a hat, and sunglasses, get a free pair of socks" # # - 'bundle_items' is a list of the items that comprise the # bundle, where: # - 'product_id' is the ID of the product # - 'quantity_needed' is the quantity necessary to complete # the bundle # - 'quantity_to_buy' is the number of bundles needed to qualify # for a discount product # - 'discount_product_selector_match_type' determines whether we # look for products that do or don't match the entered # selectors. Can be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'discount_product_selector_type' determines how qualifying # products will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'discount_product_selectors' is a list of identifiers (from # above) for qualifying products. Product/Variant ID lists # should only contain numbers (ie. no quotes). If ':all' is # used, this can also be 'nil'. # - 'quantity_to_discount' is the number of items to discount # per offer # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ BUNDLE_DISCOUNTS = [ { bundle_items: [ { product_id: 1234567890987 , quantity_needed: 1 }, { product_id: 1234567890986 , quantity_needed: 1 }, ], quantity_to_buy: 1 , discount_product_selector_match_type: :include , discount_product_selector_type: :product_id , discount_product_selectors: [ 1234567890123 ], quantity_to_discount: 1 , discount_type: :percent , discount_amount: 10 , discount_message: "Buy Product VWX, get Product Y for 10% off" , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # BundleSelector # # Finds any items that are part of the entered bundle and saves # them. # ================================================================ class BundleSelector def initialize ( bundle_items ) @bundle_items = bundle_items . reduce ({}) do | acc , bundle_item | acc [ bundle_item [ :product_id ]] = { cart_items: [], quantity_needed: bundle_item [ :quantity_needed ], total_quantity: 0 , } acc end end def build ( cart ) cart . line_items . each do | line_item | next unless @bundle_items [ line_item . variant . product . id ] @bundle_items [ line_item . variant . product . id ][ :cart_items ]. push ( line_item ) @bundle_items [ line_item . variant . product . id ][ :total_quantity ] += line_item . quantity end @bundle_items end end # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # DiscountLoop # # Loops through the supplied line items and discounts the supplied #项目的数量提供折扣。 # ================================================================ class DiscountLoop def initialize ( discount_applicator ) @discount_applicator = discount_applicator end def loop_items ( cart , line_items , num_to_discount ) line_items . each_with_index do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount split_line_item = line_item . split ( take: num_to_discount ) @discount_applicator . apply ( split_line_item ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , split_line_item ) break else @discount_applicator . apply ( line_item ) num_to_discount -= line_item . quantity end end end end # ================================================================ # BundleDiscountCampaign # # If the entered bundle is present, the entered discount is # applied to the entered product. # ================================================================ class BundleDiscountCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | bundle_selector = BundleSelector . new ( campaign [ :bundle_items ]) bundle_items = bundle_selector . build ( cart ) next if bundle_items . any? do | product_id , product_info | product_info [ :total_quantity ] < product_info [ :quantity_needed ] end num_bundles = bundle_items . map do | product_id , product_info | ( product_info [ :total_quantity ] / product_info [ :quantity_needed ]) end num_bundles = num_bundles . min . floor product_selector = ProductSelector . new ( campaign [ :discount_product_selector_match_type ], campaign [ :discount_product_selector_type ], campaign [ :discount_product_selectors ], ) discount_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if discount_items . nil? discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) discount_loop = DiscountLoop . new ( discount_applicator ) discount_loop . loop_items ( cart , discount_items , ( campaign [ :quantity_to_discount ] * num_bundles )) end end end CAMPAIGNS = [ BundleDiscountCampaign . new ( BUNDLE_DISCOUNTS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Bundle discount
使用这个脚本提供一个折扣当具体c set of products are added to the cart.
For example, offer customers 20% off the purchase of a t-shirt, a hat, and a pair of sunglasses.
# ================================ Customizable Settings ================================ # ================================================================ # Buy Products WXY, get Z Discount # # Buy a specific bundle of products, get that bundle at a # discount. For example: # # "Buy a t-shirt, a hat, and sunglasses, get 20% off each" # # - 'bundle_items' is a list of the items that comprise the # bundle, where: # - 'product_id' is the ID of the product # - 'quantity_needed' is the quantity necessary to complete # the bundle # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ BUNDLE_DISCOUNTS = [ { bundle_items: [ { product_id: 1234567890987 , quantity_needed: 1 }, { product_id: 1234567890986 , quantity_needed: 1 }, ], discount_type: :percent , discount_amount: 10 , discount_message: "Buy Product X and Product Y, get 10% off!" , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # BundleSelector # # Finds any items that are part of the entered bundle and saves # them. # ================================================================ class BundleSelector def initialize ( bundle_items ) @bundle_items = bundle_items . reduce ({}) do | acc , bundle_item | acc [ bundle_item [ :product_id ]] = { cart_items: [], quantity_needed: bundle_item [ :quantity_needed ], total_quantity: 0 , } acc end end def build ( cart ) cart . line_items . each do | line_item | next if line_item . line_price_changed? next unless @bundle_items [ line_item . variant . product . id ] @bundle_items [ line_item . variant . product . id ][ :cart_items ]. push ( line_item ) @bundle_items [ line_item . variant . product . id ][ :total_quantity ] += line_item . quantity end @bundle_items end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # DiscountLoop # # Loops through the supplied line items and discounts the supplied #项目的数量提供折扣。 # ================================================================ class DiscountLoop def initialize ( discount_applicator ) @discount_applicator = discount_applicator end def loop_items ( cart , line_items , num_to_discount ) line_items . each_with_index do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount split_line_item = line_item . split ( take: num_to_discount ) @discount_applicator . apply ( split_line_item ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , split_line_item ) break else @discount_applicator . apply ( line_item ) num_to_discount -= line_item . quantity end end end end # ================================================================ # BundleDiscountCampaign # # If the entered bundle is present, the entered discount is # applied to each item in the bundle. # ================================================================ class BundleDiscountCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | bundle_selector = BundleSelector . new ( campaign [ :bundle_items ]) bundle_items = bundle_selector . build ( cart ) next if bundle_items . any? do | product_id , product_info | product_info [ :total_quantity ] < product_info [ :quantity_needed ] end num_bundles = bundle_items . map do | product_id , product_info | ( product_info [ :total_quantity ] / product_info [ :quantity_needed ]) end num_bundles = num_bundles . min . floor discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) discount_loop = DiscountLoop . new ( discount_applicator ) bundle_items . each do | product_id , product_info | discount_loop . loop_items ( cart , product_info [ :cart_items ], ( product_info [ :quantity_needed ] * num_bundles ), ) end end end end CAMPAIGNS = [ BundleDiscountCampaign . new ( BUNDLE_DISCOUNTS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Buy one get one (BOGO) discount
Use this script to offer a percentage discount on an item when a customer purchases a set number of a different product.
For example, offer customers a hat for 10% off if they purchase 2 t-shirts.
Note
This script will not add the 'get' item(s) to the cart. It can only discount items that are already in the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Buy V of Product W, Get X of Product Y for Z Discount # # Buy a certain number of matching items, get a certain number of # a different set of matching items with the entered discount # applied. For example: # # "Buy 2 t-shirts, get 1 hat for 10% off" # # - 'buy_product_selector_match_type' determines whether we look # for products that do or don't match the entered selectors. # Can be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'buy_product_selector_type' determines how eligible products # will be identified. Can be: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'buy_product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should only # contain numbers (ie. no quotes). If ':all' is used, this # can also be 'nil'. # - 'quantity_to_buy' is the number of products needed to #合格 # - 'get_selector_match_type' is the same idea as the "Buy" # version above # - 'get_product_selector_type' is the same idea as the "Buy" # version above # - 'get_product_selectors' is the same idea as the "Buy" # version above # - 'quantity_to_discount' is the number of products to discount # - 'allow_incomplete_bundle' determines whether a portion of # the items to discount can be discounted, or all items # need to be present. Can be: # - 'true' # - 'false' # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ BUYVOFW_GETXOFY_FORZ = [ { buy_product_selector_match_type: :include , buy_product_selector_type: :tag , buy_product_selectors: [ "your_tag" , "another_tag" ], quantity_to_buy: 1 , get_product_selector_match_type: :include , get_product_selector_type: :tag , get_product_selectors: [ "your_other_tag" , "a_different_tag" ], quantity_to_discount: 1 , allow_incomplete_bundle: false , discount_type: :percent , discount_amount: 100 , discount_message: 'Buy a Product X, get a Product Y free!' , }, { buy_product_selector_match_type: :include , buy_product_selector_type: :product_id , buy_product_selectors: [ 1234567890987 , 1234567890986 ], quantity_to_buy: 1 , get_product_selector_match_type: :include , get_product_selector_type: :product_id , get_product_selectors: [ 1234567890985 , 1234567890984 ], quantity_to_discount: 1 , allow_incomplete_bundle: false , discount_type: :dollar , discount_amount: 10 , discount_message: 'Buy a Product X, get $10 off a Product Y!' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # DiscountLoop # # Loops through the supplied line items and discounts the supplied #项目的数量提供折扣。 # ================================================================ class DiscountLoop def initialize ( discount_applicator ) @discount_applicator = discount_applicator end def loop_items ( cart , line_items , num_to_discount ) line_items . each do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount split_line_item = line_item . split ( take: num_to_discount ) @discount_applicator . apply ( split_line_item ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , split_line_item ) break else @discount_applicator . apply ( line_item ) num_to_discount -= line_item . quantity end end end end # ================================================================ # BuyVofWGetXofYForZCampaign # # Buy a certain number of matching items, get a certain number of # a different set of matching items with the entered discount # applied. # ================================================================ class BuyVofWGetXofYForZCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | buy_product_selector = ProductSelector . new ( campaign [ :buy_product_selector_match_type ], campaign [ :buy_product_selector_type ], campaign [ :buy_product_selectors ], ) get_product_selector = ProductSelector . new ( campaign [ :get_product_selector_match_type ], campaign [ :get_product_selector_type ], campaign [ :get_product_selectors ], ) buy_items = [] get_items = [] cart . line_items . each do | line_item | buy_items . push ( line_item ) if buy_product_selector . match? ( line_item ) get_items . push ( line_item ) if get_product_selector . match? ( line_item ) end next if buy_items . empty? || get_items . empty? get_items = get_items . sort_by { | line_item | line_item . variant . price } quantity_to_buy = campaign [ :quantity_to_buy ] quantity_to_discount = campaign [ :quantity_to_discount ] buy_offers = ( buy_items . map ( & :quantity ). reduce ( 0 , : + ) / quantity_to_buy ). floor if campaign [ :allow_incomplete_bundle ] number_of_bundles = buy_offers else get_offers = ( get_items . map ( & :quantity ). reduce ( 0 , : + ) / quantity_to_discount ). floor number_of_bundles = [ buy_offers , get_offers ]. min end number_of_discountable_items = number_of_bundles * quantity_to_discount next unless number_of_discountable_items > 0 discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) discount_loop = DiscountLoop . new ( discount_applicator ) discount_loop . loop_items ( cart , get_items , number_of_discountable_items ) end end end CAMPAIGNS = [ BuyVofWGetXofYForZCampaign . new ( BUYVOFW_GETXOFY_FORZ ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Buy a specific quantity of a product for a specific amount
Use this script to offer a number of products for a chosen price.
For example, offer customers 2 t-shirts for $20.
# ================================ Customizable Settings ================================ # ================================================================ # Buy X of Product Y for $Z # # Buy a certain number of matching items for a specific price. # For example: # # "Buy 2 t-shirts for $20" # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'quantity_to_buy' is the number of products needed to #合格 # - 'final_price` is the amount to charge for all products that # are part of the offer # - 'discount_message' is the message to show when a discount # is applied # ================================================================ BUY_X_GET_Y_FOR_Z = [ { product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "your_tag" ], quantity_to_buy: 2 , final_price: 100 , discount_message: “买2for $20' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DollarDiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DollarDiscountApplicator def initialize ( discount_message ) @discount_message = discount_message end def apply ( line_item , discount_amount ) new_line_price = line_item . line_price - discount_amount line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # BuyXOfYForZCampaign # # Buy a certain number of matching items for a specific price. # ================================================================ class BuyXOfYForZCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) eligible_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if eligible_items . nil? eligible_item_count = eligible_items . map ( & :quantity ). reduce ( 0 , : + ) quantity_to_buy = campaign [ :quantity_to_buy ] number_of_offers = ( eligible_item_count / quantity_to_buy ). floor next unless number_of_offers > 0 number_of_discountable_items = number_of_offers * quantity_to_buy total_offer_price = Money . new ( cents: 100 ) * ( number_of_offers * campaign [ :final_price ]) discount_applicator = DollarDiscountApplicator . new ( campaign [ :discount_message ]) self . loop_items ( cart , eligible_items , number_of_discountable_items , total_offer_price , discount_applicator ) end end def loop_items ( cart , line_items , num_to_discount , total_price , discount_applicator ) current_price = Money . zero avg_price = total_price * ( 1 / num_to_discount ) line_items = line_items . sort_by { | line_item | line_item . variant . price } line_items . each do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount split_line_item = line_item . split ( take: num_to_discount ) discount_amount = split_line_item . line_price - ( total_price - current_price ) discount_applicator . apply ( split_line_item , discount_amount ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , split_line_item ) break elsif line_item . quantity == num_to_discount discount_amount = line_item . line_price - ( total_price - current_price ) discount_applicator . apply ( line_item , discount_amount ) break else if line_item . variant . price <= avg_price current_price += line_item . line_price else discount_amount = ( line_item . variant . price - avg_price ) * line_item . quantity current_price += ( line_item . line_price - discount_amount ) discount_applicator . apply ( line_item , discount_amount ) end num_to_discount -= line_item . quantity end end end end CAMPAIGNS = [ BuyXOfYForZCampaign . new ( BUY_X_GET_Y_FOR_Z ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Free gift with purchase
Use this script to offer a discount on a specific product if the cart total is greater than a certain amount.
For example, offer customers a free gift if they spend more than $75.
Note
This script will not add the 'get' item(s) to the cart. It can only discount items that are already in the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Spend $X, get Product Y for Z Discount # # If the cart total is greater than (or equal to) the entered # threshold (less the discounted amount), the entered number of # matching items is discounted by the entered amount. # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'threshold' is the dollar amount needed to spend to qualify # - 'quantity_to_discount' is the number of items to discount # if qualified # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ SPENDX_GETY_FORZ = [ { product_selector_match_type: :include , product_selector_type: :product_id , product_selectors: [ 1234567890123 ], threshold: 75 , quantity_to_discount: 1 , discount_type: :percent , discount_amount: 100 , discount_message: 'Spend $75 or more and get a free Product X!' , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # DiscountLoop # # Loops through the supplied line items and discounts the supplied #项目的数量提供折扣。 # ================================================================ class DiscountLoop def initialize ( discount_applicator ) @discount_applicator = discount_applicator end def loop_items ( cart , line_items , num_to_discount ) line_items . each do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount split_line_item = line_item . split ( take: num_to_discount ) @discount_applicator . apply ( split_line_item ) position = cart . line_items . find_index ( line_item ) cart . line_items . insert ( position + 1 , split_line_item ) break else @discount_applicator . apply ( line_item ) num_to_discount -= line_item . quantity end end end end # ================================================================ # SpendXGetYForZCampaign # # If the cart total is greater than (or equal to) the entered # threshold (less the discounted amount), the entered number of # matching items is discounted by the entered amount. # ================================================================ class SpendXGetYForZCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) @campaigns . each do | campaign | threshold = Money . new ( cents: 100 ) * campaign [ :threshold ] next if cart . subtotal_price < threshold product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) eligible_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if eligible_items . nil? eligible_items = eligible_items . sort_by { | line_item | line_item . variant . price } num_to_discount = campaign [ :quantity_to_discount ] cart_total = cart . subtotal_price eligible_items . each do | line_item | break if num_to_discount <= 0 if line_item . quantity > num_to_discount cart_total -= line_item . variant . price * num_to_discount break else cart_total -= line_item . line_price num_to_discount -= line_item . quantity end end next if cart_total < threshold discount_applicator = discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) discount_loop = DiscountLoop . new ( discount_applicator ) discount_loop . loop_items ( cart , eligible_items , campaign [ :quantity_to_discount ]) end end end CAMPAIGNS = [ SpendXGetYForZCampaign . new ( SPENDX_GETY_FORZ ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Product discount by customer tag
Use this script to offer a discount on specific products to customers with specific tags.
For example, offer a 20% discount if the customer has theVIP
tag.
# ================================ Customizable Settings ================================ # ================================================================ # Product Discounts by Customer Tag # # If we have a matching customer (by tag), the entered discount # will be applied to any matching items. # # - 'customer_tag_match_type' determines whether we look for the customer # to be tagged with any of the entered tags or not. Can be: # - ':include' to check if the customer is tagged # - ':exclude' to make sure the customer isn't tagged # - 'customer_tags' is a list of tags to identify qualified # customers # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ DISCOUNTS_FOR_CUSTOMER_TAG = [ { customer_tag_match_type: :include , customer_tags: [ "VIP" ], product_selector_match_type: :include , product_selector_type: :all , product_selectors: nil , discount_type: :percent , discount_amount: 20 , discount_message: "Discount for VIP customers!" , }, ] # ================================ Script Code (do not edit) ================================ # ================================================================ # CustomerTagSelector # #发现提供的客户是否有任何the entered tags. # ================================================================ class CustomerTagSelector def initialize ( match_type , tags ) @comparator = match_type == :include ? 'any?' : 'none?' @tags = tags . map { | tag | tag . downcase . strip } end def match? ( customer ) customer_tags = customer . tags . map { | tag | tag . downcase . strip } ( @tags & customer_tags ). send ( @comparator ) end end # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # DiscountForCustomerTagCampaign # # If we have a matching customer (by tag), the entered discount # is applied to any matching items. # ================================================================ class DiscountForCustomerTagCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) return unless cart . customer & . tags @campaigns . each do | campaign | customer_tag_selector = CustomerTagSelector . new ( campaign [ :customer_tag_match_type ], campaign [ :customer_tags ]) next unless customer_tag_selector . match? ( cart . customer ) product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ] ) discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ DiscountForCustomerTagCampaign . new ( DISCOUNTS_FOR_CUSTOMER_TAG ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Product discount by customer marketing
Use this script to offer a discount on specific products to customers who accept marketing.
For example, offer 10% off all products for any customers that accept marketing.
# ================================ Customizable Settings ================================ # ================================================================ # Product Discount by Customer Marketing # # If the customer accepts marketing, any matching items are # discounted by the entered amount. # # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNTS_BY_CUSTOMER_MARKETING = [ { product_selector_match_type: :include , product_selector_type: :all , product_selectors: nil , discount_type: :percent , discount_amount: 10 , discount_message: '10% off for subscribed customers!' } ] # ================================ Script Code (do not edit) =============================== # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # ProductDiscountByCustomerMarketingCampaign # # If the customer accepts marketing, any matching items are # discounted by the entered amount. # ================================================================ class ProductDiscountByCustomerMarketingCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) return if cart . customer . nil? @campaigns . each do | campaign | next unless cart . customer . accepts_marketing? product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ] ) discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ ProductDiscountByCustomerMarketingCampaign . new ( PRODUCT_DISCOUNTS_BY_CUSTOMER_MARKETING ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Product discount by customer order count
Use this script to offer a discount on specific products for customers with a specific order count.
For example, offer 10% off for any customer with no orders.
# ================================ Customizable Settings ================================ # ================================================================ # Product Discount by Order Count # # If the customer has made a matching number of orders, any # matching items are discounted by the entered amount. # # - 'order_count_match_type' determines how we compare the # customer's order count to the entered limit. Can be: # - ':greater_than' to ensure that the customer's order count # is greater than the entered limit # - ':greater_than_equal' to ensure that the customer's order # count is greater than, or equal to, the entered limit # - ':less_than' to ensure that the customer's order count is # less than the entered limit # - ':less_than_equal' to ensure that the customer's order # count is less than, or equal to, the entered limit # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'discount_type' is the type of discount to provide. Can be # either: # - ':percent' # - ':dollar' # - 'discount_amount' is the percentage/dollar discount to # apply (per item) # - 'discount_message' is the message to show when a discount # is applied # ================================================================ PRODUCT_DISCOUNTS_BY_ORDER_COUNT = [ { order_count_match_type: :less_than , order_count_limit: 1 , product_selector_match_type: :include , product_selector_type: :all , product_selectors: nil , discount_type: :percent , discount_amount: 10 , discount_message: '10% off for first time buyers!' } ] # ================================ Script Code (do not edit) =============================== # ================================================================ # OrderCountSelector # # Finds whether the customer has made a certain number of orders # ================================================================ class OrderCountSelector def initialize ( match_type , limit ) @match_type = match_type @limit = limit end def match? ( customer ) if self . respond_to? ( @match_type ) self . send ( @match_type , customer , @limit ) else raise RuntimeError . new ( 'Invalid order count match type' ) end end def greater_than ( customer , limit ) customer . orders_count > limit end def greater_than_equal ( customer , limit ) customer . orders_count >= limit end def less_than ( customer , limit ) customer . orders_count < limit end def less_than_equal ( customer , limit ) customer . orders_count <= limit end end # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DiscountApplicator # # Applies the entered discount to the supplied line item. # ================================================================ class DiscountApplicator def initialize ( discount_type , discount_amount , discount_message ) @discount_type = discount_type @discount_message = discount_message @discount_amount = if discount_type == :percent 1 - ( discount_amount * 0.01 ) else Money . new ( cents: 100 ) * discount_amount end end def apply ( line_item ) new_line_price = if @discount_type == :percent line_item . line_price * @discount_amount else [ line_item . line_price - ( @discount_amount * line_item . quantity ), Money . zero ]. max end line_item . change_line_price ( new_line_price , message: @discount_message ) end end # ================================================================ # ProductDiscountByOrderCountCampaign # # If the customer has made a matching number of orders, any # matching items are discounted by the entered amount. # ================================================================ class ProductDiscountByOrderCountCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) return if cart . customer . nil? @campaigns . each do | campaign | order_count_selector = OrderCountSelector . new ( campaign [ :order_count_match_type ], campaign [ :order_count_limit ] ) next unless order_count_selector . match? ( cart . customer ) product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ] ) discount_applicator = DiscountApplicator . new ( campaign [ :discount_type ], campaign [ :discount_amount ], campaign [ :discount_message ] ) cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) discount_applicator . apply ( line_item ) end end end end CAMPAIGNS = [ ProductDiscountByOrderCountCampaign . new ( PRODUCT_DISCOUNTS_BY_ORDER_COUNT ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Disable discount codes
Use this script to disable use of discount codes in the checkout process.
For example, prevent customers from using discount codes while your store is offering a sale.
# ================================ Customizable Settings ================================ # ================================================================ # Disable Discount Code Use # # Any discount codes will be rejected with the entered message. # # - 'REJECTION_MESSAGE' is the message to show when a discount # code is rejected # ================================================================ REJECTION_MESSAGE = 'Discount codes cannot be used during this sale' # ================================ Script Code (do not edit) ================================ # ================================================================ # DisableDiscountCodesCampaign # # Any discount codes will be rejected with the entered message. # ================================================================ class DisableDiscountCodesCampaign def initialize ( rejection_message ) @rejection_message = rejection_message end def run ( cart ) return if cart . discount_code . nil? cart . discount_code . reject ( message: @rejection_message ) end end CAMPAIGNS = [ DisableDiscountCodesCampaign . new ( REJECTION_MESSAGE ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Disable discount codes for product
Use this script to disable use of discount codes in the checkout process if specific products are in the cart.
For example, prevent customers from using discount codes if a product tagged withdiscounted
is in the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Disable Discount Code(s) For Products # # If any matching discount codes are used, and any matching items # are in the cart, the discount code is rejected with the entered # message. # # - 'discount_code_match_type' determines whether the below # strings should be an exact or partial match. Can be: # - ':exact' for an exact match # - ':partial' for a partial match # - 'discount_codes' is a list of strings to identify discount # codes # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'rejection_message' is the message to show when a discount # code is rejected # ================================================================ REJECT_DISCOUNT_CODE_FOR_PRODUCTS = [ { discount_code_match_type: :exact , discount_codes: [ "TESTCODE1" , "TESTCODE2" ], product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "discounted" ], rejection_message: "Discount codes can't be used with 'discounted' products" } ] # ================================ Script Code (do not edit) ================================ # ================================================================ # DiscountCodeSelector # # Finds whether the supplied discount code matches any of the # entered codes. # ================================================================ class DiscountCodeSelector def initialize ( match_type , discount_codes ) @comparator = match_type == :exact ? '==' : 'include?' @discount_codes = discount_codes . map { | discount_code | discount_code . upcase . strip } end def match? ( discount_code ) @discount_codes . any? { | code | discount_code . code . upcase . send ( @comparator , code ) } end end # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # DisableDiscountCodesForProductsCampaign # # If any matching discount codes are used, and any matching items # are in the cart, the discount code is rejected with the entered # message. # ================================================================ class DisableDiscountCodesForProductsCampaign def initialize ( campaigns ) @campaigns = campaigns end def run ( cart ) return if cart . discount_code . nil? @campaigns . each do | campaign | discount_code_selector = DiscountCodeSelector . new ( campaign [ :discount_code_match_type ], campaign [ :discount_codes ] ) next unless discount_code_selector . match? ( cart . discount_code ) product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ], ) next unless cart . line_items . any? { | line_item | product_selector . match? ( line_item ) } cart . discount_code . reject ( message: campaign [ :rejection_message ]) end end end CAMPAIGNS = [ DisableDiscountCodesForProductsCampaign . new ( REJECT_DISCOUNT_CODE_FOR_PRODUCTS ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart
Limit Product Quantities
Use this script to apply quantity limits to specific products.
For example, prevent customers from buying more than 1 'Product X' in a single order.
Note
This script does not prevent the initial adding of the items. It only removes any excess items after the fact. There is no notice to customers that these items have been removed from the cart.
# ================================ Customizable Settings ================================ # ================================================================ # Product Quantity Limits # # If the quantity of any matching items is greater than the # entered threshold, the excess items are removed from the cart. # It should be noted that there will be no notice to the customer # when this happens. # # - 'enable' determines whether the campaign will run. Can be: # - 'true' to run # - 'false' to not run # - 'product_selector_match_type' determines whether we look for # products that do or don't match the entered selectors. Can # be: # - ':include' to check if the product does match # - ':exclude' to make sure the product doesn't match # - 'product_selector_type' determines how eligible products # will be identified. Can be either: # - ':tag' to find products by tag # - ':type' to find products by type # - ':vendor' to find products by vendor # - ':product_id' to find products by ID # - ':variant_id' to find products by variant ID #——“:订阅”找到依靠tion products # - ':all' for all products # - 'product_selectors' is a list of identifiers (from above) # for qualifying products. Product/Variant ID lists should # only contain numbers (ie. no quotes). If ':all' is used, # this can also be 'nil'. # - 'variant_level_limit' determines whether the below limit # is applied on a variant, or a total quantity, level. For # example, can I have X number of individual matching items, # or can I only have X number total of matching items? # Can be: # - 'true' to limit at a variant level # - 'false' to limit total quantity # - 'quantity_allowed' is the number of products allowed # ================================================================ QUANTITY_LIMITS = { enable: true , campaigns: [ { product_selector_match_type: :include , product_selector_type: :tag , product_selectors: [ "limited" ], variant_level_limit: true , quantity_allowed: 2 , }, ] } # ================================ Script Code (do not edit) ================================ # ================================================================ # ProductSelector # # Finds matching products by the entered criteria. # ================================================================ class ProductSelector def initialize ( match_type , selector_type , selectors ) @match_type = match_type @comparator = match_type == :include ? 'any?' : 'none?' @selector_type = selector_type @selectors = selectors end def match? ( line_item ) if self . respond_to? ( @selector_type ) self . send ( @selector_type , line_item ) else raise RuntimeError . new ( 'Invalid product selector type' ) end end def tag ( line_item ) product_tags = line_item . variant . product . tags . map { | tag | tag . downcase . strip } @selectors = @selectors . map { | selector | selector . downcase . strip } ( @selectors & product_tags ). send ( @comparator ) end def type ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . product_type . downcase . strip ) end def vendor ( line_item ) @selectors = @selectors . map { | selector | selector . downcase . strip } ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . vendor . downcase . strip ) end def product_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . product . id ) end def variant_id ( line_item ) ( @match_type == :include ) == @selectors . include? ( line_item . variant . id ) end def subscription ( line_item ) ! line_item . selling_plan_id . nil? end def all ( line_item ) true end end # ================================================================ # ProductQuantityLimitCampaign # # If the quantity of any matching items is greater than the # entered threshold, the excess items are removed from the cart. # ================================================================ class ProductQuantityLimitCampaign def initialize ( enable , campaigns ) @enable = enable @campaigns = campaigns end def run ( cart ) return unless @enable @campaigns . each do | campaign | product_selector = ProductSelector . new ( campaign [ :product_selector_match_type ], campaign [ :product_selector_type ], campaign [ :product_selectors ] ) if campaign [ :variant_level_limit ] applicable_items = {} cart . line_items . each do | line_item | next unless product_selector . match? ( line_item ) id = line_item . variant . id if applicable_items [ id ]. nil? applicable_items [ id ] = { items: [], total_quantity: 0 } end applicable_items [ id ][ :items ]. push ( line_item ) applicable_items [ id ][ :total_quantity ] += line_item . quantity end next if applicable_items . nil? applicable_items . each do | id , info | next unless info [ :total_quantity ] > campaign [ :quantity_allowed ] num_to_remove = info [ :total_quantity ] - campaign [ :quantity_allowed ] self . loop_items ( cart , info [ :items ], num_to_remove ) end else applicable_items = cart . line_items . select { | line_item | product_selector . match? ( line_item ) } next if applicable_items . nil? total_quantity = applicable_items . map ( & :quantity ). reduce ( 0 , : + ) next unless total_quantity > campaign [ :quantity_allowed ] num_to_remove = total_quantity - campaign [ :quantity_allowed ] self . loop_items ( cart , applicable_items , num_to_remove ) end end end def loop_items ( cart , line_items , num_to_remove ) line_items . each do | line_item | if line_item . quantity > num_to_remove split_line_item = line_item . split ( take: num_to_remove ) break else index = cart . line_items . find_index ( line_item ) cart . line_items . delete_at ( index ) num_to_remove -= line_item . quantity end break if num_to_remove <= 0 end end end CAMPAIGNS = [ ProductQuantityLimitCampaign . new ( QUANTITY_LIMITS [ :enable ], QUANTITY_LIMITS [ :campaigns ], ), ] CAMPAIGNS . each do | campaign | campaign . run ( Input . cart ) end Output . cart = Input . cart