Mechanic is a development and ecommerce automation platform for Shopify. :)
Use this task to sell a single product in different pack sizes, keeping available inventory levels synchronized appropriately, storing the "main" inventory level in a product tag (e.g. "inventory:50").
Runs Occurs whenever an order is created, Occurs whenever a product is created, and Occurs whenever a product is updated, ordered, or variants are added, removed or updated. Configuration includes product inventory tag prefix, variant pack size option name, variant pack size metafield namespace dot key, and run when orders are paid instead of when they are created.
Use this task to sell a single product in different pack sizes, keeping available inventory levels synchronized appropriately, storing the "main" inventory level in a product tag (e.g. "inventory:50").
New orders for a pack-sized variant will result in the product's inventory tag being updated, and all pack-sized variants having their available inventory levels re-synced. And, manually updating a product's inventory tag will automatically re-sync variant available inventory levels as well.
The pack size for each variant can be determined by a numeric product option (e.g. "Pack size"), or by metafield.
This task will skip any products that do not have an inventory tag, and will skip any variants ordered that have no pack size information.
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?)
{% if options.run_when_orders_are_paid_instead_of_when_they_are_created__boolean %} shopify/orders/paid {% else %} shopify/orders/create {% endif %} shopify/products/create shopify/products/update
{% assign product_inventory_tag_prefix = options.product_inventory_tag_prefix__required %} {% assign product_inventory_tag_prefix_size = product_inventory_tag_prefix.size %} {% assign pack_size_option_name = options.variant_pack_size_option_name %} {% assign metafield_namespace_dot_key = options.variant_pack_size_metafield_namespace_dot_key %} {% if pack_size_option_name == blank and metafield_namespace_dot_key == blank %} {% error "Choose a method for determining variant pack size: either option name, or metafield." %} {% elsif pack_size_option_name != blank and metafield_namespace_dot_key != blank %} {% error "Choose to determine the variant pack size by *either* option name or by metafield - not both." %} {% endif %} {% if pack_size_option_name != blank %} {% assign pack_size_mode = "option" %} {% else %} {% assign pack_size_mode = "metafield" %} {% endif %} {% assign primary_location = shop.locations[shop.primary_location_id] %} {% assign product_considerations = hash %} {% if event.topic contains "shopify/orders/" %} {% capture query %} query { order(id: {{ order.admin_graphql_api_id | json }}) { lineItems(first: 200) { nodes { quantity variant { product { id tags } {% if pack_size_mode == "option" %} selectedOptions { name value } {% else %} metafield(key: {{ metafield_namespace_dot_key | json }}) { value } {% endif %} } } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "order": { "lineItems": { "nodes": [ { "quantity": 2, "variant": { "product": { "id": "gid://shopify/Product/1234567890", "tags": {{ product_inventory_tag_prefix | append: 80 | json }} }, {% if pack_size_mode == "option" %} "selectedOptions": [{ "name": {{ pack_size_option_name | json }}, "value": "25" }] {% else %} "metafield": { "value": "25" } {% endif %} } } ] } } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% comment %} -- group line items by product, to handle case of multiple pack variants of same product being on the order {% endcomment %} {% for line_item in result.data.order.lineItems.nodes %} {% assign product_id = line_item.variant.product.id %} {% assign pack_size = nil %} {% if pack_size_mode == "option" %} {% assign pack_size_option = line_item.variant.selectedOptions | where: "name", pack_size_option_name | first %} {% assign pack_size = pack_size_option.value %} {% else %} {% assign pack_size = line_item.variant.metafield.value %} {% endif %} {% if pack_size == blank %} {% continue %} {% endif %} {% assign quantity_times_pack_size = line_item.quantity | times: pack_size %} {% if product_considerations[product_id] == blank %} {% assign product_considerations[product_id] = hash %} {% assign product_considerations[product_id]["tags"] = line_item.variant.product.tags %} {% endif %} {% assign product_considerations[product_id]["inventory_difference"] = product_considerations[product_id]["inventory_difference"] | default: 0 | plus: quantity_times_pack_size %} {% endfor %} {% elsif event.topic contains "shopify/products/" %} {% assign product_id = product.admin_graphql_api_id %} {% assign product_tags = product.tags | split: ", " %} {% if event.preview %} {% assign product_id = "gid://shopify/Product/1234576890" %} {% assign product_tags = product_inventory_tag_prefix | append: 80 %} {% endif %} {% assign product_considerations[product_id] = hash %} {% assign product_considerations[product_id]["tags"] = product_tags %} {% endif %} {% for keyval in product_considerations %} {% assign product_consideration_id = keyval[0] %} {% assign product_consideration = keyval[1] %} {% assign product_inventory_old = nil %} {% for product_tag in product_consideration.tags %} {% assign product_tag_possible_prefix = product_tag | slice: 0, product_inventory_tag_prefix_size %} {% if product_tag_possible_prefix == product_inventory_tag_prefix %} {% assign product_inventory_old = product_tag | slice: product_inventory_tag_prefix_size, 100 | times: 1 %} {% break %} {% endif %} {% endfor %} {% if product_inventory_old == blank %} {% continue %} {% endif %} {% assign product_inventory_new = product_inventory_old | minus: product_consideration.inventory_difference %} {% capture query %} query { product(id: {{ product_consideration_id | json }}) { variants(first: 100) { nodes { inventoryItem { id inventoryLevel(locationId: {{ primary_location.admin_graphql_api_id | json }}) { quantities(names: "available") { name quantity } } } {% if pack_size_mode == "option" %} selectedOptions { name value } {% else %} metafield(key: {{ metafield_namespace_dot_key | json }}) { value } {% endif %} } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "product": { "variants": { "nodes": [ { "inventoryItem": { "id": "gid://shopify/InventoryItem/1234567890", "inventoryLevel": { "quantities": [ { "name": "available", "quantity": {% if event.topic contains "shopify/orders/" %}3{% else %}0{% endif %} } ] } }, {% if pack_size_mode == "option" %} "selectedOptions": [ { "name": {{ pack_size_option_name | json }}, "value": "25" } ] {% else %} "metafield": { "value": "25" } {% endif %} }, { "inventoryItem": { "id": "gid://shopify/InventoryItem/2345678901", "inventoryLevel": { "quantities": [ { "name": "available", "quantity": {% if event.topic contains "shopify/orders/" %}1{% else %}0{% endif %} } ] } }, {% if pack_size_mode == "option" %} "selectedOptions": [ { "name": {{ pack_size_option_name | json }}, "value": "50" } ] {% else %} "metafield": { "value": "50" } {% endif %} } ] } } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign variants = result.data.product.variants.nodes %} {% assign inventory_adjustments = array %} {% for variant in variants %} {% assign pack_size = nil %} {% if pack_size_mode == "option" %} {% assign pack_size_option = variant.selectedOptions | where: "name", pack_size_option_name | first %} {% assign pack_size = pack_size_option.value %} {% else %} {% assign pack_size = variant.metafield.value %} {% endif %} {% if pack_size == blank %} {% continue %} {% endif %} {% assign new_inventory_level = product_inventory_new | times: 1.0 | divided_by: pack_size | floor %} {% if new_inventory_level == variant.inventoryItem.inventoryLevel.quantities.first.quantity %} {% continue %} {% endif %} {% assign inventory_adjustment = hash %} {% assign inventory_adjustment["inventoryItemId"] = variant.inventoryItem.id %} {% assign inventory_adjustment["locationId"] = primary_location.admin_graphql_api_id %} {% assign inventory_adjustment["delta"] = new_inventory_level | minus: variant.inventoryItem.inventoryLevel.quantities.first.quantity %} {% assign inventory_adjustments = inventory_adjustments | push: inventory_adjustment %} {% endfor %} {% capture mutations %} {% if product_inventory_old != product_inventory_new %} tagsRemove( id: {{ product_consideration_id | json }} tags: {{ product_inventory_tag_prefix | append: product_inventory_old | json }} ) { userErrors { field message } } tagsAdd( id: {{ product_consideration_id | json }} tags: {{ product_inventory_tag_prefix | append: product_inventory_new | json }} ) { userErrors { field message } } {% endif %} {% if inventory_adjustments != blank %} inventoryAdjustQuantities( input: { reason: "correction" name: "available" changes: {{ inventory_adjustments | graphql_arguments }} } ) { inventoryAdjustmentGroup { reason changes(quantityNames: "available") { name delta item { id sku } location { name } } } userErrors { code field message } } {% endif %} {% endcapture %} {% if mutations != blank %} {% action "shopify" %} mutation { {{ mutations }} } {% endaction %} {% endif %} {% endfor %}
inventory:
Pack size