Sync order timeline comments to the customer note with Mechanic.

Mechanic is the one-tool-does-it-all automation app for Shopify. :)

Sync order timeline comments to the customer note

by Isaac Bowen (team@usemechanic.com)

This task scans all orders (optionally filtering by the query of your choice), and copies any new timeline comments to the customer's note. Useful for getting a snapshot of order activity when looking at the customer's record. Runs manually, hourly, and/or daily.

Runs when a user triggers the task. Configuration includes only scan orders matching this query, add new comments to the beginning, comment date format, run daily, and run hourly.

15-day free trial – unlimited tasks

Documentation

This task scans all orders (optionally filtering by the query of your choice), and copies any new timeline comments to the customer's note. Any existing content in the customer note will be preserved; each new comment will be added to the end of the note (or, optionally, to the beginning). Any edited timeline comments will be added as new note lines during the next scan.

Use the "Run task" button to run the scan manually. Or, enable the "Run daily" and/or "Run hourly" options to have Mechanic run this task automatically.

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Gurus, 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.

Events
when a user triggers the task (mechanic/user/trigger)
Options
only scan orders matching this query, add new comments to the beginning (boolean), comment date format (required), run daily (boolean), run hourly (boolean)
Script
{% assign cursor = nil %}
{% assign order_ids_to_scan = array %}

{% for n in (0..10000) %}
  {% capture query %}
    query {
      orders(
        first: 250
        after: {{ cursor | json }}
        sortKey: CREATED_AT
        query: {{ options.only_scan_orders_matching_this_query | json }}
      ) {
        pageInfo {
          hasNextPage
        }
        edges {
          cursor
          node {
            id
            hasTimelineComment
          }
        }
      }
    }
  {% endcapture %}

  {% assign result = query | shopify %}

  {% assign order_ids_to_scan = result.data.orders.edges | map: "node" | where: "hasTimelineComment", true | map: "id" | concat: order_ids_to_scan %}

  {% if result.data.orders.pageInfo.hasNextPage %}
    {% assign cursor = result.data.orders.edges.last.cursor %}
  {% else %}
    {% break %}
  {% endif %}
{% endfor %}

{% if event.preview %}
  {% assign order_ids_to_scan = '["gid://shopify/Order/1234567890"]' | parse_json %}
{% endif %}

{% assign customer_ids_and_order_comments = hash %}
{% assign customer_ids_and_existing_notes = hash %}

{% for order_id in order_ids_to_scan %}
  {% assign cursor = nil %}

  {% for n in (0..10000) %}
    {% capture query %}
      query {
        order(
          id: {{ order_id | json }}
        ) {
          name
          customer {
            id
            note
          }
          events(
            first: 250
            after: {{ cursor | json }}
            query: "verb:comment"
            sortKey: CREATED_AT
          ) {
            pageInfo {
              hasNextPage
            }
            edges {
              node {
                createdAt
                message
              }
            }
          }
        }
      }
    {% endcapture %}

    {% assign result = query | shopify %}

    {% if event.preview %}
      {% break %}
    {% endif %}

    {% assign order_node = result.data.order %}

    {% assign customer_ids_and_existing_notes[order_node.customer.id] = order_node.customer.note | strip %}

    {% for event_edge in order_node.events.edges %}
      {% assign event_node = event_edge.node %}

      {% assign order_comment = hash %}
      {% assign order_comment["created_at"] = event_node.createdAt %}
      {% assign order_comment["message"] = event_node.message %}

      {% if customer_ids_and_order_comments[order_node.customer.id] == nil %}
        {% assign customer_ids_and_order_comments[order_node.customer.id] = array %}
      {% endif %}

      {% assign _order_comments_count = customer_ids_and_order_comments[order_node.customer.id].size %}
      {% assign customer_ids_and_order_comments[order_node.customer.id][_order_comments_count] = order_comment %}
    {% endfor %}

    {% if order_node.events.pageInfo.hasNextPage %}
      {% assign cursor = order_node.events.edges.last.cursor %}
    {% else %}
      {% break %}
    {% endif %}
  {% endfor %}
{% endfor %}

{% if event.preview %}
  {% assign customer_ids_and_existing_notes["gid://shopify/Customer/1234567890"] = "(existing note content)" %}

  {% assign order_preview_comment = hash %}
  {% assign order_preview_comment["created_at"] = "now" %}
  {% assign order_preview_comment["message"] = "This is a comment that's been left." %}

  {% assign customer_ids_and_order_comments["gid://shopify/Customer/1234567890"] = array %}
  {% assign customer_ids_and_order_comments["gid://shopify/Customer/1234567890"][0] = order_preview_comment %}
{% endif %}

{% for keyval in customer_ids_and_existing_notes %}
  {% assign customer_id = keyval[0] %}
  {% assign existing_note = keyval[1] %}
  {% assign existing_note_lines = existing_note | split: newline %}
  {% assign new_note_lines = array %}

  {% assign order_comments_sorted = customer_ids_and_order_comments[customer_id] | sort: "created_at" %}
  {% if options.add_new_comments_to_the_beginning__boolean %}
    {% assign order_comments_sorted = order_comments_sorted | reverse %}
  {% endif %}

  {% for order_comment in order_comments_sorted %}
    {% assign order_comment_message_formatted = order_comment.message | strip | strip_html | prepend: '"' | append: '"' %}

    {% assign order_comment_found = false %}
    {% for note_line in existing_note_lines %}
      {% if note_line contains order_comment_message_formatted %}
        {% assign order_comment_found = true %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% unless order_comment_found %}
      {% capture order_comment_as_note_line %}
        {{ order_comment.created_at | date: options.comment_date_format__required }} - {{ order_comment_message_formatted }} ({{ order_node.name }})
      {% endcapture %}

      {% assign new_note_lines[new_note_lines.size] = order_comment_as_note_line | strip %}
    {% endunless %}
  {% endfor %}

  {% if new_note_lines == empty %}
    {% continue %}
  {% endif %}

  {% assign double_newline = newline | append: newline %}

  {% if options.add_new_comments_to_the_beginning__boolean %}
    {% assign updated_note = new_note_lines | join: double_newline | append: double_newline | append: existing_note | strip %}
  {% else %}
    {% assign updated_note = new_note_lines | join: double_newline | prepend: double_newline | prepend: existing_note | strip %}
  {% endif %}

  {"log": {"customer_id": {{ customer_id | json }}, "existing_note": {{ existing_note | json }}}}

  {% action "shopify" %}
    mutation {
      customerUpdate(
        input: {
          id: {{ customer_id | json }}
          note: {{ updated_note | json }}
        }
      ) {
        customer {
          note
        }
        userErrors{
          field
          message
        }
      }
    }
  {% endaction %}
{% endfor %}
Mechanic tasks are written in Liquid, which makes them easy to write and easy to modify. Learn more about our platform.
Defaults
Only scan orders matching this query
status:open
Comment date format
%d/%m/%y