Receive a nightly out-of-stock report, with Mechanic.

Mechanic is an automation development platform for Shopify. :)

Receive a nightly out-of-stock report

by Isaac Bowen (team@usemechanic.com)

Based on an out-of-stock threshold you define, this task sends you an email summary (with CSV attachment) of all variants that are out of stock in any location.

Runs when a user triggers the task and every day at midnight. Configuration includes out of stock quantity, report recipient email, and report subject.

15-day free trial – unlimited tasks

Documentation

Based on an out-of-stock threshold you define, this task sends you an email summary (with CSV attachment) of all variants that are out of stock in any location. Run this task manually to receive the report on demand.

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)
every day at midnight (mechanic/scheduler/daily)
Options
out of stock quantity (number, required), report recipient email (email, required), report subject (required)
Script
{% assign out_of_stock_list_items = array %}

{% assign report_csv = array %}
{% assign report_csv[0] = array %}
{% assign report_csv[0][0] = "Display name" %}
{% assign report_csv[0][1] = "Product ID" %}
{% assign report_csv[0][2] = "Variant ID" %}
{% assign report_csv[0][3] = "Location ID" %}

{% if event.preview %}
  {% comment %}
    Signal that we need access to products
  {% endcomment %}
  {% capture query %}
    query {
      productVariants(
        first: 250
      ) {
        edges {
          cursor
          node {
            id
          }
        }
      }
    }
  {% endcapture %}
  {% assign result = query | shopify %}

  {% comment %}
    Signal that we need access to inventory
  {% endcomment %}
  {% capture query %}
    query {
      inventoryItems(first: 5) {
        edges {
          node {
            id
          }
        }
      }
    }
  {% endcapture %}
  {% assign result = query | shopify %}

  {% assign out_of_stock_list_items[0] = "<li><a>Short Sleeve T-Shirt - M, Red</a> (0 in Warehouse)</a></li>" %}
  {% assign out_of_stock_list_items[1] = "<li><a>Short Sleeve T-Shirt - S, Blue</a> (-1 in Warehouse)</a></li>" %}
  {% assign out_of_stock_list_items[2] = "<li><a>Short Sleeve T-Shirt - XL, Green</a> (-5 in Storefront)</a></li>" %}
{% else %}
  {% assign cursor = nil %}

  {% for n in (0..100) %}
    {% capture query %}
      query {
        productVariants(
          first: 9
          after: {{ cursor | json }}
          sortKey: FULL_TITLE
        ) {
          pageInfo {
            hasNextPage
          }
          edges {
            cursor
            node {
              displayName
              id
              legacyResourceId
              product {
                legacyResourceId
              }
              inventoryItem {
                inventoryLevels(first: 50) {
                  edges {
                    node {
                      location {
                        name
                        legacyResourceId
                      }
                      available
                    }
                  }
                }
              }
            }
          }
        }
      }
    {% endcapture %}

    {% assign result = query | shopify %}

    {% for variant_edge in result.data.productVariants.edges %}
      {% for inventoryLevel_edge in variant_edge.node.inventoryItem.inventoryLevels.edges %}
        {% if inventoryLevel_edge.node.available <= options.out_of_stock_quantity__number_required %}
          {% capture list_item %}
            <li>
              <a href="https://{{ shop.domain }}/admin/products/{{ variant_edge.node.product.legacyResourceId }}/variants/{{ variant_edge.node.legacyResourceId }}">{{ variant_edge.node.displayName }}</a>
              ({{ inventoryLevel_edge.node.available }} in {{ inventoryLevel_edge.node.location.name }})
            </li>
          {% endcapture %}
          {% assign out_of_stock_list_items[out_of_stock_list_items.size] = list_item %}

          {% assign report_csv_row = array %}
          {% assign report_csv_row[0] = variant_edge.node.displayName %}
          {% assign report_csv_row[1] = variant_edge.node.product.legacyResourceId %}
          {% assign report_csv_row[2] = variant_edge.node.legacyResourceId %}
          {% assign report_csv_row[3] = inventoryLevel_edge.node.location.legacyResourceId %}
          {% assign report_csv[report_csv.size] = report_csv_row %}
        {% endif %}
      {% endfor %}
    {% endfor %}

    {% if result.data.productVariants.pageInfo.hasNextPage %}
      {% assign cursor = result.data.productVariants.edges.last.cursor %}
    {% else %}
      {% break %}
    {% endif %}
  {% endfor %}
{% endif %}

{% if out_of_stock_list_items != empty %}
  {% action "email" %}
    {
      "to": {{ options.report_recipient_email__email_required | json }},
      "subject": {{ options.report_subject__required | json }},
      "body": {{ out_of_stock_list_items | join: newline | prepend: "<p>Hello,</p><p>The following variants are out of stock, in the named locations:</p><ul>" | append: "</ul><p>Thanks,<br>Mechanic, for " | append: shop.name | append: "</p>" | json }},
      "attachments": {
        {{ "now" | date: "%Y%m%d" | prepend: "out-of-stock-" | append: ".csv" | json }}: {{ report_csv | csv | json }}
      }
    }
  {% endaction %}
{% endif %}
Mechanic tasks are written in Liquid, which makes them easy to write and easy to modify. Learn more about our platform.
Defaults
Out of stock quantity
0
Report subject
Out of stock report ({{ "now" | date: "%Y-%m-%d" }})