В этой статье мы подробно рассмотрим, как правильно удалить все элементы справочника в системе 1С:Предприятие 8.3, с учетом возможных ошибок, особенностей работы с расширениями и рекомендаций по сохранению целостности базы данных. Мы проанализируем распространенную ошибку, связанную с выполнением серверного кода на клиенте, и предложим несколько методов очистки справочника, от самых безопасных до потенциально опасных, но быстрых.
Разберем исходную проблему, с которой столкнулся автор темы. Ошибка "Переменная не определена (Справочники)" возникает, когда вы пытаетесь обратиться к глобальному объекту Справочники (или другим объектам метаданных, таким как Документы, РегистрыСведений и т.д.) в коде, который выполняется на клиентской стороне (например, в модуле формы без специального указания).
Платформа 1С 8.3 строго разделяет клиентский и серверный контексты выполнения. Объекты метаданных, такие как Справочники, доступны только на сервере. Чтобы код мог обращаться к ним, его необходимо выполнять на сервере.
Для этого необходимо пометить процедуру или функцию, в которой содержится обращение к объектам метаданных, специальной директивой компиляции &НаСервере (или &НаСервереБезКонтекста, если контекст формы не требуется).
Посмотрим на пример исходного кода автора и его исправление:
// Исходный код автора, который выдает ошибку на клиенте:
// Выборка = Справочники.Задачи.Выбрать;
// Пока Выборка.Следующий Цикл
// СправочникЗадачи = Выборка.ПолучитьОбъект;
// СправочникЗадачи.Удалить;
// КонецЦикла
// Исправленный код, который будет выполняться на сервере:
&НаСервере
Процедура УдалитьВсеЭлементыСправочникаНаСервере()
// Проверяем, существует ли справочник "Задачи"
Если Метаданные.Справочники.Найти("Задачи") = Неопределено Тогда
Сообщить("Справочник 'Задачи' не найден в конфигурации.");
Возврат;
КонецЕсли;
// Получаем менеджер справочника
МенеджерСправочника = Справочники.Задачи;
// Создаем выборку элементов справочника
Выборка = МенеджерСправочника.Выбрать();
Пока Выборка.Следующий() Цикл
Попытка
// Получаем объект справочника для удаления
СправочникЗадачиОбъект = Выборка.ПолучитьОбъект();
СправочникЗадачиОбъект.Удалить();
// Вместо физического удаления часто рекомендуется пометка на удаление
// СправочникЗадачиОбъект.ПометкаУдаления = Истина;
// СправочникЗадачиОбъект.Записать();
Исключение
Сообщить("Не удалось удалить элемент справочника """ + Выборка.Наименование + """: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЦикла;
Сообщить("Процесс удаления элементов справочника 'Задачи' завершен.");
КонецПроцедуры
// Вызов этой процедуры на клиенте, например, по нажатию кнопки:
// &НаКлиенте
// Процедура КнопкаУдалитьВсеНажатие(Команда)
// УдалитьВсеЭлементыСправочникаНаСервере();
// КонецПроцедуры
Директива &НаСервере указывает платформе, что код процедуры УдалитьВсеЭлементыСправочникаНаСервере() должен быть выполнен на сервере 1С:Предприятия, где доступны все объекты метаданных и прямое взаимодействие с базой данных.
Даже если мы исправили ошибку с серверным вызовом, возникает вопрос архитектуры: где этот код должен находиться? Как правильно указали участники форума, помещать такой код непосредственно в модуль формы элемента справочника или даже в модуль объекта справочника — это плохая практика. Почему?
Наиболее подходящие места для такого кода:
&НаСервере и &Экспорт.Прежде чем приступать к физическому удалению данных, необходимо понимать, что элементы справочника могут иметь ссылки из других объектов базы данных (документов, регистров сведений, других справочников и т.д.). Физическое удаление элемента, на который есть ссылки, приведет к ошибкам нарушения ссылочной целостности.
Наиболее безопасным и рекомендуемым способом удаления является пометка элементов на удаление с последующим использованием типовой обработки "Удаление помеченных объектов". Этот механизм встроен в платформу 1С и позволяет:
Для пометки всех элементов справочника на удаление мы используем свойство ПометкаУдаления:
&НаСервере
Процедура ПометитьВсеЭлементыСправочникаНаУдалениеНаСервере()
Если Метаданные.Справочники.Найти("Задачи") = Неопределено Тогда
Сообщить("Справочник 'Задачи' не найден в конфигурации.");
Возврат;
КонецЕсли;
МенеджерСправочника = Справочники.Задачи;
Выборка = МенеджерСправочника.Выбрать();
Пока Выборка.Следующий() Цикл
Попытка
ОбъектСправочника = Выборка.ПолучитьОбъект();
Если НЕ ОбъектСправочника.ПометкаУдаления Тогда
ОбъектСправочника.ПометкаУдаления = Истина;
ОбъектСправочника.Записать();
КонецЕсли;
Исключение
Сообщить("Не удалось пометить на удаление элемент справочника """ + Выборка.Наименование + """: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЦикла;
Сообщить("Процесс пометки элементов справочника 'Задачи' на удаление завершен.");
КонецПроцедуры
После выполнения этого кода необходимо запустить типовую обработку "Удаление помеченных объектов" (обычно находится в разделе "Администрирование" или "Все функции"), которая произведет физическое удаление или укажет на связанные объекты.
Если по какой-то причине вам необходимо произвести немедленное физическое удаление элементов справочника без использования механизма пометки на удаление, вы можете использовать метод Удалить() объекта справочника. Однако, это действие несет в себе значительные риски:
Удалить() вызовет ошибку, и транзакция будет отменена. Это может быть проблемой при массовом удалении, когда вы не можете заранее знать, какие элементы имеют ссылки, а какие нет.Чтобы ускорить процесс и избежать срабатывания некоторых проверок при физическом удалении (например, обработчиков ПередУдалением, ПриУдалении в модуле объекта), можно временно установить параметр ОбменДанными.Загрузка = Истина. Это дает системе понять, что операция выполняется в режиме загрузки данных, и некоторые проверки можно пропустить.
&НаСервере
Процедура ФизическиУдалитьВсеЭлементыСправочникаНаСервере()
Если Метаданные.Справочники.Найти("Задачи") = Неопределено Тогда
Сообщить("Справочник 'Задачи' не найден в конфигурации.");
Возврат;
КонецЕсли;
// Сохраняем текущее значение ОбменДанными.Загрузка
ТекущееЗначениеЗагрузка = ОбменДанными.Загрузка;
// Устанавливаем режим загрузки для ускорения и отключения проверок
ОбменДанными.Загрузка = Истина;
Попытка
МенеджерСправочника = Справочники.Задачи;
Выборка = МенеджерСправочника.Выбрать();
НачалоТранзакции(); // Начинаем одну большую транзакцию для всей операции
Пока Выборка.Следующий() Цикл
Попытка
ОбъектСправочника = Выборка.ПолучитьОбъект();
ОбъектСправочника.Удалить();
Исключение
Сообщить("Не удалось физически удалить элемент справочника """ + Выборка.Наименование + """: " + ОписаниеОшибки(), СтатусСообщения.Важное);
// Продолжаем удаление других элементов, но можно и прервать транзакцию:
// ОтменитьТранзакцию();
// ОбменДанными.Загрузка = ТекущееЗначениеЗагрузка;
// Возврат;
КонецПопытки;
КонецЦикла;
ЗафиксироватьТранзакцию(); // Фиксируем все изменения
Исключение
ОтменитьТранзакцию(); // Откатываем все изменения при ошибке
Сообщить("Произошла общая ошибка при физическом удалении: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
КонецПопытки;
// Восстанавливаем исходное значение ОбменДанными.Загрузка
ОбменДанными.Загрузка = ТекущееЗначениеЗагрузка;
Сообщить("Процесс физического удаления элементов справочника 'Задачи' завершен.");
КонецПроцедуры
Важно: Использование ОбменДанными.Загрузка = Истина требует особой осторожности. Отключаются не только обработчики событий, но и некоторые другие проверки платформы, что может привести к неконсистентным данным, если вы не полностью контролируете процесс.
Вопрос об удалении элементов справочника, созданного в расширении, часто возникает. Платформа 1С:Предприятие 8.3 позволяет создавать новые объекты метаданных (справочники, документы, регистры и т.д.) непосредственно в расширениях конфигурации.
Хорошая новость: Программный интерфейс для работы с такими справочниками абсолютно идентичен работе со справочниками, созданными в основной конфигурации. Это означает, что все приведенные выше примеры кода (как для пометки на удаление, так и для физического удаления) будут работать точно так же для справочника, который находится в расширении. Вам просто нужно обращаться к нему по его полному имени, включая возможные префиксы, если они были заданы в расширении.
Например, если справочник "Задачи" создан в расширении "МоеРасширение", вы все равно будете обращаться к нему как Справочники.Задачи. Платформа самостоятельно определит, что этот объект находится в расширении.
При работе с тысячами, десятками тысяч или даже миллионами записей обычный цикл с вызовом ПолучитьОбъект() и Удалить() (или Записать() для пометки) может работать крайне медленно и приводить к таймаутам или чрезмерному потреблению ресурсов. Для таких объемов данных мы рекомендуем следующие методы оптимизации:
// Пример вызова фонового задания
ФоновыеЗадания.Выполнить("ИмяОбщегоМодуля.ИмяЭкспортнойПроцедурыДляУдаления", "УдалениеЗадач", ПараметрыЗадания);
Где ИмяОбщегоМодуля.ИмяЭкспортнойПроцедурыДляУдаления – это экспортируемая процедура в общем модуле с директивой &НаСервере или &НаСервереБезКонтекста.
&НаСервере
Процедура ПорционноеУдалениеЭлементовНаСервере()
КоличествоВПорции = 1000;
СчетчикУдаленных = 0;
Выборка = Справочники.Задачи.Выбрать();
Пока Истина Цикл
НачалоТранзакции();
Попытка
Для Инд = 1 По КоличествоВПорции Цикл
Если НЕ Выборка.Следующий() Тогда
Прервать; // Вышли за пределы выборки
КонецЕсли;
ОбъектСправочника = Выборка.ПолучитьОбъект();
ОбъектСправочника.Удалить(); // Или ОбъектСправочника.ПометкаУдаления = Истина; ОбъектСправочника.Записать();
СчетчикУдаленных = СчетчикУдаленных + 1;
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Ошибка при удалении порции: " + ОписаниеОшибки(), СтатусСообщения.Важное);
Прервать;
КонецПопытки;
Если НЕ Выборка.Следующий() Тогда // Проверяем, остались ли еще элементы после порции
Прервать;
Иначе
// Если элементы есть, перемещаемся назад на один, чтобы следующая порция взяла этот же элемент
// (так как Выборка.Следующий() уже сработал)
Выборка.Предыдущий();
КонецЕсли;
Сообщить("Удалено " + СчетчикУдаленных + " элементов...");
КонецЦикла;
Сообщить("Всего удалено: " + СчетчикУдаленных + " элементов.");
КонецПроцедуры
ОбменДанными.Загрузка = Истина может ускорить процесс, отключая часть обработчиков событий и проверок. Используйте с крайней осторожностью.Как справедливо заметили в обсуждении, удалять элементы, не проверяя ссылки на них, рискованно. В 1С есть встроенный механизм для проверки ссылок на объект – это метод НайтиПоСсылкам() у объекта-ссылки.
Перед физическим удалением каждого элемента (или группы элементов) можно программно проверить, есть ли на него ссылки в других объектах. Если массив найденных ссылок пуст, объект можно удалять физически без риска нарушить целостность базы.
&НаСервере
Процедура ПроверитьИУдалитьБезСсылокНаСервере()
Если Метаданные.Справочники.Найти("Задачи") = Неопределено Тогда
Сообщить("Справочник 'Задачи' не найден в конфигурации.");
Возврат;
КонецЕсли;
МенеджерСправочника = Справочники.Задачи;
Выборка = МенеджерСправочника.Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем ссылку на элемент справочника
СсылкаНаЭлемент = Выборка.Ссылка;
// Ищем ссылки на данный элемент в других объектах
МассивСсылок = СсылкаНаЭлемент.НайтиПоСсылкам();
Если МассивСсылок.Количество() = 0 Тогда
// Если ссылок нет, можно удалять физически
Попытка
ОбъектСправочника = СсылкаНаЭлемент.ПолучитьОбъект();
ОбъектСправочника.Удалить();
Сообщить("Элемент """ + СсылкаНаЭлемент + """ успешно удален (ссылок не найдено).");
Исключение
Сообщить("Ошибка при физическом удалении элемента """ + СсылкаНаЭлемент + """: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Иначе
Сообщить("Элемент """ + СсылкаНаЭлемент + """ не может быть удален, так как на него есть ссылки.", СтатусСообщения.Важное);
// Можно вывести подробную информацию о ссылках:
// Для Каждого СсылкаИзДругогоОбъекта Из МассивСсылок Цикл
// Сообщить(" - Ссылка из: " + СсылкаИзДругогоОбъекта, СтатусСообщения.Важное);
// КонецЦикла;
КонецЕсли;
КонецЦикла;
Сообщить("Проверка и удаление элементов справочника 'Задачи' по отсутствию ссылок завершены.");
КонецПроцедуры
Если справочник имеет иерархию (состоит из групп и элементов), важно учитывать порядок удаления. Нельзя удалить группу, если в ней есть элементы или подгруппы. Платформа 1С не позволит этого сделать и выдаст ошибку.
Для корректного удаления иерархического справочника необходимо сначала удалить все элементы, а затем группы. Это можно сделать, перебирая элементы в обратном порядке иерархии (от младших к старшим) или сначала удаляя элементы, а потом группы в отдельном цикле.
&НаСервере
Процедура УдалитьИерархическийСправочникНаСервере()
Если Метаданные.Справочники.Найти("МойИерархическийСправочник") = Неопределено Тогда
Сообщить("Справочник 'МойИерархическийСправочник' не найден.");
Возврат;
КонецЕсли;
МенеджерСправочника = Справочники.МойИерархическийСправочник;
// 1. Сначала удаляем все элементы
ВыборкаЭлементов = МенеджерСправочника.Выбрать();
Пока ВыборкаЭлементов.Следующий() Цикл
Если НЕ ВыборкаЭлементов.ЭтоГруппа Тогда
Попытка
ОбъектЭлемента = ВыборкаЭлементов.ПолучитьОбъект();
ОбъектЭлемента.Удалить(); // Или пометить на удаление
Исключение
Сообщить("Не удалось удалить элемент """ + ВыборкаЭлементов.Наименование + """: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецЦикла;
// 2. Теперь удаляем все группы (после того как элементы из них удалены)
// Важно: может потребоваться несколько проходов, если группы вложенные
// Или перебирать группы в обратном порядке
Пока Истина Цикл
ЕстьНеудаленныеГруппы = Ложь;
ВыборкаГрупп = МенеджерСправочника.Выбрать();
Пока ВыборкаГрупп.Следующий() Цикл
Если ВыборкаГрупп.ЭтоГруппа Тогда
Попытка
ОбъектГруппы = ВыборкаГрупп.ПолучитьОбъект();
ОбъектГруппы.Удалить(); // Или пометить на удаление
Сообщить("Удалена группа: " + ВыборкаГрупп.Наименование);
ЕстьНеудаленныеГруппы = Истина; // Если удалили, возможно, есть еще группы
Исключение
// Если ошибка - скорее всего, в группе еще есть элементы/подгруппы
Сообщить("Не удалось удалить группу """ + ВыборкаГрупп.Наименование + """: " + ОписаниеОшибки(), СтатусСообщения.Информация);
КонецПопытки;
КонецЕсли;
КонецЦикла;
Если НЕ ЕстьНеудаленныеГруппы Тогда
Прервать; // Все группы удалены
КонецЕсли;
КонецЦикла;
Сообщить("Удаление иерархического справочника завершено.");
КонецПроцедуры
Для пользователей и даже для программистов, не желающих писать код, существует удобный инструмент в современных конфигурациях на базе БСП (Библиотека стандартных подсистем) — "Групповое изменение реквизитов".
Этот способ является очень мощным и относительно безопасным, поскольку обработка разработана с учетом всех нюансов платформы и может работать с большими объемами данных.
В обсуждении прозвучала идея использовать команду TRUNCATE TABLE ИмяТаблицы напрямую в СУБД (например, в MS SQL Server). Хотя это самый быстрый способ очистки таблицы, он категорически не рекомендуется для баз данных 1С по следующим причинам:
Не делайте этого! Всегда используйте только штатные средства платформы 1С для работы с данными.
Заключение:
Мы рассмотрели различные подходы к удалению всех элементов справочника в 1С 8.3, начиная от исправления базовой ошибки выполнения кода на клиенте и заканчивая рекомендациями по оптимизации и безопасности. Самым безопасным и рекомендуемым методом является пометка на удаление с последующей обработкой "Удаление помеченных объектов". Если требуется физическое удаление, используйте программный метод Удалить() с осторожностью, возможно, в связке с ОбменДанными.Загрузка = Истина и проверкой ссылок, а для больших объемов — через фоновые задания с порционным удалением. Категорически избегайте прямого вмешательства в базу данных через SQL.