품목 스크립트 예제
품목 스크립트는 카트 품목에 영향을 미치며 카트의 속성 및 가격을 변경할 수 있습니다. 이러한 스크립트는 품목 추가 또는 제거, 발송 주소 변경 또는 할인 코드를 추가하는 등 카트 특성이 변경될 때마다 실행됩니다.
이 페이지의 템플릿을 사용하려면 빈 템플릿으로 새 스크립트를 만드십시오.
단계:
- Shopify Admin에서 앱> Script Editor로 이동합니다.
- 스크립트 생성을 클릭합니다.
- 품목을 클릭합니다.
- 빈 템플릿을 선택하고 스크립트 생성을 클릭합니다.
- Ruby 소스 코드 섹션에서 기본 코드 라인을 삭제합니다.
Output.cart = Input.cart
- 이 페이지에서 스크립트를 복사하여 Ruby 소스 코드 섹션에 붙여넣습니다.
- 스크립트의 사용자 지정 가능 설정 섹션을 편집하여 스토어에서 작동시킵니다.
- 스크립트를 테스트합니다. 자세한 내용은 Shopify Scripts 테스트 및 디버그를 참조하십시오.
- 테스트 후에는
- 초안 저장을 클릭하여 해당 스크립트의 게시되지 않은 초안을 저장하거나
- 저장 및 게시를 클릭하여 스크립트를 생성하고 게시합니다.
이 페이지의 정보
지출별 계층 카트 할인
이 스크립트를 사용하여 카트에 있는 특정 품목의 전체 지출액에 따라 증가하는 달러 금액 할인을 제공합니다. 이 할인은 카트의 개별 품목 전체에서 가능한 한 균등하게 분배됩니다.
예를 들어, 고객이 $150 이상을 지출하면 전체 지출액에서 $25 할인, $300 이상을 지출하면 $50 할인, $400 이상을 지출하면 $75 할인을 제공할 수 있습니다.
# ================================ 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
지출별 계층 할인
이 스크립트를 사용하면 카트에 있는 특정 품목의 전체 지출액에 따라 증가하는 백분율 할인을 제공합니다.
예를 들어 고객이 $30 이상을 지출하면 10% 할인, $50 이상을 지출하면 15% 할인, $100 이상을 지출하면 20% 할인을 제공할 수 있습니다.
# ================================ 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
수량에 따른 계층형 제품 할인
이 스크립트를 사용하면 특정 제품에 대량 할인을 제공하여 카트에 있는 해당 제품의 수에 따라 백분율 할인이 적용됩니다.
예를 들어 모자 2개 이상을 구매하는 고객에게 각 모자를 10% 할인해 주거나 모자 5개 이상을 구매하면 15% 할인을 제공합니다.
# ================================ 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
# - ':subscription' to find subscription 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:
# - 'quantity' is the minimum quantity you need to buy 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
# ================================================================
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
제품 지출에 따른 계층형 제품 할인
이 스크립트를 사용하여 카트에 있는 특정 품목의 전체 지출액에 따라 증가하는 백분율 할인을 제공합니다.
예를 들어, 고객이 $30 이상을 지출하면 10% 할인, $50 이상을 지출하면 15% 할인, $100 이상 지출 시 20% 할인을 제공하지만 특정 태그와 일치하는 품목에 대해서만 제공할 수 있습니다.
# ================================ 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
# - ':subscription' to find subscription 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
# 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
# ================================================================
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
제품 할인
특정 품목에 대한 할인을 제공하려면 이 스크립트를 사용하십시오.
예를 들어 discounted
태그가 지정된 품목에 대해 고객에게 10% 할인을 제공합니다.
# ================================ Customizable Settings ================================
# ================================================================
# Discount by Product
#
# Any matching item will be 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
# - ':subscription' to find subscription 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
#
# Any matching item will be discounted by the entered 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
할인 코드별 제품 할인
특정 할인 코드가 사용되는 경우 이 스크립트를 사용하여 특정 품목에 대한 할인을 제공할 수 있습니다.
예를 들어 고객이 DISCOUNT_10
할인 코드를 사용하는 경우 discounted
태그가 지정된 품목에 대해 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
# - ':subscription' to find subscription 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
특정 금액의 제품을 구매하고 할인으로 얻는 두 번째 금액
특정 수량 이상을 구매한 경우 이 스크립트를 사용하여 특정 품목에 대한 할인을 제공할 수 있습니다.
예를 들어, discount
태그가 지정된 품목을 3개 구매하고 discount
태그가 지정된 4번째 품목에 대해 고객에게 50% 할인을 받도록 제공합니다.
# ================================ 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
# - ':subscription' to find subscription 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
# qualify
# - '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
번들 구매, 할인된 금액으로 추가 제품 사기
이 스크립트를 사용하여 다른 여러 품목을 구매한 경우 특정 품목에 대한 할인을 제공합니다.
예를 들어 모자, 티셔츠 및 선글라스를 구매한 고객에게 무료로 양말 한 켤레를 제공할 수 있습니다.
# ================================ 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
# - ':subscription' to find subscription 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
# number of items by the supplied discount.
# ================================================================
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
번들 할인
특정 제품 세트가 카트에 추가되면 이 스크립트를 사용하여 할인을 제공할 수 있습니다.
예를 들어 티셔츠, 모자 및 선글라스를 구매한 고객에게 20% 할인을 제공할 수 있습니다.
# ================================ 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
# number of items by the supplied discount.
# ================================================================
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
원 플러스 원(BOGO) 할인
이 스크립트를 사용하여 고객이 다른 제품의 세트 번호를 구매할 때 품목에 대한 백분율 할인을 제공합니다.
예를 들어 티셔츠 2장을 구매하면 고객에게 10% 할인 혜택을 제공할 수 있습니다.
# ================================ 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
# - ':subscription' to find subscription 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
# qualify
# - '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
# number of items by the supplied discount.
# ================================================================
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
특정 금액에 해당하는 제품의 특정 수량 구매
이 스크립트를 사용하여 선택한 가격으로 여러 제품을 제공할 수 있습니다.
예를 들어 티셔츠 2장을 $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
# - ':subscription' to find subscription 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
# qualify
# - '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: 'Buy 2 for $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
구매 시 경품 증정
카트 총액이 특정 금액보다 큰 경우에 이 스크립트를 사용하여 특정 제품에 대한 할인을 제공할 수 있습니다.
예를 들어, $75 이상 구매한 경우에는 고객에게 무료 선물을 제공할 수 있습니다.
# ================================ 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
# - ':subscription' to find subscription 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
# number of items by the supplied discount.
# ================================================================
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
고객 태그별 제품 할인
이 스크립트를 사용하여 특정 태그가 있는 고객에게 특정 제품에 대한 할인을 제공합니다.
예를 들어, 고객에게 VIP
태그가 지정된 경우 20% 할인을 제공합니다.
# ================================ 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
# - ':subscription' to find subscription 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
#
# Finds whether the supplied customer has any of 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
고객 마케팅별 제품 할인
마케팅을 수락하는 고객에게 특정 제품에 대한 할인을 제공하려면 이 스크립트를 사용하십시오.
예를 들어, 마케팅을 수락하는 고객에게 모든 제품을 10% 할인하여 제공합니다.
# ================================ 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
# - ':subscription' to find subscription 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
고객 주문 건수별 제품 할인
특정 주문 건수의 고객에게 특정 제품에 대한 할인을 제공하려면 이 스크립트를 사용하십시오.
예를 들어 주문 내역이 없는 모든 고객에게 10% 할인을 제공할 수 있습니다.
# ================================ 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
# - ':subscription' to find subscription 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 customers!'
}
]
# ================================ 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
할인 코드 사용 안 함
이 스크립트를 사용하여 결제 프로세스에서 할인 코드 사용을 비활성화합니다.
예를 들어, 스토어에서 할인을 제공하는 동안 고객이 할인 코드를 사용하지 못하도록 합니다.
# ================================ 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
제품에 대한 할인 코드 비활성화
특정 제품이 카트에 있는 경우 이 스크립트를 사용하여 결제 프로세스에서 할인 코드를 사용할 수 없도록 합니다.
예를 들어, discounted
태그가 지정된 제품이 카트에 있는 경우 고객이 할인 코드를 사용하지 못하도록 합니다.
# ================================ 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
# - ':subscription' to find subscription 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
제품 수량 제한
이 스크립트를 사용하여 특정 제품에 수량 제한을 적용합니다.
예를 들어, 고객이 한 번 주문에 2개 이상 '제품 X'를 구매하지 못하도록 합니다.
# ================================ 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
# - ':subscription' to find subscription 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