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