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