Как проверить, открыта ли форма документа, и программно закрыть её или обновить данные?

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

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

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

Почему нельзя проверить «открытость» формы на сервере?

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

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

Способ 1. Поиск и закрытие формы на клиенте через «ПолучитьОкна»

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

Рассмотрим пример кода, который следует выполнить на клиенте перед вызовом серверной процедуры записи:


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

Преимущество: мы полностью устраняем риск конфликта версий данных, так как форма документа перестает существовать до момента записи на сервере.

Способ 2. Проверка блокировки объекта на сервере

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

Разберем, как это реализовать в серверном коде:


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

Важный нюанс: если форма открыта только для просмотра, метод Заблокировать() отработает успешно. Блокировка устанавливается только в режиме редактирования.

Способ 3. Использование механизма оповещений (Оповестить)

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

Алгоритм действий следующий:

  1. На сервере вносим изменения в документ и записываем его.
  2. После возврата управления на клиент вызываем метод Оповестить() или ОповеститьОбИзменении().
  3. В форме самого документа прописываем реакцию на это оповещение.

Посмотрим на программную реализацию в форме задачи:


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

Теперь в форме Внутреннего документа нам нужно добавить обработчик ОбработкаОповещения:


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

Способ 4. Использование КлючаУникальности

Если вы точно знаете, как открывалась форма документа (например, через ваш программный код), вы можете использовать КлючУникальности. При поиске формы методом ПолучитьФорму() или НайтиФорму() с указанием того же ключа, система вернет вам существующий экземпляр формы. Если форма найдена — её свойство Открыта будет равно Истина, и вы сможете вызвать метод Закрыть().

Выясним причину конфликта версий

Почему система выдает ошибку, если мы не закрыли форму? У каждого объекта в 1С есть скрытый реквизит — ВерсияДанных. При открытии формы версия считывается в контекст клиента. Когда мы записываем объект на сервере (например, через Объект.Записать()), версия в базе данных обновляется. При попытке пользователя нажать «Записать» в своей открытой форме, система сравнивает «старую» версию из формы и «новую» версию из базы. Если они не совпадают — запись блокируется.

Резюмируем:

Применение этих методов позволит вам создавать стабильные решения в 1С:Документообороте и других конфигурациях на управляемых формах, избегая досадных ошибок потери данных у пользователей.

← На главную