Manage collection templates by product inventory, with Mechanic.

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

Manage collection templates by product inventory

Use this task to automatically switch collections over to an appropriate theme template, when every product in the collection has gone completely out of stock, and to switch them back when inventory arrives.

Runs Occurs whenever an inventory level is updated, Occurs when a user manually triggers the task, and Occurs when a bulk operation is completed. Configuration includes collection template suffix when all products are out of stock, default collection template suffix, run every time an inventory level is updated, run daily, and run hourly.

15-day free trial – unlimited tasks

Documentation

Use this task to automatically switch collections over to an appropriate theme template, when every product in the collection has gone completely out of stock, and to switch them back when inventory arrives.

This task can be run manually (using the "Run task" button), or be configured to run daily/hourly. If you encounter performance issues when running every time an inventory level is updated, disable this option.

Note: For this task, products which do not track inventory in Shopify will be considered as in stock.

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
{% if options.run_every_time_an_inventory_level_is_updated__boolean %}shopify/inventory_levels/update{% endif %}
mechanic/user/trigger
{% if options.run_daily__boolean %}mechanic/scheduler/daily{% endif %}
{% if options.run_hourly__boolean %}mechanic/scheduler/hourly{% endif %}
mechanic/shopify/bulk_operation
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
collection template suffix when all products are out of stock (required), default collection template suffix, run every time an inventory level is updated (boolean), run daily (boolean), run hourly (boolean)
Code
{% assign out_of_stock_collection_template_suffix = options.collection_template_suffix_when_all_products_are_out_of_stock__required %}
{% assign default_collection_template_suffix = options.default_collection_template_suffix %}

