К статье
Практический материал

Разработка и Конфигурация · 13 мин

Когда выполняется обработчик «При записи» в 1С

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

Событие «При записи» — один из самых востребованных обработчиков в 1С: в нём программисты проверяют заполнение реквизитов, формируют связанные данные, запускают дополнительные проверки. Но именно с этим событием связано больше всего ошибок: одни ожидают, что оно сработает до записи в базу, другие пытаются внутри него изменить сам объект и получают рекурсию. Чтобы использовать обработчик правильно, нужно чётко понимать его место в цепочке событий записи.

Событие «При записи» существует и на клиенте (в модуле формы), и на сервере (в модуле объекта), и это разные обработчики с разным поведением. Клиентский «При записи» формы вызывается при интерактивной записи из формы, серверный «При записи» объекта — при любом вызове метода Записать(), независимо от того, откуда он инициирован: из формы, из кода, из обмена данными или фонового задания.

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

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

При записи объекта (справочника, документа, плана счетов и т.д.) платформа выполняет строго определённую цепочку событий. Для серверного контекста она выглядит так: «Перед записью» → «При записи» → физическая запись в базу данных → «После записи» (для формы). Важно: «При записи» выполняется внутри транзакции записи, то есть данные ещё не зафиксированы в базе, но объект уже считается записываемым.

Если запись инициирована из формы, цепочка шире: сначала срабатывают клиентские обработчики формы («Перед записью» на клиенте), затем управление передаётся на сервер — «Перед записью» и «При записи» модуля объекта, после чего выполняются серверные обработчики формы («После записи на сервере») и клиентские («После записи»). Программный вызов Записать() из кода минует все обработчики формы — выполнятся только события модуля объекта.

Для документов с проведением картина сложнее: после «При записи» дополнительно выполняются события «Обработка проведения» или «Обработка удаления проведения», в зависимости от режима записи. Поэтому логику, связанную с движениями по регистрам, размещают именно в «Обработке проведения», а не в «При записи».

Момент вызоваДо физической записи в БД

Обработчик работает внутри транзакции записи, данные ещё не зафиксированы

Свойство СсылкаУже доступна

На момент «При записи» у объекта уже есть ссылка, её можно использовать в запросах и связанных записях

Отказ = ИстинаОтменяет всю транзакцию

Установка Отказ в «При записи» откатывает запись полностью, как и в «Перед записью»

Запись из кодаОбработчик сработает

Модуль объекта выполняется при любом Записать() — из формы, обмена, фонового задания

Отличие «При записи» от «Перед записью»

Ключевое различие — наличие ссылки у объекта. В «Перед записью» у нового объекта ссылка ещё не присвоена (точнее, присвоена временная ссылка нового, но объект в базе отсутствует). В «При записи» объект уже получил постоянную ссылку, и её можно использовать: записывать связанные объекты, регистрировать изменения, строить запросы с учётом текущей ссылки.

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

Третье различие — повторный вызов. При записи документа с проведением «Перед записью» вызывается один раз, а в некоторых сценариях (например, при перепроведении из формы с вопросами пользователю) цепочка может прерываться и запускаться заново. Полагаться на счётчики вызовов между этими событиями нельзя.

Где размещать логику

Перед записью (модуль объекта)

Проверка заполнения, отказ от записи, изменение реквизитов самого объекта. Ссылка ещё не гарантирована

При записи (модуль объекта)

Действия, требующие ссылки: связанные записи, регистрация изменений, обращения к другим объектам

При записи (модуль формы)

Только интерактивные сценарии: вопросы пользователю, обновление интерфейса. Не сработает при программной записи

Обработка проведения

Всё, что связано с движениями по регистрам документа

Серверный «При записи» в модуле объекта

Обработчик «При записи» в модуле объекта объявляется с параметром Отказ: Процедура ПриЗаписи(Отказ). Он выполняется на сервере внутри транзакции записи. Установка Отказ = Истина приводит к откату всей транзакции — запись объекта не состоится, а пользователь или вызывающий код получит исключение.

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

