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