Как программно удалить пустые элементы справочника в 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Использование типовой обработки: "Групповая обработка справочников и документов"

Прежде чем приступать к написанию кода, всегда рассмотрим возможность использования стандартных инструментов 1С. Для массового удаления или пометки на удаление элементов справочников существует типовая обработка "Групповая обработка справочников и документов". Это самый безопасный и рекомендуемый способ для пользователя, так как он уже учитывает многие нюансы, связанные с проверкой ссылочной целостности.

  1. Откройте обработку (обычно доступна через меню "Все функции" или "Администрирование" -> "Обслуживание" -> "Групповое изменение реквизитов" или "Групповая обработка").
  2. В поле "Тип объектов" выберите нужный справочник, например, Справочник.Номенклатура.
  3. Перейдите на вкладку "Отбор и сортировка" или подобную, чтобы установить условие отбора. Здесь мы можем задать условие, например, "Наименование равно пустой строке" или "Наименование не заполнено".
  4. После того как отбор сформирован и список пустых элементов отображен, перейдите на вкладку "Изменение выделенных".
  5. Установите флажок "Пометить на удаление" или "Установить пометку удаления" и примените изменения.
  6. После пометки всех необходимых объектов на удаление запустите стандартное регламентное задание "Удаление помеченных объектов", которое окончательно удалит элементы из базы, предварительно проверив ссылочную целостность.

Этот метод предпочтителен, так как он минимизирует риски потери данных и нарушения ссылочной целостности.

Программное удаление элементов справочника

Если типовая обработка по каким-то причинам не подходит или требуется более тонкая настройка логики удаления, мы можем написать свой программный код. Здесь важно понимать различия между методами удаления и соблюдать осторожность.

Различия между пометкой на удаление и непосредственным удалением

В 1С существуют два основных способа "удаления" объектов:

  1. Пометка на удаление: Это более безопасный способ. Мы устанавливаем свойство объекта ПометкаУдаления в значение Истина. Объект при этом не удаляется физически из базы, а лишь помечается как подлежащий удалению. Он становится невидимым в пользовательских формах (если не включен специальный режим отображения) и не может быть использован в новых документах. Фактическое удаление происходит позже, при выполнении регламентного задания "Удаление помеченных объектов". Это задание перед удалением проверяет, нет ли ссылок на помеченный объект из других частей системы. Если ссылки есть, объект не удаляется, и система выдает предупреждение.
    
    ОбъектСправочника.ПометкаУдаления = Истина;
    ОбъектСправочника.Записать();
    
  2. Непосредственное удаление: Метод Удалить() производит немедленное физическое удаление элемента из базы данных. Крайне важно понимать, что этот метод не выполняет проверку ссылочной целостности! Если на удаляемый объект существуют ссылки из других объектов (документов, других справочников и т.д.), эти ссылки станут "битыми", что может привести к ошибкам в работе программы, потере данных и нестабильности системы. Используйте метод Удалить() только в случаях, когда вы абсолютно уверены в отсутствии ссылок на объект или когда сознательно допускаете нарушение ссылочной целостности (например, при очистке тестовой базы). ОбъектСправочника.Удалить();

Важность проверки типов данных при работе с запросами

Одна из распространенных ошибок, как мы видим из обсуждения, связана с неправильным пониманием типов данных в запросах и объектных моделях. Поле Наименование справочника имеет тип Строка, а не Ссылка. Мы не можем применить к строке метод ПустаяСсылка, так как он предназначен только для ссылочных типов.

Неправильный подход (и почему он не работает):


Если ВыборкаДетальныеЗаписи.Наименование.ПустаяСсылка Тогда // Ошибка! Наименование - строка, не ссылка
    // ...
КонецЕсли;

Такой код вызовет ошибку, потому что свойство Наименование является строкой, и у него нет метода ПустаяСсылка.

Правильный подход:

  1. Для проверки "пустоты" строкового поля Наименование используйте сравнение с пустой строкой: ВыборкаДетальныеЗаписи.Наименование = "".
  2. Чтобы получить объект справочника для удаления или пометки, нам нужна его Ссылка. Поэтому в запросе мы должны выбирать поле Ссылка, а затем использовать его для получения объекта: ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект().

Пример кода для удаления пустых элементов (на основе Наименования)

Теперь рассмотрим правильный пример кода, который позволяет найти и удалить элементы справочника Номенклатура с пустым наименованием. Мы будем использовать метод Удалить(), помня о его опасностях.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Номенклатура.Наименование,
    |    Номенклатура.Ссылка
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Наименование = &ПустоеНаименование"; // Добавим условие в запрос для оптимизации

Запрос.УстановитьПараметр("ПустоеНаименование", "");

Результат = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = Результат.Выбрать();

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

