Tag customers by order tier, with Mechanic.

Mechanic is a development platform for Shopify. :)

Tag customers by order tier

by Isaac Bowen (team@usemechanic.com)

Use this task to tag customers by tier, based on how many orders they've placed or by the sum of all their order totals. Optionally, configure an order query, allowing you to apply purchase tiers based on the last month of orders, or based only on fulfilled orders, etc.

Runs when a user triggers the task and when a bulk operation finishes. Configuration includes order minimums and customer tags, only keep the customer tag for the highest order minimum, tag customers by number of orders, tag customers by sum of order totals, only tag customers with an active account, only count orders matching this query, run hourly, and run daily.

15-day free trial – unlimited tasks

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Gurus, 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.

Events
when a user triggers the task (mechanic/user/trigger)
when a bulk operation finishes (mechanic/shopify/bulk_operation)
Options
order minimums and customer tags (keyval, required), only keep the customer tag for the highest order minimum (boolean), tag customers by number of orders (boolean), tag customers by sum of order totals (boolean), only tag customers with an active account (boolean), only count orders matching this query, run hourly (boolean), run daily (boolean)
Script
{% comment %}
  Preferred option order:

  {{ options.order_minimums_and_customer_tags__keyval_required }}
  {{ options.only_keep_the_customer_tag_for_the_highest_order_minimum__boolean }}
  {{ options.tag_customers_by_number_of_orders__boolean }}
  {{ options.tag_customers_by_sum_of_order_totals__boolean }}
  {{ options.only_tag_customers_with_an_active_account__boolean }}
  {{ options.only_count_orders_matching_this_query }}
  {{ options.run_hourly__boolean }}
  {{ options.run_daily__boolean }}
{% endcomment %}

{% if options.tag_customers_by_number_of_orders__boolean and options.tag_customers_by_sum_of_order_totals__boolean %}
  {% error "Choose only one of 'Tag customers by number of orders' and 'Tag customers by sum of order totals'. :)" %}
{% elsif options.tag_customers_by_number_of_orders__boolean == false and options.tag_customers_by_sum_of_order_totals__boolean == false %}
  {% error "Choose one of 'Tag customers by number of orders' and 'Tag customers by sum of order totals'. :)" %}
{% endif %}

