Вопрос о правильном расположении проверок в документах — один из ключевых при разработке в 1С. От того, где мы разместим код контроля, зависит не только надежность системы, но и ее производительность и удобство для пользователя. Давайте вместе разберемся, как грамотно подходить к этой задаче и какие механизмы платформы использовать.
Выясним причину, почему этот вопрос так важен. Дело в том, что проверки бывают разными. Одни должны гарантировать целостность самого документа (например, что все обязательные поля заполнены), а другие — контролировать состояние всей системы (например, не допускать продажи товара, которого нет на складе). Для этих разных задач существуют разные подходы и места в коде.
Самый правильный подход — это разделить всю логику контроля на несколько уровней. Проанализируем каждый из них и определим, для каких задач он подходит лучше всего.
Проверки на уровне формы (для удобства пользователя)
Это самый первый рубеж контроля, который видит пользователь. Его главная задача — обеспечить быстрый отклик и не дать пользователю совершить очевидную ошибку. Такие проверки выполняются на клиенте или в форме на сервере еще до начала записи объекта в базу данных.
ПроверкаЗаполнения в значение Выдавать ошибку. Платформа сделает все сама.ОбработкаПроверкиЗаполненияНаСервере: Если проверка заполнения зависит от других данных в документе (например, поле "Договор" обязательно, только если выбран определенный "Тип операции"), используйте этот обработчик в модуле формы. Он позволяет реализовать сложную логику и сразу сообщить пользователю об ошибке.Важно: Помните, что логика в форме — это UI (пользовательский интерфейс). Основные, критически важные бизнес-проверки не должны находиться здесь, так как их можно обойти, например, при программном создании документов или при обмене данными.
Проверки при записи объекта (событие ПередЗаписью)
Здесь мы размещаем контроли, которые отвечают за целостность самого документа как объекта. Эти проверки гарантируют, что в базу данных не запишется "бракованный" или бессмысленный документ, даже если он еще не проведен. Это идеальное место для проверок, не требующих анализа данных из регистров.
Что здесь проверяем:
Размещать этот код можно как в модуле самого объекта документа, так и в подписке на событие. Подробнее об этом выборе поговорим ниже.
Проверки при проведении документа (событие ОбработкаПроведения)
Это место для самой "тяжелой" и важной бизнес-логики. Именно здесь происходят проверки, которые затрагивают состояние всей системы и требуют обращения к регистрам. Эти контроли выполняются в рамках транзакции, что гарантирует целостность данных.
Что здесь проверяем:
Важный момент: Блокировать пользователю возможность записать документ (сохранить как черновик) из-за нехватки остатков — не всегда хорошая идея. Это может вызвать раздражение, ведь пользователь, возможно, просто хотел сохранить набросок документа, чтобы вернуться к нему позже. Поэтому контроль остатков почти всегда размещают именно в ОбработкеПроведения.
Теперь, когда мы определились с тем, *когда* выполнять проверку, разберем, *где* именно писать код.
Модуль объекта документа (например, ПередЗаписью или ОбработкаПроведения)
Это основной и самый очевидный способ. Здесь следует размещать всю логику, которая является уникальной для данного вида документа. Код находится прямо в "своем" объекте, его легко найти и поддерживать.
Подписка на событие
Подписки на события — это мощный инструмент для реализации сквозной функциональности, которая должна применяться ко многим документам одного типа. Например, если у вас есть общая проверка для всех расходных документов, логично вынести ее в одну подписку, а не копировать код в каждый документ.
Важно помнить: Код в подписке на событие выполняется после одноименного обработчика в модуле объекта. Это позволяет использовать подписки для финальных, самых строгих проверок, которые должны выполняться в последнюю очередь.
Контроль отрицательных остатков — одна из самых частых задач. Рассмотрим две основные методики его реализации в обработчике ОбработкаПроведения.
"Старая" методика: сначала проверка, потом запись движений
Сначала мы выполняем запрос к регистрам, чтобы проверить, хватает ли товаров. Если все в порядке, мы формируем и записываем движения. Этот подход интуитивно понятен, но при высокой нагрузке может приводить к проблемам с блокировками, так как мы читаем данные для проверки, а только потом их изменяем.
"Новая" (рекомендуемая) методика: сначала запись, потом контроль в той же транзакции
Этот метод более производительный. Разберем по шагам, как он работает:
Отказ = Истина. Транзакция откатывается, и проведения документа не происходит.Посмотрим на примерный код для новой методики в ОбработкеПроведения:
// 1. Записываем движения без предварительных проверок
Движения.ОстаткиТоваров.Записывать = Истина;
Для Каждого Стр Из Товары Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = Стр.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = Стр.Количество;
КонецЦикла;
// 2. Выполняем контроль остатков после записи движений
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваровОстатки.Номенклатура,
| ОстаткиТоваровОстатки.КоличествоОстаток
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, Номенклатура В (&СписокНоменклатуры)) КАК ОстаткиТоваровОстатки
|ГДЕ
| ОстаткиТоваровОстатки.КоличествоОстаток < 0";
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));
Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
// 3. Если есть отрицательные остатки, отменяем проведение
Отказ = Истина;
Сообщить("Обнаружены отрицательные остатки! Проведение отменено.");
КонецЕсли;
В реальной жизни часто возникают ситуации, когда проверки нужно временно отключить. Самый яркий пример — загрузка данных при обмене.
Обязательно оборачивайте все ваши проверки в условие, которое отключает их, когда документ загружается программно. Для этого используется свойство ОбменДанными.Загрузка.
Посмотрим на пример:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат; // Ничего не проверяем при обмене
КонецЕсли;
// ... здесь идет ваш код проверок ...
Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
Сообщить("Не заполнен контрагент!");
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Этот простой прием должен стать стандартом при разработке. Кроме того, для более гибкого управления можно использовать функциональные опции (если проверка должна включаться/отключаться в настройках программы) или параметры сеанса для временного отключения контролей при выполнении сервисных обработок.
← К списку