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