Auto-fulfill items that don't require shipping, with Mechanic.

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

Auto-fulfill items that don't require shipping

Useful for digital products, shipping insurance, gift cards, or anything else that needs to be fulfilled instantly. This task watches for new orders, and auto-fulfills all line items that don't require shipping.

Runs Occurs whenever an order is created, Occurs whenever an order is updated, with a 30 second delay, and Occurs when a user manually triggers the task. Configuration includes include products with any of these tags, exclude products with any of these tags, only process paid orders, wait until any other shippable items are fulfilled, and fulfill gift cards.

15-day free trial – unlimited tasks

Documentation

Useful for digital products, shipping insurance, gift cards, or anything else that needs to be fulfilled instantly. This task watches for new orders, and auto-fulfills all line items that don't require shipping.

Optionally, choose whether to:
- Only process paid orders
- Wait until other shippable items are fulfilled
- Fulfill gift cards
- Include or exclude products by tag

This task may also be run manually to process existing open, unfulfilled orders.

Important:
- Exclusion tags on a product will take priority over inclusion tags
- Shopify has an optional admin setting to auto-fulfill gift cards when orders are paid. Depending on how this task is configured, that setting may need to be turned off to avoid fulfillment conflicts.

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/orders/create
shopify/orders/updated+30.seconds
mechanic/user/trigger
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
include products with any of these tags (array), exclude products with any of these tags (array), only process paid orders (boolean), wait until any other shippable items are fulfilled (boolean), fulfill gift cards (boolean)
Code
{% assign inclusion_tags = options.include_products_with_any_of_these_tags__array %}
{% assign exclusion_tags = options.exclude_products_with_any_of_these_tags__array %}
{% assign only_process_paid_orders = options.only_process_paid_orders__boolean %}
{% assign wait_until_any_other_shippable_items_are_fulfilled = options.wait_until_any_other_shippable_items_are_fulfilled__boolean %}
{% assign fulfill_gift_cards = options.fulfill_gift_cards__boolean %}

{% if event.topic contains "shopify/orders/" %}
  {% comment %}
    -- get all open or in progress fulfillment orders for this order
  {% endcomment %}

  {% capture query %}
    query {
      order(id: {{ order.admin_graphql_api_id | json }}) {
        id
        name
        displayFinancialStatus
        displayFulfillmentStatus
        fulfillmentOrders(
          first: 10
          query: "status:open OR status:in_progress"
        ) {
          nodes {
            id
            assignedLocation {
              location {
                id
              }
            }
            lineItems(first: 50) {
              nodes {
                id
                remainingQuantity
                requiresShipping
                variant {
                  id
                  displayName
                  product {
                    id
                    isGiftCard
                    tags
                  }
                }
              }
            }
          }
        }
      }
    }
  {% endcapture %}

  {% assign result = query | shopify %}
  {% assign orders = array | push: result.data.order %}