Критическое ограничение: внутри «При записи» нельзя вызывать Записать() для того же самого объекта — это приведёт к рекурсивному вызову обработчика и зацикливанию. Если нужно изменить собственные реквизиты, делайте это присваиванием значений (изменения попадут в текущую запись), а не повторной записью. Также здесь недоступен интерактивный контекст: нельзя задавать вопросы пользователю и показывать формы.

bsl
Процедура ПриЗаписи(Отказ)
	Если ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	// Регистрация изменения в очереди интеграции
	МенеджерЗаписи = РегистрыСведений.ОчередьОтправки.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.Объект = Ссылка;
	МенеджерЗаписи.ДатаРегистрации = ТекущаяДатаСеанса();
	МенеджерЗаписи.Записать();
КонецПроцедуры

Клиентский «При записи» в модуле формы

В модуле формы обработчик «При записи» имеет два параметра: Отказ и ПараметрыЗаписи. Он вызывается на клиенте при интерактивной записи — когда пользователь нажимает «Записать», «Провести» или «Провести и закрыть». Через ПараметрыЗаписи можно передать в серверные обработчики дополнительные сведения, например признак того, что пользователь подтвердил специальный режим записи.

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

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

Типовые сценарии и практические приёмы

Первый приём — проверка признака ОбменДанными.Загрузка. Если объект записывается в режиме загрузки (обмен данными, миграция, начальное заполнение), бизнес-логику обычно нужно пропустить. Стандартный шаблон: в начале обработчика проверить Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли;. Без этой проверки обмен может падать из-за проверок заполнения или плодить лишние записи в очередях.

Второй приём — определение нового объекта. В «При записи» ссылка уже есть, поэтому проверка «это новый объект» делается через ДополнительныеСвойства: в «Перед записью» сохраняют признак ЭтоНовый(), а в «При записи» читают его. Напрямую в «При записи» метод ЭтоНовый() уже вернёт Ложь.

Третий приём — передача данных между обработчиками через ДополнительныеСвойства. Это структура, живущая в течение всей цепочки записи: в «Перед записью» можно вычислить старые значения реквизитов (запросом по ссылке до изменения), а в «При записи» сравнить их с новыми и записать историю изменений. Это стандартный способ реализации аудита изменений.

Как реализовать историю изменений реквизита
  1. 1
    В «Перед записью» модуля объекта проверьте ОбменДанными.Загрузка и выйдите при загрузке
  2. 2
    Если объект не новый, запросом получите старое значение реквизита по Ссылке и сохраните в ДополнительныеСвойства
  3. 3
    В «При записи» сравните текущее значение с сохранённым из ДополнительныеСвойства
  4. 4
    При различии запишите запись в регистр сведений истории с указанием Ссылка, пользователя и даты
  5. 5
    Проверьте результат: измените реквизит в форме и убедитесь, что в регистре появилась запись

Типичные ошибки и их последствия

Самая частая ошибка — рекурсивная запись: внутри «При записи» вызывается Записать() того же объекта или объект записывает сам себя через цепочку связанных объектов. Результат — переполнение стека и аварийное завершение сеанса. Защита: флаг в ДополнительныеСвойствах («ЗаписьВыполняется») или изменение реквизитов без повторной записи.

Вторая ошибка — тяжёлые операции в транзакции записи. «При записи» выполняется внутри транзакции, и длительные запросы, обходы больших таблиц, обращения к внешним сервисам удерживают блокировки в базе. В многопользовательской среде это приводит к конфликтам блокировок и таймаутам у других пользователей. Внешние вызовы (HTTP, почта) из транзакции записи делать нельзя — используйте отложенную обработку через очередь.

Третья ошибка — ожидание, что «При записи» формы сработает всегда. Разработчик размещает проверку в форме, тестирует интерактивно, всё работает — а потом обмен данными записывает объект мимо всех проверок. Четвёртая ошибка — модификация реквизитов объекта в «При записи» с ожиданием, что они сохранятся: изменения, сделанные после начала записи в некоторых сценариях, требуют аккуратности, надёжнее менять данные в «Перед записью».

