Mechanic is a development and ecommerce automation platform for Shopify. :)
Need to print price tags whenever a SKU is added? Or make a note of a new variant price? Use this task to tag products that need your attention, whenever a specific variant attribute changes.
Runs Occurs whenever a product is created, Occurs whenever a product is updated, ordered, or variants are added, removed or updated, Occurs when a user manually triggers the task, and Occurs when a bulk operation is completed. Configuration includes variant attribute to watch for changes, tag product with the titles of each variant that has changed, product tag to add, and ignore products with any of these tags.
Need to print price tags whenever a SKU is added? Or make a note of a new variant price? Use this task to tag products that need your attention, whenever a specific variant attribute changes.
Configure this task with a specific variant attribute to watch for changes. Valid attributes include "barcode", "compareAtPrice", "price", "sku", and "taxable".
Important: After saving this task, you must click the "Run task" button before the task will start monitoring your existing products. This task run will allow the task to scan your existing products, and store information about their existing variant attributes. This is what lets the task determine whether or not a specific variant attribute has changed.
Enable the "Tag product with the titles of each variant that has changed" option to have this task add a separate tag for each variant that has had its specific attribute change values. The variant title will be appended to your configured tag, resulting in one or more tags per product, resembling "SKU RED" or "repriced 5 / SMALL", depending on your tag choice and variant options.
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?)
shopify/products/create shopify/products/update mechanic/user/trigger mechanic/shopify/bulk_operation
{% assign variant_attribute = options.variant_attribute_to_watch_for_changes__required %} {% assign tag_product_with_variant_titles = options.tag_product_with_the_titles_of_each_variant_that_has_changed__boolean %} {% assign product_tag_to_add = options.product_tag_to_add__required %} {% assign ignore_products_with_any_of_these_tags = options.ignore_products_with_any_of_these_tags__array %} {% comment %} -- verify a supported variant value has been configured {% endcomment %} {% assign valid_variant_attributes = "barcode,compareAtPrice,price,sku,taxable" | split: "," %} {% unless valid_variant_attributes contains variant_attribute %} {% capture message -%} {{ variant_attribute | json }} is not a valid variant attribute. Choose one of these instead: {{ valid_variant_attributes | join: ", " }} {%- endcapture %} {% error message %} {% endunless %} {% assign metafield_key = "variant_ids_and_" | append: variant_attribute | append: "s" %} {% assign products = array %} {% if event.topic == "shopify/products/create" or event.topic == "shopify/products/update" %} {% comment %} -- get all variants (up to 2K) for this product {% endcomment %} {% assign cursor = nil %} {% assign variants = array %} {% for n in (1..8) %} {% capture query %} query { product(id: {{ product.admin_graphql_api_id | json }}) { id tags metafield( namespace: "mechanic" key: {{ metafield_key | json }} ) { jsonValue } variants( first: 250 after: {{ cursor | json }} ) { pageInfo { hasNextPage endCursor } nodes { legacyResourceId title {{ variant_attribute }} } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "product": { "id": "gid://shopify/Product/1234567890", "variants": { "nodes": [ { "legacyResourceId": "1234567890", "title": "Widget", {{ variant_attribute | json }}: "PREVIEW" } ] } } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign products[0] = result.data.product %} {% assign variants = variants | concat: result.data.product.variants.nodes %} {% if result.data.product.variants.pageInfo.hasNextPage %} {% assign cursor = result.data.product.variants.pageInfo.endCursor %} {% else %} {% break %} {% endif %} {% endfor %} {% elsif event.topic == "mechanic/user/trigger" %} {% comment %} -- get all active products in the shop and their variants {% endcomment %} {% capture bulk_operation_query %} query { products( query: "status:active" ) { edges { node { __typename id tags metafield( namespace: "mechanic" key: {{ metafield_key | json }} ) { jsonValue } variants { edges { node { __typename legacyResourceId title {{ variant_attribute }} } } } } } } } {% endcapture %} {% action "shopify" %} mutation { bulkOperationRunQuery( query: {{ bulk_operation_query | json }} ) { bulkOperation { id status } userErrors { field message } } } {% endaction %} {% break %} {% elsif event.topic == "mechanic/shopify/bulk_operation" %} {% if event.preview %} {% capture jsonl_string %} {"__typename":"Product","id":"gid://shopify/Product/1234567890"} {"__typename":"ProductVariant","legacyResourceId":"1234567890","title":"Widget",{{ variant_attribute | json }}:"PREVIEW","__parentId":"gid://shopify/Product/1234567890"} {% endcapture %} {% assign bulkOperation = hash %} {% assign bulkOperation["objects"] = jsonl_string | parse_jsonl %} {% endif %} {% assign products = bulkOperation.objects | where: "__typename", "Product" %} {% assign bulk_variants = bulkOperation.objects | where: "__typename", "ProductVariant" %} {% endif %} {% comment %} -- process all products returned by bulk op query, or the product that caused the create/update event -- for product create/update, "variants" will have been assigned in paginated queries above {% endcomment %} {% assign metafield_inputs = array %} {% for product in products %} {% if event.topic == "mechanic/shopify/bulk_operation" %} {% assign variants = bulk_variants | where: "__parentId", product.id %} {% endif %} {% comment %} -- see if this product should be excluded by tag {% endcomment %} {% assign exclude_product = nil %} {% if ignore_products_with_any_of_these_tags != blank %} {% for tag in product.tags %} {% if ignore_products_with_any_of_these_tags contains tag %} {% log message: "Product is disqualified by tag", disqualifying_product_tag: tag %} {% assign exclude_product = true %} {% break %} {% endif %} {% endfor %} {% endif %} {% if exclude_product %} {% continue %} {% endif %} {% assign previous_variant_ids_and_values = product.metafield.jsonValue %} {% comment %} -- only tag and check existing metafield variant values when a product is updated -- new products and bulk scans will always set metafields (but not tags) {% endcomment %} {% if event.topic == "shopify/products/update" %} {% if previous_variant_ids_and_values == blank %} {% unless event.preview %} {% error "Mechanic has not had an opportunity to store this product's previous variant values. Use the 'Run task' button to re-scan your products. :)" %} {% endunless %} {% endif %} {% assign product_qualifies = nil %} {% assign tags_to_add = array %} {% unless tag_product_with_variant_titles %} {% assign tags_to_add = tags_to_add | push: product_tag_to_add %} {% endunless %} {% for variant in variants %} {% if previous_variant_ids_and_values[variant.legacyResourceId] != variant[variant_attribute] %} {% assign product_qualifies = true %} {% if tag_product_with_variant_titles %} {% if variant.title == "Default Title" %} {% assign tags_to_add = tags_to_add | push: product_tag_to_add %} {% else %} {% assign tags_to_add[tags_to_add.size] = product_tag_to_add | append: " " | append: variant.title %} {% endif %} {% endif %} {% endif %} {% endfor %} {% unless product_qualifies %} {% break %} {% endunless %} {% action "shopify" %} mutation { tagsAdd( id: {{ product.id | json }} tags: {{ tags_to_add | json }} ) { userErrors { field message } } } {% endaction %} {% endif %} {% comment %} -- product has variant changes, or has been newly scanned or created; set metafield values {% endcomment %} {% assign metafield_value = hash %} {% for variant in variants %} {% assign variant_id_string = variant.id | append: "" %} {% assign metafield_value[variant.legacyResourceId] = variant[variant_attribute] %} {% endfor %} {% assign metafield_input = hash %} {% assign metafield_input["ownerId"] = product.id %} {% assign metafield_input["namespace"] = "mechanic" %} {% assign metafield_input["key"] = metafield_key %} {% assign metafield_input["type"] = "json" %} {% assign metafield_input["value"] = metafield_value | json %} {% assign metafield_inputs = metafield_inputs | push: metafield_input %} {% endfor %} {% comment %} -- process any metafield updates from this task run {% endcomment %} {% if metafield_inputs != blank %} {% assign groups_of_metafield_inputs = metafield_inputs | in_groups_of: 25, fill_with: false %} {% for group_of_metafield_inputs in groups_of_metafield_inputs %} {% action "shopify" %} mutation { metafieldsSet( metafields: {{ group_of_metafield_inputs | graphql_arguments }} ) { metafields { id namespace key type value owner { ... on Product { id } } } userErrors { code field message } } } {% endaction %} {% endfor %} {% endif %}