Разработчики, продолжающие поддерживать и развивать решения на платформе «1С:Предприятие 7.7», часто сталкиваются с неочевидным поведением интерфейса и механизмов блокировки ядра СУБД. Одна из классических задач — необходимость изменить реквизиты документа (например, зафиксировать факт печати, проставить статус или записать комментарий) непосредственно из внешней печатной формы (ВПФ) или подключаемого модуля, а затем мгновенно обновить открытую форму документа на экране пользователя.
В этой статье мы подробно разберем, как ведет себя платформа в различных режимах открытия документов («Только просмотр», просмотр через «лупу», обычное редактирование), почему стандартные методы обновления отображения могут не работать, и как решить эту задачу как стандартными средствами встроенного языка, так и с привлечением возможностей популярной внешней компоненты FormEx.
---
Для начала проанализируем, как платформа «1С:Предприятие 7.7» обрабатывает открытие документов. Существует несколько принципиально разных сценариев запуска экранной формы:
ОткрытьФорму(Контекст, , 0). В этом случае на уровне базы данных (СУБД) накладывается пессимистическая блокировка на объект. Другой пользователь не сможет редактировать этот документ. Изменение реквизитов через переданный контекст формы в ВПФ и последующая запись происходят без проблем.ПриОткрытии()) отрабатывает команда Форма.ТолькоПросмотр(1). Важно понимать: блокировка объекта на уровне базы данных при этом сохраняется. Интерфейс блокирует поля ввода для пользователя, но физически документ заблокирован сеансом.Основная сложность возникает в третьем сценарии. Если документ открыт на просмотр через «лупу», и мы пытаемся изменить реквизиты через контекст формы (Контекст.Комментарий = "Новое значение"), то последующий вызов Контекст.Записать() завершится неудачей или не принесет желаемого результата, так как форма работает с изолированной копией данных. Более того, повторный вызов функции ОткрытьФорму() для уже открытого «лупой» документа не приведет к перечитыванию данных из базы: 1С просто активизирует существующее окно с кэшированными (старыми) данными.
---
Рассмотрим, как решить задачу стандартными средствами платформы. Если нам необходимо изменить реквизиты документа из ВПФ и корректно обновить его отображение, сначала нужно выяснить, заблокирован ли объект текущей формой (то есть открыта ли она в режиме редактирования/программного просмотра) или же объект свободен (открыт через «лупу»).
Выяснить статус блокировки внутри сеанса можно с помощью попытки записи или методом Блокировка(1). Проанализируем следующий пример кода, который можно разместить в модуле ВПФ:
// Пример проверки статуса блокировки объекта в 1С 7.7
Док = СоздатьОбъект("Документ." + КонтекстДокумента.Вид());
Док.НайтиДокумент(КонтекстДокумента.ТекущийДокумент());
// Попытаемся установить блокировку программно
Если Док.Блокировка(1) = 0 Тогда
// Объект уже заблокирован (скорее всего, нашей же формой редактирования)
Сообщить("Документ заблокирован формой редактирования.");
Иначе
// Объект свободен (форма открыта в режиме просмотра "лупой")
Сообщить("Документ открыт на просмотр без блокировки.");
// Обязательно снимаем установленную нами блокировку
Док.Блокировка(0);
КонецЕсли;
Если объект заблокирован нашей формой (Сценарии 1 и 2), мы можем напрямую изменять реквизиты через переданный контекст:
КонтекстДокумента.Комментарий = "Значение из печатной формы";
КонтекстДокумента.Записать();
КонтекстДокумента.Форма.Обновить(0);
Если же форма открыта без блокировки (Сценарий 3), то изменять реквизиты необходимо через созданный объект Документ:
Док = СоздатьОбъект("Документ." + КонтекстДокумента.Вид());
Док.НайтиДокумент(КонтекстДокумента.ТекущийДокумент());
Док.Комментарий = "Значение изменено при просмотре";
Док.Записать();
Для того чтобы пользователь увидел изменения на экране в случае "лупы", форму придется закрыть и открыть заново. Чтобы избежать мгновенного закрытия в процессе работы кода ВПФ, операцию переоткрытия часто выносят в обработку ожидания:
// В модуле ВПФ при закрытии или завершении работы
КонтекстДокумента.Форма.Закрыть();
// Запуск обработки ожидания для открытия обновленного документа
Форма.ОбработкаОжидания("ОткрытьДокументЗаново", 1);
Этот способ рабочий, но имеет существенный недостаток: на экране пользователя происходит заметное «моргание» интерфейса, окно документа закрывается и позиционируется заново.
---
Если на предприятии допускается использование внешних компонент, идеальным решением станет применение библиотеки FormEx. Она предоставляет разработчику объект РасширениеФормы, который позволяет управлять элементами диалога напрямую, и объект Сервис, позволяющий вызывать стандартные процедуры (например, запись) в обход ограничений интерфейса 1С.
Проанализируем пошагово алгоритм решения этой задачи с помощью FormEx:
ТолькоПросмотр.Сервис и возвращаем признак обратно.Документ. Затем, используя РасширениеФормы, мы напрямую подменяем значения отображаемых реквизитов прямо в элементах диалога открытого окна без переоткрытия формы.Рассмотрим подробный пример реализации этого механизма в модуле внешней печатной формы:
Перем Конт; // Переменная для хранения контекста родительской формы
//======================================================================
Процедура ВыполнитьМодификацию()
Если ТипЗначения(Конт) <> 100 Тогда
Предупреждение("Контекст формы недоступен или форма документа была закрыта!", 10);
Возврат;
КонецЕсли;
// Считываем текущую настройку просмотра формы
ТолькоПросмотр = Конт.Форма.ТолькоПросмотр(0);
// Проверяем, освободилась ли форма.
// Если ТолькоПросмотр(0) вернул 0, значит, форма жестко заблокирована системой (редактирование)
ФормаСвободна = Конт.Форма.ТолькоПросмотр(0);
Если ФормаСвободна = 0 Тогда
// Сценарий А: Форма удерживает блокировку. Изменяем через контекст.
Конт.Комментарий = "Печать выполнена: " + ТекущееВремя();
// Возвращаем исходный режим просмотра
Конт.Форма.ТолькоПросмотр(ТолькоПросмотр);
// Используем объект "Сервис" компоненты FormEx для программной записи заблокированной формы
Сервис = СоздатьОбъект("Сервис");
Сервис.ВыполнитьПроцедуру(Конт, "Записать");
// Обновляем отображение
Конт.Форма.Обновить(0);
Иначе
// Сценарий Б: Форма открыта на просмотр ("лупа"), объект в БД не заблокирован.
// Модифицируем данные напрямую в базе данных
Док = СоздатьОбъект("Документ." + Конт.Вид());
Док.НайтиДокумент(Конт.ТекущийДокумент());
Док.Комментарий = "Просмотрено и напечатано: " + ТекущееВремя();
Док.Записать();
// Используем "РасширениеФормы" FormEx для бесшовной подмены данных на экране
РасшФормы = СоздатьОбъект("РасширениеФормы");
РасшФормы.УстановитьФорму(Cont.Форма);
// Получаем доступ к атрибуту формы "Комментарий" и меняем его визуальное значение
АтрибутФормы = РасшФормы.ПолучитьАтрибут("Комментарий");
АтрибутФормы.Значение = Док.Комментарий;
// Обновляем форму без закрытия и мерцания
Конт.Форма.Обновить(0);
КонецЕсли;
КонецПроцедуры
//======================================================================
Процедура ПриОткрытии()
// Получаем контекст вызвавшей формы из параметров
Конт = Форма.Параметр;
// Запускаем алгоритм модификации реквизитов
ВыполнитьМодификацию();
// Закрываем форму самой ВПФ, так как нам нужно было лишь выполнить код
СтатусВозврата(0);
КонецПроцедуры
---
Еще одна важная задача при разработке универсальных модулей — понять, откуда именно была вызвана ВПФ. В типовых конфигурациях (например, «Торговля и Склад 9.2») печать может запускаться:
ПриОткрытии(), выполняет печать и сразу закрывается через СтатусВозврата(0)).Выяснить это программно нам поможет анализ активности элементов диалога. На этапе выполнения встроенной предопределенной процедуры ПриОткрытии() родительской формы визуальная часть интерфейса еще не построена, и фокус ввода отсутствует. На помощь приходит метод расширения формы АктивныйЭлемент():
// Метод определения контекста запуска печатной формы
Если ПустоеЗначение(Конт.Форма.АктивныйЭлемент()) = 1 Тогда
// Форма еще не отображена на экране, вызов происходит из процедуры ПриОткрытии()
Сообщить("ВПФ запущена в фоновом режиме печати из журнала.");
Иначе
// Форма уже визуализирована, пользователь нажал кнопку на диалоге
Сообщить("ВПФ запущена интерактивно из открытого окна документа.");
КонецЕсли;
Использование этого признака позволяет гибко управлять логикой: например, не производить переоткрытие окон, если печать выполняется в фоновом режиме из журнала документов, и делать полное бесшовное обновление интерфейса, если пользователь работает непосредственно с документом на экране.