Циклический триггер — безлимитные действия и уведомления

Циклический триггер — безлимитные действия и уведомления

RetailCRM Tips and Tricks by Alexey Erm

Представим задачу — необходимо с регулярностью производить какое-то действие, до победного конца. Например — отправлять менеджеру уведомление, что клиент должен оплатить заказ, пока он находится в статусе «Ожидает оплаты». Но для онлайн оплаты вы хотите напомнить об этом через 6 часов, 24 часа, 3 дня и, если клиент сказал что оплатит чуть позже, еще через 3 дня и 6 дней, а через 9 дней уведомить менеджера что заказ надо отменить, а через 12 дней оповестить руководителя, что заказ надо отменить. И тоже самое проделать для заказов с безналичной оплатой, но только уже с другими интервалами.

Всё это можно реализовать имея:
— 2 пользовательских поля
— 1 шаблон уведомлений
— 3 триггера

Такой набор вам предоставит невероятную гибкость в настройке, которая будет ограничена исключительно вашей фантазией.


Поле «Циклический триггер»

Сущность: Для заказа

Тип: Строка

Название: circular_trigger

Видимость: Не отображается

Цель: Будем записывать в него информацию, сколько времени прошло, регулярно его обновляя. В моём примере ниже, оно будет обновляться каждые 6 часов, записывая туда данные в формате 6, 12, 18, 24 и так вечно, пока условие триггера выполняются.


Поле «Текст уведомления»

Сущность: Для заказа

Тип: Строка

Название: message_text

Видимость: Не отображается

Цель: Сюда будем записывать текст уведомления, которое необходимо отправить менеджеру. Значение этого поля в сыром виде будем передавать в уведомление, которое будет отправляться ответственному менеджеру. Запись текста в поле и последующая передача, обусловлены тем, что в результатах работы триггера мы можем менять, либо НЕ менять данные поля, в зависимости от исходных условий. Так, если бы мы привязали напрямую уведомление, то мы не смогли бы отправить одним и тем же триггером через 6, 24, 72 часа, при условии что триггер работает каждые 6 часов. Простое уведомление отправлялось каждые 6 часов. А поле можем записывать уже с более тонкими условиями.


Шаблон оповещения «Сообщение менеджеру»

Такой шаблон потребуется всего один, на любые уведомления, которые будут отправляться через поле заказа «Текст уведомления».

{% set __message_text = null %}
{% set __message_text = order.getCustomField('message_text') %}
{% set __message_text = __message_text|replace({'{PLUSADMIN}':"", '{ONLYADMIN}':"", '{my_1pattern}':order.customer.getCustomField('some_field')[7:]}) %}
{% if __message_text matches '/{....}$/' %}{% set __message_text = ((__message_text|reverse)[6:])|reverse %}{% endif %}
<div>{{ __message_text|raw }}</div>

Мы передаем поле не напрямую, а объявляем его новой переменной, которую сначала обрабатываем и только потом выводим в уведомлении. Это нужно для следующих целей:

1. Например могут быть одинаковые уведомления, но оповещения могут отправляться только когда произошло изменение поля «Текст уведомления». Поэтому исходный текст уведомления может выглядеть так:

Напомнить клиенту об оплате заказа {b4j5}

В то время, как менеджер будет видеть текст, без окончания {b4j5}, вот так:

Напомнить клиенту об оплате заказа

За это отвечает следующее условие:

{% if __message_text matches '/{....}$/' %}{% set __message_text = ((__message_text|reverse)[6:])|reverse %}{% endif %}

Если текст заканчивается на {4_любых_знака}, то перевернуть, обрезать 6 символов и снова перевернуть.

Также вы можете увидеть конструкцию, которая удаляет текст {PLUSADMIN} и {ONLYADMIN}.