{% if event.topic contains "shopify/inventory_levels/" %}
  {% comment %}
    -- get this product's total inventory, and up to 250 collections
  {% endcomment %}

  {% capture query %}
    query {
      inventoryItem(id: {{ inventory_level.inventory_item_id | prepend: "gid://shopify/InventoryItem/" | json }}) {
        variant {
          product {
            id
            totalInventory
            tracksInventory
            collections(first: 250) {
              nodes {
                id
                templateSuffix
              }
            }
          }
        }
      }
    }
  {% endcapture %}

  {% assign result = query | shopify %}

  {% if event.preview %}
    {% capture result_json %}
      {
        "data": {
          "inventoryItem": {
            "variant": {
              "product": {
                "id": "gid://shopify/Product/1234567890",
                "totalInventory": 0,
                "tracksInventory": true,
                "collections": {
                  "nodes": [
                    {
                      "id": "gid://shopify/Collection/1234567890",
                      "templateSuffix": {{ default_collection_template_suffix | json }}
                    }
                  ]
                }
              }
            }
          }
        }
      }
    {% endcapture %}

    {% assign result = result_json | parse_json %}
  {% endif %}

  {% assign product = result.data.inventoryItem.variant.product %}
  {% assign collections = product.collections.nodes %}

  {% for collection in collections %}
    {% assign has_default_template = nil %}
    {% assign has_out_of_stock_template = nil %}
    {% assign has_a_product_in_stock = nil %}

    {% assign current_template_suffix = collection.templateSuffix %}

    {% comment %}
      -- Shopify stores this as *either* an empty string or as nil. Mechanic auto-nils empty strings for task options, so we coerce blanks to nils here.
    {% endcomment %}

    {% if current_template_suffix == blank %}
      {% assign current_template_suffix = nil %}
    {% endif %}

    {% if current_template_suffix == default_collection_template_suffix %}
      {% assign has_default_template = true %}

    {% elsif current_template_suffix == out_of_stock_collection_template_suffix %}
      {% assign has_out_of_stock_template = true %}
    {% endif %}

    {% comment %}
      -- if the product's stock status matches the paired collection template, then stop processing that collection
      -- NOTE: another product might cause the collection to change templates, but that should  be handled in an inventory level event for that product
    {% endcomment %}

    {% if product.totalInventory <= 0 and product.tracksInventory %}
      {% if has_out_of_stock_template %}
        {% continue %}
      {% endif %}

    {% elsif has_default_template %}
      {% continue %}
    {% endif %}

    {% comment %}
      -- query collection for up to 25K products, getting inventory of each
      -- break the query loop early when at least one product is in stock and the collection has the out of stock template
    {% endcomment %}

    {% assign cursor = nil %}

    {% for n in (1..100) %}
      {% capture query %}
        query {
          collection(id: {{ collection.id | json }}) {
            products(
              first: 250
              after: {{ cursor | json }}
            ) {
              pageInfo {
                hasNextPage
                endCursor
              }
              nodes {
                id
                totalInventory
                tracksInventory
              }
            }
          }
        }
      {% endcapture %}

      {% assign result = query | shopify %}

      {% if event.preview %}
        {% capture result_json %}
          {
            "data": {
              "collection": {
                "products": {
                  "nodes": [
                    {
                      "id": "gid://shopify/Product/1234567890",
                      "totalInventory": 0,
                      "tracksInventory": true
                    }
                  ]
                }
              }
            }
          }
        {% endcapture %}

        {% assign result = result_json | parse_json %}
      {% endif %}

      {% for product in result.data.collection.products.nodes %}
        {% if product.totalInventory > 0 or product.tracksInventory == false %}
          {% assign has_a_product_in_stock = true %}
          {% break %}
        {% endif %}
      {% endfor %}

      {% if has_a_product_in_stock %}
        {% break %}
      {% endif %}

      {% if result.data.collection.products.pageInfo.hasNextPage %}
        {% assign cursor = result.data.collection.products.pageInfo.endCursor %}
      {% else %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% if has_a_product_in_stock %}
      {% unless has_default_template %}
        {% action "shopify" %}
          mutation {
            collectionUpdate(
              input: {
                id: {{ collection.id | json }}
                templateSuffix: {{ default_collection_template_suffix | json }}
              }
            ) {
              collection {
                id
                title
                templateSuffix
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endunless %}

    {% else %}
      {% unless has_out_of_stock_template %}
        {% action "shopify" %}
          mutation {
            collectionUpdate(
              input: {
                id: {{ collection.id | json }}
                templateSuffix: {{ out_of_stock_collection_template_suffix | json }}
              }
            ) {
              collection {
                id
                title
                templateSuffix
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endunless %}
    {% endif %}
  {% endfor %}

{% elsif event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
  {% capture bulk_operation_query %}
    query {
      collections {
        edges {
          node {
            __typename
            id
            templateSuffix
            products {
              edges {
                node {
                  __typename
                  id
                  totalInventory
                  tracksInventory
                }
              }
            }
          }
        }
      }
    }
  {% 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 jsonl_string %}
      {"__typename":"Collection","id":"gid://shopify/Collection/1234567890","templateSuffix":{{ default_collection_template_suffix | json }}}
      {"__typename":"Product","id":"gid://shopify/Product/1234567890","totalInventory":0,"__parentId":"gid://shopify/Collection/1234567890"}
      {"__typename":"Product","id":"gid://shopify/Product/2345678901","totalInventory":0,"__parentId":"gid://shopify/Collection/1234567890"}
    {% endcapture %}

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

  {% assign collections = bulkOperation.objects | where: "__typename", "Collection" %}
  {% assign products = bulkOperation.objects | where: "__typename", "Product" %}

  {% for collection in collections %}
    {% assign has_default_template = nil %}
    {% assign has_out_of_stock_template = nil %}
    {% assign has_a_product_in_stock = nil %}

    {% assign current_template_suffix = collection.templateSuffix %}

    {% comment %}
      -- Shopify stores this as *either* an empty string or as nil. Mechanic auto-nils empty strings for task options, so we coerce blanks to nils here.
    {% endcomment %}

    {% if current_template_suffix == blank %}
      {% assign current_template_suffix = nil %}
    {% endif %}

    {% if current_template_suffix == default_collection_template_suffix %}
      {% assign has_default_template = true %}

    {% elsif current_template_suffix == out_of_stock_collection_template_suffix %}
      {% assign has_out_of_stock_template = true %}
    {% endif %}

    {% assign collection_products = products | where: "__parentId", collection.id %}

    {% comment %}
      -- break the products loop early when at least one product is in stock
    {% endcomment %}

    {% for product in collection_products %}
      {% if product.totalInventory > 0 or product.tracksInventory == false %}
        {% assign has_a_product_in_stock = true %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% if has_a_product_in_stock %}
      {% unless has_default_template %}
        {% action "shopify" %}
          mutation {
            collectionUpdate(
              input: {
                id: {{ collection.id | json }}
                templateSuffix: {{ default_collection_template_suffix | json }}
              }
            ) {
              collection {
                id
                title
                templateSuffix
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endunless %}

    {% else %}
      {% unless has_out_of_stock_template %}
        {% action "shopify" %}
          mutation {
            collectionUpdate(
              input: {
                id: {{ collection.id | json }}
                templateSuffix: {{ out_of_stock_collection_template_suffix | json }}
              }
            ) {
              collection {
                id
                title
                templateSuffix
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endunless %}
    {% endif %}
  {% endfor %}
{% endif %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more
Defaults
Collection template suffix when all products are out of stock
out-of-stock
Run every time an inventory level is updated
true