Auto-tag products that meet a sales threshold, with Mechanic.

Mechanic is a development and ecommerce automation platform for Shopify. :)

Auto-tag products that meet a sales threshold

Out of the box, this task scans the last week's worth of paid orders, and auto-tags products that meet the sales threshold of your choice, removing the tag from products that no longer qualify. This scan can be run on demand (with the "Run task" button), and will otherwise run nightly.

Runs Occurs when a user manually triggers the task, Occurs every day at midnight (in local time), and Occurs when a bulk operation is completed. Configuration includes tally by line item quantity, tally by line item subtotals, tally by number of orders, minimum tally threshold per product, product tag to apply, and order query.

15-day free trial – unlimited tasks

Documentation

Out of the box, this task scans the last week's worth of paid orders, and auto-tags products that meet the sales threshold of your choice, removing the tag from products that no longer qualify. This scan can be run on demand (with the "Run task" button), and will otherwise run nightly.

You can choose between a threshold defined by line item quantity (for the total number of units sold), by line item subtotals (for the total amount of money each product earned), or by number of orders (for the total number of times a customer placed an order including that product).

Configure the "Order query" option to get specific about which orders you want to consider. When altering the "processed_at" query parameter, you may use the following values:

This task can take some time to run, depending on the number of orders that need to be scanned. :)

For the interested, see the very end of the "Task result" portion of each task run's event, for a logged dump of product IDs mapped to the tallies Mechanic has arrived at for each one. You can use this to make sure you understand why Mechanic is making the tagging decisions it's making.

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.

That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.

(By the way, have you seen our documentation? Have you joined the Slack community?)

Open source
View on GitHub to contribute to this task
Subscriptions
mechanic/user/trigger
mechanic/scheduler/daily
mechanic/shopify/bulk_operation
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
tally by line item quantity (boolean), tally by line item subtotals (boolean), tally by number of orders (boolean), minimum tally threshold per product (required, number), product tag to apply (required), order query (required)
Code
{% comment %}
  Preferred option order:

  {{ options.tally_by_line_item_quantity__boolean }}
  {{ options.tally_by_line_item_subtotals__boolean }}
  {{ options.tally_by_number_of_orders__boolean }}
  {{ options.minimum_tally_threshold_per_product__required_number }}
  {{ options.product_tag_to_apply__required }}
  {{ options.order_query__required }}
{% endcomment %}

{% assign mode_selections = 0 %}
{% if options.tally_by_line_item_quantity__boolean %}
  {% assign mode = "line_item_quantity" %}
  {% assign mode_selections = mode_selections | plus: 1 %}
{% endif %}
{% if options.tally_by_line_item_subtotals__boolean %}
  {% assign mode = "line_item_subtotal" %}
  {% assign mode_selections = mode_selections | plus: 1 %}
{% endif %}
{% if options.tally_by_number_of_orders__boolean %}
  {% assign mode = "order_count" %}
  {% assign mode_selections = mode_selections | plus: 1 %}
{% endif %}

{% if mode_selections == 0 %}
  {"error": "Choose one tally mode."}
{% elsif mode_selections > 1 %}
  {"error": "Choose exactly one tally mode - more than one is not supported."}
{% endif %}

{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
  {% capture bulk_operation_query %}
    query {
      products(
        query: {{ options.product_tag_to_apply__required | json | prepend: "tag:" | json }}
      ) {
        edges {
          node {
            __typename
            id
          }
        }
      }

      orders(
        query: {{ options.order_query__required | json }}
      ) {
        edges {
          node {
            __typename
            id
            createdAt
            lineItems {
              edges {
                node {
                  __typename
                  id
                  quantity
                  originalTotalSet {
                    shopMoney {
                      amount
                    }
                  }
                  product {
                    id
                  }
                }
              }
            }
          }
        }
      }
    }
  {% endcapture %}

  {% action "shopify" %}
    mutation {
      bulkOperationRunQuery(
        query: {{ bulk_operation_query | json }}
      ) {
        bulkOperation {
          id
          status
        }
        userErrors {
          field
          message
        }
      }
    }
  {% endaction %}

{% elsif event.topic == "mechanic/shopify/bulk_operation" %}
  {% assign products_already_tagged = bulkOperation.objects | where: "__typename", "Product" | map: "id" %}
  {% assign line_items = bulkOperation.objects | where: "__typename", "LineItem" %}

  {% assign product_tallies = hash %}
  {% assign products_deserving_tag = array %}

  {% case mode %}
  {% when "line_item_quantity" %}
    {% for line_item in line_items %}
      {% if line_item.product %}
        {% assign product_tallies[line_item.product.id] = product_tallies[line_item.product.id] | default: 0 | plus: line_item.quantity %}
      {% endif %}
    {% endfor %}
  {% when "line_item_subtotal" %}
    {% for line_item in line_items %}
      {% if line_item.product %}
        {% assign product_tallies[line_item.product.id] = product_tallies[line_item.product.id] | default: 0 | plus: line_item.originalTotalSet.shopMoney.amount %}
      {% endif %}
    {% endfor %}
  {% when "order_count" %}
    {% assign product_ids = line_items | map: "product" | map: "id" | compact %}
    {% for product_id in product_ids %}
      {% assign product_tallies[product_id] = product_tallies[product_id] | default: 0 | plus: 1 %}
    {% endfor %}
  {% endcase %}

  {% if event.preview %}
    {% assign product_tallies["gid://shopify/Product/1234567890"] = options.minimum_tally_threshold_per_product__required_number %}
  {% endif %}

  {% for keyval in product_tallies %}
    {% assign product_id = keyval[0] %}
    {% assign tally = keyval[1] %}

    {% if tally >= options.minimum_tally_threshold_per_product__required_number %}
      {% assign products_deserving_tag[products_deserving_tag.size] = product_id %}

      {% unless products_already_tagged contains product_id %}
        {% action "shopify" %}
          mutation {
            tagsAdd(
              id: {{ product_id | json }}
              tags: {{ options.product_tag_to_apply__required | json }}
            ) {
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endunless %}
    {% endif %}
  {% endfor %}

  {% for product_id in products_already_tagged %}
    {% unless products_deserving_tag contains product_id %}
      {% action "shopify" %}
        mutation {
          tagsRemove(
            id: {{ product_id | json }}
            tags: {{ options.product_tag_to_apply__required | json }}
          ) {
            userErrors {
              field
              message
            }
          }
        }
      {% endaction %}
    {% endunless %}
  {% endfor %}

  {"log": {{ product_tallies | json }}}
{% endif %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more
Defaults
Tally by line item quantity
true
Order query
financial_status:paid processed_at:past_week