В современных типовых конфигурациях 1С, таких как «Управление торговлей, ред. 11», «ERP Управление предприятием» или «Комплексная автоматизация», механизмы контроля ссылочной целостности и бизнес-логики глубоко интегрированы на уровне платформы и конфигурации. Обычный вызов метода Удалить() для объектной сущности документа часто приводит к системным ошибкам или блокировкам. Это связано с тем, что система пытается выполнить каскад проверок в модулях объекта, подписках на события и связанных регистрах.
В этой статье мы подробно разберем, почему возникает ошибка при программном удалении документа ПриобретениеТоваровУслуг, проанализируем внутренние механизмы платформы и предложим несколько эффективных способов решения этой проблемы по шагам.
Когда мы программно вызываем метод Удалить() для объекта документа, платформа выполняет целый комплекс неявных действий:
ПередУдалением и ПриЗаписи (для очистки движений).УстановкаЦенНоменклатуры, регистры сведений РеестрДокументов, СвязанныеДокументы), СУБД или логика конфигурации блокируют операцию.Рассмотрим подробнее основные преграды и способы их обхода.
Самый простой и в то же время радикальный способ обойти большинство программных проверок и подписок на события при программном удалении — это использование свойства ОбменДанными.Загрузка. Установка этого флага в значение Истина сообщает системе, что объект записывается или удаляется в режиме переноса данных, поэтому выполнять стандартные проверки бизнес-логики и заполнения реквизитов не требуется.
Важный нюанс: просто вызвать метод Удалить() у ссылки нельзя, так как проверки все равно сработают. Нам необходимо сначала получить объект документа, установить свойство и только затем выполнить удаление. Разберем этот процесс на примере кода:
// Получаем объект документа по ссылке
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокОбъект <> Неопределено Тогда
// Отключаем выполнение подписок на события и проверок бизнес-логики
ДокОбъект.ОбменДанными.Загрузка = Истина;
Попытка
ДокОбъект.Удалить();
Сообщить("Документ успешно удален из базы данных.");
Исключение
// Обрабатываем исключение, если возникла ошибка на уровне СУБД
Сообщить("Не удалось удалить документ. Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Этот метод решает проблему в 90% случаев, когда удалению мешают внутренние подписки конфигурации (например, ПроверитьВозможностьУдаленияДокумента).
Часто документ ПриобретениеТоваровУслуг является основанием для автоматического формирования цен через документ УстановкаЦенНоменклатуры. В системе существует жесткая логическая связь. Если мы попытаемся удалить приобретение, на которое ссылается установка цен, система выдаст ошибку ссылочной целостности.
Проанализируем ситуацию: просто пометить документ УстановкаЦенНоменклатуры на удаление или очистить его реквизит ДокументОснование в шапке бывает недостаточно. Ссылки на документ-приобретение могут храниться непосредственно в табличных частях (например, в табличной части Товары) или в скрытых реквизитах строк.
Разберем по шагам, как программно разорвать эту связь перед удалением основного документа:
// Шаг 1. Находим связанные документы УстановкаЦенНоменклатуры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| СвязанныеДокументы.Ссылка КАК Ссылка
|ИЗ
| Документ.УстановкаЦенНоменклатуры КАК СвязанныеДокументы
|ГДЕ
| СвязанныеДокументы.ДокументОснование = &ДокументОснование";
Запрос.УстановитьПараметр("ДокументОснование", СсылкаНаПриобретение);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Шаг 2. Получаем объект связанного документа для редактирования
ОбъектУстановки = Выборка.Ссылка.ПолучитьОбъект();
ОбъектУстановки.ОбменДанными.Загрузка = Истина;
// Очищаем ссылку в шапке
ОбъектУстановки.ДокументОснование = Документы.ПриобретениеТоваровУслуг.ПустаяСсылка();
// Шаг 3. Очищаем возможные ссылки в табличной части "Товары"
Для Каждого СтрокаТовара Из ОбъектУстановки.Товары Цикл
Если СтрокаТовара.Свойство("ДокументОснование") И СтрокаТовара.ДокументОснование = СсылкаНаПриобретение Тогда
СтрокаТовара.ДокументОснование = Неопределено;
КонецЕсли;
КонецЦикла;
Попытка
ОбъектУстановки.Записать(РежимЗаписиДокумента.Запись);
Исключение
Сообщить("Не удалось обновить связанную Установку цен: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецЕсли;
После того как все внешние связи разорваны, можно повторно вызвать метод Удалить() для документа приобретения.
Современные версии 1С:УТ 11 активно используют вспомогательные регистры сведений для оптимизации списков и отчетов. К ним относятся регистры РеестрДокументов и СвязанныеДокументы. При вызове стандартного удаления подписки на события пытаются очистить записи в этих регистрах, но из-за конфликтов блокировок транзакций этот процесс может завершиться ошибкой.
Рекомендуется перед непосредственным удалением документа принудительно очистить его движения и записи в этих реестрах. Посмотрим на практический пример очистки записей регистра сведений по регистратору:
// Очищаем записи в регистре сведений "Реестр документов"
НаборЗаписейРеестра = РегистрыСведений.РеестрДокументов.СоздатьНаборЗаписей();
НаборЗаписейРеестра.Отбор.Регистратор.Установить(СсылкаНаПриобретение);
Попытка
НаборЗаписейРеестра.Записать(); // Запись пустого набора удаляет движения
Сообщить("Движения в реестре документов успешно очищены.");
Исключение
Сообщить("Ошибка очистки реестра документов: " + ОписаниеОшибки());
КонецПопытки;
Аналогичным образом рекомендуется поступить со всеми регистрами накопления, в которые документ делает движения при проведении. Хотя при удалении система пытается делать это автоматически, явная предварительная очистка снижает нагрузку на СУБД и предотвращает взаимоблокировки.
В коде модулей конфигураций ERP/УТ 11 разработчики фирмы «1С» закладывают специальные флаги-параметры в коллекцию ДополнительныеСвойства объекта. Эти флаги позволяют управлять поведением процедур ПередЗаписью, ПриЗаписи и ПередУдалением.
Если при попытке удаления система выдает специфические ошибки (например, требования к заполнению временных таблиц менеджера или проверки контроля остатков), мы можем передать в объект управляющие свойства:
ОбъектДляУдаления = СсылкаНаПриобретение.ПолучитьОбъект();
// Передаем системные флаги для обхода проверок
ОбъектДляУдаления.ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаРедактирования", Истина);
ОбъектДляУдаления.ДополнительныеСвойства.Вставить("УдалениеОбъекта", Истина);
ОбъектДляУдаления.ДополнительныеСвойства.Вставить("ОтключитьКонтрольОстатков", Истина);
Попытка
ОбъектДляУдаления.Удалить();
Исключение
Сообщить("Удаление не выполнено: " + ОписаниеОшибки());
КонецПопытки;
Использование коллекции ДополнительныеСвойства позволяет тонко управлять логикой удаления без полного отключения подписок на события (в отличие от свойства ОбменДанными.Загрузка).
Необходимо помнить, что непосредственное физическое удаление документов (методом Удалить()) в работающей базе данных — это крайняя мера, способная нарушить ссылочную целостность и породить множество "битых" ссылок (объектов вида <Объект не найден...>).
Рекомендуем придерживаться следующего безопасного алгоритма:
УстановитьПометкуУдаления(Истина). При этом система автоматически пометит на удаление связанные движения и проверит логические связи в интерактивном режиме.