Как удалить строки из табличной части документа в 1С по условию?

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

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

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

Метод 1: Удаление строк через выгрузку в ТаблицуЗначений и обратную загрузку

Этот метод является одним из наиболее надежных и производительных, особенно когда речь идет о большом количестве строк или сложных условиях отбора. Суть метода заключается в том, что мы выгружаем данные табличной части во временную ТаблицуЗначений, производим все необходимые изменения (удаление строк) в этой временной таблице, а затем загружаем обновленные данные обратно в табличную часть документа.

  1. Формирование запроса для получения ссылок и номеров строк:

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

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

    Обратите внимание на конструкцию ИТОГИ ПО УстановкаКурсаВалют.Ссылка. Она позволяет нам сгруппировать результаты запроса по документам, чтобы мы могли обработать каждый документ по отдельности.

  2. Выполнение запроса и обход результатов:

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

    
    ВыборкаДокумент = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    

    Внутри цикла по документам мы получаем объект документа, выгружаем его табличную часть Курсы в ТаблицуЗначений и индексируем ее по колонке НомерСтроки для быстрого поиска.

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

    Далее мы снова обходим выборку, но уже в контексте текущей группировки (текущего документа). Для каждой строки, которую нужно удалить, мы находим ее во временной ТаблицеЗначений с помощью метода Найти() по НомерСтроки и удаляем ее из ТЗ.

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

    Важно проверить, что СтрокаДляУдаления не является Неопределено, чтобы избежать ошибок, если строка по какой-то причине не найдена (хотя в нашем случае это маловероятно, если запрос составлен корректно).

  4. Загрузка обновленной ТаблицыЗначений и запись документа:

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

    
        // Загружаем обновленную ТаблицуЗначений обратно в табличную часть документа
        ДокОбъект.Курсы.Загрузить(ТЗ);
        
        // Записываем документ. Если документ был проведен, записываем с проведением.
        ДокОбъект.Записать(?(ДокОбъект.Проведен, РежимЗаписиДокумента.Проведение, РежимЗаписиДокумента.Запись));
    КонецЦикла;
    

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

Метод 2: Прямое удаление строк с итерацией с конца

Альтернативный метод предполагает непосредственное удаление строк из табличной части объекта документа, но с критически важным условием: итерацию необходимо производить с конца табличной части к началу. Это позволяет избежать проблем с изменением индексов строк после каждого удаления.

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

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

    
    // Предположим, что ДокОбъект уже получен, например, так:
    // ДокСсылка = СсылкиНаДокументыДляОбработки[0]; // Из какого-то списка ссылок
    // ДокОбъект = ДокСсылка.ПолучитьОбъект();
    
    КолСтрок = ДокОбъект.Курсы.Количество();
    
  2. Итерация с конца и удаление:

    Самый важный шаг — это цикл. Мы начинаем его с индекса, равного КолСтрок - 1 (последняя строка, так как индексация начинается с 0), и идем до 0 включительно, с шагом -1. В каждой итерации мы получаем текущую строку и проверяем ее на условие (является ли валюта предопределенной). Если условие выполняется (валюта не предопределена), мы удаляем эту строку.

    
    Для Счетчик = КолСтрок - 1 По 0 СШагом -1 Цикл
        ТекСтрока = ДокОбъект.Курсы[Счетчик];
        
        // Проверяем условие: является ли валюта предопределенной
        Если НЕ ТекСтрока.Валюта.Предопределенный Тогда
            ДокОбъект.Курсы.Удалить(ТекСтрока); // Удаляем строку
        КонецЕсли;
    КонецЦикла;
    

    Почему важно идти с конца? Если бы мы шли с начала (Для Счетчик = 0 По КолСтрок - 1 Цикл), то при удалении строки индексы всех последующих строк сдвигались бы. Это привело бы к тому, что следующая итерация цикла пропустила бы одну строку (ту, которая теперь заняла место удаленной), или попыталась бы обратиться к несуществующему индексу, если удалялась последняя строка. Итерация с конца гарантирует, что удаление строки не повлияет на индексы еще не обработанных строк.

    Важный нюанс: В исходном сообщении было предложено ТекСтрока = Поступление.Курсы[КолСтрок-1] внутри цикла, а затем ДокументОбъект.Курсы.Удалить(ТекСтрока). Однако при таком подходе всегда удалялась бы только последняя строка. Правильная итерация с конца, использующая переменную Счетчик для доступа к изменяющимся строкам, была уточнена позже, как КолСтрок - Счетчик в случае цикла с шагом 1 или просто Счетчик в цикле с шагом -1, как показано выше.

  3. Запись документа:

    Как и в первом методе, после всех изменений документ необходимо записать в базу данных.

    
    ДокОбъект.Записать(?(ДокОбъект.Проведен, РежимЗаписиДокумента.Проведение, РежимЗаписиДокумента.Запись));
    

Общие рекомендации и важные моменты при удалении строк

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

  1. Различие между данными формы и данными объекта

    Всегда помните, что мы работаем с данными объекта документа, а не с элементами формы. Изменения, внесенные в табличную часть объекта (например, ДокОбъект.Курсы), будут сохранены только после вызова метода Записать() у самого объекта документа.

  2. Свойство Предопределенный

    Для проверки, является ли элемент справочника предопределенным, используйте стандартное свойство Предопределенный у ссылки на элемент справочника (например, ВыборкаНоменклатурныйСостав.ВалютаТЧ.Предопределенный). Это наиболее прямой и эффективный способ.

  3. Метод НайтиСтроки()

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

    
    СтруктураОтбора = Новый Структура("Валюта", СсылкаНаНеПредопределеннуюВалюту);
    МассивСтрокДляУдаления = ДокОбъект.Курсы.НайтиСтроки(СтруктураОтбора);
    
    Для Каждого СтрокаТЧ Из МассивСтрокДляУдаления Цикл
        ДокОбъект.Курсы.Удалить(СтрокаТЧ);
    КонецЦикла; // В этом случае порядок удаления не важен, так как мы удаляем по ссылкам на строки, а не по индексам.
    

    Этот способ достаточно удобен, но может быть менее эффективен для большого количества различных условий или при необходимости удаления большого процента строк, так как каждая операция Удалить() внутри цикла может быть затратной.

  4. Метод Очистить()

    Если вам нужно удалить абсолютно все строки из табличной части, используйте метод Очистить(). Это самый быстрый способ полной очистки.

    ДокОбъект.Курсы.Очистить();
  5. Производительность при большом количестве строк

    Для табличных частей с очень большим количеством строк (сотни или тысячи) методы, которые включают выгрузку данных в ТаблицуЗначений, их обработку и последующую загрузку, часто оказываются более производительными, чем многократные операции удаления непосредственно в объекте документа. Это связано с тем, что манипуляции с временными таблицами в памяти могут быть оптимизированы платформой.

  6. Транзакции

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

    
    НачатьТранзакцию();
    Попытка
        // Ваш код по удалению строк
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
    

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

← На главную