Типичные ошибки с «При записи»
01
Вызов Записать() того же объекта внутри обработчика — рекурсия и падение сеанса
02
Логика только в модуле формы — не срабатывает при программной записи и обмене
03
Обращение к внешним сервисам внутри транзакции — блокировки и недоступность при сбое сервиса
04
Отсутствие проверки ОбменДанными.Загрузка — сбои обмена и лишние записи в очередях
05
Проверка ЭтоНовый() в «При записи» — всегда Ложь, ссылка уже присвоена

Как проверить, что обработчик работает правильно

Базовая проверка — отладчик: поставьте точку останова в «При записи» модуля объекта и запишите объект тремя способами: интерактивно из формы, программно через Записать() в консоли кода, через обмен или загрузку. Обработчик должен срабатывать во всех трёх случаях. Если в каком-то сценарии логика не нужна — это повод добавить проверку ОбменДанными.Загрузка или источника вызова.

Проверьте поведение при отказе: установите Отказ = Истина по условию и убедитесь, что объект не записался, а связанные данные (которые обработчик мог изменить до отказа) откатились вместе с транзакцией. Если связанные записи остались — значит, они записывались вне транзакции, например через отдельное соединение или в «После записи».

Для нагруженных систем дополнительно проверьте длительность записи: замерьте время выполнения обработчика на реальном объёме данных. Если запись документа занимает секунды из-за логики в «При записи», пользователи многопользовательской базы столкнутся с блокировками — такую логику нужно выносить в отложенные механизмы.

Контрольные точки

Как проверить результат

Срабатывание при программной записи
Норма

Обработчик модуля объекта выполняется при Записать() из кода

Тревожный признак

Логика работает только из формы — она в модуле формы

Отказ от записи
Норма

При Отказ = Истина объект и связанные записи отсутствуют в базе

Тревожный признак

Объект не записался, но связанные данные остались

Запись в режиме загрузки
Норма

При ОбменДанными.Загрузка = Истина бизнес-логика пропускается

Тревожный признак

Обмен падает на проверках или создаёт лишние записи

Время записи
Норма

Запись объекта выполняется быстро, без ожиданий внешних ресурсов

Тревожный признак

Запись занимает секунды, в журнале конфликты блокировок

Часто задаваемые вопросы

Почему «При записи» срабатывает дважды?

Чаще всего причина в том, что внутри цепочки записи объект записывается повторно — либо вашим кодом, либо подпиской на событие, которая вызывает Записать() связанного или того же объекта. Также двойной вызов бывает при проведении документа с последующей записью. Определить источник помогает стек вызовов в отладчике в момент второго срабатывания.

Чем подписка на событие «При записи» отличается от обработчика в модуле объекта?

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

Можно ли в «При записи» изменить реквизиты самого объекта?

Технически присвоение значений реквизитам возможно, но надёжнее делать это в «Перед записью»: там изменения гарантированно попадут в записываемый набор данных. В «При записи» объект уже находится в процессе сохранения, и поведение изменений зависит от момента вызова. Вызов Записать() для сохранения изменений недопустим — это рекурсия.

Сработает ли «При записи» при записи через обмен данными?

Да, серверный обработчик модуля объекта выполняется при любой записи, включая обмен. Однако при загрузке через планы обмена обычно устанавливается ОбменДанными.Загрузка = Истина, и стандартная практика — пропускать бизнес-логику по этому признаку. Обработчики формы при обмене не выполняются никогда.

Где правильно проверять заполнение реквизитов?

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

Можно ли из «При записи» отправить письмо или вызвать веб-сервис?

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

Итог

Что важно запомнить

Серверный «При записи» модуля объекта выполняется внутри транзакции записи, когда ссылка уже существует, но данные ещё не зафиксированы. Критичную логику размещайте в модуле объекта, а не формы; проверку ОбменДанными.Загрузка делайте всегда; повторная запись объекта и внешние вызовы внутри обработчика недопустимы.