"ПриЗаписи" или "ОбработкаПроведения": Где правильно формировать движения документа в 1С?

Программист 1С v8.3 (Управляемые формы)
← К списку

Разбираемся с одним из ключевых вопросов при разработке в 1С:Предприятие – в каком обработчике события документа следует размещать логику формирования движений по регистрам. Этот выбор критически важен для корректности учета, производительности и удобства сопровождения конфигурации. Мы проанализируем различные подходы, их преимущества и недостатки, а также разберем, как эти события взаимодействуют с транзакциями.

Единая транзакция: основа понимания

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

  1. ПередЗаписью: Срабатывает перед началом записи объекта в базу данных.

  2. Собственно запись: Платформа записывает основные данные документа в его таблицы.

  3. ПриЗаписи: Выполняется после записи данных документа, но до завершения транзакции.

  4. ОбработкаПроведения: Если документ проводится, этот обработчик вызывается после ПриЗаписи, но все еще в рамках той же транзакции.

  5. ОбработкаПроверкиЗаполнения: Также может быть вызван в этой цепочке.

Что это означает для нас? Если на любом этапе этой цепочки (в ПередЗаписью, ПриЗаписи, ОбработкаПроверкиЗаполнения или ОбработкаПроведения) мы установим параметр Отказ = Истина, вся транзакция будет отменена. Это приведет к тому, что все изменения, сделанные в базе данных до этого момента в рамках данной транзакции, будут полностью откачены. Таким образом, документ не будет записан, а движения, которые могли быть сформированы, не попадут в регистры.

ОбработкаПроведения: Основное место для учетных движений

Рассмотрим основной и наиболее логичный подход к формированию движений – использование обработчика ОбработкаПроведения.

Когда использовать:

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

Логическое обоснование и преимущества:

  1. Соответствие логике учета: Движения по регистрам — это последствия проведения документа. Логично, что эта функциональность должна располагаться именно в процедуре, отвечающей за проведение.

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

  3. Автоматическая запись движений и блокировки: Платформа 1С обладает механизмом автоматической записи всех измененных наборов записей регистров, которые находятся в свойстве Движения документа. Эта автоматическая запись происходит после завершения всех обработчиков и гарантирует строго одинаковый порядок блокировки таблиц движений для разных документов, что помогает снизить вероятность возникновения взаимоблокировок при одновременной работе нескольких пользователей.

    Если мы явно вызываем Движения.Регистр.Записать() в ОбработкаПроведения, повторной автоматической записи этого набора записей не произойдёт, так как он уже не будет считаться изменённым. Автоматическая запись движений происходит с замещением, если требуется запись с добавлением, это необходимо выполнять явно.

Пример использования ОбработкаПроведения:

В этом обработчике мы обычно очищаем текущие движения документа и формируем новые на основании данных документа.


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Очищаем существующие движения документа
    Движения.ОстаткиТоваров.Очистить();
    Движения.Взаиморасчеты.Очистить();

    // Далее формируем новые движения
    // Например, получаем данные из табличной части документа
    Для Каждого СтрокаТЧ Из Товары Цикл
        Движение = Движения.ОстаткиТоваров.Добавить();
        Движение.Период = Дата;
        Движение.Номенклатура = СтрокаТЧ.Номенклатура;
        Движение.Количество = СтрокаТЧ.Количество;
        // ... и так далее для других измерений и ресурсов
    КонецЦикла;

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

ПриЗаписи: Для дополнительных данных, не связанных с проведением

Теперь рассмотрим, для чего может быть полезен обработчик ПриЗаписи.

Когда использовать:

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

  1. Не являются основными движениями документа и не зависят напрямую от факта проведения.

  2. Должны быть записаны в той же транзакции, что и сам документ.

  3. Используются для документов, которые не имеют механизма проведения (например, для регистров сведений, справочников).

Важное ограничение:

В ПриЗаписи категорически запрещается изменять содержимое записываемого объекта. На момент выполнения обработчика объект уже записан в базу данных, и любые попытки его изменения приведут к некорректному поведению или ошибкам.

Пример использования ПриЗаписи:

Предположим, нам нужно обновить какую-то вспомогательную информацию в регистре сведений, которая не является движением документа, но должна быть связана с фактом его записи.


Процедура ПриЗаписи(Отказ)
    // Записываем информацию о последнем изменении в регистр сведений
    НаборЗаписей = РегистрыСведений.ПоследниеИзмененияДокументов.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Документ.Установить(Ссылка);
    НаборЗаписей.Прочитать();

    Если НаборЗаписей.Количество() = 0 Тогда
        Запись = НаборЗаписей.Добавить();
    Иначе
        Запись = НаборЗаписей[0];
    КонецЕсли;

    Запись.Документ = Ссылка;
    Запись.ДатаИзменения = ТекущаяДата();
    Запись.Пользователь = ПараметрыСеанса.ТекущийПользователь;
    НаборЗаписей.Записать();

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

ПослеЗаписи (модуля формы): Для действий после успешной транзакции

Отдельно стоит упомянуть обработчик ПослеЗаписи, который находится в модуле формы документа.

Когда использовать:

Этот обработчик выполняется на клиенте уже после завершения транзакции записи. Его можно использовать для:

  1. Вывода сообщений пользователю о результате записи.

  2. Обновления данных формы или других элементов интерфейса.

  3. Выполнения других действий, которые должны произойти только после 100% успешной записи документа в базу данных.

В этом обработчике мы не можем установить Отказ = Истина, так как транзакция уже завершена.

Важные аспекты и рекомендации

Проанализируем дополнительные важные моменты, которые помогут нам в принятии решения.

Использование параметра Отказ

Установка Отказ = Истина в ПередЗаписью, ПриЗаписи, ОбработкаПроверкиЗаполнения или ОбработкаПроведения прерывает текущую операцию и откатывает всю транзакцию. Критически важно информировать пользователя о причине отказа. Простое присвоение Отказ = Истина без вывода сообщения может привести к неинформативным ошибкам, затрудняющим отладку и понимание проблемы. Согласно стандартам разработки, не следует присваивать параметру Отказ значение Ложь, так как это может случайно отменить корректный отказ, установленный другими проверками или подписками на событие.

Отмена проведения и пометка удаления

При отмене проведения документа вызывается процедура ОбработкаУдаленияПроведения. Свойство документа "Удаление движений" (в палитре свойств) определяет поведение платформы при отмене проведения. Если оно установлено в "Удалять автоматически", платформа сама удалит движения документа. Если в "Не удалять автоматически", логику удаления движений необходимо реализовать вручную в ОбработкаУдаленияПроведения. Пометка на удаление проведённого документа часто инициирует его отмену проведения, что, в свою очередь, вызывает ОбработкаУдаленияПроведения.

Зависимые регистры

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

Регламентные задания

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

Заключение

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

← К списку