運費費率指令碼範例

運送指令碼會與運費費率互動,可變更運費名稱、可見性、顯示順序和價格。每當顧客在結帳時進入運送選項頁面,這類指令碼就會執行。

提供訂閱折扣的運送指令碼僅會套用於該訂閱的第一筆付款。該指令碼不會為後續的付款提供折扣。

若要在此頁面使用範本,請使用空白範本建立新的指令碼。

步驟:

  1. 在 Shopify 管理介面 中,前往應用程式 > Script Editor
  2. 按一下建立指令碼
  3. 點擊「運費費率」。
  4. 選取空白範本,然後按一下建立指令碼
  5. Ruby 來源程式碼區段中,刪除預設程式碼行: Output.cart = Input.cart
  6. 從此頁面複製一個指令碼,貼入 Ruby 來源程式碼區段。
  7. 編輯此指令碼的自訂設定區段,以便您的商店啟用此功能。
  8. 測試您的指令碼。如需更多資訊,請參閱「對 Shopify 指令碼進行測試和偵錯」。
  9. 測試之後:
    • 按一下「儲存草稿」,儲存尚未發佈的指令碼草稿,或
    • 按一下「儲存並發佈」,建立並發佈指令碼

將訊息新增至特定省份及國家/地區的費率名稱中

使用此指令碼,依運送地址的省份和國家/地區,新增訊息至運費費率中。

舉例而言,針對不列顛哥倫比亞省和安大略省 (加拿大),以及華盛頓州和紐約州 (美國) 的運費費率,新增以下訊息: Due to COVID-19 disruptions, shipping might take longer than normal.

# ================================ Customizable Settings ================================
# ================================================================
# Add Message to Rate Names for Province or Country
#
# If the cart's shipping address province/country matches
# the entered settings, all shipping rates will have the
# entered message appended to their name.
#
# - 'country_code' is a 2-character abbreviation for the
# applicable country
# - 'province_code_match_type' determines whether we look for
# provinces that do, or don't, match the entered options, or
# all provinces. Can be:
# - ':include' to look for provinces that DO match
# - ':exclude' to look for provinces that DO NOT match
# - ':all' to look for all provinces
# - 'province_codes' is a list of 2-character abbreviations for
# the applicable provinces
# - 'message' is the message to append to rate names
# ================================================================
RATE_MESSAGE_FOR_PROVINCE_COUNTRY = [
  {
    country_code: "CA",
    province_code_match_type: :include,
    province_codes: ["BC", "ON"],
    message: "Due to COVID-19 disruptions, shipping might take longer than normal."
  },
  {
    country_code: "US",
    province_code_match_type: :include,
    province_codes: ["NY", "WA"],
    message: "Due to COVID-19 disruptions, shipping might take longer than normal."
  },
]

# ================================ Script Code (do not edit) ===============================
# ================================================================
# ProvinceSelector
#
# Finds whether the supplied province code matches the entered
# strings.
# ================================================================
class ProvinceSelector
  def initialize(match_type, provinces)
    @match_type = match_type
    @provinces = provinces.map { |province| province.upcase.strip }
  end

  def match?(province_code)
    if @match_type == :all
      true
    else
      (@match_type == :include) == @provinces.any? { |province| province_code.upcase.strip == province }
    end
  end
end

# ================================================================
# AddMessageToRateForProvinceCountryCampaign
#
# If the cart's shipping address country/province matches the
# entered settings, all shipping rates will have the entered
# message appended to their name
# ================================================================
class AddMessageToRateForProvinceCountryCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    return if cart.shipping_address.nil?

    address = cart.shipping_address

    @campaigns.each do |campaign|
      next unless address.country_code.upcase.strip == campaign[:country_code].upcase.strip

      province_selector = ProvinceSelector.new(campaign[:province_code_match_type],campaign[:province_codes])

      next unless province_selector.match?(address.province_code)

      shipping_rates.each do |shipping_rate|
        rate_name = shipping_rate.name + ' - ' + campaign[:message]
        shipping_rate.change_name(rate_name)
      end
    end
  end
end

