Skip to content

Commit

Permalink
new task: tags customers with excessive refunds (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
tekhaus authored Nov 28, 2023
1 parent 86beaba commit 2eea4c4
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 0 deletions.
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Tag customers in bulk by email address](./tag-customers-in-bulk-by-email-address)
* [Tag customers on the anniversary of their first order](./tag-customers-on-the-anniversary-of-their-first-order)
* [Tag customers when their last order is before/after x days ago](./tag-customers-when-their-last-order-is-before-after-x-days-ago)
* [Tag customers who reach a certain threshold of refunded orders](./tag-customers-who-reach-a-certain-threshold-of-refunded-orders)
* [Tag online orders by ?ls= Locksmith secret link used](./tag-orders-by-locksmith-secret-link)
* [Tag online orders by their ?ref= referral codes](./tag-orders-by-url-referrer)
* [Tag orders in bulk by order name](./tag-orders-in-bulk-by-order-name)
Expand Down Expand Up @@ -714,6 +715,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Tag customers in bulk by email address](./tag-customers-in-bulk-by-email-address)
* [Tag customers on the anniversary of their first order](./tag-customers-on-the-anniversary-of-their-first-order)
* [Tag customers when their last order is before/after x days ago](./tag-customers-when-their-last-order-is-before-after-x-days-ago)
* [Tag customers who reach a certain threshold of refunded orders](./tag-customers-who-reach-a-certain-threshold-of-refunded-orders)
* [Temporarily enable tax-exempt status when a customer is tagged](./temporarily-enable-tax-exempt-status-when-a-customer-is-tagged)
* [Update empty customer data from addresses](./update-empty-customer-data-from-addresses)

Expand Down Expand Up @@ -1079,6 +1081,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Send an email when a product's price goes below its cost](./send-an-email-when-a-products-price-goes-below-its-cost)
* [Set product or variant metafields values in bulk](./set-product-or-variant-metafields-in-bulk)
* [Sync inventory levels to variant metafields](./sync-inventory-levels-to-variant-metafields)
* [Tag customers who reach a certain threshold of refunded orders](./tag-customers-who-reach-a-certain-threshold-of-refunded-orders)
* [Track incoming donations in a store metafield](./track-incoming-donations-in-a-store-metafield)

### Multi-Location
Expand Down Expand Up @@ -1424,6 +1427,7 @@ This directory is built automatically. Each task's documentation is generated fr

* [Auto-untag customers when a certain product is refunded](./auto-untag-customers-when-a-certain-product-is-refunded)
* [Cancel fulfillments when an order is fully refunded](./cancel-fulfillments-when-an-order-is-fully-refunded)
* [Tag customers who reach a certain threshold of refunded orders](./tag-customers-who-reach-a-certain-threshold-of-refunded-orders)

### Reminder

Expand Down Expand Up @@ -1653,6 +1657,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Tag customers by order tier](./tag-customers-by-order-tier)
* [Tag customers in bulk by email address](./tag-customers-in-bulk-by-email-address)
* [Tag customers on the anniversary of their first order](./tag-customers-on-the-anniversary-of-their-first-order)
* [Tag customers who reach a certain threshold of refunded orders](./tag-customers-who-reach-a-certain-threshold-of-refunded-orders)
* [Tag products with no images](./tag-products-with-no-images)
* [Temporarily enable tax-exempt status when a customer is tagged](./temporarily-enable-tax-exempt-status-when-a-customer-is-tagged)
* [Trigger order emails with a tag](./trigger-order-emails-with-a-tag)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Tag customers who reach a certain threshold of refunded orders

Tags: Customers, Metafields, Refunds, Tag

This task keeps track of how many lifetime refunded orders a customer has in your shop, and if that count hits a certain threshold, then it will apply a tag to the customer. This is useful for keeping tabs on serial returners.

