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