Как предотвратить рассогласование данных при обмене, если в событии формы «ПриЗаписи» происходит отказ?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В процессе разработки механизмов интеграции и обмена данными в реальном времени программисты часто сталкиваются с коварной особенностью платформы 1С:Предприятие 8 — порядком выполнения событий при записи объекта из формы. Рассмотрим ситуацию: мы создали подписку на событие ПриЗаписи, которая отправляет данные во внешнюю систему. Все работает отлично, пока пользователь не нажимает кнопку записи в форме, где какой-то другой разработчик (или мы сами) прописал проверку с установкой параметра Отказ = Истина именно в событии ПриЗаписи модуля формы.

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

Разберем механизм транзакции и порядок событий

Для понимания сути проблемы нам необходимо четко представлять жизненный цикл записи объекта, инициированной из формы. Когда пользователь нажимает «Записать», все нижеперечисленные действия выполняются внутри одной неявной транзакции:

  1. Обработчик формы ПередЗаписью: Здесь можно прервать процесс, и транзакция даже не начнется.
  2. Обработчик модуля объекта ПередЗаписью: Стандартное место для проверок заполнения.
  3. Подписки на событие ПередЗаписью: Выполняются для всех зарегистрированных в метаданных подписок.
  4. Начало физической записи в БД: Данные помещаются в таблицы, но еще не зафиксированы (Commit не вызван).
  5. Обработчик модуля объекта ПриЗаписи: Объект уже имеет ссылку и записан в базу внутри транзакции.
  6. Подписки на событие ПриЗаписи: Именно здесь часто настраивают обмен.
  7. Обработчик формы ПриЗаписи: Внимание! Это событие выполняется после того, как отработали все системные механизмы и подписки.
  8. Фиксация транзакции: Если на любом этапе выше (включая шаг 7) возник Отказ, вся транзакция откатывается (Rollback).

Проанализируем ситуацию автора темы: в шаге 6 данные ушли в другую базу через HTTP-запрос или COM. В шаге 7 форма обнаружила отсутствие, например, веса номенклатуры и установила Отказ = Истина. Транзакция отменилась, объекта в базе нет. Но внешняя база уже «знает» об этом объекте. При следующей попытке записи будет создан новый объект с тем же идентификатором, что приведет к дублированию или конфликтам уникальности.

Решение 1: Перенос проверок в событие «ПередЗаписью»

Самый правильный и надежный метод — это соблюдение стандартов разработки 1С. Любые проверки валидности данных, которые могут привести к отказу от записи, обязательно должны располагаться в обработчиках ПередЗаписью. Причем желательно делать это в модуле объекта, а не формы, чтобы логика работала и при программной записи объекта.

Если мы перенесем проверку заполнения реквизитов из модуля формы в модуль объекта, то отказ произойдет на шаге 2. В этом случае выполнение до шага 6 (отправка данных в подписке) просто не дойдет, и целостность данных между базами сохранится.

Решение 2: Использование регистра-очереди (Outbox Pattern)

Если специфика системы не позволяет гарантировать отсутствие отказов в форме (например, в случае сложных самописных конфигураций), следует отказаться от синхронной отправки данных непосредственно в подписке. Рассмотрим алгоритм работы через вспомогательную таблицу очереди:

  1. Создадим регистр сведений ОчередьВыгрузки.
  2. В подписке на событие ПриЗаписи вместо прямой отправки будем записывать в этот регистр только ссылку на объект.
  3. Поскольку запись в регистр происходит в той же транзакции, что и запись объекта, при Отказе в форме запись в регистре ОчередьВыгрузки также исчезнет (откатится).
  4. Настроим регламентное задание, которое будет раз в минуту выбирать записи из регистра, отправлять данные во внешнюю систему и очищать очередь.

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

Решение 3: Контроль через ДополнительныеСвойства

В некоторых случаях мы можем использовать механизм ДополнительныеСвойства для передачи флагов между различными этапами записи. Посмотрим на пример кода, который можно внедрить для контроля:


// В модуле формы в событии ПриЗаписи
Процедура ПриЗаписи(Отказ)
    Если Не ЗначениеЗаполнено(Вес) Тогда
        Отказ = Истина;
        Сообщить("Заполнение веса обязательно!");
        Возврат;
    КонецЕсли;
    
    // Если мы дошли сюда, значит запись успешна с точки зрения формы
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ПроверкаФормыПройдена", Истина);
КонецПроцедуры

Однако этот метод имеет существенный изъян: подписка ПриЗаписи срабатывает до выполнения кода формы. Следовательно, в подписке мы еще не знаем, установит ли форма отказ. Поэтому данный подход эффективен только в обратную сторону: когда подписка передает что-то в форму.

Решение 4: Асинхронная регистрация в Планах обмена

Если используется механизм планов обмена, важно понимать причину появления «битых» ссылок. Если вы регистрируете изменения вручную через ПланыОбмена.ЗарегистрироватьИзменения(), всегда делайте это внутри транзакции записи. Стандартная авторегистрация изменений 1С работает корректно: если транзакция откатывается из-за формы, запись в таблице регистрации изменений также удаляется.

Выясним причину появления дублей: они возникают, когда вы используете прямой вызов Web-сервиса или запись в другую БД без использования механизмов гарантированной доставки (таких как очереди сообщений или планы обмена). Никогда не делайте внешних вызовов (HTTP, COM, WS), результат которых нельзя откатить, внутри транзакции записи 1С.

Подведем итоги

Для надежной работы системы обмена данными при наличии возможных отказов в форме «ПриЗаписи» мы рекомендуем:

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

← На главную