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