Как изменить реквизит документа при интерактивной пометке на удаление в 1С 7.7?

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

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

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

Проанализируем механику работы платформы 1С 7.7 при интерактивной установке пометки на удаление:

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

Блокировка снимается платформой только после полного завершения процедуры ПриУдаленииДокумента и закрытия системной транзакции. Следовательно, изменить реквизит внутри самого события синхронно невозможно — модификацию необходимо отложить во времени.

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

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

Разберем реализацию по шагам:

  1. В глобальном модуле объявляем глобальную переменную — список значений для накопления очереди документов.
  2. В процедуре ПриУдаленииДокумента добавляем текущий документ в этот список и запускаем таймер платформы через ОбработкаОжидания.
  3. Создаем служебную процедуру, которая обходит накопленный список, открывает каждый документ для записи, меняет реквизит, сохраняет изменения и отключает таймер.

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


Перем глСписокУдаляемыхДокументов Экспорт;

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

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

Способ 2. Отложенное внешнее событие с помощью внешних компонент (FormEx)

Если в конфигурации подключена библиотека FormEx или 1С++, можно использовать механизм отложенной отправки сообщений Windows (PostMessage). Это позволяет передать управление в процедуру ОбработкаВнешнегоСобытия сразу после завершения текущей транзакции удаления, не ожидая секундного интервала таймера.

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

Способ 3. Перенос хранения данных в отдельный объект метаданных

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

Запись в сторонний объект метаданных не блокируется внутренней транзакцией документа. В процедуре ПриУдаленииДокумента можно напрямую создать элемент служебного справочника или добавить запись в лог-журнал:


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

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

← На главную