Как записать документ в глобальном событии ПослеЗакрытия библиотеки FormEx в 1С 7.7

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

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

Причина возникновения ошибки «Документ заблокирован»

Проанализируем механику работы платформы 1С 7.7 и компонента FormEx. Событие ПослеЗакрытия(Конт) выбывается сразу после закрытия диалогового окна формы. Однако сама платформа снятие блокировки с объекта (документа) производит лишь после полного завершения цепочки вызовов закрытия окна и возврата управления в главный цикл обработки событий.

Когда мы пытаемся создать объект Документ, найти его по ссылке из контекста Конт.ТекущийДокумент() и выполнить модификацию с вызовом Записать(), платформа отвергает действие, так как форма еще удерживает фоновую блокировку записи.

Способ 1. Использование асинхронного вызова через ОбработкаОжидания

Наиболее простое и элегантное решение — разнести момент закрытия формы и момент записи документа по времени. Для этого используется штатная процедура 1С 7.7 ОбработкаОжидания().

Рассмотрим по шагам, как реализовать этот подход:

  1. В процедуре ПослеЗакрытия(Конт) проверяем, что закрытый контекст является документом и он сохранен в базе.
  2. Запоминаем ссылку на документ в глобальную переменную или глобальный список значений.
  3. Запускаем однократный таймер через ОбработкаОжидания.
  4. В процедуре обработки таймера первым делом отключаем его, получаем объект документа, заполняем реквизит и сохраняем изменения.

Посмотрим на пример реализации в глобальном модуле:


Переменная глДокументДляЗаписи;

Процедура ПослеЗакрытия(Конт)
    Если ТипЗначенияСтр(Конт) = "ГруппаКонтекста" Тогда
        Если Конт.Вид() = "Документ" Тогда
            Если Конт.ТекущийДокумент().Выбран() = 1 Тогда
                глДокументДляЗаписи = Конт.ТекущийДокумент();
                ОбработкаОжидания("глОтложеннаяЗаписьДокумента", 1);
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

К моменту вызова процедуры глОтложеннаяЗаписьДокумента() главный цикл обработки событий 1С успевает завершить закрытие окна, и блокировка с документа полностью снимается.

Способ 2. Перехват события ПриЗаписи с помощью библиотеки 1С++ (класс Hooker)

Если задача заключается в гарантии записи реквизита при любом сохранении документа (а не только при закрытии окна), то закрытие формы — не самое лучшее место для такой логики. Например, если пользователь закрывает форму без сохранения или нажимает «Отмена», отложенная запись может модифицировать документ вопреки логике.

В таком случае рекомендуется использовать класс Hooker (Перехватчик) из библиотеки 1С++. Он позволяет перехватывать глобальное событие СобытиеГМ_ПриЗаписи до или в момент сохранения объекта.

Разберем порядок настройки перехватчика:

  1. Подключаем класс Hooker через ЗагрузитьВнешнююКомпоненту("1cpp.dll").
  2. В глобальном модуле перехватываем предопределенное событие записи документа.
  3. Модифицируем реквизит непосредственно в моменте записи, когда документ находится в транзакции и доступен для изменения.

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


Переменная Перехватчик;

Процедура ПриНачалеРаботыСистемы()
    ЗагрузитьВнешнююКомпоненту("1cpp.dll");
    Перехватчик = СоздатьОбъект("Hooker");
    Перехватчик.Заразить("СобытиеГМ_ПриЗаписи");
КонецПроцедуры

Процедура СобытиеГМ_ПриЗаписи(Конт, ФлагЗапроса)
    // Данная процедура перехватывает ПриЗаписи для всех документов
    Если Конт.Вид() = "Документ" Тогда
        Конт.НашОбщийРеквизит = "Значение";
    КонецЕсли;
КонецПроцедуры

Альтернативные варианты и важные ограничения

Выясним, какие еще подходы можно применить и о каких нюансах стоит помнить:

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

← На главную