ПередЗаписью, ПриЗаписи, ПриПроведении

Разбираемся, в каком порядке срабатывают события записи объекта и зачем каждое из них нужно.

События записи — процедуры модуля объекта (ПередЗаписью, ПриЗаписи, ОбработкаПроведения), которые платформа вызывает в строго определённом порядке, когда вы сохраняете элемент или проводите документ.

«Расскажите, что происходит, когда пользователь нажимает Записать?» — этот вопрос интервьюер задаёт, чтобы понять, разбираетесь ли вы в жизненном цикле объекта. Ответить «данные сохраняются в базу» мало: важно назвать события по порядку и объяснить, за что отвечает каждое. Именно здесь новички путают, где проверять данные, а где формировать движения — и на боевой базе это оборачивается дублями и битыми остатками.

Вопрос-крючок

«Есть документ РасходТовара. Нужно: не дать записать документ без склада, а при проведении списать товар с остатков. В каких событиях это делать и почему именно там?»

Правильный ход мысли — сначала вспомнить порядок событий, а потом разложить задачу по ним.

Порядок событий записи

Когда пользователь записывает объект, платформа открывает транзакцию и вызывает события в таком порядке:

  1. ПередЗаписью — объект ещё не в базе, но уже внутри транзакции. Здесь проверяют данные и могут отменить запись через Отказ.
  2. ПриЗаписи — объект уже записан в таблицу, но транзакция не закрыта. Здесь связывают его с другими данными.
  3. ОбработкаПроведения (только у документов, только при проведении) — формируют движения по регистрам.

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

ПередЗаписью — здесь проверяем

Это событие вызывается до того, как объект попал в таблицу базы, поэтому запись ещё можно отменить дёшево. Отмена делается флагом Отказ, который платформа передаёт параметром:

Процедура ПередЗаписью(Отказ)

    Если НЕ ЗначениеЗаполнено(Склад) Тогда
        Сообщить("Не заполнен склад — документ не будет записан");
        Отказ = Истина;
        Возврат;
    КонецЕсли;

    Если Сумма <= 0 Тогда
        Сообщить("Сумма документа должна быть больше нуля");
        Отказ = Истина;
    КонецЕсли;

КонецПроцедуры

Функция ЗначениеЗаполнено() возвращает Ложь для пустой ссылки, нуля, пустой строки и пустой даты — это стандартный способ проверить, что реквизит заполнен. Поставили Отказ = Истина и вышли через Возврат — платформа прервёт запись, и до базы дело не дойдёт.

ПриЗаписи — объект уже в базе

К моменту ПриЗаписи строка объекта уже во временном состоянии внутри транзакции: ей присвоен номер, есть ссылка. Событие удобно, когда нужно записать что-то связанное — например, обновить регистр сведений, где ключом выступает ссылка на этот документ. Проверять заполнение здесь уже поздно: объект записан, откат обойдётся дороже.

Процедура ПриЗаписи(Отказ)

    // Ссылка уже существует — можно записать связанные данные
    Запись = РегистрыСведений.ОтветственныеЗаДокументы.СоздатьМенеджерЗаписи();
    Запись.Документ = Ссылка;
    Запись.Ответственный = Ответственный;
    Запись.Записать();

КонецПроцедуры

ОбработкаПроведения — формируем движения

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

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

    Движения.ТоварыНаСкладах.Записывать = Истина;

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

КонецПроцедуры

Флаг Записывать = Истина говорит платформе: этот набор движений нужно сохранить в базу вместе с документом. Проверку остатков (хватает ли товара) тоже делают здесь — после блокировки данных регистра, но это тема раздела про блокировки.

Как отвечать

Соберите ответ в три предложения: «Порядок такой — ПередЗаписью, затем запись в базу, затем ПриЗаписи, и при проведении ОбработкаПроведения. Всё это одна транзакция, поэтому проверки заполнения ставлю в ПередЗаписью и отменяю через Отказ, пока запись дешёвая. Движения формирую в ОбработкаПроведения, потому что только там доступен набор Движения». Такой ответ показывает, что вы понимаете и порядок, и причину порядка.

Как это работает под капотом

Почему проверку заполнения делают именно в ПередЗаписью? Потому что до этого момента объект ещё не вставлен в таблицу базы: платформа держит его в оперативной памяти внутри открытой транзакции. Установка Отказ = Истина заставляет платформу выполнить ОтменитьТранзакцию и не писать ничего. Если же отказаться на этапе ПриЗаписи, строка объекта уже во временном состоянии транзакции — откат всё равно уберёт её, но вы зря потратили запись. А в ОбработкаПроведения объект и вовсе записан, и откат утянет за собой и его, и уже сформированные движения. Поэтому дешевле отказать как можно раньше.

Частые ошибки на собеседовании

  • Путают ПриЗаписи и ОбработкаПроведения. Формируют движения в ПриЗаписи — но набор Движения там ещё не про проведение, и на неоперативном перепроведении логика ломается.
  • Проверяют заполнение в ОбработкаПроведения. Тогда пустой документ спокойно запишется (просто не проведётся), и в базе появится мусор. Проверку заполнения — в ПередЗаписью.
  • Забывают про транзакцию. Говорят «в ПередЗаписью сохраню лог в отдельный регистр» — а запись падает, транзакция откатывается, и лог исчезает вместе с ней. Внутри событий вы всегда внутри транзакции документа.
  • Считают, что события есть у всех объектов. ОбработкаПроведения — только у документов и только при проведении; у справочника её нет.

Итоги-шпаргалка

  • ПередЗаписью(Отказ) — до вставки в базу; проверяем данные, отменяем через Отказ = Истина.
  • ПриЗаписи(Отказ) — объект уже записан в транзакции; пишем связанные данные.
  • ОбработкаПроведения(Отказ, Режим) — только документы, только проведение; формируем движения через Движения, ставим Записывать = Истина.
  • Все события — на сервере, в одной транзакции; ошибка на любом шаге откатывает всё.
  • Правило ответа: проверяй раньше, пиши позже — заполнение в ПередЗаписью, движения в ОбработкаПроведения.
Проверьте себя
1. В каком событии правильнее всего проверить, что у документа заполнен склад, и при необходимости отменить запись?
AОбработкаПроведения
BПриЗаписи
CПередЗаписью
DПослеЗаписи
2. Что произойдёт, если в ОбработкаПроведения возникнет исключение уже после того, как документ был записан?
AДокумент останется записанным, а движения — нет
BОткатится вся транзакция: не будет ни документа, ни движений
CДвижения запишутся, а документ откатится
DНичего, платформа проигнорирует ошибку