* View in the task library: [tasks.mechanic.dev/tag-customers-who-reach-a-certain-threshold-of-refunded-orders](https://tasks.mechanic.dev/tag-customers-who-reach-a-certain-threshold-of-refunded-orders)
* Task JSON, for direct import: [task.json](../../tasks/tag-customers-who-reach-a-certain-threshold-of-refunded-orders.json)
* Preview task code: [script.liquid](./script.liquid)

## Default options

```json
{
"refunded_orders_count_threshold_for_tagging__number_required": null,
"customer_tag_to_apply__required": null,
"refunded_orders_count_metafield_namespace_dot_key__required": null
}
```

[Learn about task options in Mechanic](https://learn.mechanic.dev/core/tasks/options)

## Subscriptions

```liquid
shopify/refunds/create
mechanic/user/trigger
mechanic/shopify/bulk_operation
```

[Learn about event subscriptions in Mechanic](https://learn.mechanic.dev/core/tasks/subscriptions)

## Documentation

This task keeps track of how many lifetime refunded orders a customer has in your shop, and if that count hits a certain threshold, then it will apply a tag to the customer. This is useful for keeping tabs on serial returners.

It runs when new refunds are created and may also be run manually to apply the metafield and tagging logic to all customers who have returned at least one order in your shop.

Configure the task with your numeric threshold, the customer tag to apply, and the customer metafield namespace and key for tracking the refunded orders count (e.g. "custom.refunded_orders_count"). Run it once manually to have it scan historical orders, and again as needed if you change the threshold number.

**IMPORTANT:** The [Read all orders](https://learn.mechanic.dev/platform/shopify/read-all-orders#configuration) option must be enabled in Mechanic so that this task can query orders beyond the past 60 days.



## Installing this task

Find this task [in the library at tasks.mechanic.dev](https://tasks.mechanic.dev/tag-customers-who-reach-a-certain-threshold-of-refunded-orders), and use the "Try this task" button. Or, import [this task's JSON export](../../tasks/tag-customers-who-reach-a-certain-threshold-of-refunded-orders.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/)!
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
{% assign refunded_orders_count_threshold_for_tagging = options.refunded_orders_count_threshold_for_tagging__number_required %}
{% assign customer_tag_to_apply = options.customer_tag_to_apply__required %}
{% assign refunded_orders_count_metafield_namespace_dot_key = options.refunded_orders_count_metafield_namespace_dot_key__required %}

{% if event.preview %}
{% comment %}
-- use minimal threshold for preview data
{% endcomment %}

{% assign refunded_orders_count_threshold_for_tagging = 1 %}
{% endif %}

{% assign customers = array %}

{% if event.topic == "shopify/refunds/create" %}
{% comment %}
-- get historical refunded orders for this customer
{% endcomment %}

{% capture query %}
query {
customer(id: {{ refund.order.customer.admin_graphql_api_id | json }}) {
id
tags
metafield(key: {{ refunded_orders_count_metafield_namespace_dot_key | json }}) {
value
}
orders(
first: 250
query: "financial_status:refunded,partially_refunded"
) {
nodes {
id
}
}
}
}
{% endcapture %}

{% assign result = query | shopify %}

{% if event.preview %}
{% capture result_json %}
{
"data": {
"customer": {
"id": "gid://shopify/Customer/1234567890",
"orders": {
"nodes": [
{
"id": "gid://shopify/Order/1234567890"
}
]
}
}
}
}
{% endcapture %}

{% assign result = result_json | parse_json %}
{% endif %}

{% comment %}
-- add the count of refunded orders to a cloned customer object
{% endcomment %}

{% assign customers[0] = result.data.customer | except: "orders" | json | parse_json %}
{% assign customers[0]["refunded_orders_count"] = result.data.customer.orders.nodes.size %}


{% elsif event.topic == "mechanic/user/trigger" %}
{% comment %}
-- get all customers who have placed an order and the ID's of their orders that have been refunded or partially refunded (if any)
{% endcomment %}

{% capture bulk_operation_query %}
query {
customers(
query: "orders_count:>0"
) {
edges {
node {
__typename
id
tags
metafield(key: {{ refunded_orders_count_metafield_namespace_dot_key | json }}) {
value
}
orders(
query: "financial_status:refunded,partially_refunded"
) {
edges {
node {
__typename
id
}
}
}
}
}
}
}
{% endcapture %}

{% action "shopify" %}
mutation {
bulkOperationRunQuery(
query: {{ bulk_operation_query | json }}
) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
{% endaction %}

{% elsif event.topic == "mechanic/shopify/bulk_operation" %}
{% if event.preview %}
{% capture bulkOperation_objects_jsonl %}
{"__typename":"Customer","id":"gid:\/\/shopify\/Customer\/1234567890"}
{"__typename":"Order","id":"gid:\/\/shopify\/Order\/1234567890","__parentId":"gid:\/\/shopify\/Customer\/1234567890"}
{% endcapture %}

{% assign bulkOperation = hash %}
{% assign bulkOperation["objects"] = bulkOperation_objects_jsonl | parse_jsonl %}
{% endif %}

{% assign bulk_customers = bulkOperation.objects | where: "__typename", "Customer" %}
{% assign bulk_orders = bulkOperation.objects | where: "__typename", "Order" %}

{% for bulk_customer in bulk_customers %}
{% assign refunded_orders = bulk_orders | where: "__parentId", bulk_customer.id %}

{% if refunded_orders == blank %}
{% continue %}
{% endif %}

{% comment %}
-- clone the customer object and add the count of refunded orders to it
{% endcomment %}

{% assign customer = bulk_customer | json | parse_json %}
{% assign customer["refunded_orders_count"] = refunded_orders.size %}
{% assign customers = customers | push: customer %}
{% endfor %}
{% endif %}

{% comment %}
-- loop through all customers with refunds to check threshold and set metafields
{% endcomment %}

{% assign metafield_inputs = array %}

{% for customer in customers %}
{% comment %}
-- set metafield as needed (irrespective of configured threshold for tagging)
{% endcomment %}

{% assign metafield_value = customer.metafield.value | times: 1 %}

{% if metafield_value != customer.refunded_orders_count %}
{% assign metafield_input = hash %}
{% assign metafield_input["ownerId"] = customer.id %}
{% assign metafield_input["namespace"] = refunded_orders_count_metafield_namespace_dot_key | split: "." | first %}
{% assign metafield_input["key"] = refunded_orders_count_metafield_namespace_dot_key | split: "." | last %}
{% assign metafield_input["type"] = "number_integer" %}
{% assign metafield_input["value"] = customer.refunded_orders_count | append: "" %}

{% assign metafield_inputs = metafield_inputs | push: metafield_input %}
{% endif %}

{% comment %}
-- add/remove customer tag based on refunded orders threshold
{% endcomment %}

{% if customer.refunded_orders_count >= refunded_orders_count_threshold_for_tagging %}
{% unless customer.tags contains customer_tag_to_apply %}
{% action "shopify" %}
mutation {
tagsAdd(
id: {{ customer.id | json }}
tags: {{ customer_tag_to_apply | json }}
) {
userErrors {
field
message
}
}
}
{% endaction %}
{% endunless %}

{% else %}
{% if customer.tags contains customer_tag_to_apply %}
{% action "shopify" %}
mutation {
tagsRemove(
id: {{ customer.id | json }}
tags: {{ customer_tag_to_apply | json }}
) {
userErrors {
field
message
}
}
}
{% endaction %}
{% endif %}
{% endif %}
{% endfor %}

{% comment %}
-- set metafields for all customers with count of refunded orders
{% endcomment %}

{% assign groups_of_metafield_inputs = metafield_inputs | in_groups_of: 25, fill_with: false %}

{% for group_of_metafield_inputs in groups_of_metafield_inputs %}
{% action "shopify" %}
mutation {
metafieldsSet(
metafields: {{ group_of_metafield_inputs | graphql_arguments }}
) {
userErrors {
code
field
message
}
}
}
{% endaction %}
{% endfor %}
Loading

0 comments on commit 2eea4c4

Please sign in to comment.