Почему зависает 1С при попытке открыть управляемую форму из кода

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда необходимо при открытии одной формы перенаправить пользователя в другую или открыть конкретный экземпляр документа. Однако неаккуратное использование методов ОткрытьФорму или ПолучитьФорму внутри обработчиков событий самой формы может привести к критической ошибке — бесконечному циклу (рекурсии), из-за которого программа просто «зависает».

Рассмотрим подробно, почему это происходит, разберем типичные ошибки в коде и изучим правильные архитектурные подходы для решения этой задачи.

Разбор ситуации: механизм возникновения бесконечного цикла

Проанализируем ситуацию, описанную автором темы. Разработчик пытается в событии ПриОткрытии программно открыть форму того же самого документа. Давайте посмотрим на логику работы платформы в этот момент:

  1. Пользователь или система инициирует открытие формы документа КарточкаРемонта.
  2. Платформа создает экземпляр формы и запускает обработчик ПриОткрытии на стороне клиента.
  3. Внутри этого обработчика выполняется код ОткрытьФорму("Документ.КарточкаРемонта.Форма.ФормаДокумента", Ключ).
  4. Система послушно создает второй экземпляр той же самой формы.
  5. Для второго экземпляра снова срабатывает событие ПриОткрытии, которое опять вызывает ОткрытьФорму.
  6. Процесс повторяется до бесконечности (или до исчерпания стека вызовов/памяти), что внешне выглядит как «зависание» программы.

Это классическая ловушка рекурсии. Чтобы её избежать, нам нужно либо изменить место вызова кода, либо добавить условие прерывания этого цикла.

Решение №1: Использование параметров формы для прерывания рекурсии

Если логику открытия необходимо оставить именно внутри формы, мы должны как-то «сообщить» второму экземпляру формы, что его открыли программно и повторно вызывать ОткрытьФорму не нужно. Для этого используются Параметры формы.

Рассмотрим пример правильной реализации с использованием вспомогательного флага:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    
    // Проверяем, не является ли это открытие уже результатом нашего "перехода"
    ЭтоПереход = Ложь;
    Если Параметры.Свойство("ЭтоУжеОткрыто", ЭтоПереход) И ЭтоПереход Тогда
        Возврат; // Выходим из процедуры, чтобы не создавать новый цикл
    КонецЕсли;

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

КонецПроцедуры

В этом примере мы используем параметр ЭтоУжеОткрыто. При первом запуске его нет в структуре Параметры, и код выполняется. При втором запуске (вызванном методом ОткрытьФорму) параметр уже равен Истина, срабатывает Возврат, и цикл разрывается.

Решение №2: Использование параметра «Ключ» и оптимизация запроса

Важно помнить, что в управляемом приложении параметр Ключ является зарезервированным. Если в структуру параметров метода ОткрытьФорму передать свойство Ключ со значением ссылки на объект, система автоматически попытается открыть форму именно этого объекта.

Также проанализируем функцию поиска документа. Рассмотрим, как сделать её эффективнее. Вместо перебора всех записей в цикле Пока ... Следующий(), лучше использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1.


&НаСервереБезКонтекста
Функция ПолучитьСсылкуНаНужныйДок(ДокументОснование)
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    КарточкаРемонта.Ссылка КАК Ссылка
    |ИЗ
    |    Документ.КарточкаРемонта КАК КарточкаРемонта
    |ГДЕ
    |    КарточкаРемонта.ДокументОснование = &ДокументОснование";
    
    Запрос.УстановитьПараметр("ДокументОснование", ДокументОснование);
    РезультатЗапроса = Запрос.Выполнить();
    
    Если НЕ РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    КонецЕсли;
    
    Возврат Документы.КарточкаРемонта.ПустаяСсылка();
КонецФункции

Использование ВЫБРАТЬ ПЕРВЫЕ 1 существенно снижает нагрузку на сервер базы данных, так как поиск прекращается сразу после нахождения подходящей записи.

Решение №3: Перенос логики в вызывающую команду (лучшая практика)

Самый правильный с точки зрения архитектуры 1С способ — это вообще не допускать открытия «неправильной» формы. Вместо того чтобы писать код в обработчике ПриОткрытии, логику поиска и выбора формы следует вынести в Команду.

Выясним, как это работает по шагам:

  1. Создаем общую команду или команду объекта (например, «Открыть карточку ремонта»).
  2. В обработчике команды на клиенте сначала вызываем серверную функцию для поиска нужной ссылки.
  3. И только после того, как ссылка получена, вызываем ОткрытьФорму.

Рассмотрим пример кода команды:


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

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

Важные замечания по методам открытия

Проанализируем еще несколько ошибок, встретившихся в обсуждении:

Подведем итог: Основная причина зависания при открытии управляемой формы — рекурсивный вызов события открытия. Для решения используйте либо флаги в параметрах формы, либо (что предпочтительнее) выносите логику определения открываемого объекта в команды или модули, предшествующие созданию формы.

← На главную