CAMPAIGNS = [
  AddMessageToRateForProvinceCountryCampaign.new(RATE_MESSAGE_FOR_PROVINCE_COUNTRY),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

針對特定郵遞區號、省份及國家/地區顯示費率

使用此指令碼,針對來自特定地點的顧客,只顯示指定運費費率。

舉例而言,針對在加州郵遞區號為 90210 的顧客,只顯示特定的運費費率。該費率會對所有其他地點隱藏。

# ================================ Customizable Settings ================================
# ================================================================
# Show Rate(s) for Zip/Province/Country
#
# If the cart's shipping address country/province/zip match the
# entered settings, the entered rate(s) are shown, and all other
# rates are hidden. Otherwise, the entered rate(s) are hidden.
#
# - 'country_code' is a 2-character abbreviation for the
# applicable country
# - 'province_code' is a list of 2-character abbreviations for
# the applicable province(s)
# - 'zip_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
# - 'zip_codes' is a list of strings to identify zip codes
# - 'rate_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
# - ':all' for all rates
# - 'rate_names' is a list of strings to identify rates
# - if using ':all' above, this can be set to 'nil'
# ================================================================
SHOW_RATES_FOR_ZIP_PROVINCE_COUNTRY = [
  {
    country_code: "US",
    province_code: "CA",
    zip_code_match_type: :exact,
    zip_codes: ["90210"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate"],
  },
]

# ================================ Script Code (do not edit) ================================
# ================================================================
# ZipCodeSelector
#
# Finds whether the supplied zip code matches any of the entered
# strings.
# ================================================================
class ZipCodeSelector
  def initialize(match_type, zip_codes)
    @comparator = match_type == :exact ? '==' : 'include?'
    @zip_codes = zip_codes.map { |zip_code| zip_code.upcase.strip }
  end

  def match?(zip_code)
    @zip_codes.any? { |zip| zip_code.to_s.upcase.strip.send(@comparator, zip) }
  end
end

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @match_type = match_type
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names&.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    if @match_type == :all
      true
    else
      @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
    end
  end
end

# ================================================================
# ShowRatesForZipProvinceCountryCampaign
#
# If the cart's shipping address zip/province/country match the
# entered settings, the entered rate(s) are shown, and all other
# rates are hidden. Otherwise, the entered rate(s) are hidden.
# ================================================================
class ShowRatesForZipProvinceCountryCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    address = cart.shipping_address

    @campaigns.each do |campaign|
      zip_code_selector = ZipCodeSelector.new(campaign[:zip_code_match_type], campaign[:zip_codes])
      rate_name_selector = RateNameSelector.new(campaign[:rate_match_type], campaign[:rate_names])

      if address.nil?
        full_match = false
      else
        country_match = address.country_code.upcase.strip == campaign[:country_code].upcase.strip
        province_match = address.province_code.upcase.strip == campaign[:province_code].upcase.strip
        zip_match = zip_code_selector.match?(address.zip)
        full_match = country_match && province_match && zip_match
      end

      shipping_rates.delete_if do |shipping_rate|
        rate_name_selector.match?(shipping_rate) != full_match
      end
    end
  end
end

CAMPAIGNS = [
  ShowRatesForZipProvinceCountryCampaign.new(SHOW_RATES_FOR_ZIP_PROVINCE_COUNTRY),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

針對特定郵遞區號、省份及國家/地區隱藏費率

使用此指令碼以對來自特定地點的顧客隱藏費率。

舉例而言,針對在加州郵遞區號為 90210 的顧客,隱藏特定的運費費率。

# ================================ Customizable Settings ================================
# ================================================================
# Hide Rate(s) for Zip/Province/Country
#
# If the cart's shipping address country/province/zip match the
# entered settings, the entered rate(s) are hidden.
#
# - 'country_code' is a 2-character abbreviation for the
# applicable country or region
# - 'province_code' is a list of 2-character abbreviations for
# the applicable provinces or states
# - 'zip_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
# - 'zip_codes' is a list of strings to identify zip codes
# - 'rate_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
# - ':all' for all rates
# - 'rate_names' is a list of strings to identify rates
# - if using ':all' above, this can be set to 'nil'
# ================================================================
HIDE_RATES_FOR_ZIP_PROVINCE_COUNTRY = [
  {
    country_code: "US",
    province_code: "CA",
    zip_code_match_type: :exact,
    zip_codes: ["90210"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate"],
  },
]

# ================================ Script Code (do not edit) ================================
# ================================================================
# ZipCodeSelector
#
# Finds whether the supplied zip code matches any of the entered
# strings.
# ================================================================
class ZipCodeSelector
  def initialize(match_type, zip_codes)
    @comparator = match_type == :exact ? '==' : 'include?'
    @zip_codes = zip_codes.map { |zip_code| zip_code.upcase.strip }
  end

  def match?(zip_code)
    @zip_codes.any? { |zip| zip_code.to_s.upcase.strip.send(@comparator, zip) }
  end
end

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @match_type = match_type
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names&.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    if @match_type == :all
      true
    else
      @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
    end
  end
end

# ================================================================
# HideRatesForZipProvinceCountryCampaign
#
# If the cart's shipping address zip/province/country match the
# entered settings, the entered rate(s) are hidden.
# ================================================================
class HideRatesForZipProvinceCountryCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    address = cart.shipping_address

    return if address.nil?

    @campaigns.each do |campaign|
      zip_code_selector = ZipCodeSelector.new(campaign[:zip_code_match_type], campaign[:zip_codes])

      country_match = address.country_code.upcase.strip == campaign[:country_code].upcase.strip
      province_match = address.province_code.upcase.strip == campaign[:province_code].upcase.strip
      zip_match = zip_code_selector.match?(address.zip)

      next unless country_match && province_match && zip_match

      rate_name_selector = RateNameSelector.new(campaign[:rate_match_type], campaign[:rate_names])

      shipping_rates.delete_if do |shipping_rate|
        rate_name_selector.match?(shipping_rate)
      end
    end
  end
end

CAMPAIGNS = [
  HideRatesForZipProvinceCountryCampaign.new(HIDE_RATES_FOR_ZIP_PROVINCE_COUNTRY),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

在指定國家/地區中隱藏特定產品的費率

使用此指令碼,在來自特定國家/地區的顧客購買特定品項時,對其隱藏費率。

舉例而言,針對加拿大境內購買 T 恤的顧客,隱藏特定運費費率。

# ================================ Customizable Settings ================================
# ================================================================
# Hide Rate(s) for Product/Country
#
# If the cart contains any matching items, and we have a matching
# country, the entered rate(s) are hidden.
#
# - '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
# - 'product_selectors' is a list of tags or IDs to identify
# associated products
# - 'country_code_match_type' determines whether we look for
# countries that do, or don't, match the entered options, or
# all countries. Can be:
# - ':include' to look for countries that DO match
# - ':exclude' to look for countries that DO NOT match
# - ':all' to look for all countries
# - 'country_codes' is a list of country code abbreviations
# - ie. United States would be `US`
# - 'rate_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
# - ':all' for all rates
# - 'rate_names' is a list of strings to identify rates
# - if using ':all' above, this can be set to 'nil'
# ================================================================
HIDE_RATES_FOR_PRODUCT_AND_COUNTRY = [
  {
    product_selector_match_type: :include,
    product_selector_type: :product_id,
    product_selectors: [1234567890987, 1234567890986],
    country_code_match_type: :include,
    country_codes: ["CA"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate"],
  },
]

# ================================ 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
end

# ================================================================
# CountrySelector
#
# Finds whether the supplied country code matches the entered
# strings.
# ================================================================
class CountrySelector
  def initialize(match_type, countries)
    @match_type = match_type
    @countries = countries.map { |country| country.upcase.strip }
  end

  def match?(country_code)
    if @match_type == :all
      true
    else
      (@match_type == :include) == @countries.any? { |country| country_code.upcase.strip == country }
    end
  end
end

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @match_type = match_type
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names&.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    if @match_type == :all
      true
    else
      @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
    end
  end
end

# ================================================================
# HideRatesForProductCountryCampaign
#
# If the cart contains any matching items, and we have a matching
# country, the entered rate(s) are hidden.
# ================================================================
class HideRatesForProductCountryCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    address = cart.shipping_address

    return if address.nil?

    @campaigns.each do |campaign|
      product_selector = ProductSelector.new(
        campaign[:product_selector_match_type],
        campaign[:product_selector_type],
        campaign[:product_selectors],
      )

      country_selector = CountrySelector.new(campaign[:country_code_match_type], campaign[:country_codes])
      product_match = cart.line_items.any? { |line_item| product_selector.match?(line_item) }
      country_match = country_selector.match?(address.country_code)

      next unless product_match && country_match

      rate_name_selector = RateNameSelector.new(
        campaign[:rate_match_type],
        campaign[:rate_names],
      )

      shipping_rates.delete_if do |shipping_rate|
        rate_name_selector.match?(shipping_rate)
      end
    end
  end
end

CAMPAIGNS = [
  HideRatesForProductCountryCampaign.new(HIDE_RATES_FOR_PRODUCT_AND_COUNTRY),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

隱藏郵政信箱配送費率

使用此指令碼,在顧客的運送地址是郵政信箱時,對其隱藏特定費率。

# ================================ Customizable Settings ================================
# ================================================================
# Hide Rate(s) for PO Box addresses
#
# If the shipping address contains any of the entered "PO Box"
# identifiers, the entered rate(s) are hidden.
#
# - 'po_box_triggers' is a list of possible strings for a PO
# Box address
# - 'rate_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
# - ':all' for all rates
# - 'rate_names' is a list of rates to show/hide if the above
# conditions are met
# ================================================================
HIDE_RATES_FOR_PO_BOX = [
  {
    po_box_triggers: [
      "po box", "post office", "p o box", "p.o.box", "p.o. box", "p.o box", "pobox",
      "post office box", "post box", "p. o. box", "po. box", "postal box",
    ],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate", "Other Shipping Rate"],
  },
]

# ================================ Script Code (do not edit) ================================
# ================================================================
# AddressSelector
#
# Finds whether the supplied address contains any of the entered
# strings.
# ================================================================
class AddressSelector
  def initialize(triggers)
    @triggers = triggers.map { |trigger| trigger.downcase.strip }
  end

  def match?(address)
    address_fields = [address.address1, address.address2].map do |line|
      line.nil? ? "" : line.downcase
    end

    address_fields = address_fields.join(" ")
    @triggers.any? { |trigger| address_fields.include?(trigger) }
  end
end

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @match_type = match_type
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    if @match_type == :all
      true
    else
      @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
    end
  end
end

# ================================================================
# HideRatesForPOBoxCampaign
#
# If the shipping address contains any of the entered "PO Box"
# identifiers, the entered rate(s) are hidden.
# ================================================================
class HideRatesForPOBoxCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    address = cart.shipping_address

    return if address.nil?

    @campaigns.each do |campaign|
      next unless AddressSelector.new(campaign[:po_box_triggers]).match?(address)

      rate_name_selector = RateNameSelector.new(
        campaign[:rate_match_type],
        campaign[:rate_names],
      )

      shipping_rates.delete_if do |shipping_rate|
        rate_name_selector.match?(shipping_rate)
      end
    end
  end
end

CAMPAIGNS = [
  HideRatesForPOBoxCampaign.new(HIDE_RATES_FOR_PO_BOX),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

依顧客標籤顯示費率

使用此指令碼來針對已特別標記的顧客,只顯示特定費率。

舉例而言,針對有 VIP 標籤的顧客,只顯示特定的運費費率。

# ================================ Customizable Settings ================================
# ================================================================
# Show Rate(s) for Customer Tag
#
# If we have a matching customer, the entered rate(s) will be
# shown, and all others will be hidden. Otherwise, the entered
# rate(s) will be hidden.
#
# - '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 customer tags to trigger the
# campaign
# - 'rate_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
# - 'rate_names' is a list of strings to identify rates
# ================================================================
SHOW_RATES_FOR_CUSTOMER_TAG = [
  {
    customer_tag_match_type: :include,
    customer_tags: ["customer_tag", "another_tag"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate", "Other Shipping Rate"],
  },
]

# ================================ 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

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
  end
end

# ================================================================
# ShowRateForCustomerTagCampaign
#
# If we have a matching customer, the entered rate(s) will be
# shown, and all others will be hidden. Otherwise, the entered
# rate(s) will be hidden.
# ================================================================
class ShowRateForCustomerTagCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    @campaigns.each do |campaign|
      customer_tag_selector = CustomerTagSelector.new(
        campaign[:customer_tag_match_type],
        campaign[:customer_tags]
      )

      customer_match = cart.customer.nil? ? false : customer_tag_selector.match?(cart.customer)

      rate_name_selector = RateNameSelector.new(
        campaign[:rate_match_type],
        campaign[:rate_names]
      )

      shipping_rates.delete_if do |shipping_rate|
        rate_name_selector.match?(shipping_rate) != customer_match
      end
    end
  end
end

CAMPAIGNS = [
  ShowRateForCustomerTagCampaign.new(SHOW_RATES_FOR_CUSTOMER_TAG),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

重新排序費率

使用此指令碼變更提供給您客戶的預設訂單運費費率。

# ================================ Customizable Settings ================================
# ================================================================
# The order in which you would like your rates to display
# ================================================================
DESIRED_RATE_ORDER = [
  "Shipping Rate 1", "Shipping Rate 2", "Shipping Rate 3",
]

# ================================ Script Code (do not edit) ================================
# ================================================================
# ReorderRatesCampaign
#
# Reorders rates into the entered order
# ================================================================
class ReorderRatesCampaign
  def initialize(desired_order)
    @desired_order = desired_order.map { |item| item.downcase.strip }
  end

  def run(cart, shipping_rates)
    shipping_rates.sort_by! { |rate| @desired_order.index(rate.name.downcase.strip) || Float::INFINITY }
  end
end

CAMPAIGNS = [
  ReorderRatesCampaign.new(DESIRED_RATE_ORDER),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

依折扣代碼決定折扣費率

使用此指令碼,針對在結帳頁面使用指定折扣代碼的顧客,提供特定比例的折扣。

舉例而言,如果顧客已使用「DISCOUNT_15」折扣代碼,便享有特定比例的折扣。

# ================================ Customizable Settings ================================
# ================================================================
# Discount Rate(s) by Discount Code(s)
#
# If one of the entered discount codes is used, the entered
# rate(s) are 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
# - 'rate_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
# - 'rate_names' is a list of strings to identify rates
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
DISCOUNTS_FOR_DISCOUNT_CODES = [
  {
    discount_code_match_type: :exact,
    discount_codes: ["TESTCODE1", "TESTCODE2"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate", "Other Shipping Rate"],
    discount_type: :percent,
    discount_amount: 100,
    discount_message: "Free Shipping with discount code"
  },
]

# ================================ 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

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names.
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
  end
end

# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied shipping rate.
# ================================================================
class DiscountApplicator
  def initialize(discount_type, discount_amount, discount_message)
    @discount_type = discount_type
    @discount_message = discount_message

    @discount_amount = if discount_type == :percent
      discount_amount * 0.01
    else
      Money.new(cents: 100) * discount_amount
    end
  end

  def apply(shipping_rate)
    rate_discount = if @discount_type == :percent
      shipping_rate.price * @discount_amount
    else
      @discount_amount
    end

    shipping_rate.apply_discount(rate_discount, message: @discount_message)
  end
end

# ================================================================
# DiscountRatesForDiscountCodeCampaign
#
# If one of the entered discount codes is used, the entered
# rate(s) are discounted by the entered amount.
# ================================================================
class DiscountRatesForDiscountCodeCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    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)

      rate_name_selector = RateNameSelector.new(campaign[:rate_match_type], campaign[:rate_names])
      discount_applicator = DiscountApplicator.new(
        campaign[:discount_type],
        campaign[:discount_amount],
        campaign[:discount_message],
      )

      shipping_rates.each do |shipping_rate|
        next unless rate_name_selector.match?(shipping_rate)
        discount_applicator.apply(shipping_rate)
      end
    end
  end
end

CAMPAIGNS = [
  DiscountRatesForDiscountCodeCampaign.new(DISCOUNTS_FOR_DISCOUNT_CODES),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

依顧客標籤決定折扣費率

使用此指令碼來針對已特別標記的顧客,提供特定的優惠費率。

舉例而言,針對有 VIP 標籤的顧客,提供 10% 的特定比例折扣。

# ================================ Customizable Settings ================================
# ================================================================
# Discount Rate(s) for Customer Tag(s)
#
# If we have a matching customer, the entered rate(s) are
# discounted by the entered amount.
#
# - '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 customer tags to qualify for
# a discount
# - 'rate_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
# - 'rate_names' is a list of strings to identify rates
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
DISCOUNTS_FOR_CUSTOMER_TAG = [
  {
    customer_tag_match_type: :include,
    customer_tags: ["customer_tag", "another_tag"],
    rate_match_type: :exact,
    rate_names: ["Shipping Rate", "Other Shipping Rate"],
    discount_type: :percent,
    discount_amount: 10,
    discount_message: "10% off shipping for tagged 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

# ================================================================
# RateNameSelector
#
# Finds whether the supplied rate name matches any of the entered
# names
# ================================================================
class RateNameSelector
  def initialize(match_type, rate_names)
    @comparator = match_type == :exact ? '==' : 'include?'
    @rate_names = rate_names.map { |rate_name| rate_name.downcase.strip }
  end

  def match?(shipping_rate)
    @rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
  end
end

# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied shipping rate
# ================================================================
class DiscountApplicator
  def initialize(discount_type, discount_amount, discount_message)
    @discount_type = discount_type
    @discount_message = discount_message

    @discount_amount = if discount_type == :percent
      discount_amount * 0.01
    else
      Money.new(cents: 100) * discount_amount
    end
  end

  def apply(shipping_rate)
    rate_discount = if @discount_type == :percent
      shipping_rate.price * @discount_amount
    else
      @discount_amount
    end

    shipping_rate.apply_discount(rate_discount, message: @discount_message)
  end
end

# ================================================================
# DiscountRatesForCustomerTagCampaign
#
# If we have a matching customer, the entered rate(s) are
# discounted by the entered amount.
# ================================================================
class DiscountRatesForCustomerTagCampaign
  def initialize(campaigns)
    @campaigns = campaigns
  end

  def run(cart, shipping_rates)
    return if cart.customer.nil?

    @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)

      rate_name_selector = RateNameSelector.new(campaign[:rate_match_type], campaign[:rate_names])
      discount_applicator = DiscountApplicator.new(
        campaign[:discount_type],
        campaign[:discount_amount],
        campaign[:discount_message],
      )

      shipping_rates.each do |shipping_rate|
        next unless rate_name_selector.match?(shipping_rate)
        discount_applicator.apply(shipping_rate)
      end
    end
  end
end

CAMPAIGNS = [
  DiscountRatesForCustomerTagCampaign.new(DISCOUNTS_FOR_CUSTOMER_TAG),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.cart, Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

僅顯示貨運業者計算運費

使用此指令碼來僅顯示貨運業者計算費率。若無法使用貨運業者計算費率,則會顯示您手動建立的費率。

# ================================================================
# Carrier Calculated Rate Fallback
#
# As long as carrier calculated rates are available, Shopify rates
# will be hidden.
# ================================================================
class CalculatedRateFallbackCampaign
  def run(shipping_rates)
    has_calculated_rates = shipping_rates.any?{ |shipping_rate| shipping_rate.source.downcase != 'shopify' }

    return unless has_calculated_rates

    shipping_rates.delete_if { |shipping_rate| shipping_rate.source.downcase == 'shopify' }
  end
end

CAMPAIGNS = [
  CalculatedRateFallbackCampaign.new(),
]

CAMPAIGNS.each do |campaign|
  campaign.run(Input.shipping_rates)
end

Output.shipping_rates = Input.shipping_rates

準備好開始透過 Shopify 銷售商品了嗎?

免費試用