Как программно закрыть или предотвратить появление всплывающего окна Цены и валюта в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление нашей фирмой
← На главную

При разработке внешних обработок или автоматизации заполнения документов в среде 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда типовые механизмы конфигурации прерывают выполнение кода всплывающими диалоговыми окнами. Типичный пример — окно «Цены и валюта» в конфигурации 1С:УНФ, которое появляется при изменении организации в документе «Заказ покупателя». Это окно требует реакции пользователя, что делает невозможным фоновую или полностью автоматизированную обработку данных.

В этой статье мы подробно разберем, почему возникают ошибки при попытке программного закрытия таких окон, как правильно искать активные формы в сеансе и какие методы расширения конфигурации позволяют элегантно решить проблему «на корню».

Анализ проблемы: почему метод ПолучитьФорму вызывает ошибку?

Рассмотрим ситуацию, с которой часто начинают новички. Попытка перехватить форму через стандартный метод ПолучитьФорму часто приводит к ошибке вида «Поле объекта не обнаружено (ДатаДокумента)». Давайте разберем причину этого поведения. Проанализируем типичный неудачный код:


Форма = ПолучитьФорму("ОбщаяФорма.ФормаЦеныИВалюта");
Если Форма.Открыта() Тогда
    Форма.Закрыть();                        
КонецЕсли;

Проблема заключается в том, что метод ПолучитьФорму без передачи параметров владельца и структуры параметров инициализирует новый экземпляр формы. В типовых конфигурациях, таких как УНФ или ERP, форма «Цены и валюта» при создании на сервере (процедура ПриСозданииНаСервере) обращается к данным владельца (документа), чтобы получить дату, валюту и другие реквизиты. Поскольку мы создаем «пустую» форму, обращение к Объект.ДатаДокумента или аналогичным реквизитам вызывает исключение, так как контекст документа отсутствует. Нам же нужно не создавать новую форму, а найти ту, которая уже была открыта системой.

Решение №1: Использование коллекции окон приложения

Наиболее универсальный способ, который позволяет закрыть уже открытое окно, — это использование метода ПолучитьОкна(). Этот метод возвращает массив всех окон, открытых в текущем сеансе пользователя. Проанализируем, как можно реализовать поиск и закрытие нужного окна.

Разберем по шагам алгоритм поиска:

  1. Получаем массив всех активных окон приложения.
  2. В цикле обходим каждое окно.
  3. Проверяем заголовок окна или, что более надежно, имя основной формы внутри окна.
  4. При совпадении условий программно вызываем метод закрытия.

Пример кода для реализации этого подхода:


ОкнаПриложения = ПолучитьОкна();
Для каждого ТекущееОкно Из ОкнаПриложения Цикл
    // Проверяем содержимое окна
    Если ТекущееОкно.Основное Тогда
        Продолжить; // Основное окно конфигурации закрывать не нужно
    КонецЕсли;

    // В коллекции Содержимое обычно находится форма
    Для каждого ЭлементСодержимого Из ТекущееОкно.Содержимое Цикл
        // Проверяем имя формы (это надежнее, чем проверять заголовок)
        Если ЭлементСодержимого.ИмяФормы = "ОбщаяФорма.ФормаЦеныИВалюта" Тогда
            ЭлементСодержимого.Закрыть();
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Важное замечание: Использование свойства Заголовок (например, Ок.Заголовок = "Цены и валюта") крайне не рекомендуется. Заголовок может измениться после обновления конфигурации, зависеть от языка интерфейса пользователя или содержать дополнительные данные (например, номер документа). Идентификация по ИмяФормы является эталонной.

Решение №2: Корректное закрытие с учетом ОписанияОповещения

Выясним важную деталь: когда типовой код открывает форму «Цены и валюта», он почти всегда использует ОписаниеОповещения. Это означает, что система ожидает от этой формы какой-то результат (например, подтверждение пересчета цен). Если мы просто закроем форму методом Закрыть(), процедура-обработчик оповещения получит результат Неопределено.

