В процессе разработки механизмов интеграции и обмена данными в реальном времени программисты часто сталкиваются с коварной особенностью платформы 1С:Предприятие 8 — порядком выполнения событий при записи объекта из формы. Рассмотрим ситуацию: мы создали подписку на событие ПриЗаписи, которая отправляет данные во внешнюю систему. Все работает отлично, пока пользователь не нажимает кнопку записи в форме, где какой-то другой разработчик (или мы сами) прописал проверку с установкой параметра Отказ = Истина именно в событии ПриЗаписи модуля формы.
В этот момент возникает критическая ошибка логики: внешняя система уже получила данные, так как подписка сработала первой, а в текущей базе транзакция откатилась, и объект не сохранился. Проанализируем, почему так происходит и как правильно спроектировать систему, чтобы избежать дублей и «битых» ссылок в планах обмена.
Для понимания сути проблемы нам необходимо четко представлять жизненный цикл записи объекта, инициированной из формы. Когда пользователь нажимает «Записать», все нижеперечисленные действия выполняются внутри одной неявной транзакции:
ПередЗаписью: Здесь можно прервать процесс, и транзакция даже не начнется.ПередЗаписью: Стандартное место для проверок заполнения.ПередЗаписью: Выполняются для всех зарегистрированных в метаданных подписок.ПриЗаписи: Объект уже имеет ссылку и записан в базу внутри транзакции.ПриЗаписи: Именно здесь часто настраивают обмен.ПриЗаписи: Внимание! Это событие выполняется после того, как отработали все системные механизмы и подписки.Отказ, вся транзакция откатывается (Rollback).Проанализируем ситуацию автора темы: в шаге 6 данные ушли в другую базу через HTTP-запрос или COM. В шаге 7 форма обнаружила отсутствие, например, веса номенклатуры и установила Отказ = Истина. Транзакция отменилась, объекта в базе нет. Но внешняя база уже «знает» об этом объекте. При следующей попытке записи будет создан новый объект с тем же идентификатором, что приведет к дублированию или конфликтам уникальности.
Самый правильный и надежный метод — это соблюдение стандартов разработки 1С. Любые проверки валидности данных, которые могут привести к отказу от записи, обязательно должны располагаться в обработчиках ПередЗаписью. Причем желательно делать это в модуле объекта, а не формы, чтобы логика работала и при программной записи объекта.
Если мы перенесем проверку заполнения реквизитов из модуля формы в модуль объекта, то отказ произойдет на шаге 2. В этом случае выполнение до шага 6 (отправка данных в подписке) просто не дойдет, и целостность данных между базами сохранится.
Если специфика системы не позволяет гарантировать отсутствие отказов в форме (например, в случае сложных самописных конфигураций), следует отказаться от синхронной отправки данных непосредственно в подписке. Рассмотрим алгоритм работы через вспомогательную таблицу очереди:
ОчередьВыгрузки.ПриЗаписи вместо прямой отправки будем записывать в этот регистр только ссылку на объект.Отказе в форме запись в регистре ОчередьВыгрузки также исчезнет (откатится).Этот метод гарантирует, что данные уйдут во внешнюю систему только в том случае, если они были успешно зафиксированы в основной базе данных.
В некоторых случаях мы можем использовать механизм ДополнительныеСвойства для передачи флагов между различными этапами записи. Посмотрим на пример кода, который можно внедрить для контроля:
// В модуле формы в событии ПриЗаписи
Процедура ПриЗаписи(Отказ)
Если Не ЗначениеЗаполнено(Вес) Тогда
Отказ = Истина;
Сообщить("Заполнение веса обязательно!");
Возврат;
КонецЕсли;
// Если мы дошли сюда, значит запись успешна с точки зрения формы
ЭтотОбъект.ДополнительныеСвойства.Вставить("ПроверкаФормыПройдена", Истина);
КонецПроцедуры
Однако этот метод имеет существенный изъян: подписка ПриЗаписи срабатывает до выполнения кода формы. Следовательно, в подписке мы еще не знаем, установит ли форма отказ. Поэтому данный подход эффективен только в обратную сторону: когда подписка передает что-то в форму.
Если используется механизм планов обмена, важно понимать причину появления «битых» ссылок. Если вы регистрируете изменения вручную через ПланыОбмена.ЗарегистрироватьИзменения(), всегда делайте это внутри транзакции записи. Стандартная авторегистрация изменений 1С работает корректно: если транзакция откатывается из-за формы, запись в таблице регистрации изменений также удаляется.
Выясним причину появления дублей: они возникают, когда вы используете прямой вызов Web-сервиса или запись в другую БД без использования механизмов гарантированной доставки (таких как очереди сообщений или планы обмена). Никогда не делайте внешних вызовов (HTTP, COM, WS), результат которых нельзя откатить, внутри транзакции записи 1С.
Для надежной работы системы обмена данными при наличии возможных отказов в форме «ПриЗаписи» мы рекомендуем:
Отказ = Истина в обработчик ПередЗаписью модуля объекта.ОбменДанными.Загрузка: Убедиться, что ваши проверки не блокируют запись при загрузке данных из других систем.ПослеЗаписи: В управляемых формах для действий, которые должны произойти только после гарантированного сохранения объекта в БД, используйте обработчик ПослеЗаписи.Помните, что архитектурно правильное проектирование событийной модели 1С избавляет от необходимости «героически» исправлять ошибки рассогласования данных в будущем.