Обработчики событий модуля объекта 1С делят работу так: ОбработкаПроверкиЗаполнения проверяет данные вне транзакции, ПередЗаписью дозаполняет объект и выполняет критичные проверки уже в транзакции, ПриЗаписи пишет связанные данные в другие объекты, ОбработкаЗаполнения отвечает за заполнение нового объекта и ввод на основании. Во всех обработчиках записи первым делом проверяется ОбменДанными.Загрузка, а параметр Отказ только устанавливается в Истина, но никогда не сбрасывается.
Ниже — практический разбор этих правил по стандартам разработки фирмы «1С» с примерами кода и типичными ошибками, которые я регулярно нахожу при аудите доработок.
Порядок событий при записи объекта
Чтобы понимать, куда класть код, нужно держать в голове последовательность. Для документа, записываемого интерактивно с проведением, она такая:
| Шаг | Обработчик | Транзакция | Что делать |
|---|---|---|---|
| 1 | ОбработкаПроверкиЗаполнения | Нет | Проверки корректности заполнения |
| 2 | ПередЗаписью | Да | Дозаполнение, сравнение со старыми значениями, критичные проверки |
| 3 | Запись в базу | Да | Делает платформа |
| 4 | ПриЗаписи | Да | Запись связанных данных в другие объекты |
| 5 | ОбработкаПроведения | Да | Формирование движений |

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




