При разработке в среде 1С:Предприятие 7.7 разработчики нередко сталкиваются с необходимостью зафиксировать факт или причину удаления документа непосредственно в его реквизитах при интерактивных действиях пользователя в журнале. Однако стандартная попытка модифицировать и записать документ в глобальной процедуре ПриУдаленииДокумента приводит к ошибке времени выполнения: «Объект заблокирован». Выясним причину такого поведения платформы и разберем надежные варианты решения этой задачи.
Проанализируем механику работы платформы 1С 7.7 при интерактивной установке пометки на удаление:
Del или выбирает пункт меню «Пометить на удаление», платформа автоматически открывает внутреннюю неявную транзакцию.ПриУдаленииДокумента(КонтекстДокумента, Режим).Записать() (напрямую или через новый объект СоздатьОбъект("Документ")), платформа определяет повторную попытку блокировки уже заблокированного в текущей транзакции объекта и выбрасывает исключение.Блокировка снимается платформой только после полного завершения процедуры ПриУдаленииДокумента и закрытия системной транзакции. Следовательно, изменить реквизит внутри самого события синхронно невозможно — модификацию необходимо отложить во времени.
Рассмотрим наиболее доступный штатный способ без использования сторонних библиотек. Идея заключается в том, чтобы зафиксировать ссылку на удаляемый документ в момент события, а саму перезапись реквизита выполнить чуть позже по системному таймеру, когда транзакция платформы уже завершится.
Разберем реализацию по шагам:
ПриУдаленииДокумента добавляем текущий документ в этот список и запускаем таймер платформы через ОбработкаОжидания.Посмотрим на пример кода в глобальном модуле:
Перем глСписокУдаляемыхДокументов Экспорт;
Процедура глОбработатьУдаленныеДокументы() Экспорт
// Отключаем обработку ожидания, чтобы исключить циклические вызовы
ОбработкаОжидания("глОбработатьУдаленныеДокументы", 0);
Если глСписокУдаляемыхДокументов.РазмерСписка() = 0 Тогда
Возврат;
КонецЕсли;
Док = СоздатьОбъект("Документ");
Для Ном = 1 По глСписокУдаляемыхДокументов.РазмерСписка() Цикл
ТекДок = глСписокУдаляемыхДокументов.ПолучитьЗначение(Ном);
Если Док.НайтиДокумент(ТекДок) = 1 Тогда
// Объект уже разблокирован платформой
Док.Комментарий = "Помечен на удаление пользователем " + ИмяПользователя();
Док.Записать();
КонецЕсли;
КонецЦикла;
// Очищаем обработанный список
глСписокУдаляемыхДокументов.УдалитьВсе();
КонецПроцедуры
Процедура ПриУдаленииДокумента(Док, Режим)
// Режим = 1 (установка пометки), Режим = 2 (снятие пометки)
Если Режим = 1 Тогда
Если ПустоеЗначение(глСписокУдаляемыхДокументов) = 1 Тогда
глСписокУдаляемыхДокументов = СоздатьОбъект("СписокЗначений");
КонецЕсли;
глСписокУдаляемыхДокументов.ДобавитьЗначение(Док.ТекущийДокумент());
ОбработкаОжидания("глОбработатьУдаленныеДокументы", 1);
КонецЕсли;
КонецПроцедуры
Если в конфигурации подключена библиотека FormEx или 1С++, можно использовать механизм отложенной отправки сообщений Windows (PostMessage). Это позволяет передать управление в процедуру ОбработкаВнешнегоСобытия сразу после завершения текущей транзакции удаления, не ожидая секундного интервала таймера.
ПриУдаленииДокумента генерируем внешнее событие с передачей идентификатора или ссылки на документ.ОбработкаВнешнегоСобытия(Источник, Событие, Данные) перехватываем отправленный сигнал.Записать().Проанализируем архитектурную альтернативу: если требуется сохранять служебные данные (например, имя пользователя, причину отмены, историю удаления или дату действия), менять сам документ не обязательно.
Запись в сторонний объект метаданных не блокируется внутренней транзакцией документа. В процедуре ПриУдаленииДокумента можно напрямую создать элемент служебного справочника или добавить запись в лог-журнал:
Процедура ПриУдаленииДокумента(Док, Режим)
СпрЛог = СоздатьОбъект("Справочник.ИсторияУдалений");
СпрЛог.Новый();
СпрЛог.Документ = Док.ТекущийДокумент();
СпрЛог.Пользователь = ИмяПользователя();
СпрЛог.ДатаДействия = ТекущаяДата();
СпрЛог.ВремяДействия = ТекущееВремя();
СпрЛог.ВидОперации = ?(Режим = 1, "Установка пометки", "Снятие пометки");
СпрЛог.Записать();
КонецПроцедуры
Такой подход полностью исключает конфликты параллельных блокировок, гарантирует целостность аудита и не требует использования таймеров или внешних компонент.