{% set __message_text = __message_text|replace({'{PLUSADMIN}':"", '{ONLYADMIN}':"" %}

Она используется для того, чтобы триггеры, которые реагируют на оповещение, понимали, надо ли продублировать оповещение руководителю, либо вообще отправить оповещение только руководителю и НЕ отправлять оповещение ответственному менеджеру.

Ситуации когда нужно только менеджеру, менеджеру + руководителю, только руководителю сможете придумать сами. А также сможете масштабировать данные метки, например ввести роли промежуточного руководителя {PLUSHEAD} и {ONLYHEAD} в дополнение к ADMIN.

Еще можно заметить, что есть конструкция, которая заменяет паттерн:

{% set __message_text = __message_text|replace({'{my_1pattern}':order.customer.getCustomField('some_field')[7:]}) %}

Такая конструкция используется на случай, если сразу вытащить данные через триггер не получится и требуется предварительная обработка. В данном примере, если внутри текста, который запишем в поле «Текст уведомления» будет найден текст {amb_pass_id}, то его заменит на данные из поля клиента passid, предварительно обрезав 7 знаков от начала. Твиг позволяет делать очень сложные обработки, которые в Pipeline физически реализовать не получится.


Ниже я рассмотрю на примере заказа в статусе «Требуется подтвердить» или «Ожидает оплаты». В моей системе это группа «Согласование», поэтому я сажаю данные триггеры сразу на несколько статусов, уведомления по которым приходят по разному.


Триггер «ГРУППА "Согласование" — Регулярные напоминания для обновления статуса / Запуск»

Событие: Изменение заказа

Условие действия:

(
changeSet.hasChangedField("status")
or (changeSet.hasChangedField("custom_circular_trigger") and changeSet.getNewValue("custom_circular_trigger") matches '/^\\d+$/u')
)
and order.status.code in ['to-be-confirmed', 'availability-confirmed']

Смысл состоит в том, что триггер срабатывает в случае если
( изменился Статус_заказа ИЛИ (изменилось поле Циклический_триггер и значение этого поля равно любому целому числу)) и Статус_заказа равен (Ожидает_подтверждения или Ожидает_оплаты).

Это означает, что триггер будет срабатывать только, когда заказ находится в данных статусах и перестанет срабатывать, когда выйдет из них, а также будет срабатывать только если заказ перешел в данные статусы, либо если в заказе при нахождении в одном из данных статусов было изменено поле "Циклический триггер" и не будет реагировать на какие-либо другие действия.

Результат действия:

Изменить данные заказа «Циклический триггер»

changeSet.hasChangedField("status") ? null : order.getCustomField("circular_trigger")

В случае если у нас изменился статус, то поле очистится. А если нет, то запишется то же значение, что в нём и было. Такая комбинация условий и действий, позволяет и выполнить триггер, и одновременно не зациклить триггер на самого себя.

Этот триггер у нас служит спусковым механизмом, а вторым триггером мы будем перезапускать первый.


Триггер «ГРУППА "Согласование" — Регулярные напоминания для обновления статуса / Чекпоинт»

Событие: После срабатывания триггера для заказа

Через: 358 минут

Условие действия:

not last_run("358 minutes")
and ((date("now").format("U") - order.statusUpdatedAt.format("U"))/60) > 358
and order.status.code in ['to-be-confirmed', 'availability-confirmed']

Здесь заложен следующий смысл.

  1. В RetailCRM цепочки триггеров работают по крону, который запускается каждые 5 минут. Поэтому если указать что триггер должен сработать через 360 минут, то он может сработать и через 360 минут и через 365 минут. Но если указать 358 минут, то он точно сработает ровно через 360 минут.
  2. Так как статус заказа мог поменяться несколько раз в течение короткого периода, то триггер в цепочке сработает тоже несколько раз, через указанное время после каждого срабатывания исходного, первого триггера, поэтому нам надо ограничить срабатывание данного триггера и не перезапускать его, если он ранее уже срабатывал в предыдущие 358 минут. Для этого пишем строку:
    not last_run("358 minutes")
  3. Чтобы нам сделать срабатывание после последнего изменения статуса, то мы дополнительно проверяем, что время, которое прошло с момента последнего обновления статуса, более 358 минут, конструкцией, которая вычисляет разницу во времени. Таким образом, у нас мог, условно, заказ 10 раз в течение 3 часов перейти в статус «Ожидает оплаты», но наш второй триггер сработает через 6 часов после последнего перехода в данный статус, а не после первого, если бы мы не написали это условие. Для этого пишем строку:
    and ((date("now").format("U") - order.statusUpdatedAt.format("U"))/60) > 358
  4. И последним, конечно же проверяем, что наш заказ всё еще находится в том статусе, в который он перешел и статус не изменился. Для этого пишем строку:
    and order.status.code in ['to-be-confirmed', 'availability-confirmed']

Результат действия:

Изменить данные заказа «Циклический триггер»

order.getCustomField("circular_trigger") + 6

Так как у нас триггер срабатывает каждые 6 часов, то в результат работы пишем текущее значение + 6. В поле у нас будет хранится количество часов, которое прошло с момента перехода в статус и каждые 6 часов плюсовать 6. Будет 6, 12, 18 и так далее. Пишем сюда в таком виде, на случай, если работа триггеров вдруг встанет и триггер сработает, по какой-то неведомой причине через 7 часов или через 8 часов, мало ли бывает. У нас в этом случае все равно запишется +6 часов и дальнейшие проверки не собьются.

Изменить данные заказа «Текст уведомления»

// Для статуса "Требуется подтвердить"
order.status.code == 'to-be-confirmed' ? 
( order.getCustomField("circular_trigger") == 6 ? 'Напомнить о подтверждении заказа — прошло '~order.getCustomField("circular_trigger")~' ч' :
( order.getCustomField("circular_trigger") == 24 ? 'Напомнить о подтверждении заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн' :
( (order.getCustomField("circular_trigger")/24) in ['3','6','9','12'] ? 'Напомнить о подтверждении заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн. <b>Отменить заказ</b> — если клиент не попросил о продлении'
: order.getCustomField("message_text") )))
: order.getCustomField("message_text")

Такой код, занесет в поле следующие варианты текста:

Через 6 ч: Напомнить о подтверждении заказа — прошло 6 ч
Через 24 ч: Напомнить о подтверждении заказа — прошло 1 дн
Через 3, 6, 9, 12 дн: Напомнить о подтверждении заказа — прошло N дн

В другое время, например через 12 и 18 часов, уведомление останется таким же, не изменится и соответственно не будет отправлено в оповещения под колокольчик.

Здесь вариаций настройки может быть достаточно много. Например, вы хотите чтобы триггер бесконечно напоминал вам через каждые 3 дня, в этом случае можно написать не перечисление количества дней, а проверку что число дней делится на 3 без остатка, вот так, вместо строки:

( (order.getCustomField("circular_trigger")/24) in ['3','6','9','12'] ? 'Напомнить о подтверждении заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн. <b>Отменить заказ</b> — если клиент не попросил о продлении'

написать строку:

( (order.getCustomField("circular_trigger")/24) > 3 and (order.getCustomField("circular_trigger")/24/3) matches '/^\\d+$/' ? 'Напомнить о подтверждении заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн. <b>Отменить заказ</b> — если клиент не попросил о продлении'

Например прошло 72 часа, делим на 24 и делим на 3, получается 1 — уведомление отправится. А если прошло 90 часов, делим на 24 и делим на 3, получается 1.25 — число получилось не целое и поэтому условие не сработает и уведомление не будет отправлено.

В данном поле мы затронули лишь один статус «Ожидает подтверждения», для следующих статусов нам надо создать ещё одно действие в рамках данного триггера. Поэтому добавим еще одно действие, на то же самое поле «Текст уведомления»

Изменить данные заказа «Текст уведомления»

// Для статуса "Ожидает оплаты" с безналичной оплатой
order.status.code == 'availability-confirmed'
and order.payments|contains(item => item.type.code in ['bank-transfer'] and not item.status.getPaymentComplete() ) ?
( (order.getCustomField("circular_trigger")/24) in ['3','6'] ? 'Напомнить об оплате заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн' :
( (order.getCustomField("circular_trigger")/24) in ['9','12'] ? 'Напомнить об оплате заказа — прошло '~order.getCustomField("circular_trigger")/24~' дн. <b>Отменить заказ</b> — если клиент не попросил о продлении'
: order.getCustomField("message_text") ))
: order.getCustomField("message_text")

В данном действии я показываю пример с безналичной оплатой, в которой мы напоминаем клиенту об оплате через 3 и 6 дней, а через 9 и 12 дней менеджеру выводится сообщение, что стоит отменить заказ, если клиент не пообещал оплатить позже. Количество дней и интервалы указывайте в соответствии с вашим бизнес-процессом. А code типов платежей поправьте в соответствии с тем, какие используются в вашей системе.

Теперь давайте представим, что заказ оплатили, менеджер получил уведомление об оплате с другого триггера, но почему-то не протолкнул заказ дальше и он так и остается висеть в статусе «Ожидает оплаты». Для напоминаний о таких ситуациях сделаем ещё одно действие в рамках этого же триггера.

Изменить данные заказа «Текст уведомления»

// Для статуса "Ожидает оплаты" если платеж оплачен
order.status.code == 'availability-confirmed'
and order.fullPaidAt ?
( (order.getCustomField("circular_trigger")/24) in ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15'] ? 'Заказ ранее был уже полностью оплачен, но до сих пор не отправлен в статус «На сборку» или «Предзаказ».' :
( (order.getCustomField("circular_trigger")/24) in ['18','21','24','27','30'] ? 'Заказ ранее был уже полностью оплачен, но до сих пор не отправлен в статус «На сборку» или «Предзаказ». <b>Обратить внимание</b> — нахождение оплаченного заказа в текущем статусе недопустимо!{PLUSADMIN}'
: order.getCustomField("message_text") ))
: order.getCustomField("message_text")

Такое действие будет отправлять уведомление с 1 по 15 день каждый день. Тут стоит учитывать, что заказ мог висеть в статусе «Ожидает оплаты» уже несколько дней, в зависимости от того, сколько времени оплачивался счет. Клиент мог оплатить его как сразу, так и через 3-5-7-12 дней. По вашему вкусу, указывайте здесь либо конкретные дни, либо вариант паттерна деления на 24 и получения целого числа, по примеру как было указано выше.

Таких действий можете добавить сколько прийдется вам по вкусу. Они будут работать друг за другом и если условие не сработало, то текст останется неизменным и обработка начнется в следующем действии.

Так как условия if else внутри действия использовать нельзя, то я использую вложения тернарных операторов. Получается много скобок, но позволяет получить требуемый результат. Поэтому при расширении параметров условий, требуется следить за количеством скобок. Тоже самое относится к кавычкам, например если внутрь хотите вставить какое-то кастомное поле, например order.getCustomField("circular_trigger"), то параметр в скобках надо кавычить двойными кавычками, так как одинарные используем для обертки текста и если указать вот так order.getCustomField('circular_trigger'), то действие не сработает, либо триггер вообще не даст сохранить.


Триггер «Уведомление менеджеру»

Теперь отправка самого уведомления менеджера, которое отправляется по факту изменения поля «Текст уведомления», которое мы заполняли предыдущими триггерами.

Событие: Изменение заказа

Условие действия:

changeSet.hasChangedField("custom_message_text")
and changeSet.getNewValue("custom_message_text")
and changeSet.getNewValue("custom_message_text") not matches '/{ONLYADMIN}/'
and order.manager.id
and order.manager.active

Здесь вы видите, что идет проверка не только на предмет изменения поля, но и на то, что ответственный менеджер вообще назначен и что его аккаунт активен. Ситуации случаются разные, менеджер уволился и его аккаунт отключили, или менеджер сам взял и поставил у заказа что менеджер не назначен. Отправлять уведомления по заказу в пустоту чуть более чем бесполезно. А также смотрим, не предназначено ли уведомление только администратору, чтобы перенаправить его не менеджеру, а руководителю. Метки вместо {ONLYADMIN} можете придумать по вкусу, главное чтобы было больше 4 символов внутри фигурных скобок.

Результат действия:

Отправить оповещение «Сообщение менеджеру»

Кому: Ответственный менеджер

Лично мне, отправлять оповещения таким образом нравится больше, чем плодить десятки разных шаблонов оповещений. В этом есть свои плюсы:

  • Можно гибко распределять оповещения, например если менеджер не онлайн, то отправлять всем другим менеджерам.
  • Хранить историю оповещений в заказе.
  • Отправлять оповещения не после каждого срабатывания триггера, а исключая какие-то интервалы срабатывания.
  • И это не полный список преимуществ такой схемы.


Триггер «Уведомление администратору»

Я писал про три триггера, а это уже четвертый. К схеме регулярных безлимитных уведомлений он отношения не имеет, но в рамках условий уведомлений покажу как отправляются уведомления руководителю.

Событие: Изменение заказа

Условие действия:

changeSet.hasChangedField("custom_message_text")
and changeSet.getNewValue("custom_message_text")
and (
not (order.manager.id and order.manager.active)
or changeSet.getNewValue("custom_message_text") matches '/{ONLYADMIN}/'
or (changeSet.getNewValue("custom_message_text") matches '/{PLUSADMIN}/' and order.manager.id != 6)
)

Здесь практически тоже самое что и в уведомлении менеджеру, но есть нюансы:

  • Если в поле «Текст уведомления» будет текст {ONLYADMIN}, то такое уведомление получит только человек, указанный получателем оповещения в данном триггере
  • Если в поле «Текст уведомления» будет текст {PLUSADMIN} и при этом менеджер заказа не сам руководитель, то руководитель получит такое же оповещение как и ответственный менеджер. Исключение условия нужно для того чтобы у руководителя не дублировались такие оповещения, если в них указано {PLUSADMIN}, так он уже получит такое уведомление как ответственный менеджер.
  • И на последок, если менеджер не назначен или деактивирован, то оповещение всё равно будет доставлено и получателем оповещения будет руководитель.

Результат действия:

Отправить оповещение «Сообщение менеджеру»

Кому: Аккаунт руководителя


Теперь у вас будет меньше триггеров, меньше шаблонов оповещений, больше контроля и логичнее структура. Масштабируйте дальше схему на другие статусы. Например посылка собрана и долго не уезжает. Доставляется или возвращается после невыкупа значительно дольше обозначенных сроков, висит в статусе «Претензия» и так далее, где надо регулярно контролировать, что посылка должна выйти из данного статуса. Теперь всё это можно легко реализовать используя данную схему.


#триггер #уведомление #хак


__________________________

❤️ Поблагодарить автора 💸

__________________________

✍️ Предложить тему публикации
__________________________


Настройка RetailCRM / Триггеры RetailCRM / Валидации RetailCRM / Примеры RetailCRM / Хаки RetailCRM / Секреты RetailCRM

Оригинальный пост https://t.me/retailcrm_tips/17

Подписывайте на канал RetailCRM Tips and Tricks

Список статей по настройке RetailCRM

Report Page