Если логика документа требует, чтобы цены были пересчитаны после выбора организации, простое закрытие окна может привести к тому, что данные в табличной части останутся некорректными. Чтобы избежать этого, мы можем передать в метод Закрыть() структуру, которую ожидает вызывающий код. Рассмотрим пример:


// Предположим, мы нашли форму в коллекции окон как ПеременнаяФорма
СтруктураРезультата = Новый Структура;
СтруктураРезультата.Вставить("ПересчитатьЦены", Истина);
СтруктураРезультата.Вставить("ВалютаРасчетов", Объект.ВалютаДокумента);

ПеременнаяФорма.Закрыть(СтруктураРезультата);

Таким образом, мы имитируем нажатие кнопки «ОК» в диалоговом окне, и типовой алгоритм пересчета отработает штатно, не прерывая выполнение нашей обработки.

Решение №3: Использование расширений (аннотация &Вместо)

Если задача стоит в том, чтобы окно вообще не появлялось при работе конкретного пользователя или при определенных условиях, лучшим инструментом будет создание расширения. В расширении мы можем перехватить процедуру, отвечающую за открытие формы. В УНФ это часто процедура ОбработатьИзмененияПоКнопкеЦеныИВалюты.

Рассмотрим пример использования аннотации &Вместо. Мы полностью заменим логику процедуры, исключив из нее вызов ОткрытьФорму.

Шаги по реализации в расширении:

  1. Создаем новое расширение и добавляем в него документ (например, ЗаказПокупателя) или общий модуль, где находится процедура.
  2. Находим процедуру, вызывающую окно.
  3. Используем аннотацию &Вместо, копируем исходный код, но комментируем строку открытия формы.

&Вместо("ОбработатьИзмененияПоКнопкеЦеныИВалюты")
Процедура Расш1_ОбработатьИзмененияПоКнопкеЦеныИВалюты(Знач ВалютаПередИзменением, ПересчитатьЦены = Ложь)
    // Копируем типовую логику подготовки параметров
    // ...
    
    // Вместо ОткрытьФорму("ОбщаяФорма.ФормаЦеныИВалюта", ...)
    // Сразу вызываем логику, которая должна была выполниться после закрытия формы
    СтруктураРезультата = Новый Структура("ПересчитатьЦены, ПерезаполнитьЦены", Истина, Ложь);
    ВыполнитьОбработкуВыбораЦеныИВалюты(СтруктураРезультата, ДополнительныеПараметры);
КонецПроцедуры

Внимание: Использование &Вместо требует осторожности при обновлениях. Если в новой версии конфигурации типовая процедура изменится, ваше расширение продолжит выполнять старый код. Более безопасным вариантом является использование &ИзменениеИКонтроль, где можно точечно удалить только команду открытия окна, сохранив остальную логику в актуальном состоянии.

Решение №4: Подавление через параметры серверного вызова

Проанализируем еще один профессиональный подход. Большинство диалоговых окон в современных конфигурациях на базе БСП (Библиотеки стандартных подсистем) открываются только в клиентском контексте. Если вы переносите логику изменения реквизитов (например, организации) на сервер, используя объектную технику, окно «Цены и валюта» не появится автоматически, так как метод ОткрытьФорму недоступен на сервере.

Рассмотрим пример кода во внешней обработке:


&НаСервере
Процедура ИзменитьОрганизациюНаСервере(СсылкаНаДокумент, НоваяОрганизация)
    ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    ДокументОбъект.Организация = НоваяОрганизация;
    
    // Принудительно вызываем обработку изменения, если нужно
    // Но здесь формы открываться не будут!
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры

При таком подходе все клиентские события формы документа игнорируются, и запись происходит напрямую в базу данных. Это самый чистый способ для массовой обработки документов без вмешательства в программный код конфигурации.

Подведение итогов

Мы рассмотрели несколько способов борьбы с навязчивыми окнами в 1С:

Выбор метода зависит от ваших целей: если нужно разово исправить данные — используйте серверный контекст; если нужно изменить поведение интерфейса для пользователя — используйте расширения.

← На главную