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