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