Как изменить реквизиты документа и обновить форму только для просмотра из внешней печатной формы в 1С 7.7

Программист 1С v7.7
← На главную

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

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

---

Анализ проблемы: физические блокировки и режимы просмотра

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

  1. Обычный режим редактирования. Форма открывается интерактивно или через ОткрытьФорму(Контекст, , 0). В этом случае на уровне базы данных (СУБД) накладывается пессимистическая блокировка на объект. Другой пользователь не сможет редактировать этот документ. Изменение реквизитов через переданный контекст формы в ВПФ и последующая запись происходят без проблем.
  2. Программный режим «Только просмотр». Форма открывается в режиме редактирования, но в коде модуля формы (например, в процедуре ПриОткрытии()) отрабатывает команда Форма.ТолькоПросмотр(1). Важно понимать: блокировка объекта на уровне базы данных при этом сохраняется. Интерфейс блокирует поля ввода для пользователя, но физически документ заблокирован сеансом.
  3. Интерактивный просмотр (кнопка «Лупа» или параметр 1 в ОткрытьФорму). В этом режиме форма полностью «оторвана» от реального объекта базы данных. Платформа создает в памяти временный снимок состояния документа. Объект в базе данных остается свободным и не блокируется.

Основная сложность возникает в третьем сценарии. Если документ открыт на просмотр через «лупу», и мы пытаемся изменить реквизиты через контекст формы (Контекст.Комментарий = "Новое значение"), то последующий вызов Контекст.Записать() завершится неудачей или не принесет желаемого результата, так как форма работает с изолированной копией данных. Более того, повторный вызов функции ОткрытьФорму() для уже открытого «лупой» документа не приведет к перечитыванию данных из базы: 1С просто активизирует существующее окно с кэшированными (старыми) данными.

---

Способ 1. Проверка блокировки и штатное обновление формы

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

Выяснить статус блокировки внутри сеанса можно с помощью попытки записи или методом Блокировка(1). Проанализируем следующий пример кода, который можно разместить в модуле ВПФ:


// Пример проверки статуса блокировки объекта в 1С 7.7
Док = СоздатьОбъект("Документ." + КонтекстДокумента.Вид());
Док.НайтиДокумент(КонтекстДокумента.ТекущийДокумент());

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

Если объект заблокирован нашей формой (Сценарии 1 и 2), мы можем напрямую изменять реквизиты через переданный контекст:


КонтекстДокумента.Комментарий = "Значение из печатной формы";
КонтекстДокумента.Записать();
КонтекстДокумента.Форма.Обновить(0);

Если же форма открыта без блокировки (Сценарий 3), то изменять реквизиты необходимо через созданный объект Документ:


Док = СоздатьОбъект("Документ." + КонтекстДокумента.Вид());
Док.НайтиДокумент(КонтекстДокумента.ТекущийДокумент());
Док.Комментарий = "Значение изменено при просмотре";
Док.Записать();

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


// В модуле ВПФ при закрытии или завершении работы
КонтекстДокумента.Форма.Закрыть();
// Запуск обработки ожидания для открытия обновленного документа
Форма.ОбработкаОжидания("ОткрытьДокументЗаново", 1);

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

---

Способ 2. Бесшовное обновление через FormEx (без закрытия окон)

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

Проанализируем пошагово алгоритм решения этой задачи с помощью FormEx:

  1. Определяем, заблокирован ли документ текущей формой. Для этого используем изящный финт со свойством ТолькоПросмотр.
  2. Если форма удерживает блокировку объекта, мы временно сбрасываем признак просмотра, записываем данные через контекст с помощью объекта Сервис и возвращаем признак обратно.
  3. Если форма открыта через «лупу» (объект свободен), мы записываем изменения в базу данных через новый объект Документ. Затем, используя РасширениеФормы, мы напрямую подменяем значения отображаемых реквизитов прямо в элементах диалога открытого окна без переоткрытия формы.

Рассмотрим подробный пример реализации этого механизма в модуле внешней печатной формы:


Перем Конт; // Переменная для хранения контекста родительской формы

//======================================================================
Процедура ВыполнитьМодификацию()
    Если ТипЗначения(Конт) <> 100 Тогда
        Предупреждение("Контекст формы недоступен или форма документа была закрыта!", 10);
        Возврат;
    КонецЕсли;

    // Считываем текущую настройку просмотра формы
    ТолькоПросмотр = Конт.Форма.ТолькоПросмотр(0);
    // Проверяем, освободилась ли форма. 
    // Если ТолькоПросмотр(0) вернул 0, значит, форма жестко заблокирована системой (редактирование)
    ФормаСвободна = Конт.Форма.ТолькоПросмотр(0);

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

//======================================================================
Процедура ПриОткрытии()
    // Получаем контекст вызвавшей формы из параметров
    Конт = Форма.Параметр;
    
    // Запускаем алгоритм модификации реквизитов
    ВыполнитьМодификацию();
    
    // Закрываем форму самой ВПФ, так как нам нужно было лишь выполнить код
    СтатусВозврата(0);
КонецПроцедуры

---

Определение контекста вызова внешней формы

Еще одна важная задача при разработке универсальных модулей — понять, откуда именно была вызвана ВПФ. В типовых конфигурациях (например, «Торговля и Склад 9.2») печать может запускаться:

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


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

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

← На главную