{% elsif event.topic == "mechanic/user/trigger" %}
  {% comment %}
    -- get all open, unfulfilled orders (this includes partially fulfilled)
    -- if option enabled, restrict query to paid orders
  {% endcomment %}

  {% assign search_query = "status:open fulfillment_status:unfulfilled" %}

  {% if only_process_paid_orders %}
    {% assign search_query = search_query | append: " financial_status:paid " %}
  {% endif %}

  {% assign cursor = nil %}
  {% assign orders = array %}

  {% for n in (1..100) %}
    {% capture query %}
      query {
        orders(
          first: 25
          after: {{ cursor | json }}
          query: {{ search_query | json }}
        ) {
          pageInfo {
            hasNextPage
            endCursor
          }
          nodes {
            id
            name
            displayFinancialStatus
            displayFulfillmentStatus
            fulfillmentOrders(
              first: 10
              query: "status:open OR status:in_progress"
            ) {
              nodes {
                id
                assignedLocation {
                  location {
                    id
                  }
                }
                lineItems(first: 50) {
                  nodes {
                    id
                    remainingQuantity
                    requiresShipping
                    variant {
                      id
                      displayName
                      product {
                        id
                        isGiftCard
                        tags
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    {% endcapture %}

    {% assign result = query | shopify %}

    {% assign orders
      = result.data.orders.nodes
      | default: array
      | concat: orders
    %}

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

{% if event.preview %}
  {% capture orders_json %}
    [
      {
        "id": "gid://shopify/Order/1234567890",
        "name": "PREVIEW",
        "displayFinancialStatus": "PAID",
        "displayFulfillmentStatus": "PARTIALLY_FULFILLED",
        "fulfillmentOrders": {
          "nodes": [
            {
              "id": "gid://shopify/FulfillmentOrder/1234567890",
              "assignedLocation": {
                "location": {
                  "id": "gid://shopify/Location/1234567890"
                }
              },
              "lineItems": {
                "nodes": [
                  {
                    "id": "gid://shopify/FulfillmentOrderLineItem/1234567890",
                    "remainingQuantity": 1,
                    "requiresShipping": false,
                    "variant": {
                      "product": {
                        "tags": {{ inclusion_tags.first | json }}
                      }
                    }
                  },
                  {
                    "id": "gid://shopify/FulfillmentOrderLineItem/2345678901",
                    "remainingQuantity": 1,
                    "requiresShipping": false,
                    "variant": {
                      "product": {
                        "isGiftCard": true
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    ]
  {% endcapture %}

  {% assign orders = orders_json | parse_json %}
{% endif %}

{% for order in orders %}
  {% assign fulfillment_orders = order.fulfillmentOrders.nodes %}

  {% if fulfillment_orders == blank %}
    {% log
      message: "There are no open fulfillment orders to fulfill on this order.",
      order: order
    %}
    {% continue %}
  {% endif %}

  {% if only_process_paid_orders and order.displayFinancialStatus != "PAID" %}
    {% log
      message: "This order does not have a status of PAID, and the 'Only process paid orders' option is checked.",
      order: order
    %}
    {% continue %}
  {% endif %}

  {% comment %}
    -- fulfillments can only be created for one location at a time, so need to group fulfillment orders by location
    -- Shopify will also throw an error if gift cards are fulfilled with any other item types
  {% endcomment %}

  {% assign fulfillment_orders_by_location = hash %}
  {% assign has_unfulfilled_shippable_items = nil %}

  {% for fulfillment_order in fulfillment_orders %}
    {% assign fulfillment_order_data = hash %}
    {% assign fulfillment_order_data["fulfillment_order_id"] = fulfillment_order.id %}

    {% for fulfillment_order_line_item in fulfillment_order.lineItems.nodes %}
      {% comment %}
        -- skip items that require shipping, but set flag if any are unfulfilled in case the option to wait on them is enabled
      {% endcomment %}

      {% if fulfillment_order_line_item.requiresShipping %}
        {% if fulfillment_order_line_item.remainingQuantity > 0 %}
          {% assign has_unfulfilled_shippable_items = true %}
        {% endif %}

        {% continue %}
      {% endif %}

      {% if inclusion_tags != blank or exclusion_tags != blank %}
        {% assign has_exclusion_tag = nil %}
        {% assign has_inclusion_tag = nil %}

        {% assign product_tags = fulfillment_order_line_item.variant.product.tags %}

        {% for exclusion_tag in exclusion_tags %}
          {% if product_tags contains exclusion_tag %}
            {% assign has_exclusion_tag = true %}
            {% break %}
          {% endif %}
        {% endfor %}

        {% for inclusion_tag in inclusion_tags %}
          {% if product_tags contains inclusion_tag %}
            {% assign has_inclusion_tag = true %}
            {% break %}
          {% endif %}
        {% endfor %}

        {% comment %}
          -- exclusion tags have priority over inclusion tags
        {% endcomment %}

        {% if has_exclusion_tag %}
          {% log
            message: "This line item's product has at least one of the configured exclusion tags; skipping",
            fulfillment_order_line_item: fulfillment_order_line_item,
            product_tags: product_tags,
            exclusion_tags: exclusion_tags
          %}
          {% continue %}

        {% elsif inclusion_tags != blank %}
          {% unless has_inclusion_tag %}
            {% log
              message: "This line item's product does not contain any of the configured inclusion tags; skipping",
              fulfillment_order_line_item: fulfillment_order_line_item,
              product_tags: product_tags,
              inclusion_tags: inclusion_tags
            %}
            {% continue %}
          {% endunless %}
        {% endif %}
      {% endif %}

      {% comment %}
        -- save unfulfilled line items that do not require shipping; gift cards must be grouped separately
      {% endcomment %}

      {% if fulfillment_order_line_item.remainingQuantity > 0 %}
        {% if fulfillment_order_line_item.variant.product.isGiftCard %}
          {% if fulfill_gift_cards %}
            {% assign fulfillment_order_data["gift_cards_to_fulfill"]
              = fulfillment_order_data["gift_cards_to_fulfill"]
              | default: array
              | push: fulfillment_order_line_item
            %}
          {% endif %}

        {% else %}
          {% assign fulfillment_order_data["line_items_to_fulfill"]
            = fulfillment_order_data["line_items_to_fulfill"]
            | default: array
            | push: fulfillment_order_line_item
          %}
        {% endif %}
      {% endif %}
    {% endfor %}

    {% comment %}
      -- group fulfillment orders by location
    {% endcomment %}

    {% if fulfillment_order_data.line_items_to_fulfill != blank or fulfillment_order_data.gift_cards_to_fulfill != blank %}
      {% assign fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]
        = fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]
        | default: array
        | push: fulfillment_order_data
      %}
    {% endif %}
  {% endfor %}

  {% if wait_until_any_other_shippable_items_are_fulfilled
    and has_unfulfilled_shippable_items
    and fulfillment_orders_by_location != blank
  %}
    {% log
      message: "Unfulfilled shippable items exist on this order and the 'Wait until any other shippable items are fulfilled' option is checked; no auto fulfillments will be made in this task run.",
      order: order
    %}
    {% continue %}
  {% endif %}

  {% comment %}
    -- fulfill the line items that don't require shipping; fulfill any gift cards separately
  {% endcomment %}

  {% unless event.preview %}
    {% log
      order_name: order.name,
      fulfillment_orders_by_location: fulfillment_orders_by_location
    %}
  {% endunless %}

  {% for keyval in fulfillment_orders_by_location %}
    {% for fulfillment_order_data in keyval[1] %}
      {% if fulfillment_order_data.gift_cards_to_fulfill != blank %}
        {% action "shopify" %}
          mutation {
            fulfillmentCreate(
              fulfillment: {
                lineItemsByFulfillmentOrder: [
                  {
                    fulfillmentOrderId: {{ fulfillment_order_data.fulfillment_order_id | json }}
                    fulfillmentOrderLineItems: [
                      {% for line_item in fulfillment_order_data.gift_cards_to_fulfill %}
                        {
                          id: {{ line_item.id | json }}
                          quantity: {{ line_item.remainingQuantity }}
                        }
                      {% endfor %}
                    ]
                  }
                ]
                notifyCustomer: false
              }
            ) {
              fulfillment {
                id
                status
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endif %}

      {% if fulfillment_order_data.line_items_to_fulfill != blank %}
        {% action "shopify" %}
          mutation {
            fulfillmentCreate(
              fulfillment: {
                lineItemsByFulfillmentOrder: [
                  {
                    fulfillmentOrderId: {{ fulfillment_order_data.fulfillment_order_id | json }}
                    fulfillmentOrderLineItems: [
                      {% for line_item in fulfillment_order_data.line_items_to_fulfill %}
                        {
                          id: {{ line_item.id | json }}
                          quantity: {{ line_item.remainingQuantity }}
                        }
                      {% endfor %}
                    ]
                  }
                ]
                notifyCustomer: false
              }
            ) {
              fulfillment {
                id
                status
              }
              userErrors {
                field
                message
              }
            }
          }
        {% endaction %}
      {% endif %}
    {% endfor %}
  {% endfor %}
{% endfor %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more
Defaults
Only process paid orders
true
Fulfill gift cards
true