From 3f76a444afef8104f90d6e355eeece0b11e1cfb1 Mon Sep 17 00:00:00 2001 From: Brad Hover Date: Wed, 9 Oct 2024 12:48:06 -0700 Subject: [PATCH] Fix/api changes (#409) * switch to fulfillmentCreate * fix for productUpdate input --- .../script.liquid | 60 ++++--------------- .../script.liquid | 2 +- .../script.liquid | 2 +- .../script.liquid | 4 +- ...fill-items-that-dont-require-shipping.json | 2 +- ...e-fulfillment-status-using-order-tags.json | 2 +- .../set-product-types-by-title-keywords.json | 2 +- ...product-description-when-out-of-stock.json | 2 +- 8 files changed, 21 insertions(+), 55 deletions(-) diff --git a/docs/auto-fulfill-items-that-dont-require-shipping/script.liquid b/docs/auto-fulfill-items-that-dont-require-shipping/script.liquid index 6ea7bd5c..58383bc7 100644 --- a/docs/auto-fulfill-items-that-dont-require-shipping/script.liquid +++ b/docs/auto-fulfill-items-that-dont-require-shipping/script.liquid @@ -24,9 +24,13 @@ lineItems(first: 30) { nodes { id - inventoryItemId remainingQuantity requiresShipping + variant { + product { + tags + } + } } } } @@ -56,15 +60,13 @@ "nodes": [ { "id": "gid://shopify/FulfillmentOrderLineItem/1234567890", - "inventoryItemId": "gid://shopify/InventoryItem/1234567890", "remainingQuantity": 1, - "requiresShipping": false - }, - { - "id": "gid://shopify/FulfillmentOrderLineItem/2345678901", - "inventoryItemId": "gid://shopify/InventoryItem/2345678901", - "remainingQuantity": 2, - "requiresShipping": false + "requiresShipping": false, + "variant": { + "product": { + "tags": {{ inclusion_tags.first | json }} + } + } } ] } @@ -114,43 +116,7 @@ {% assign has_exclusion_tag = nil %} {% assign has_inclusion_tag = nil %} - {% comment %} - -- use the inventory item Id to query for product tags; product access from fulfillment order line items has been deprecated - {% endcomment %} - - {% capture query %} - query { - inventoryItem(id: {{ fulfillment_order_line_item.inventoryItemId | json }}) { - variant { - product { - tags - } - } - } - } - {% endcapture %} - - {% assign result = query | shopify %} - - {% if event.preview %} - {% capture result_json %} - { - "data": { - "inventoryItem": { - "variant": { - "product": { - "tags": {{ inclusion_tags.first | json }} - } - } - } - } - } - {% endcapture %} - - {% assign result = result_json | parse_json %} - {% endif %} - - {% assign product_tags = result.data.inventoryItem.variant.product.tags %} + {% assign product_tags = fulfillment_order_line_item.variant.product.tags %} {% for exclusion_tag in exclusion_tags %} {% if product_tags contains exclusion_tag %} @@ -226,7 +192,7 @@ {% for keyval in fulfillment_orders_by_location %} {% action "shopify" %} mutation { - fulfillmentCreateV2( + fulfillmentCreate( fulfillment: { lineItemsByFulfillmentOrder: [ {% for fulfillment_order_data in keyval[1] %} diff --git a/docs/manage-fulfillment-status-using-order-tags/script.liquid b/docs/manage-fulfillment-status-using-order-tags/script.liquid index af6220a7..decfcb35 100644 --- a/docs/manage-fulfillment-status-using-order-tags/script.liquid +++ b/docs/manage-fulfillment-status-using-order-tags/script.liquid @@ -121,7 +121,7 @@ {% for keyval in fulfillment_order_ids_by_location_and_type %} {% action "shopify" %} mutation { - fulfillmentCreateV2( + fulfillmentCreate( fulfillment: { lineItemsByFulfillmentOrder: [ {% for fulfillment_order_id in keyval[1] %} diff --git a/docs/set-product-types-by-title-keywords/script.liquid b/docs/set-product-types-by-title-keywords/script.liquid index d8e97f82..fb4cc681 100644 --- a/docs/set-product-types-by-title-keywords/script.liquid +++ b/docs/set-product-types-by-title-keywords/script.liquid @@ -113,7 +113,7 @@ {% action "shopify" %} mutation { productUpdate( - input: { + product: { id: {{ product.id | json }} productType: {{ product_type_to_set | json }} } diff --git a/docs/update-product-description-when-out-of-stock/script.liquid b/docs/update-product-description-when-out-of-stock/script.liquid index 6c3b9fca..5a5426a9 100644 --- a/docs/update-product-description-when-out-of-stock/script.liquid +++ b/docs/update-product-description-when-out-of-stock/script.liquid @@ -143,7 +143,7 @@ {% action "shopify" %} mutation { productUpdate( - input: { + product: { id: {{ product.id | json }} descriptionHtml: {{ cached_description_html | json }} } @@ -170,7 +170,7 @@ {% action "shopify" %} mutation { productUpdate( - input: { + product: { id: {{ product.id | json }} descriptionHtml: {{ updated_description_html | json }} } diff --git a/tasks/auto-fulfill-items-that-dont-require-shipping.json b/tasks/auto-fulfill-items-that-dont-require-shipping.json index 8bee7b31..1f54c60d 100644 --- a/tasks/auto-fulfill-items-that-dont-require-shipping.json +++ b/tasks/auto-fulfill-items-that-dont-require-shipping.json @@ -10,7 +10,7 @@ }, "order_status_javascript": null, "perform_action_runs_in_sequence": false, - "script": "{% assign inclusion_tags = options.include_products_with_any_of_these_tags__array %}\n{% assign exclusion_tags = options.exclude_products_with_any_of_these_tags__array %}\n{% assign wait_until_any_other_shippable_items_are_fulfilled = options.wait_until_any_other_shippable_items_are_fulfilled__boolean %}\n\n{% comment %}\n -- get all open or in progress fulfillment orders\n{% endcomment %}\n\n{% capture query %}\n query {\n order(id: {{ order.admin_graphql_api_id | json }}) {\n id\n fulfillmentOrders(\n first: 10\n query: \"status:open OR status:in_progress\"\n ) {\n nodes {\n id\n assignedLocation {\n location {\n id\n }\n }\n lineItems(first: 30) {\n nodes {\n id\n inventoryItemId\n remainingQuantity\n requiresShipping\n }\n }\n }\n }\n }\n }\n{% endcapture %}\n\n{% assign result = query | shopify %}\n\n{% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"order\": {\n \"id\": \"gid://shopify/Order/1234567890\",\n \"fulfillmentOrders\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrder/1234567890\",\n \"assignedLocation\": {\n \"location\": {\n \"id\": \"gid://shopify/Location/1234567890\"\n }\n },\n \"lineItems\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrderLineItem/1234567890\",\n \"inventoryItemId\": \"gid://shopify/InventoryItem/1234567890\",\n \"remainingQuantity\": 1,\n \"requiresShipping\": false\n },\n {\n \"id\": \"gid://shopify/FulfillmentOrderLineItem/2345678901\",\n \"inventoryItemId\": \"gid://shopify/InventoryItem/2345678901\",\n \"remainingQuantity\": 2,\n \"requiresShipping\": false\n }\n ]\n }\n }\n ]\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n{% endif %}\n\n{% assign fulfillment_orders = result.data.order.fulfillmentOrders.nodes %}\n\n{% if fulfillment_orders == blank %}\n {% log \"There are no open fulfillment orders to fulfill on this order.\" %}\n {% break %}\n{% endif %}\n\n{% comment %}\n NOTE: fulfillments can only be created for one location at a time, so need to group fulfillment orders by location\n{% endcomment %}\n\n{% assign fulfillment_orders_by_location = hash %}\n{% assign has_unfulfilled_shippable_items = nil %}\n\n{% for fulfillment_order in fulfillment_orders %}\n {% assign fulfillment_order_data = hash %}\n {% assign fulfillment_order_data[\"fulfillment_order_id\"] = fulfillment_order.id %}\n\n {% for fulfillment_order_line_item in fulfillment_order.lineItems.nodes %}\n {% comment %}\n -- skip items that do not require shipping, but set flag if any are unfulfilled\n {% endcomment %}\n\n {% if fulfillment_order_line_item.requiresShipping %}\n {% if fulfillment_order_line_item.remainingQuantity > 0 %}\n {% assign has_unfulfilled_shippable_items = true %}\n {% endif %}\n\n {% continue %}\n {% endif %}\n\n {% if inclusion_tags != blank or exclusion_tags != blank %}\n {% assign has_exclusion_tag = nil %}\n {% assign has_inclusion_tag = nil %}\n\n {% comment %}\n -- use the inventory item Id to query for product tags; product access from fulfillment order line items has been deprecated\n {% endcomment %}\n\n {% capture query %}\n query {\n inventoryItem(id: {{ fulfillment_order_line_item.inventoryItemId | json }}) {\n variant {\n product {\n tags\n }\n }\n }\n }\n {% endcapture %}\n \n {% assign result = query | shopify %}\n \n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"inventoryItem\": {\n \"variant\": {\n \"product\": {\n \"tags\": {{ inclusion_tags.first | json }}\n }\n }\n }\n }\n }\n {% endcapture %}\n \n {% assign result = result_json | parse_json %}\n {% endif %}\n \n {% assign product_tags = result.data.inventoryItem.variant.product.tags %}\n\n {% for exclusion_tag in exclusion_tags %}\n {% if product_tags contains exclusion_tag %}\n {% assign has_exclusion_tag = true %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% for inclusion_tag in inclusion_tags %}\n {% if product_tags contains inclusion_tag %}\n {% assign has_inclusion_tag = true %}\n {% break %}\n {% endif %} \n {% endfor %}\n\n {% comment %}\n -- exclusion tags have priority over inclusion tags\n {% endcomment %}\n\n {% if has_exclusion_tag %}\n {% log\n message: \"This line item's product has at least one of the configured exclusion tags; skipping\",\n fulfillment_order_line_item: fulfillment_order_line_item,\n product_tags: product_tags,\n exclusion_tags: exclusion_tags\n %}\n {% continue %}\n\n {% elsif inclusion_tags != blank %}\n {% unless has_inclusion_tag %}\n {% log\n message: \"This line item's product does not contain any of the configured inclusion tags; skipping\",\n fulfillment_order_line_item: fulfillment_order_line_item,\n product_tags: product_tags,\n inclusion_tags: inclusion_tags\n %}\n {% continue %}\n {% endunless %}\n {% endif %}\n {% endif %}\n\n {% comment %}\n -- save unfulfilled line items that do not require shipping\n {% endcomment %}\n\n {% if fulfillment_order_line_item.remainingQuantity > 0 %}\n {% assign fulfillment_order_data[\"unfulfilled_line_items\"]\n = fulfillment_order_data[\"unfulfilled_line_items\"]\n | default: array\n | push: fulfillment_order_line_item\n %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- group unfulfilled line items by location for fulfillment\n {% endcomment %}\n\n {% if fulfillment_order_data.unfulfilled_line_items != blank %}\n {% assign fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]\n = fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]\n | default: array\n | push: fulfillment_order_data\n %}\n {% endif %}\n{% endfor %}\n\n{% if wait_until_any_other_shippable_items_are_fulfilled and has_unfulfilled_shippable_items %}\n {% log \"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.\" %}\n {% break %}\n{% endif %}\n\n{% for keyval in fulfillment_orders_by_location %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentCreateV2(\n fulfillment: {\n lineItemsByFulfillmentOrder: [\n {% for fulfillment_order_data in keyval[1] %}\n {\n fulfillmentOrderId: {{ fulfillment_order_data.fulfillment_order_id | json }}\n fulfillmentOrderLineItems: [\n {% for unfulfilled_line_item in fulfillment_order_data.unfulfilled_line_items %}\n {\n id: {{ unfulfilled_line_item.id | json }}\n quantity: {{ unfulfilled_line_item.remainingQuantity }}\n }\n {% endfor %}\n ]\n }\n {% endfor %}\n ]\n notifyCustomer: false\n }\n ) {\n fulfillment {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n{% endfor %}", + "script": "{% assign inclusion_tags = options.include_products_with_any_of_these_tags__array %}\n{% assign exclusion_tags = options.exclude_products_with_any_of_these_tags__array %}\n{% assign wait_until_any_other_shippable_items_are_fulfilled = options.wait_until_any_other_shippable_items_are_fulfilled__boolean %}\n\n{% comment %}\n -- get all open or in progress fulfillment orders\n{% endcomment %}\n\n{% capture query %}\n query {\n order(id: {{ order.admin_graphql_api_id | json }}) {\n id\n fulfillmentOrders(\n first: 10\n query: \"status:open OR status:in_progress\"\n ) {\n nodes {\n id\n assignedLocation {\n location {\n id\n }\n }\n lineItems(first: 30) {\n nodes {\n id\n remainingQuantity\n requiresShipping\n variant {\n product {\n tags\n }\n }\n }\n }\n }\n }\n }\n }\n{% endcapture %}\n\n{% assign result = query | shopify %}\n\n{% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"order\": {\n \"id\": \"gid://shopify/Order/1234567890\",\n \"fulfillmentOrders\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrder/1234567890\",\n \"assignedLocation\": {\n \"location\": {\n \"id\": \"gid://shopify/Location/1234567890\"\n }\n },\n \"lineItems\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrderLineItem/1234567890\",\n \"remainingQuantity\": 1,\n \"requiresShipping\": false,\n \"variant\": {\n \"product\": {\n \"tags\": {{ inclusion_tags.first | json }}\n }\n }\n }\n ]\n }\n }\n ]\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n{% endif %}\n\n{% assign fulfillment_orders = result.data.order.fulfillmentOrders.nodes %}\n\n{% if fulfillment_orders == blank %}\n {% log \"There are no open fulfillment orders to fulfill on this order.\" %}\n {% break %}\n{% endif %}\n\n{% comment %}\n NOTE: fulfillments can only be created for one location at a time, so need to group fulfillment orders by location\n{% endcomment %}\n\n{% assign fulfillment_orders_by_location = hash %}\n{% assign has_unfulfilled_shippable_items = nil %}\n\n{% for fulfillment_order in fulfillment_orders %}\n {% assign fulfillment_order_data = hash %}\n {% assign fulfillment_order_data[\"fulfillment_order_id\"] = fulfillment_order.id %}\n\n {% for fulfillment_order_line_item in fulfillment_order.lineItems.nodes %}\n {% comment %}\n -- skip items that do not require shipping, but set flag if any are unfulfilled\n {% endcomment %}\n\n {% if fulfillment_order_line_item.requiresShipping %}\n {% if fulfillment_order_line_item.remainingQuantity > 0 %}\n {% assign has_unfulfilled_shippable_items = true %}\n {% endif %}\n\n {% continue %}\n {% endif %}\n\n {% if inclusion_tags != blank or exclusion_tags != blank %}\n {% assign has_exclusion_tag = nil %}\n {% assign has_inclusion_tag = nil %}\n\n {% assign product_tags = fulfillment_order_line_item.variant.product.tags %}\n\n {% for exclusion_tag in exclusion_tags %}\n {% if product_tags contains exclusion_tag %}\n {% assign has_exclusion_tag = true %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% for inclusion_tag in inclusion_tags %}\n {% if product_tags contains inclusion_tag %}\n {% assign has_inclusion_tag = true %}\n {% break %}\n {% endif %} \n {% endfor %}\n\n {% comment %}\n -- exclusion tags have priority over inclusion tags\n {% endcomment %}\n\n {% if has_exclusion_tag %}\n {% log\n message: \"This line item's product has at least one of the configured exclusion tags; skipping\",\n fulfillment_order_line_item: fulfillment_order_line_item,\n product_tags: product_tags,\n exclusion_tags: exclusion_tags\n %}\n {% continue %}\n\n {% elsif inclusion_tags != blank %}\n {% unless has_inclusion_tag %}\n {% log\n message: \"This line item's product does not contain any of the configured inclusion tags; skipping\",\n fulfillment_order_line_item: fulfillment_order_line_item,\n product_tags: product_tags,\n inclusion_tags: inclusion_tags\n %}\n {% continue %}\n {% endunless %}\n {% endif %}\n {% endif %}\n\n {% comment %}\n -- save unfulfilled line items that do not require shipping\n {% endcomment %}\n\n {% if fulfillment_order_line_item.remainingQuantity > 0 %}\n {% assign fulfillment_order_data[\"unfulfilled_line_items\"]\n = fulfillment_order_data[\"unfulfilled_line_items\"]\n | default: array\n | push: fulfillment_order_line_item\n %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- group unfulfilled line items by location for fulfillment\n {% endcomment %}\n\n {% if fulfillment_order_data.unfulfilled_line_items != blank %}\n {% assign fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]\n = fulfillment_orders_by_location[fulfillment_order.assignedLocation.location.id]\n | default: array\n | push: fulfillment_order_data\n %}\n {% endif %}\n{% endfor %}\n\n{% if wait_until_any_other_shippable_items_are_fulfilled and has_unfulfilled_shippable_items %}\n {% log \"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.\" %}\n {% break %}\n{% endif %}\n\n{% for keyval in fulfillment_orders_by_location %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentCreate(\n fulfillment: {\n lineItemsByFulfillmentOrder: [\n {% for fulfillment_order_data in keyval[1] %}\n {\n fulfillmentOrderId: {{ fulfillment_order_data.fulfillment_order_id | json }}\n fulfillmentOrderLineItems: [\n {% for unfulfilled_line_item in fulfillment_order_data.unfulfilled_line_items %}\n {\n id: {{ unfulfilled_line_item.id | json }}\n quantity: {{ unfulfilled_line_item.remainingQuantity }}\n }\n {% endfor %}\n ]\n }\n {% endfor %}\n ]\n notifyCustomer: false\n }\n ) {\n fulfillment {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n{% endfor %}\n", "subscriptions": [ "shopify/orders/paid", "mechanic/user/order" diff --git a/tasks/manage-fulfillment-status-using-order-tags.json b/tasks/manage-fulfillment-status-using-order-tags.json index f9f85d46..fe388e10 100644 --- a/tasks/manage-fulfillment-status-using-order-tags.json +++ b/tasks/manage-fulfillment-status-using-order-tags.json @@ -21,7 +21,7 @@ "order_status_javascript": null, "perform_action_runs_in_sequence": false, "preview_event_definitions": [], - "script": "{% assign shipment_tags_and_status = options.shipment_tags_and_status__keyval_required %}\n{% assign attempt_to_fulfill_the_order_before_setting_shipment_status = options.attempt_to_fulfill_the_order_before_setting_shipment_status__boolean %}\n{% assign send_shipment_notifications_to_customer = options.send_shipment_notifications_to_customer__boolean %}\n\n{% capture query %}\n query {\n order(id: {{ order.admin_graphql_api_id | json }}) {\n id\n name\n tags\n displayFulfillmentStatus\n fulfillments(first: 10) {\n id\n status\n displayStatus\n requiresShipping\n location {\n name\n }\n }\n fulfillmentOrders(\n first: 20\n query: \"status:open\"\n ) {\n nodes {\n id\n assignedLocation {\n name\n }\n deliveryMethod {\n methodType\n }\n }\n }\n }\n }\n{% endcapture %}\n\n{% assign result = query | shopify %}\n\n{% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"order\": {\n \"id\": \"gid://shopify/Order/1234567890\",\n \"name\": \"#SAMPLE\",\n \"tags\": {{ shipment_tags_and_status.first.first | json }},\n \"displayFulfillmentStatus\": \"FULFILLED\",\n \"fulfillments\": [\n {\n \"id\": \"gid://shopify/Fulfillment/1234567890\",\n \"status\": \"SUCCESS\",\n \"displayStatus\": null,\n \"requiresShipping\": true,\n \"location\": {\n \"name\": \"Warehouse\"\n }\n }\n ],\n \"fulfillmentOrders\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrder/1234567890\",\n \"assignedLocation\": {\n \"name\": \"Warehouse\"\n },\n \"deliveryMethod\": {\n \"methodType\": \"SHIPPING\"\n }\n }\n ]\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n{% endif %}\n\n{% assign order = result.data.order %}\n{% assign fulfillment_orders = order.fulfillmentOrders.nodes %}\n\n{% for order_tag in order.tags %}\n {% if shipment_tags_and_status[order_tag] != blank %}\n {% assign shipment_status_to_set = shipment_tags_and_status[order_tag] %}\n {% break %}\n {% endif %}\n{% endfor %}\n\n{% unless shipment_status_to_set %}\n {% log \"No shipment status tags found on this order; skipping\" %}\n {% break %}\n{% endunless %}\n\n{% if attempt_to_fulfill_the_order_before_setting_shipment_status %}\n {% if fulfillment_orders == blank %}\n {% log \"This order has no open fulfillment orders to fulfill; proceeding to check/set shipment status.\" %}\n\n {% else %}\n {% comment %}\n Note: fulfillments cannot be created with fulfillment orders at different locations or delivery types; so separate them out\n {% endcomment %}\n\n {% assign fulfillment_order_ids_by_location_and_type = hash %}\n\n {% for fulfillment_order in fulfillment_orders %}\n {% assign location_and_type\n = fulfillment_order.assignedLocation.name\n | append: \"|\"\n | append: fulfillment_order.deliveryMethod.methodType\n %}\n {% assign fulfillment_order_ids_by_location_and_type[location_and_type]\n = fulfillment_order_ids_by_location_and_type[location_and_type]\n | default: array\n | push: fulfillment_order.id\n %}\n {% endfor %}\n\n {% for keyval in fulfillment_order_ids_by_location_and_type %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentCreateV2(\n fulfillment: {\n lineItemsByFulfillmentOrder: [\n {% for fulfillment_order_id in keyval[1] %}\n { fulfillmentOrderId: {{ fulfillment_order_id | json }} }\n {% endfor %}\n ]\n notifyCustomer: {{ send_shipment_notifications_to_customer | json }}\n }\n ) {\n fulfillment {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endfor %}\n\n {% comment %}\n -- break here so the fulfillments are complete for the next task run on order update\n {% endcomment %}\n\n {% unless event.preview %}\n {% break %}\n {% endunless %}\n {% endif %}\n{% endif %}\n\n{% assign fulfillments\n = order.fulfillments\n | where: \"requiresShipping\"\n | where: \"status\", \"SUCCESS\"\n%}\n\n{% if fulfillments == blank %}\n {% log \"There are no successful fulfillments that require shipping available to update on this order; skipping.\" %}\n {% break %}\n{% endif %}\n\n{% log\n shipment_status_to_set: shipment_status_to_set,\n fulfillments: fulfillments\n%}\n\n{% for fulfillment in fulfillments %}\n {% if fulfillment.displayStatus != shipment_status_to_set %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentEventCreate(\n fulfillmentEvent: {\n fulfillmentId: {{ fulfillment.id | json }}\n status: {{ shipment_status_to_set }}\n }\n ) {\n fulfillmentEvent {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n{% endfor %}", + "script": "{% assign shipment_tags_and_status = options.shipment_tags_and_status__keyval_required %}\n{% assign attempt_to_fulfill_the_order_before_setting_shipment_status = options.attempt_to_fulfill_the_order_before_setting_shipment_status__boolean %}\n{% assign send_shipment_notifications_to_customer = options.send_shipment_notifications_to_customer__boolean %}\n\n{% capture query %}\n query {\n order(id: {{ order.admin_graphql_api_id | json }}) {\n id\n name\n tags\n displayFulfillmentStatus\n fulfillments(first: 10) {\n id\n status\n displayStatus\n requiresShipping\n location {\n name\n }\n }\n fulfillmentOrders(\n first: 20\n query: \"status:open\"\n ) {\n nodes {\n id\n assignedLocation {\n name\n }\n deliveryMethod {\n methodType\n }\n }\n }\n }\n }\n{% endcapture %}\n\n{% assign result = query | shopify %}\n\n{% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"order\": {\n \"id\": \"gid://shopify/Order/1234567890\",\n \"name\": \"#SAMPLE\",\n \"tags\": {{ shipment_tags_and_status.first.first | json }},\n \"displayFulfillmentStatus\": \"FULFILLED\",\n \"fulfillments\": [\n {\n \"id\": \"gid://shopify/Fulfillment/1234567890\",\n \"status\": \"SUCCESS\",\n \"displayStatus\": null,\n \"requiresShipping\": true,\n \"location\": {\n \"name\": \"Warehouse\"\n }\n }\n ],\n \"fulfillmentOrders\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/FulfillmentOrder/1234567890\",\n \"assignedLocation\": {\n \"name\": \"Warehouse\"\n },\n \"deliveryMethod\": {\n \"methodType\": \"SHIPPING\"\n }\n }\n ]\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n{% endif %}\n\n{% assign order = result.data.order %}\n{% assign fulfillment_orders = order.fulfillmentOrders.nodes %}\n\n{% for order_tag in order.tags %}\n {% if shipment_tags_and_status[order_tag] != blank %}\n {% assign shipment_status_to_set = shipment_tags_and_status[order_tag] %}\n {% break %}\n {% endif %}\n{% endfor %}\n\n{% unless shipment_status_to_set %}\n {% log \"No shipment status tags found on this order; skipping\" %}\n {% break %}\n{% endunless %}\n\n{% if attempt_to_fulfill_the_order_before_setting_shipment_status %}\n {% if fulfillment_orders == blank %}\n {% log \"This order has no open fulfillment orders to fulfill; proceeding to check/set shipment status.\" %}\n\n {% else %}\n {% comment %}\n Note: fulfillments cannot be created with fulfillment orders at different locations or delivery types; so separate them out\n {% endcomment %}\n\n {% assign fulfillment_order_ids_by_location_and_type = hash %}\n\n {% for fulfillment_order in fulfillment_orders %}\n {% assign location_and_type\n = fulfillment_order.assignedLocation.name\n | append: \"|\"\n | append: fulfillment_order.deliveryMethod.methodType\n %}\n {% assign fulfillment_order_ids_by_location_and_type[location_and_type]\n = fulfillment_order_ids_by_location_and_type[location_and_type]\n | default: array\n | push: fulfillment_order.id\n %}\n {% endfor %}\n\n {% for keyval in fulfillment_order_ids_by_location_and_type %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentCreate(\n fulfillment: {\n lineItemsByFulfillmentOrder: [\n {% for fulfillment_order_id in keyval[1] %}\n { fulfillmentOrderId: {{ fulfillment_order_id | json }} }\n {% endfor %}\n ]\n notifyCustomer: {{ send_shipment_notifications_to_customer | json }}\n }\n ) {\n fulfillment {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endfor %}\n\n {% comment %}\n -- break here so the fulfillments are complete for the next task run on order update\n {% endcomment %}\n\n {% unless event.preview %}\n {% break %}\n {% endunless %}\n {% endif %}\n{% endif %}\n\n{% assign fulfillments\n = order.fulfillments\n | where: \"requiresShipping\"\n | where: \"status\", \"SUCCESS\"\n%}\n\n{% if fulfillments == blank %}\n {% log \"There are no successful fulfillments that require shipping available to update on this order; skipping.\" %}\n {% break %}\n{% endif %}\n\n{% log\n shipment_status_to_set: shipment_status_to_set,\n fulfillments: fulfillments\n%}\n\n{% for fulfillment in fulfillments %}\n {% if fulfillment.displayStatus != shipment_status_to_set %}\n {% action \"shopify\" %}\n mutation {\n fulfillmentEventCreate(\n fulfillmentEvent: {\n fulfillmentId: {{ fulfillment.id | json }}\n status: {{ shipment_status_to_set }}\n }\n ) {\n fulfillmentEvent {\n id\n status\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n{% endfor %}", "subscriptions": [ "shopify/orders/updated" ], diff --git a/tasks/set-product-types-by-title-keywords.json b/tasks/set-product-types-by-title-keywords.json index 4b917605..ca9dc2c1 100644 --- a/tasks/set-product-types-by-title-keywords.json +++ b/tasks/set-product-types-by-title-keywords.json @@ -11,7 +11,7 @@ "order_status_javascript": null, "perform_action_runs_in_sequence": false, "preview_event_definitions": [], - "script": "{% assign product_types_and_keywords = options.product_types_and_keywords__keyval_multiline_required %}\n\n{% comment %}\n -- set preview values for the configuration field that will work with the preview query data\n{% endcomment %}\n\n{% if event.preview %}\n {% capture product_types_and_keywords_json %}\n {\n \"Shirts\": \"shirt\\nshirts\\nt-shirt\\nt-shirts\\ntee\\ntees\"\n }\n {% endcapture %}\n\n {% assign product_types_and_keywords = product_types_and_keywords_json | parse_json %}\n{% endif %}\n\n{% assign cursor = nil %}\n\n{% comment %}\n -- query for all products in the shop (if > 25K products, the \"100\" loop value can be adjusted upward)\n{% endcomment %}\n\n{% for n in (1..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n title\n productType\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"products\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"title\": \"Super soft tees\",\n \"productType\": \"Shoes\"\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% comment %}\n -- process each product in this result before querying for more products\n {% endcomment %}\n\n {% for product in result.data.products.nodes %}\n {% comment %}\n -- use downcase on product title and configured keywords since the \"contains\" operator is case-sensitive\n -- split on spaces in title to create an array of words to be compared to keywords\n {% endcomment %}\n\n {% assign product_title_words_downcase\n = product.title\n | downcase\n | split: \" \"\n %}\n {% assign product_type_to_set = nil %}\n\n {% for keyval in product_types_and_keywords %}\n {% assign product_type = keyval.first %}\n {% assign keywords = keyval.last | split: newline %}\n\n {% for keyword in keywords %}\n {% if keyword == blank or keyword == \"\" %}\n {% comment %}\n -- protect against accidental empty keyword lines in the task config\n {% endcomment %}\n\n {% continue %}\n {% endif %}\n\n {% assign keyword_downcase = keyword | downcase %}\n\n {% if product_title_words_downcase contains keyword_downcase %}\n {% assign product_type_to_set = product_type %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% if product_type_to_set != blank %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- set a new product type if a keyword match was made and the product does not already have that type\n {% endcomment %}\n\n {% if product_type_to_set != blank and product_type_to_set != product.productType %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n productType: {{ product_type_to_set | json }}\n }\n ) {\n product {\n title\n productType\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n {% endfor %}\n\n {% if result.data.products.pageInfo.hasNextPage %}\n {% assign cursor = result.data.products.pageInfo.endCursor %}\n {% else %}\n {% break %}\n {% endif %}\n{% endfor %}\n", + "script": "{% assign product_types_and_keywords = options.product_types_and_keywords__keyval_multiline_required %}\n\n{% comment %}\n -- set preview values for the configuration field that will work with the preview query data\n{% endcomment %}\n\n{% if event.preview %}\n {% capture product_types_and_keywords_json %}\n {\n \"Shirts\": \"shirt\\nshirts\\nt-shirt\\nt-shirts\\ntee\\ntees\"\n }\n {% endcapture %}\n\n {% assign product_types_and_keywords = product_types_and_keywords_json | parse_json %}\n{% endif %}\n\n{% assign cursor = nil %}\n\n{% comment %}\n -- query for all products in the shop (if > 25K products, the \"100\" loop value can be adjusted upward)\n{% endcomment %}\n\n{% for n in (1..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n title\n productType\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"products\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"title\": \"Super soft tees\",\n \"productType\": \"Shoes\"\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% comment %}\n -- process each product in this result before querying for more products\n {% endcomment %}\n\n {% for product in result.data.products.nodes %}\n {% comment %}\n -- use downcase on product title and configured keywords since the \"contains\" operator is case-sensitive\n -- split on spaces in title to create an array of words to be compared to keywords\n {% endcomment %}\n\n {% assign product_title_words_downcase\n = product.title\n | downcase\n | split: \" \"\n %}\n {% assign product_type_to_set = nil %}\n\n {% for keyval in product_types_and_keywords %}\n {% assign product_type = keyval.first %}\n {% assign keywords = keyval.last | split: newline %}\n\n {% for keyword in keywords %}\n {% if keyword == blank or keyword == \"\" %}\n {% comment %}\n -- protect against accidental empty keyword lines in the task config\n {% endcomment %}\n\n {% continue %}\n {% endif %}\n\n {% assign keyword_downcase = keyword | downcase %}\n\n {% if product_title_words_downcase contains keyword_downcase %}\n {% assign product_type_to_set = product_type %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% if product_type_to_set != blank %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- set a new product type if a keyword match was made and the product does not already have that type\n {% endcomment %}\n\n {% if product_type_to_set != blank and product_type_to_set != product.productType %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n product: {\n id: {{ product.id | json }}\n productType: {{ product_type_to_set | json }}\n }\n ) {\n product {\n title\n productType\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n {% endfor %}\n\n {% if result.data.products.pageInfo.hasNextPage %}\n {% assign cursor = result.data.products.pageInfo.endCursor %}\n {% else %}\n {% break %}\n {% endif %}\n{% endfor %}\n", "subscriptions": [ "mechanic/user/trigger" ], diff --git a/tasks/update-product-description-when-out-of-stock.json b/tasks/update-product-description-when-out-of-stock.json index 31c709ba..ceb817ea 100644 --- a/tasks/update-product-description-when-out-of-stock.json +++ b/tasks/update-product-description-when-out-of-stock.json @@ -10,7 +10,7 @@ }, "order_status_javascript": null, "perform_action_runs_in_sequence": false, - "script": "{% comment %}\n Options order:\n\n {{ options.only_modify_products_with_this_tag }}\n {{ options.add_this_html_to_product_description__multiline_required }}\n {{ options.add_the_html_to_end_of_product_description_instead_of_beginning__boolean }}\n{% endcomment %}\n\n{% assign products_to_check = array %}\n\n{% if event.topic == \"shopify/inventory_levels/update\" %}\n {% capture query %}\n query {\n inventoryLevel(\n id: {{ inventory_level.admin_graphql_api_id | json }}\n ) {\n item {\n variant {\n product {\n id\n descriptionHtml\n tags\n totalInventory\n }\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"inventoryLevel\": {\n \"item\": {\n \"variant\": {\n \"product\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"descriptionHtml\": \"

The best widget on the market!

\",\n \"tags\": {{ options.only_modify_products_with_this_tag | json }},\n \"totalInventory\": 0\n }\n }\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign product = result.data.inventoryLevel.item.variant.product %}\n\n {% if options.only_modify_products_with_this_tag != blank %}\n {% if product.tags contains options.only_modify_products_with_this_tag %}\n {% assign products_to_check[0] = product %}\n {% else %}\n {% log message: \"This product does not have the tag being filtered by; skipping\", tag_required: options.only_modify_products_with_this_tag, product_tags: product.tags %}\n {% endif %}\n {% else %}\n {% assign products_to_check[0] = product %}\n {% endif %}\n\n{% elsif event.topic == \"mechanic/user/trigger\" %}\n {% assign cursor = nil %}\n {% assign products_query = nil %}\n\n {% if options.only_modify_products_with_this_tag != blank %}\n {% assign products_query = options.only_modify_products_with_this_tag | json | prepend: \"tag:\" %}\n {% endif %}\n\n {% for n in (0..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n query: {{ products_query | json }}\n ) {\n pageInfo {\n hasNextPage\n }\n edges {\n cursor\n node {\n id\n descriptionHtml\n tracksInventory\n totalInventory\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"products\": {\n \"edges\": [\n {\n \"node\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"descriptionHtml\": \"

The best widget on the market!

\",\n \"tracksInventory\": true,\n \"totalInventory\": 0\n }\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign products = result.data.products.edges | map: \"node\" | where: \"tracksInventory\" %}\n {% assign products_to_check = products_to_check | concat: products %}\n\n {% if result.data.products.pageInfo.hasNextPage %}\n {% assign cursor = result.data.products.edges.last.cursor %}\n {% else %}\n {% break %}\n {% endif %}\n {% endfor %}\n{% endif %}\n\n{% if products_to_check != blank %}\n {% for product in products_to_check %}\n {% assign cache_key = product.id | append: \"__description_to_restore\" %}\n {% assign cached_description_html = cache[cache_key] %}\n\n {% if product.totalInventory > 0 %}\n {% if cached_description_html != blank %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n descriptionHtml: {{ cached_description_html | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n\n {% action \"cache\", \"del\", cache_key %}\n {% endif %}\n\n {% else %}\n {% unless product.descriptionHtml contains options.add_this_html_to_product_description__multiline_required %}\n {% if options.add_the_html_to_end_of_product_description_instead_of_beginning__boolean %}\n {% assign updated_description_html = product.descriptionHtml | append: options.add_this_html_to_product_description__multiline_required %}\n {% else %}\n {% assign updated_description_html = product.descriptionHtml | prepend: options.add_this_html_to_product_description__multiline_required %}\n {% endif %}\n\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n descriptionHtml: {{ updated_description_html | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n\n {% action \"cache\", \"set\", cache_key, product.descriptionHtml %}\n {% endunless %}\n {% endif %}\n {% endfor %}\n{% endif %}", + "script": "{% comment %}\n Options order:\n\n {{ options.only_modify_products_with_this_tag }}\n {{ options.add_this_html_to_product_description__multiline_required }}\n {{ options.add_the_html_to_end_of_product_description_instead_of_beginning__boolean }}\n{% endcomment %}\n\n{% assign products_to_check = array %}\n\n{% if event.topic == \"shopify/inventory_levels/update\" %}\n {% capture query %}\n query {\n inventoryLevel(\n id: {{ inventory_level.admin_graphql_api_id | json }}\n ) {\n item {\n variant {\n product {\n id\n descriptionHtml\n tags\n totalInventory\n }\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"inventoryLevel\": {\n \"item\": {\n \"variant\": {\n \"product\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"descriptionHtml\": \"

The best widget on the market!

\",\n \"tags\": {{ options.only_modify_products_with_this_tag | json }},\n \"totalInventory\": 0\n }\n }\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign product = result.data.inventoryLevel.item.variant.product %}\n\n {% if options.only_modify_products_with_this_tag != blank %}\n {% if product.tags contains options.only_modify_products_with_this_tag %}\n {% assign products_to_check[0] = product %}\n {% else %}\n {% log message: \"This product does not have the tag being filtered by; skipping\", tag_required: options.only_modify_products_with_this_tag, product_tags: product.tags %}\n {% endif %}\n {% else %}\n {% assign products_to_check[0] = product %}\n {% endif %}\n\n{% elsif event.topic == \"mechanic/user/trigger\" %}\n {% assign cursor = nil %}\n {% assign products_query = nil %}\n\n {% if options.only_modify_products_with_this_tag != blank %}\n {% assign products_query = options.only_modify_products_with_this_tag | json | prepend: \"tag:\" %}\n {% endif %}\n\n {% for n in (0..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n query: {{ products_query | json }}\n ) {\n pageInfo {\n hasNextPage\n }\n edges {\n cursor\n node {\n id\n descriptionHtml\n tracksInventory\n totalInventory\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"products\": {\n \"edges\": [\n {\n \"node\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"descriptionHtml\": \"

The best widget on the market!

\",\n \"tracksInventory\": true,\n \"totalInventory\": 0\n }\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign products = result.data.products.edges | map: \"node\" | where: \"tracksInventory\" %}\n {% assign products_to_check = products_to_check | concat: products %}\n\n {% if result.data.products.pageInfo.hasNextPage %}\n {% assign cursor = result.data.products.edges.last.cursor %}\n {% else %}\n {% break %}\n {% endif %}\n {% endfor %}\n{% endif %}\n\n{% if products_to_check != blank %}\n {% for product in products_to_check %}\n {% assign cache_key = product.id | append: \"__description_to_restore\" %}\n {% assign cached_description_html = cache[cache_key] %}\n\n {% if product.totalInventory > 0 %}\n {% if cached_description_html != blank %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n product: {\n id: {{ product.id | json }}\n descriptionHtml: {{ cached_description_html | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n\n {% action \"cache\", \"del\", cache_key %}\n {% endif %}\n\n {% else %}\n {% unless product.descriptionHtml contains options.add_this_html_to_product_description__multiline_required %}\n {% if options.add_the_html_to_end_of_product_description_instead_of_beginning__boolean %}\n {% assign updated_description_html = product.descriptionHtml | append: options.add_this_html_to_product_description__multiline_required %}\n {% else %}\n {% assign updated_description_html = product.descriptionHtml | prepend: options.add_this_html_to_product_description__multiline_required %}\n {% endif %}\n\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n product: {\n id: {{ product.id | json }}\n descriptionHtml: {{ updated_description_html | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n\n {% action \"cache\", \"set\", cache_key, product.descriptionHtml %}\n {% endunless %}\n {% endif %}\n {% endfor %}\n{% endif %}", "subscriptions": [ "shopify/inventory_levels/update", "mechanic/user/trigger"