Mechanic is a development and ecommerce automation platform for Shopify. :)
Use this task to automatically add product bundle components to qualifying paid orders, by configuring the task with a bundle parent SKU, unique SKUs for the bundle components, and with quantities needed from each component for each bundle parent unit. The bundle components will be added to the order at a 100% discount, and the order's subtotal will be unaffected.
Runs Occurs when a user sends an order to Mechanic and Occurs when a Mechanic action is performed. Configuration includes bundle parent sku, component skus and quantities per bundle, discount message to display on each added component, staff note for the order edit, alert email recipients, and manual mode.
Use this task to automatically add product bundle components to qualifying paid orders, by configuring the task with a bundle parent SKU, unique SKUs for the bundle components, and with quantities needed from each component for each bundle parent unit. The bundle components will be added to the order at a 100% discount, and the order's subtotal will be unaffected.
Usage:
This task is initially confiured with a "Manual mode" option enabled. This will allow you to try out the order edits on live orders by sending them manually via an admin action link. When you are satisfied with the outcomes, then disable this mode to have the task process new, paid orders.
Task configuration:
- Bundle parent SKU - The SKU of the variant that must appear on a line item for an order to qualify for the order edit.
- Component SKUs and quantities per bundle - Add unique component SKUs on the left-hand side of this option, and use the right-hand side to control how many units of each component SKU is required for each single bundle unit. If your bundle requires one wrench and two sprockets, for example, make sure to add "1" and "2" on the right-hand side, each number associated with the right component SKU.
- Discount message to display on each added componnent - This appears on the order status page and any follow on Shopify emails that includes a list of line items.
- Staff note for the order edit - Optional field to indicate a reason for the order edit in the order's timeline.
- Alert email recipients - One or more recipients that should get an email from Mechanic if any of the order editing steps fail during a task run.
IMPORTANT:
- If duplicate variants are found with the same component SKU during a task run, it will halt processing and not make any order edits.
- The bundle parent SKU can occur on multiple line items on an order, due to different line item attributes (e.g. gift messaging). This task will sum the quantites of each qualifying line item and use that as a multiplier when adding the bundle components.
- This task does NOT remove the bundle parent from the order. The bundle parent contains the original discounted price, plus any custom line item attributes that may be important for additional processes or flows.
- This task does NOT check the available inventory of the child components before adding them to the order.
- This task does NOT sync inventory of the bundle parent and the child components. This alternate task is a better fit for that use case.
Addendum:
By its nature, this task can also be used as a simple way to add free items to orders when a qualifying item is bought. Typically, a free item task has more nuance around how an order qualifies (e.g. minimum quantities or subtotal, order tags, etc.), but this task can provide a great springboard for that additional customization.
This task uses preview events for each stage of the order edit. If you are interested in seeing the sample preview data, make sure you are viewing the task in Advanced mode.
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.manual_mode__boolean %} mechanic/user/order {% else %} shopify/orders/paid {% endif %} mechanic/actions/perform
{% assign bundle_sku = options.bundle_parent_sku__required %} {% assign component_skus_and_quantities = options.component_skus_and_quantities_per_bundle__keyval_number_required %} {% assign discount_message = options.discount_message_to_display_on_each_added_component__required %} {% assign staff_note = options.staff_note_for_the_order_edit %} {% assign alert_email_recipients = options.alert_email_recipients__array_required %} {% assign component_skus = component_skus_and_quantities | keys %} {% assign component_quantities = component_skus_and_quantities | values %} {% for component_quantity in component_quantities %} {% unless component_quantity > 0 %} {% error "All component quantities must be greater than zero" %} {% endunless %} {% endfor %} {% if event.topic == "shopify/orders/create" or event.topic == "shopify/orders/paid" or event.topic == "mechanic/user/order" %} {% if event.topic == "mechanic/user/order" %} {% log "CAUTION: Sending orders manually to this task via the admin link is provided for controlled testing; however, these are edits to live orders. Once you have thoroughly tested this task, uncheck 'Manual mode' so the bundle components can be added when qualifying orders are placed." %} {% endif %} {% if event.preview %} {% capture order_json %} { "admin_graphql_api_id": "gid://shopify/Order/1234567890", "line_items": [ { "sku": {{ bundle_sku | json }}, "quantity": 1 } ] } {% endcapture %} {% assign order = order_json | parse_json %} {% endif %} {% assign bundle_parent_quantity = order.line_items | where: "sku", bundle_sku | map: "quantity" | sum %} {% unless bundle_parent_quantity > 0 %} {% log %} { "message": "No SKUs on this order match the configured bundle SKU.", "bundle_sku": {{ bundle_sku | json }}, "order_skus": {{ order.line_items | map: "sku" | json }} } {% endlog %} {% break %} {% endunless %} {% assign component_variants = array %} {% for component_sku in component_skus %} {% capture query %} query { productVariants( first: 2 query: {{ component_sku | json | prepend: "sku:" | json }} ) { nodes { id legacyResourceId sku } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "productVariants": { "nodes": [ { "id": "gid://shopify/ProductVariant/1234567890", "legacyResourceId": "1234567890", "sku": {{ component_sku | json }} } ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign variants = result.data.productVariants.nodes %} {% if variants == blank %} {% error message: "Component SKU does not match a variant in this shop.", component_sku: component_sku %} {% elsif variants.size > 1 %} {% error message: "Component SKU matches multiple variants in this shop.", component_sku: component_sku, variants: variants %} {% else %} {% assign component_variant = hash %} {% assign component_variant["id"] = variants.first.id %} {% assign component_variant["legacyResourceId"] = variants.first.legacyResourceId %} {% assign component_variant["quantity"] = bundle_parent_quantity | times: component_skus_and_quantities[component_sku] %} {% assign component_variants = component_variants | push: component_variant %} {% endif %} {% endfor %} {% comment %} -- begin the order edit and pass the component variants as meta information {% endcomment %} {% capture order_edit_begin_query %} mutation { orderEditBegin( id: {{ order.admin_graphql_api_id | json }} ) { calculatedOrder { id originalOrder { name } } userErrors { field message } } } {% endcapture %} {% action %} { "type": "shopify", "options": { "query": {{ order_edit_begin_query | json }} }, "meta": { "mutation_type": "orderEditBegin", "component_variants": {{ component_variants | json }} } } {% endaction %} {% elsif event.topic == "mechanic/actions/perform" %} {% comment %} -- NOTE: task preview events have been configured for results of orderEditBegin, orderEditAddVariant, and orderEditAddLineItemDiscount {% endcomment %} {% unless action.run.ok %} {% comment %} -- send an alert email for any errors on action runs {% endcomment %} {% capture alert_email_subject -%} ALERT - Error occured while running the Mechanic task: {{ task.name }} {%- endcapture %} {% capture alert_email_body -%} Review the <a href="https://{{ shop.myshopify_domain }}/admin/apps/mechanic/events/{{ event.id }}">task run log</a> to determine next steps. <strong>Error summary (if any):</strong> {{ action.run.error }} {%- endcapture %} {% action "email" %} { "to": {{ alert_email_recipients | json }}, "subject": {{ alert_email_subject | json }}, "body": {{ alert_email_body | newline_to_br | json }}, "reply_to": {{ shop.customer_email | json }}, "from_display_name": {{ shop.name | json }} } {% endaction %} {% break %} {% endunless %} {% if action.meta.mutation_type == "orderEditBegin" %} {% comment %} -- add all of the component variants to the order {% endcomment %} {% assign calculated_order = action.run.result.data.orderEditBegin.calculatedOrder %} {% assign add_variant_mutations = array %} {% for component_variant in action.meta.component_variants %} {% capture add_variant_mutation %} orderEditAddVariant_{{ component_variant.legacyResourceId }}: orderEditAddVariant( id: {{ calculated_order.id | json }} variantId: {{ component_variant.id | json }} quantity: {{ component_variant.quantity }} allowDuplicates: true ) { calculatedLineItem { id sku variant { legacyResourceId } } userErrors { field message } } {% endcapture %} {% assign add_variant_mutations = add_variant_mutations | push: add_variant_mutation %} {% endfor %} {% capture add_variant_query %} mutation { {{ add_variant_mutations | join: newline }} } {% endcapture %} {% action %} { "type": "shopify", "options": { "query": {{ add_variant_query | json }} }, "meta": { "mutation_type": "orderEditAddVariant", "calculated_order": {{ calculated_order | json }} } } {% endaction %} {% elsif action.meta.mutation_type == "orderEditAddVariant" %} {% comment %} -- add 100% line item discounts after adding component variants {% endcomment %} {% assign calculated_order = action.meta.calculated_order %} {% assign add_line_item_discount_mutations = array %} {% for add_variant_result in action.run.result.data %} {% assign added_line_item = add_variant_result[1].calculatedLineItem %} {% capture add_line_item_discount_mutation %} orderEditAddLineItemDiscount_{{ added_line_item.variant.legacyResourceId }}: orderEditAddLineItemDiscount( id: {{ calculated_order.id | json }} lineItemId: {{ added_line_item.id | json }} discount: { description: {{ discount_message | json }} percentValue: 100 } ) { calculatedLineItem { id discountedUnitPriceSet { shopMoney { amount } } } userErrors { field message } } {% endcapture %} {% assign add_line_item_discount_mutations = add_line_item_discount_mutations | push: add_line_item_discount_mutation %} {% endfor %} {% capture add_line_item_discount_query %} mutation { {{ add_line_item_discount_mutations | join: newline }} } {% endcapture %} {% action %} { "type": "shopify", "options": { "query": {{ add_line_item_discount_query | json }} }, "meta": { "mutation_type": "orderEditAddLineItemDiscount", "calculated_order": {{ calculated_order | json }} } } {% endaction %} {% elsif action.meta.mutation_type == "orderEditAddLineItemDiscount" %} {% comment %} -- commit the order edit after adding line item discounts {% endcomment %} {% assign calculated_order = action.meta.calculated_order %} {% action "shopify" %} mutation { orderEditCommit( id: {{ calculated_order.id | json }} notifyCustomer: false staffNote: {{ staff_note | json }} ) { order { id } userErrors { field message } } } {% endaction %} {% endif %} {% endif %}
Bundle components auto-added by Mechanic
true