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

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

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

Разберем по шагам, с какими сложностями можно столкнуться при реализации этой задачи, выясним причину частых ошибок начинающих разработчиков и рассмотрим правильные варианты решения — от простых алгоритмов до оптимизированных с точки зрения производительности и ссылочной целостности базы данных.

Ошибки при выборке документов и опасность прямого физического удаления

Посмотрим на типичный код, с которого часто начинают написание подобных обработок:


Для каждого Элемент ИЗ Документы Цикл
    Выборка = Элемент.Выбрать();
    Пока Выборка.Следующий() Цикл
        Выборка.Ссылка.ПолучитьОбъект().Удалить();
    КонецЦикла;
КонецЦикла;

В этом фрагменте допущены сразу две критические ошибки:

  1. Обход всей коллекции метаданных: коллекция Документы содержит менеджеры всех документов конфигурации. Запуская цикл по всей коллекции без фильтрации, мы затрагиваем абсолютно все типы документов в системе.
  2. Использование метода Удалить(): непосредственное физическое удаление объекта из базы безвозвратно стирает запись из таблицы. При этом не выполняется контроль ссылочной целостности, из-за чего в связанных объектах появляются «битые ссылки» (отображаемые как <Объект не найден>). Кроме того, если удаляемый документ был проведен, в регистрах могут остаться зависшие движения без регистратора, что неминуемо приведет к искажению финансовой и складской отчетности.

Как ограничить выборку конкретными видами документов

Выясним, как правильно адресовать выборку только тем документам, которые нам нужны. Существует два подхода к ограничению типов.

Первый вариант — проверка типа менеджера при итерации по коллекции Документы:


Для каждого МенеджерДокумента ИЗ Документы Цикл
    Если ТипЗнч(МенеджерДокумента) = Тип("ДокументМенеджер.РеализацияТоваровУслуг") 
        Или ТипЗнч(МенеджерДокумента) = Тип("ДокументМенеджер.ПоступлениеТоваровУслуг") Тогда
        // Обработка выборки
    КонецЕсли;
КонецЦикла;

Второй, более лаконичный и быстрый способ — прямое обращение к конкретным менеджерам без лишнего цикла по всей метаинформации базы:


МассивМенеджеров = Новый Массив;
МассивМенеджеров.Добавить(Документы.РеализацияТоваровУслуг);
МассивМенеджеров.Добавить(Документы.ПоступлениеТоваровУслуг);

Для каждого Менеджер Из МассивМенеджеров Цикл
    Выборка = Менеджер.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Обработка каждого документа
    КонецЦикла;
КонецЦикла;

Как определить четность номера документа

Номер документа в платформе «1С:Предприятие» хранится в виде строки. Это обусловлено тем, что номер может содержать лидирующие нули (например, 0000000124), префиксы узлов РИБ или организаций (например, УТ-000012). Применить математическую операцию остатка от деления % напрямую к строке нельзя.

Рассмотрим способы определения четности в зависимости от формата номеров:

  1. Преобразование в число (если префиксов нет): если номер состоит только из цифр и лидирующих нулей, используем функцию Число():
    
    Если Число(Выборка.Номер) % 2 = 0 Тогда
        // Номер четный
    КонецЕсли;
    
  2. Пропуск фиксированного префикса: если в базе используется стандартный двух- или трехзначный префикс (например, два символа префикса и дефис), можно отсечь его с помощью функции Сред():
    
    // Отсекаем префикс из 2 символов
    Если Число(Сред(Выборка.Номер, 3)) % 2 = 0 Тогда
        // Номер четный
    КонецЕсли;
    
  3. Проверка последней цифры строки: это самый простой и быстрый способ, не требующий вычисления длины префикса. Достаточно проверить, является ли последний символ строки четной цифрой:
    
    Если Найти("02468", Прав(Выборка.Номер, 1)) > 0 Тогда
        // Номер четный
    КонецЕсли;
    
    Обратите внимание: данный способ сработает безошибочно только в том случае, если номер не оканчивается на буквенные индексы, суффиксы или пробелы (например, при ручной нумерации вида «12-А»). Если в базе возможны нецифровые символы на конце, строку перед проверкой потребуется предварительно очистить.

Корректная установка пометки на удаление

Вместо физического удаления документов рекомендуется устанавливать пометку на удаление. Разберем два штатных способа установки пометки.

Способ 1. Использование метода УстановитьПометкуУдаления():

Метод объекта документа автоматически снимает проведение и устанавливает пометку:


ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
    ДокументОбъект.УстановитьПометкуУдаления(Истина);
КонецЕсли;

Способ 2. Явное изменение реквизита и отмена проведения:

Если требуется точно контролировать режим записи, используем присвоение реквизита ПометкаУдаления и запись в режиме отмены проведения:


ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если ДокументОбъект <> Неопределено Тогда
    ДокументОбъект.ПометкаУдаления = Истина;
    ДокументОбъект.Записать(РежимЗаписиДокумента.ОтменаПроведения);
КонецЕсли;

Оптимальное решение: использование запроса и ускорение записи

Проанализируем производительность: метод Менеджер.Выбрать() считывает из базы всю информацию по объектам. Если документов десятки тысяч, это приведет к высокому расходу памяти и медленной работе. Намного эффективнее использовать объект Запрос, выбрав только ссылки и номера еще не помеченных на удаление документов, а также отключить дополнительные проверки при записи через флаг ОбменДанными.Загрузка.

Посмотрим на готовый пример оптимизированного кода:


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

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

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

После того как документы помечены на удаление, их окончательное удаление выполняется стандартным платформенным механизмом Удаление помеченных объектов (или программным методом УдалитьОбъекты() с предварительным контролем через НайтиПоСсылкам()). Это гарантирует сохранение ссылочной целостности базы данных и отсутствие «битых» записей.

← На главную