Mechanic is a development and ecommerce automation platform for Shopify. :)
This task checks to see if a product's variants have been discounted (by having a price set below the compare at price), and for each variant that meets one ore more configured discount percentage thresholds, it will save the matched filters in a variant metafield for use with Online Store 2.0 filtering.
Runs Occurs whenever a product is updated, ordered, or variants are added, removed or updated and Occurs when a user manually triggers the task. Configuration includes metafield namespace and key and discount filters and percentage thresholds.
This task checks to see if a product's variants have been discounted (by having a price set below the compare at price), and for each variant that meets one ore more configured discount percentage thresholds, it will save the matched filters in a variant metafield for use with Online Store 2.0 filtering.
Variants that are not discounted or do not meet any of the configured thresholds will have their metafield deleted if it exists.
This task runs when products are updated, and may also be run manually to scan all variants in the shop.
Important notes:
- Before configuring and running this task, a metafield definition must be created in the Shopify admin with a "Single line text" type configured to "Accept list of values", per the documentation on creating custom metafield definitions.
- For the Discount filters and percentage thresholds task configuration field, enter the discount filter values that will display on the storefront (e.g. 25% off or more) on the left-hand side, and the percentage thresholds as whole numbers (e.g. 25) on the right.
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/update mechanic/user/trigger
{% assign metafield_namespace_and_key = options.metafield_namespace_and_key__required | match: "(?<namespace>[\w-]{3,})\.(?<key>[\w-]{3,})" %} {% assign discount_filters_and_percentage_thresholds = options.discount_filters_and_percentage_thresholds__keyval_number_required %} {% assign metafield_namespace = metafield_namespace_and_key.named_captures.namespace %} {% assign metafield_key = metafield_namespace_and_key.named_captures.key %} {% if metafield_namespace == blank or metafield_key == blank %} {% error "The metafield key and namespace must each be at least 3 characters long and separated only by a '.'.\n\nValid characters are a-z, A-Z, 0-9, underscores, and dashes." %} {% break %} {% endif %} {% assign variants = array %} {% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %} {% assign cursor = nil %} {% for n in (1..200) %} {% capture query %} query { productVariants( first: 250 after: {{ cursor | json }} ) { pageInfo { hasNextPage endCursor } nodes { id displayName price compareAtPrice metafield( key: {{ metafield_namespace_and_key | json }} ) { value } } } } {% endcapture %} {% assign result = query | shopify %} {% assign variants = result.data.productVariants.nodes | default: array | concat: variants %} {% if result.data.productVariants.pageInfo.hasNextPage %} {% assign cursor = result.data.productVariants.pageInfo.endCursor %} {% else %} {% break %} {% endif %} {% endfor %} {% elsif event.topic == "shopify/products/update" %} {% capture query %} query { product(id: {{ product.admin_graphql_api_id | json }}) { variants(first: 100) { nodes { id displayName price compareAtPrice metafield( key: {{ metafield_namespace_and_key | json }} ) { value } } } } } {% endcapture %} {% assign result = query | shopify %} {% assign variants = result.data.product.variants.nodes %} {% endif %} {% if event.preview %} {% capture variants_json %} [ { "id": "gid://shopify/ProductVariant/1234567890", "price": "0.00", "compareAtPrice": "1.00" } ] {% endcapture %} {% assign variants = variants_json | parse_json %} {% endif %} {% assign metafields_to_set = array %} {% assign metafields_to_delete = array %} {% assign variant_logs = array %} {% for variant in variants %} {% assign variant_log = hash %} {% assign variant_log["variant"] = variant %} {% assign price = variant.price | times: 1.0 %} {% assign compare_at_price = variant.compareAtPrice | times: 1.0 %} {% assign metafield_value = variant.metafield.value | default: "[]" | parse_json %} {% comment %} Note: converting to floats above will handle cases where compareAtPrice is empty or "0.00", as it will become 0.0 in either case {% endcomment %} {% if compare_at_price <= price %} {% if metafield_value != blank %} {% assign metafield_to_delete = hash %} {% assign metafield_to_delete["ownerId"] = variant.id %} {% assign metafield_to_delete["namespace"] = metafield_namespace %} {% assign metafield_to_delete["key"] = metafield_key %} {% assign metafields_to_delete = metafields_to_delete | push: metafield_to_delete %} {% endif %} {% assign variant_log["message"] = "Variant is not discounted; removing metafield if it exists." %} {% assign variant_logs = variant_logs | push: variant_log %} {% continue %} {% endif %} {% assign discount_percentage = price | times: -100 | divided_by: compare_at_price | plus: 100 %} {% assign discount_filters_should_have = array %} {% for keyval in discount_filters_and_percentage_thresholds %} {% assign discount_filter = keyval.first %} {% assign percentage_threshold = keyval.last %} {% if discount_percentage >= percentage_threshold %} {% assign discount_filters_should_have = discount_filters_should_have | push: discount_filter %} {% endif %} {% endfor %} {% assign variant_log["discount_percentage"] = discount_percentage %} {% assign variant_log["discount_filters_should_have"] = discount_filters_should_have %} {% if discount_filters_should_have == blank %} {% if metafield_value != blank %} {% assign metafield_to_delete = hash %} {% assign metafield_to_delete["ownerId"] = variant.id %} {% assign metafield_to_delete["namespace"] = metafield_namespace %} {% assign metafield_to_delete["key"] = metafield_key %} {% assign metafields_to_delete = metafields_to_delete | push: metafield_to_delete %} {% endif %} {% assign variant_log["message"] = "Variant is discounted, but it does not meet any of the configured percentage thresholds; removing metafield if it exists." %} {% assign variant_logs = variant_logs | push: variant_log %} {% continue %} {% endif %} {% if metafield_value == discount_filters_should_have %} {% continue %} {% endif %} {% assign variant_log["message"] = "Variant is discounted and metafield does not match the discount filters it should have; updating." %} {% assign variant_logs = variant_logs | push: variant_log %} {% assign metafield_to_set = hash %} {% assign metafield_to_set["ownerId"] = variant.id %} {% assign metafield_to_set["namespace"] = metafield_namespace %} {% assign metafield_to_set["key"] = metafield_key %} {% assign metafield_to_set["type"] = "list.single_line_text_field" %} {% assign metafield_to_set["value"] = discount_filters_should_have | json %} {% assign metafields_to_set = metafields_to_set | push: metafield_to_set %} {% endfor %} {% log variant_logs %} {% if metafields_to_set != blank %} {% assign groups_of_metafields_to_set = metafields_to_set | in_groups_of: 25, fill_with: false %} {% for group_of_metafields_to_set in groups_of_metafields_to_set %} {% action "shopify" %} mutation { metafieldsSet( metafields: {{ group_of_metafields_to_set | graphql_arguments }} ) { metafields { id namespace key type value owner { ... on ProductVariant { id displayName } } } userErrors { code field message } } } {% endaction %} {% endfor %} {% endif %} {% if metafields_to_delete != blank %} {% assign groups_of_metafields_to_delete = metafields_to_delete | in_groups_of: 250, fill_with: false %} {% for group_of_metafields_to_delete in groups_of_metafields_to_delete %} {% action "shopify" %} mutation { metafieldsDelete( metafields: {{ group_of_metafields_to_delete | graphql_arguments }} ) { deletedMetafields { ownerId namespace key } userErrors { field message } } } {% endaction %} {% endfor %} {% endif %}