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