При разработке механизмов лояльности, таких как бонусные баллы в 1С, программисты часто сталкиваются с неприятной особенностью: подписка на событие ПриЗаписи регистра накопления срабатывает дважды. Если в этом событии прописана логика отправки СМС или Email покупателю, он получит два одинаковых уведомления, что выглядит непрофессионально и ведет к лишним затратам компании. В этой статье мы подробно разберем, почему платформа 1С ведет себя именно так, и рассмотрим эффективные способы фильтрации лишних событий.
Для начала проанализируем внутренние механизмы платформы 1С. Когда документ (например, Чек ККМ) проводится, система выполняет запись движений по регистрам. Двойной вызов события ПриЗаписи — это не ошибка, а стандартное поведение системы в следующих случаях:
Движения.БонусныеБаллы.Записывать = Истина, и при этом в коде встречается явный вызов метода Движения.БонусныеБаллы.Записать(), событие сработает дважды.Рассмотрим способы, которые помогут нам оставить только одно, «правильное» срабатывание.
Это самый простой и эффективный метод отсечь первый вызов, связанный с очисткой движений. Мы проанализируем количество записей в объекте НаборЗаписей, который передается в обработчик события.
Если набор пустой — значит, система просто очищает старые движения, и отправлять уведомление не нужно. Рассмотрим пример кода для подписки на событие:
Процедура ПодпискаПриЗаписиРегистраБонусы(Источник, Отказ, Замещение) Экспорт
// Источник — это НаборЗаписейРегистраНакопления.БонусныеБаллы
// Шаг 1: Проверяем, что это не пустая запись (очистка)
Если Источник.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Шаг 2: Проверяем режим обмена данными, чтобы не слать СМС при загрузке из РИБ
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Далее следует логика отправки уведомления...
КонецПроцедуры
Используя этот метод, мы игнорируем технический вызов платформы и реагируем только на фактическое появление данных в регистре.
Отправка уведомлений напрямую из события регистра — рискованная затея. Дело в том, что событие ПриЗаписи происходит внутри транзакции проведения документа. Если после записи регистра в документе возникнет ошибка (например, не хватит товара на складе), транзакция откатится. При этом запись в регистре исчезнет, а СМС клиенту уже уйдет, так как внешнее действие (вызов API провайдера) отменить невозможно.
Проанализируем архитектурно верный подход: использование вспомогательного регистра «Очередь уведомлений».
ПриЗаписи регистра накопления мы не отправляем СМС, а записываем данные в специальный регистр сведений ОчередьУведомлений.Такой подход решает проблему дублей, экономит деньги на СМС и защищает от ошибок при откате транзакций.
Если логика бизнес-процесса позволяет, мы можем перенести подписку с регистра накопления на событие документа-регистратора (например, ОбработкаПроведения чека ККМ). В этом случае мы гарантированно получим управление один раз в момент завершения проведения.
Однако здесь важно учитывать, что один и тот же регистр бонусов могут двигать разные типы документов: Чек, Возврат, Корректировка. В таком случае придется создавать подписки на каждый вид документа, что не всегда удобно. Если же вы решите оставить подписку на регистр, обязательно добавьте проверку статуса документа-регистратора:
// Внутри обработчика набора записей регистра
Регистратор = Источник.Отбор.Регистратор.Значение;
Если НЕ (ТипЗнч(Регистратор) = Тип("ДокументСсылка.ЧекККМ") И Регистратор.Проведен) Тогда
Возврат;
КонецЕсли;
Чтобы избежать повторного срабатывания в рамках одной транзакции, мы можем использовать коллекцию ДополнительныеСвойства. Это «карман», который живет вместе с объектом в памяти в течение текущего вызова.
Проанализируем ситуацию: если мы программно вызываем Записать(), а потом платформа вызывает его еще раз автоматически, мы можем пометить набор при первом вызове.
Процедура ПередЗаписью(Источник, Отказ, Замещение) Экспорт
// Проверяем, не обрабатывали ли мы этот набор ранее в этой цепочке вызовов
Если Источник.ДополнительныеСвойства.Свойство("УведомлениеУжеОбработано") Тогда
Возврат;
КонецЕсли;
// Выполняем действия...
// Ставим отметку
Источник.ДополнительныеСвойства.Вставить("УведомлениеУжеОбработано", Истина);
КонецПроцедуры
Подведем итоги. Двойное срабатывание события ПриЗаписи — это нормальная работа платформы 1С. Для решения проблемы с дублями уведомлений рекомендуется использовать комбинированный подход:
Источник.Количество() > 0, чтобы отсечь техническую очистку движений.ОбменДанными.Загрузка, чтобы избежать спама при синхронизации данных.Выбрав один из предложенных вариантов, вы обеспечите корректную работу программы и лояльность ваших клиентов, которые будут получать только актуальную информацию о своем бонусном балансе.