forked from lightward/mechanic-tasks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunpublish-products-that-have-been-out-of-stock-for-x-days.json
27 lines (27 loc) · 13.4 KB
/
unpublish-products-that-have-been-out-of-stock-for-x-days.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"docs": "This task monitors for inventory changes, and records the time when a product's inventory falls to 0 or less. Then, on an hourly basis, the task will unpublish any products with a recorded out-of-stock time of at least x days ago.\n\nThis task watches for inventory updates. When a product's total inventory becomes 0 or less, the current time will be recorded for that product. Then, on an hourly basis, this task will unpublish any products with a recorded out-of-stock time of at least x days ago.\r\n\r\nNotes:\r\n\r\n* The first time the hourly scan runs, it may encounter products that were already out of stock, before this task was installed. For those products, the _current_ time will be recorded as their out-of-stock time. This means that these products will wait for another x days before being automatically unpublished.\r\n* If a product that this task unpublishes is manually published, and its total inventory is still 0 or less, this task will unpublish it again during its next hourly scan.\r\n* This task includes a test mode. Enable it to have the task report what it _would_ do, if test mode was disabled.",
"halt_action_run_sequence_on_error": false,
"name": "Unpublish products that have been out of stock for x days",
"online_store_javascript": null,
"options": {
"number_of_days_to_wait_before_unpublishing__number_required": null,
"sales_channel_names__required_array": [
"Online Store"
],
"test_mode__boolean": true,
"only_include_products_matching_this_search_query": null
},
"order_status_javascript": null,
"perform_action_runs_in_sequence": false,
"script": "{% comment %}\n Preferred option order:\n\n {{ options.number_of_days_to_wait_before_unpublishing__number_required }}\n {{ options.sales_channel_names__required_array }}\n {{ options.only_include_products_matching_this_search_query }}\n {{ options.test_mode__boolean }}\n{% endcomment %}\n\n{% comment %}\n 1. Watch for updates to inventory levels.\n 2. Look up the associated product, when an update comes in.\n 3. If the product's total inventory is 0, record the current time in a metafield. If the\n total inventory is not 0, delete that metafield, if it exists.\n 4. Scan all products, on a schedule, retrieving each product's out-of-stock time metafield.\n For products found with a total inventory of 0, with a recorded time that's at least a\n configurable distance in days from the current time, unpublish the product.\n{% endcomment %}\n\n{% assign time_format = \"%FT%T%z\" %}\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 id\n item {\n variant {\n product {\n id\n title\n totalInventory\n metafield(\n namespace: \"mechanic\"\n key: \"out_of_stock_at\"\n ) {\n id\n value\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 \"inventoryLevel\": {\n \"id\": \"gid://shopify/InventoryLevel/1234567890?inventory_item_id=1234567890\",\n \"item\": {\n \"variant\": {\n \"product\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"title\": \"Short sleeve t-shirt\",\n \"totalInventory\": 0,\n \"metafield\": null\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 product.totalInventory <= 0 and product.metafield == nil %}\n {% if options.test_mode__boolean %}\n {% action \"echo\" %}\n {% capture message %}Product {{ product.title | json }} ({{ product.id }}) is out of stock. Its out-of-stock time should be recorded.{% endcapture %}\n {{ message | json }}\n {% endaction %}\n {% else %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n metafields: [\n {\n namespace: \"mechanic\"\n key: \"out_of_stock_at\"\n type: \"date_time\"\n value: {{ \"now\" | date: time_format | json }}\n }\n ]\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n {% elsif product.totalInventory > 0 and product.metafield != nil %}\n {% if options.test_mode__boolean %}\n {% action \"echo\" %}\n {% capture message %}Product {{ product.title | json }} ({{ product.id }}) is back in stock. Its out-of-stock time should be cleared.{% endcapture %}\n {{ message | json }}\n {% endaction %}\n {% else %}\n {% action \"shopify\" %}\n mutation {\n metafieldDelete(\n input: {\n id: {{ product.metafield.id | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n {% endif %}\n{% elsif event.topic contains \"mechanic/scheduler/\" or event.topic == \"mechanic/user/trigger\" %}\n {% assign now_s = \"now\" | date: \"%s\" | times: 1 %}\n {% assign minimum_out_of_stock_at_distance_s = options.number_of_days_to_wait_before_unpublishing__number_required | times: 24 | times: 60 | times: 60 %}\n\n {% capture query %}\n query {\n publications(first: 250) {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% assign publications = array %}\n\n {% for publication_edge in result.data.publications.edges %}\n {% if options.sales_channel_names__required_array contains publication_edge.node.name %}\n {% assign publications[publications.size] = publication_edge.node %}\n {% endif %}\n {% endfor%}\n\n {% if event.preview %}\n {% assign publications[0] = hash %}\n {% assign publications[0][\"id\"] = \"gid://shopify/Publication/1234567890\" %}\n {% elsif publications.size != options.sales_channel_names__required_array.size %}\n {% log publications_named: options.sales_channel_names__required_array, publications_available: result.data.publications.edges, publications_matched: publications %}\n {% error \"Unable to find all named publications. Double-check your task configuration.\" %}\n {% endif %}\n\n {% assign products_query = \"inventory_total:<=0\" %}\n\n {% if options.only_include_products_matching_this_search_query != blank %}\n {% assign products_query = products_query | append: \" AND (\" | append: options.only_include_products_matching_this_search_query | append: \")\" %}\n {% log products_query: products_query %}\n {% endif %}\n\n {% assign cursor = nil %}\n\n {% for n in (0..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n query: {{ products_query | json }}\n after: {{ cursor | json }}\n ) {\n pageInfo {\n hasNextPage\n }\n edges {\n cursor\n node {\n id\n title\n totalInventory\n {% for publication in publications %}\n publishedOnPublication{{ forloop.index }}: publishedOnPublication(publicationId: {{ publication.id | json }})\n {% endfor %}\n metafield(\n namespace: \"mechanic\"\n key: \"out_of_stock_at\"\n ) {\n id\n value\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 \"products\": {\n \"pageInfo\": {\n \"hasNextPage\": false\n },\n \"edges\": [\n {\n \"cursor\": \"eyJsYXN0X2lkIjo0MzQzOTQxNDMxMzMxLCJsYXN0X3ZhbHVlIjo0MzQzOTQxNDMxMzMxfQ==\",\n \"node\": {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"title\": \"Short sleeve t-shirt\",\n \"totalInventory\": 0,\n {% for publication in publications %}\n {{ \"publishedOnPublication\" | append: forloop.index | json }}: true,\n {% endfor %}\n \"metafield\": {\n \"value\": {{ now_s | minus: minimum_out_of_stock_at_distance_s | minus: 1 | append: \"\" | json }}\n }\n }\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% for product_edge in result.data.products.edges %}\n {% assign product = product_edge.node %}\n\n {% if product.metafield == nil %}\n {% log %}\n {% capture message %}Product {{ product.id }} is out of stock, but its out-of-stock time was not recorded. This product will not be unpublished now, but its out-of-stock time will be set to the current time.{% endcapture %}\n {{ message | json }}\n {% endlog %}\n\n {% unless options.test_mode__boolean %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n metafields: [\n {\n namespace: \"mechanic\"\n key: \"out_of_stock_at\"\n type: \"date_time\"\n value: {{ \"now\" | date: time_format | json }}\n }\n ]\n }\n ) {\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endunless %}\n\n {% continue %}\n {% endif %}\n\n {% assign out_of_stock_at_s = product.metafield.value | date: \"%s\" | times: 1 %}\n {% assign out_of_stock_at_distance_s = now_s | minus: out_of_stock_at_s %}\n\n {% assign unpublishings = array %}\n\n {% for publication in publications %}\n {% assign key = \"publishedOnPublication\" | append: forloop.index %}\n {% if product[key] %}\n {% assign unpublishing = array %}\n {% assign unpublishing[0] = product.id %}\n {% assign unpublishing[1] = publication.id %}\n {% assign unpublishings[unpublishings.size] = unpublishing %}\n {% endif %}\n {% endfor %}\n\n {% if unpublishings == empty %}\n {% log %}\n {% capture message %}Product {{ product.title | json }} ({{ product.id }}) has been out of stock for {{ out_of_stock_at_distance_s | divided_by: 60 | divided_by: 60 | divided_by: 24 | round: 2 }} day(s), but is not published - nothing to do.{% endcapture %}\n {{ message | json }}\n {% endlog %}\n {% continue %}\n {% endif %}\n\n {% if out_of_stock_at_distance_s < minimum_out_of_stock_at_distance_s %}\n {% log %}\n {% capture message %}Product {{ product.title | json }} ({{ product.id }}) is out of stock, and is published, but has only been out of stock for {{ out_of_stock_at_distance_s | divided_by: 60 | divided_by: 60 | divided_by: 24 | round: 2 }} day(s).{% endcapture %}\n {{ message | json }}\n {% endlog %}\n {% continue %}\n {% endif %}\n\n {% if options.test_mode__boolean %}\n {% action \"echo\" %}\n {% capture message %}Product {{ product.title | json }} ({{ product.id }}) is out of stock, and has been out of stock for {{ out_of_stock_at_distance_s | divided_by: 60 | divided_by: 60 | divided_by: 24 | round: 2 }} day(s). It should be unpublished.{% endcapture %}\n {{ message | json }}\n {% endaction %}\n {% else %}\n {% action \"shopify\" %}\n mutation {\n {% for unpublishing in unpublishings %}\n publishableUnpublish{{ forloop.index }}: publishableUnpublish(\n id: {{ unpublishing[0] | json }}\n input: {\n publicationId: {{ unpublishing[1] | json }}\n }\n ) {\n userErrors {\n field\n message\n }\n }\n {% endfor %}\n }\n {% endaction %}\n {% endif %}\n {% endfor %}\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 %}",
"subscriptions": [
"shopify/inventory_levels/update",
"mechanic/scheduler/hourly"
],
"subscriptions_template": "shopify/inventory_levels/update\nmechanic/scheduler/hourly",
"tags": [
"Inventory",
"Products",
"Unpublish"
]
}