{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
  {% assign customers_query = nil %}

  {% if options.only_tag_customers_with_an_active_account__boolean %}
    {% assign customers_query = "state:enabled" %}
  {% endif %}

  {% capture bulk_operation_query %}
    query {
      customers(query: {{ customers_query | json }}) {
        edges {
          node {
            __typename
            id
            email
            tags
            state
            orders(query: {{ options.only_count_orders_matching_this_query | json }}) {
              edges {
                node {
                  __typename
                  id
                  name
                  totalPriceSet {
                    shopMoney {
                      amount
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  {% endcapture %}

  {% action "shopify" %}
    mutation {
      bulkOperationRunQuery(
        query: {{ bulk_operation_query | json }}
      ) {
        bulkOperation {
          id
          status
        }
        userErrors {
          field
          message
        }
      }
    }
  {% endaction %}
{% elsif event.topic == "mechanic/shopify/bulk_operation" %}
  {% if event.preview %}
    {% capture bulkOperation_objects_jsonl %}
      {"__typename":"Customer","id":"gid:\/\/shopify\/Customer\/1234567890","tags":[],"state":"ENABLED"}
      {% if options.tag_customers_by_number_of_orders__boolean -%}
        {% assign count = options.order_minimums_and_customer_tags__keyval_required.first.first | times: 1 -%}
        {% for n in (0..count) -%}
          {"__typename":"Order","id":"gid:\/\/shopify\/Order\/1234567890","totalPriceSet":{"shopMoney":{"amount":"10.0"}},"__parentId":"gid:\/\/shopify\/Customer\/1234567890"}
        {% endfor -%}
      {% elsif options.tag_customers_by_sum_of_order_totals__boolean -%}
        {"__typename":"Order","id":"gid:\/\/shopify\/Order\/1234567890","totalPriceSet":{"shopMoney":{"amount":{{ options.order_minimums_and_customer_tags__keyval_required.first.first | json}}}},"__parentId":"gid:\/\/shopify\/Customer\/1234567890"}
      {% endif %}
    {% endcapture %}

    {% assign bulkOperation = hash %}
    {% assign bulkOperation["objects"] = bulkOperation_objects_jsonl | parse_jsonl %}
  {% endif %}

  {% assign customers = bulkOperation.objects | where: "__typename", "Customer" %}
  {% assign orders = bulkOperation.objects | where: "__typename", "Order" %}
  
  {% for customer in customers %}
    {% assign customer_orders = orders | where: "__parentId", customer.id %}
    {% assign customer_orders_value = nil %}

    {% if options.tag_customers_by_number_of_orders__boolean %}
      {% assign customer_orders_value = customer_orders.size %}
    {% elsif options.tag_customers_by_sum_of_order_totals__boolean %}
      {% assign customer_orders_value = 0 %}
      {% for order in customer_orders %}
        {% assign customer_orders_value = customer_orders_value | plus: order.totalPriceSet.shopMoney.amount %}
      {% endfor %}
    {% endif %}

    {% if customer_orders != empty %}
      {% assign customer_orders_names = customer_orders | map: "name" %}
      {% log customer_id: customer.id, customer_email: customer.email, customer_tags: customer.tags, customer_orders_names: customer_orders_names, customer_orders_value: customer_orders_value %}
    {% endif %}

    {% assign best_fitting_tag_minimum = 0 %}
    {% assign best_fitting_tag = nil %}
    {% assign all_fitting_tags = array %}
    {% assign all_possible_tags = array %}

    {% for pair in options.order_minimums_and_customer_tags__keyval_required %}
      {% assign tag_minimum = pair[0] | times: 1 %}
      {% assign tag = pair[1] %}

      {% assign all_possible_tags[all_possible_tags.size] = tag %}

      {% if customer_orders_value >= tag_minimum %}
        {% assign all_fitting_tags[all_fitting_tags.size] = tag %}

        {% if tag_minimum >= best_fitting_tag_minimum %}
          {% assign best_fitting_tag = tag %}
          {% assign best_fitting_tag_minimum = tag_minimum %}
        {% endif %}
      {% endif %}
    {% endfor %}

    {% assign tags_to_add = array %}
    {% assign tags_to_remove = array %}

    {% if best_fitting_tag %}
      {% unless customer.tags contains best_fitting_tag %}
        {% assign tags_to_add[tags_to_add.size] = best_fitting_tag %}
      {% endunless %}
    {% endif %}

    {% if options.only_keep_the_customer_tag_for_the_highest_order_minimum__boolean %}
      {% for tag in all_possible_tags %}
        {% if tag != best_fitting_tag and customer.tags contains tag %}
          {% assign tags_to_remove[tags_to_remove.size] = tag %}
        {% endif %}
      {% endfor %}
    {% else %}
      {% for tag in all_fitting_tags %}
        {% unless tag == best_fitting_tag or customer.tags contains tag %}
          {% assign tags_to_add[tags_to_add.size] = tag %}
        {% endunless %}
      {% endfor %}
    {% endif %}

    {% if tags_to_add != empty or tags_to_remove != empty %}
      {% action "shopify" %}
        mutation {
          {% if tags_to_add != empty %}
            tagsAdd(
              id: {{ customer.id | json }}
              tags: {{ tags_to_add | json }}
            ) {
              node {
                ... on Customer {
                  id
                  email
                  tags
                }
              }
              userErrors {
                field
                message
              }
            }
          {% endif %}

          {% if tags_to_remove != empty %}
            tagsRemove(
              id: {{ customer.id | json }}
              tags: {{ tags_to_remove | json }}
            ) {
              node {
                ... on Customer {
                  id
                  email
                  tags
                }
              }
              userErrors {
                field
                message
              }
            }
          {% endif %}
        }
      {% endaction %}
    {% endif %}
  {% endfor %}
{% endif %}
Mechanic tasks are written in Liquid, which makes them easy to write and easy to modify. Learn more about our platform.
Defaults
Order minimums and customer tags
{"10"=>"10-orders", "100"=>"100-orders"}
Only keep the customer tag for the highest order minimum
true
Tag customers by number of orders
true