Пояснения к коду:

  1. Мы выбираем как Наименование, так и Ссылка, чтобы иметь доступ к обоим свойствам.
  2. Условие Номенклатура.Наименование = &ПустоеНаименование добавлено непосредственно в запрос. Это значительно эффективнее, чем фильтровать данные в цикле Пока, особенно при большом количестве элементов.
  3. Запрос.УстановитьПараметр("ПустоеНаименование", "") устанавливает значение параметра запроса.
  4. ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект() корректно получает объект справочника по его ссылке.
  5. Использование НачатьТранзакцию() и ЗафиксироватьТранзакцию() обертывает операцию удаления в транзакцию. Это улучшает производительность при массовом удалении и обеспечивает атомарность операции (либо все удаляется, либо ничего).
  6. Блок Попытка...Исключение позволяет перехватывать ошибки, которые могут возникнуть при удалении (например, из-за ссылок), и продолжать выполнение для других элементов.

Учет ссылочной целостности: проверка перед удалением

Как мы уже выяснили, непосредственное удаление методом Удалить() опасно. Чтобы минимизировать риски, мы можем программно проверить наличие ссылок на удаляемый объект перед его удалением. Для этого используется метод НайтиПоСсылкам().


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

Мы рекомендуем использовать этот подход, чтобы повысить безопасность программного удаления.

Исключение предопределенных элементов

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


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

Полный пример безопасного программного удаления "пустых" элементов

Давайте скомбинируем все рассмотренные лучшие практики в одном, максимально безопасном коде для пометки на удаление.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Номенклатура.Наименование,
    |    Номенклатура.Ссылка,
    |    Номенклатура.Предопределенный // Выбираем также признак предопределенности
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Наименование = &ПустоеНаименование";

Запрос.УстановитьПараметр("ПустоеНаименование", "");

Результат = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = Результат.Выбрать();

НачатьТранзакцию();
КоличествоПомечено = 0;
КоличествоПропущеноПредопределенных = 0;
КоличествоПропущеноИзЗаСсылок = 0;

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

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

        // 2. Проверяем наличие ссылок на объект
        МассивСсылок = ОбъектСправочника.Ссылка.НайтиПоСсылкам();
        Если МассивСсылок.Количество() > 0 Тогда
            Сообщить("Пропущен элемент " + ОбъектСправочника.Наименование + " (" + ОбъектСправочника.Ссылка + ") из-за ссылок (" + МассивСсылок.Количество() + " шт.)");
            // Для отладки можно вывести список ссылок
            // Для Каждого СсылкаНаОбъект Из МассивСсылок Цикл
            //     Сообщить("  Ссылка из: " + СсылкаНаОбъект.Метаданные().Имя + " " + СсылкаНаОбъект);
            // КонецЦикла;
            КоличествоПропущеноИзЗаСсылок = КоличествоПропущеноИзЗаСсылок + 1;
            Продолжить;
        КонецЕсли;
        
        // 3. Если все проверки пройдены, помечаем на удаление
        ОбъектСправочника.ПометкаУдаления = Истина;
        ОбъектСправочника.Записать();
        Сообщить("Элемент успешно помечен на удаление: " + ОбъектСправочника.Наименование + " (" + ОбъектСправочника.Ссылка + ")");
        КоличествоПомечено = КоличествоПомечено + 1;

    Исключение
        Сообщить("Ошибка при обработке элемента " + ВыборкаДетальныеЗаписи.Ссылка + ": " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

ЗафиксироватьТранзакцию(); // Важно зафиксировать транзакцию, если все прошло успешно

Сообщить("--- Отчет по обработке ---");
Сообщить("Всего элементов с пустым наименованием: " + Результат.Выгрузить().Количество());
Сообщить("Помечено на удаление: " + КоличествоПомечено);
Сообщить("Пропущено (предопределенные): " + КоличествоПропущеноПредопределенных);
Сообщить("Пропущено (имеют ссылки): " + КоличествоПропущеноИзЗаСсылок);
Сообщить("--- Для окончательного удаления запустите регламентное задание 'Удаление помеченных объектов'. ---");

Особенности удаления иерархических справочников

Если мы работаем с иерархическим справочником (например, группы номенклатуры), и используем метод ОбъектСправочника.Удалить() для элемента, то помимо самого элемента, будут удалены и все подчиненные ему элементы (группы и входящие в них элементы). Это может быть как желаемым поведением, так и причиной нежелательной потери данных. Если требуется удалять только конечные элементы, необходимо проверять, является ли элемент группой, и обрабатывать его соответствующим образом.

Права пользователя

Для выполнения операций пометки на удаление или непосредственного удаления у пользователя, от имени которого выполняется код, должны быть соответствующие права доступа к объекту справочника (роли "Изменение" или "Полные права"). Если прав нет, операция завершится ошибкой.

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

← На главную