diff --git a/docs/README.md b/docs/README.md index d8def18a..b25cb24a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -145,6 +145,7 @@ This directory is built automatically. Each task's documentation is generated fr * [Auto-tag products by their options](./auto-tag-products-by-their-options) * [Auto-tag products by their publish date](./auto-tag-products-by-their-publish-date) * [Auto-tag products in a manual collection](./auto-tag-products-in-a-manual-collection) +* [Auto-tag products marked "Continue selling when out of stock"](./auto-tag-products-marked-continue-selling-when-out-of-stock) * [Auto-tag products that are missing costs](./auto-tag-products-that-are-missing-costs) * [Auto-tag products that have a "compare at" price](./auto-tag-products-that-have-a-compare-at-price) * [Auto-tag products that meet a sales threshold](./auto-tag-products-that-meet-a-sales-threshold) @@ -509,6 +510,7 @@ This directory is built automatically. Each task's documentation is generated fr * [Auto-tag products by their options](./auto-tag-products-by-their-options) * [Auto-tag products by their publish date](./auto-tag-products-by-their-publish-date) * [Auto-tag products in a manual collection](./auto-tag-products-in-a-manual-collection) +* [Auto-tag products marked "Continue selling when out of stock"](./auto-tag-products-marked-continue-selling-when-out-of-stock) * [Auto-tag products that are missing costs](./auto-tag-products-that-are-missing-costs) * [Auto-tag products that have a "compare at" price](./auto-tag-products-that-have-a-compare-at-price) * [Auto-tag products that meet a sales threshold](./auto-tag-products-that-meet-a-sales-threshold) @@ -628,6 +630,7 @@ This directory is built automatically. Each task's documentation is generated fr * [Auto-sort collections by inventory levels](./auto-sort-collections-by-inventory-levels) * [Auto-tag orders by product collections](./auto-tag-orders-by-product-collections) * [Auto-tag products in a manual collection](./auto-tag-products-in-a-manual-collection) +* [Auto-tag products marked "Continue selling when out of stock"](./auto-tag-products-marked-continue-selling-when-out-of-stock) * [Delete the oldest x products from a specific collection](./delete-the-oldest-x-products-from-a-specific-collection) * [Maintain a collection of new products](./maintain-a-collection-of-new-products) * [Maintain a collection of recently purchased products](./maintain-a-collection-of-recently-purchased-products) @@ -1007,6 +1010,7 @@ This directory is built automatically. Each task's documentation is generated fr * [Auto-connect new products to all locations](./auto-connect-new-products-to-all-locations) * [Auto-sort collections by inventory levels](./auto-sort-collections-by-inventory-levels) * [Auto-tag out-of-stock products](./auto-tag-out-of-stock-products) +* [Auto-tag products marked "Continue selling when out of stock"](./auto-tag-products-marked-continue-selling-when-out-of-stock) * [Auto-tag products with incoming inventory](./auto-tag-products-with-incoming-inventory) * [Backup inventory to SFTP in Shopify CSV format](./backup-scheduled-inventory-exports-in-shopifys-csv-format) * [Create a product inventory CSV feed](./create-a-product-inventory-feed) @@ -1384,6 +1388,7 @@ This directory is built automatically. Each task's documentation is generated fr * [Auto-tag products by their options](./auto-tag-products-by-their-options) * [Auto-tag products by their publish date](./auto-tag-products-by-their-publish-date) * [Auto-tag products in a manual collection](./auto-tag-products-in-a-manual-collection) +* [Auto-tag products marked "Continue selling when out of stock"](./auto-tag-products-marked-continue-selling-when-out-of-stock) * [Auto-tag products that are missing costs](./auto-tag-products-that-are-missing-costs) * [Auto-tag products that have a "compare at" price](./auto-tag-products-that-have-a-compare-at-price) * [Auto-tag products that meet a sales threshold](./auto-tag-products-that-meet-a-sales-threshold) diff --git a/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/README.md b/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/README.md new file mode 100644 index 00000000..6c4a2034 --- /dev/null +++ b/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/README.md @@ -0,0 +1,47 @@ +# Auto-tag products marked "Continue selling when out of stock" + +Tags: Auto-Tag, Collections, Inventory, Products + +This task will automatically tag any products which have variants set to "continue selling when out of stock." This allows you to set up a product collection that includes "available" products and not just products with inventory greater than zero. Configure the task with a tag to apply, and Mechanic will take care of applying and removing the tag as appropriate. + +* View in the task library: [tasks.mechanic.dev/auto-tag-products-marked-continue-selling-when-out-of-stock](https://tasks.mechanic.dev/auto-tag-products-marked-continue-selling-when-out-of-stock) +* Task JSON, for direct import: [task.json](../../tasks/auto-tag-products-marked-continue-selling-when-out-of-stock.json) +* Preview task code: [script.liquid](./script.liquid) + +## Default options + +```json +{ + "tag_for_continue_selling_products__required": "continue-selling" +} +``` + +[Learn about task options in Mechanic](https://learn.mechanic.dev/core/tasks/options) + +## Subscriptions + +```liquid +shopify/products/create +shopify/products/update +mechanic/user/trigger +``` + +[Learn about event subscriptions in Mechanic](https://learn.mechanic.dev/core/tasks/subscriptions) + +## Documentation + +This task will automatically tag any products which have variants set to "continue selling when out of stock." This allows you to set up a product collection that includes "available" products and not just products with inventory greater than zero. Configure the task with a tag to apply, and Mechanic will take care of applying and removing the tag as appropriate. + +Run this task manually to update your entire product catalog at once. + +## Installing this task + +Find this task [in the library at tasks.mechanic.dev](https://tasks.mechanic.dev/auto-tag-products-marked-continue-selling-when-out-of-stock), and use the "Try this task" button. Or, import [this task's JSON export](../../tasks/auto-tag-products-marked-continue-selling-when-out-of-stock.json) – see [Importing and exporting tasks](https://learn.mechanic.dev/core/tasks/import-and-export) to learn how imports work. + +## Contributions + +Found a bug? Got an improvement to add? Start here: [../../CONTRIBUTING.md](../../CONTRIBUTING.md). + +## Task requests + +Submit your [task requests](https://mechanic.canny.io/task-requests) for consideration by the Mechanic community, and they may be chosen for development and inclusion in the [task library](https://tasks.mechanic.dev/)! diff --git a/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/script.liquid b/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/script.liquid new file mode 100644 index 00000000..85bdeb1f --- /dev/null +++ b/docs/auto-tag-products-marked-continue-selling-when-out-of-stock/script.liquid @@ -0,0 +1,192 @@ +{% assign tag_for_continue_selling_products = options.tag_for_continue_selling_products__required %} + +{% comment %} + -- for product create/update, filter the products query with the product ID that caused the event +{% endcomment %} + +{% if event.topic contains "shopify/products/" %} + {% assign search_query = product.id | prepend: "id:" %} +{% endif %} + +{% comment %} + -- query product(s) in the shop; variants will be queried separately as needed to support up to 2K variants +{% endcomment %} + +{% assign cursor = nil %} +{% assign products = array %} + +{% for n in (1..100) %} + {% capture query %} + query { + products( + first: 250 + after: {{ cursor | json }} + query: {{ search_query | json }} + ) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + tags + } + } + } + {% endcapture %} + + {% assign result = query | shopify %} + + {% if event.preview %} + {% capture result_json %} + { + "data": { + "products": { + "nodes": [ + { + "id": "gid://shopify/Product/1234567890" + } + ] + } + } + } + {% endcapture %} + + {% assign result = result_json | parse_json %} + {% endif %} + + {% assign products = products | concat: result.data.products.nodes %} + + {% if result.data.products.pageInfo.hasNextPage %} + {% assign cursor = result.data.products.pageInfo.endCursor %} + {% else %} + {% break %} + {% endif %} +{% endfor %} + +{% comment %} + -- process each product by querying as many variants as needed to determine whether the product should be tagged +{% endcomment %} + +{% for product in products %} + {% assign product_set_to_continue_selling = nil %} + {% assign cursor = nil %} + + {% for n in (1..8) %} + {% capture query %} + query { + product(id: {{ product.id | json }}) { + variants( + first: 250 + after: {{ cursor | json }} + ) { + pageInfo { + hasNextPage + endCursor + } + nodes { + inventoryPolicy + } + } + } + } + {% endcapture %} + {% action "echo" query %} + + {% assign result = query | shopify %} + + {% if event.preview %} + {% capture result_json %} + { + "data": { + "product": { + "variants": { + "nodes": [ + { + "inventoryPolicy": "CONTINUE" + } + ] + } + } + } + } + {% endcapture %} + + {% assign result = result_json | parse_json %} + {% endif %} + + {% assign variants = result.data.product.variants.nodes %} + + {% comment %} + -- check batch of variants to see if any is marked "continue selling" + {% endcomment %} + + {% for variant in variants %} + {% if variant.inventoryPolicy == "CONTINUE" %} + {% assign product_set_to_continue_selling = true %} + {% break %} + {% endif %} + {% endfor %} + + {% comment %} + -- only query for more variants if the product has not yet qualified as "continue selling" AND if there are more variants + {% endcomment %} + + {% if product_set_to_continue_selling %} + {% break %} + {% elsif result.data.product.variants.pageInfo.hasNextPage %} + {% assign cursor = result.data.product.variants.pageInfo.endCursor %} + {% else %} + {% break %} + {% endif %} + {% endfor %} + + {% comment %} + -- adjust tags on product as needed + {% endcomment %} + + {% assign tag_to_add = nil %} + {% assign tag_to_remove = nil %} + + {% if product_set_to_continue_selling %} + {% unless product.tags contains tag_for_continue_selling_products %} + {% assign tag_to_add = tag_for_continue_selling_products %} + {% endunless %} + + {% else %} + {% if product.tags contains tag_for_continue_selling_products %} + {% assign tag_to_remove = tag_for_continue_selling_products %} + {% endif %} + + {% endif %} + + {% if tag_to_add or tag_to_remove %} + {% action "shopify" %} + mutation { + {% if tag_to_add %} + tagsAdd( + id: {{ product.id | json }} + tags: {{ tag_to_add | json }} + ) { + userErrors { + field + message + } + } + {% endif %} + + {% if tag_to_remove %} + tagsRemove( + id: {{ product.id | json }} + tags: {{ tag_to_remove | json }} + ) { + userErrors { + field + message + } + } + {% endif %} + } + {% endaction %} + {% endif %} +{% endfor %} diff --git a/tasks/auto-tag-products-marked-continue-selling-when-out-of-stock.json b/tasks/auto-tag-products-marked-continue-selling-when-out-of-stock.json new file mode 100644 index 00000000..da3af4be --- /dev/null +++ b/tasks/auto-tag-products-marked-continue-selling-when-out-of-stock.json @@ -0,0 +1,24 @@ +{ + "docs": "This task will automatically tag any products which have variants set to \"continue selling when out of stock.\" This allows you to set up a product collection that includes \"available\" products and not just products with inventory greater than zero. Configure the task with a tag to apply, and Mechanic will take care of applying and removing the tag as appropriate.\n\nRun this task manually to update your entire product catalog at once.", + "halt_action_run_sequence_on_error": false, + "name": "Auto-tag products marked \"Continue selling when out of stock\"", + "online_store_javascript": null, + "options": { + "tag_for_continue_selling_products__required": "continue-selling" + }, + "perform_action_runs_in_sequence": false, + "preview_event_definitions": [], + "script": "{% assign tag_for_continue_selling_products = options.tag_for_continue_selling_products__required %}\n\n{% comment %}\n -- for product create/update, filter the products query with the product ID that caused the event\n{% endcomment %}\n\n{% if event.topic contains \"shopify/products/\" %}\n {% assign search_query = product.id | prepend: \"id:\" %}\n{% endif %}\n\n{% comment %}\n -- query product(s) in the shop; variants will be queried separately as needed to support up to 2K variants\n{% endcomment %}\n\n{% assign cursor = nil %}\n{% assign products = array %}\n\n{% for n in (1..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n query: {{ search_query | json }}\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n tags\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 }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign products = products | concat: result.data.products.nodes %}\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\n{% comment %}\n -- process each product by querying as many variants as needed to determine whether the product should be tagged\n{% endcomment %}\n\n{% for product in products %}\n {% assign product_set_to_continue_selling = nil %}\n {% assign cursor = nil %}\n\n {% for n in (1..8) %}\n {% capture query %}\n query {\n product(id: {{ product.id | json }}) {\n variants(\n first: 250\n after: {{ cursor | json }}\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n inventoryPolicy\n }\n }\n }\n }\n {% endcapture %}\n {% action \"echo\" query %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"product\": {\n \"variants\": {\n \"nodes\": [\n {\n \"inventoryPolicy\": \"CONTINUE\"\n }\n ]\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign variants = result.data.product.variants.nodes %}\n\n {% comment %}\n -- check batch of variants to see if any is marked \"continue selling\"\n {% endcomment %}\n\n {% for variant in variants %}\n {% if variant.inventoryPolicy == \"CONTINUE\" %}\n {% assign product_set_to_continue_selling = true %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- only query for more variants if the product has not yet qualified as \"continue selling\" AND if there are more variants\n {% endcomment %}\n\n {% if product_set_to_continue_selling %}\n {% break %}\n {% elsif result.data.product.variants.pageInfo.hasNextPage %}\n {% assign cursor = result.data.product.variants.pageInfo.endCursor %}\n {% else %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- adjust tags on product as needed\n {% endcomment %}\n\n {% assign tag_to_add = nil %}\n {% assign tag_to_remove = nil %}\n\n {% if product_set_to_continue_selling %}\n {% unless product.tags contains tag_for_continue_selling_products %}\n {% assign tag_to_add = tag_for_continue_selling_products %}\n {% endunless %}\n\n {% else %}\n {% if product.tags contains tag_for_continue_selling_products %}\n {% assign tag_to_remove = tag_for_continue_selling_products %}\n {% endif %}\n\n {% endif %}\n\n {% if tag_to_add or tag_to_remove %}\n {% action \"shopify\" %}\n mutation {\n {% if tag_to_add %}\n tagsAdd(\n id: {{ product.id | json }}\n tags: {{ tag_to_add | json }}\n ) {\n userErrors {\n field\n message\n }\n }\n {% endif %}\n\n {% if tag_to_remove %}\n tagsRemove(\n id: {{ product.id | json }}\n tags: {{ tag_to_remove | json }}\n ) {\n userErrors {\n field\n message\n }\n }\n {% endif %}\n }\n {% endaction %}\n {% endif %}\n{% endfor %}\n", + "subscriptions": [ + "shopify/products/create", + "shopify/products/update", + "mechanic/user/trigger" + ], + "subscriptions_template": "shopify/products/create\nshopify/products/update\nmechanic/user/trigger", + "tags": [ + "Auto-Tag", + "Collections", + "Inventory", + "Products" + ] +}