Keep variant inventory in sync across locations, with Mechanic.

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

Keep variant inventory in sync across locations

When an inventory level is updated, this task will adjust the "available" inventory for all other locations of the same variant to be the same quantity. Optionally, configure this task to filter for specific product types. Leaving this option blank means the task will monitor and update inventory for all products in the shop.

Runs Occurs whenever an inventory level is updated. Configuration includes filter by these product types.

15-day free trial – unlimited tasks

Documentation

When an inventory level is updated, this task will adjust the "available" inventory for all other locations of the same variant to be the same quantity. Optionally, configure this task to filter for specific product types. Leaving this option blank means the task will monitor and update inventory for all products in the shop.

CAUTION: This task might not make accurate adjustments in certain high-volume scenarios. For example, an inventory import that sets mismatched inventory levels for a variant monitored by this task will cause a cascading effect of reapplying the deltas between each incorrect inventory level.

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
shopify/inventory_levels/update
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
filter by these product types (array)
Code
{% assign filter_by_these_product_types = options.filter_by_these_product_types__array %}

{% comment %}
  -- query for "available" quantites at all locations where this inventory item / variant is stocked
{% endcomment %}

{% capture query %}
  query {
    inventoryLevel(id: {{ inventory_level.admin_graphql_api_id | json }}) {
      item {
        id
        variant {
          legacyResourceId
          product {
            productType
          }
        }
        inventoryLevels(first: 100) {
          nodes {
            id
            location {
              id
            }
            quantities(names: "available") {
              quantity
            }
          }
        }
      }
    }
  }
{% endcapture %}

{% assign result = query | shopify %}

{% if event.preview %}
  {% capture result_json %}
    {
      "data": {
        "inventoryLevel": {
          "item": {
            "id": "gid://shopify/InventoryItem/1234567890",
            "variant": {
              "legacyResourceId": "1234567890",
              "product": {
                "productType": {{ filter_by_these_product_types.first | json }}
              }
            },
            "inventoryLevels": {
              "nodes": [
                {
                  "id": "gid://shopify/InventoryLevel/1234567890?inventory_item_id=1234567890",
                  "location": {
                    "id": "gid://shopify/Location/1234567890"
                  },
                  "quantities": [
                    {
                      "quantity": 1
                    }
                  ]
                },
                {
                  "id": "gid://shopify/InventoryLevel/2345678901?inventory_item_id=1234567890",
                  "location": {
                    "id": "gid://shopify/Location/2345678901"
                  },
                  "quantities": [
                    {
                      "quantity": 0
                    }
                  ]
                }
              ]
            }
          }
        }
      }
    }
  {% endcapture %}

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

{% assign inventory_item = result.data.inventoryLevel.item %}
{% assign variant = inventory_item.variant %}
{% assign product = inventory_item.variant.product %}
{% assign inventory_levels = inventory_item.inventoryLevels.nodes %}

{% comment %}
  -- if product types are configured in the task, make sure this inventory level update belongs to a product with one of the configured types
{% endcomment %}

{% if filter_by_these_product_types != blank %}
  {% unless filter_by_these_product_types contains product.productType %}
    {% log
      message: "Product type is not in the configured product types; skipping.",
      variant: variant,
      filter_by_these_product_types: filter_by_these_product_types
    %}
    {% break %}
  {% endunless %}
{% endif %}

{% if inventory_levels.size < 2 %}
  {% log
    message: "This variant is only stocked at a single location; no inventory sync needed.",
    variant: variant
  %}
  {% break %}
{% endif %}

{% comment %}
  -- get the "available" quantity for the inventory level that caused the update event
{% endcomment %}

{% assign source_inventory_level
  = inventory_levels
  | where: "id", inventory_level.admin_graphql_api_id
  | first
%}
{% assign source_quantity = source_inventory_level.quantities.first.quantity %}

{% comment %}
  -- since this task updates ALL inventory levels of a variant in bulk, then a source quantity that doesn't equal the cached value for this variant indicates the change was likely not made via a prior run of this task
{% endcomment %}

{% assign cache_key = "variant-inventory-level-" | append: inventory_item.variant.legacyResourceId %}
{% assign cached_value = cache[cache_key] %}

{% if cached_value != blank %}
  {% if cached_value == source_quantity %}
    {% log
      message: "The 'available' inventory level of this variant at this location (i.e. source quantity) matches the cached value for this variant; no updates to make.",
      cached_value: cached_value,
      source_quantity: source_quantity
    %}
    {% break %}
  {% endif %}

  {% log
    message: "The 'available' inventory level of this variant at this location (i.e. source quantity) does not match the cached value for this variant; update the cache and adjust the other levels as needed.",
    cached_value: cached_value,
    source_quantity: source_quantity
  %}
{% endif %}

{% comment %}
  -- no cached value exists OR this new value does not match it, so set it and then adjust "available" quantities across locations, using the source quantity to determine deltas
{% endcomment %}

{% action "cache", "setex", cache_key, 60, source_quantity %}

{% assign adjustments = array %}

{% for inventory_level in inventory_levels %}
  {% assign delta = source_quantity | minus: inventory_level.quantities.first.quantity %}

  {% if delta != 0 %}
    {% assign adjustment = hash %}
    {% assign adjustment["inventoryItemId"] = inventory_item.id %}
    {% assign adjustment["locationId"] = inventory_level.location.id %}
    {% assign adjustment["delta"] = delta %}
    {% assign adjustments = adjustments | push: adjustment %}
  {% endif %}
{% endfor %}

{% if adjustments != blank %}
  {% action "shopify" %}
    mutation {
      inventoryAdjustQuantities(
        input: {
          name: "available"
          reason: "correction"
          changes: {{ adjustments | graphql_arguments }}
        }
      ) {
        userErrors {
          code
          field
          message
        }
      }
    }
  {% endaction %}
{% endif %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more