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