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

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

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

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

Способ 1. Простой обход коллекции документов через Выбрать()

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

Рассмотрим пример кода, реализующего эту логику:


Процедура УдалениеЛишнихУроков() Экспорт
    // Выбираем все документы данного типа
    УчебныеДни = Документы.УчебныйДень.Выбрать();
    
    Пока УчебныеДни.Следующий() Цикл
        // Получаем объект документа для изменения
        ДокументОбъект = УчебныеДни.ПолучитьОбъект();
        ТабличнаяЧасть = ДокументОбъект.Уроки;
        
        // Определяем индекс последней строки
        // Количество строк в ТЧ может быть разным
        ИндексТекСтроки = ТабличнаяЧасть.Количество() - 1;
        
        // Удаляем строки, пока индекс больше или равен 5 (т.е. остается 5 строк с индексами 0,1,2,3,4)
        Пока ИндексТекСтроки >= 5 Цикл
            ТабличнаяЧасть.Удалить(ИндексТекСтроки);
            ИндексТекСтроки = ИндексТекСтроки - 1;
        КонецЦикла;
        
        // Записываем измененный документ
        ДокументОбъект.Записать();
    КонецЦикла;
КонецПроцедуры

Важный нюанс: Обратите внимание, что мы удаляем строки с конца. Это золотое правило при работе с коллекциями в 1С. Если удалять строки с начала по индексу (например, от 5 до 10), то после удаления 5-й строки, 6-я автоматически станет 5-й, и цикл либо пропустит элементы, либо вызовет ошибку обращения к индексу.

Способ 2. Использование массива для удаления строк

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

Проанализируем этот подход на примере:


СтрокиДляУдаления = Новый Массив;
НомерУрока = 1;

// Сначала помечаем, что нужно удалить
Для каждого Строка Из ДокументОбъект.Уроки Цикл
    Если НомерУрока > 5 Тогда
        СтрокиДляУдаления.Добавить(Строка);
    КонецЕсли;
    НомерУрока = НомерУрока + 1;
КонецЦикла;

// Теперь удаляем именно объекты строк, а не по индексу
Для каждого Строка Из СтрокиДляУдаления Цикл
    ДокументОбъект.Уроки.Удалить(Строка);
КонецЦикла;

ДокументОбъект.Записать();

Этот метод считается более надежным и понятным для чтения кода. Нам не нужно высчитывать Количество() - 1, мы просто оперируем объектами строк.

Способ 3. Оптимизированный подход через Запросы (Профессиональный)

Метод Выбрать() и последующее ПолучитьОбъект() для каждого документа — это очень «тяжелая» операция для системы. Если в базе 10 000 документов, а лишние уроки есть только в двух, программа все равно попытается прочитать и создать в памяти объекты для всех 10 000 записей. Это неэффективно.

Правильный путь — использовать Запрос, чтобы сразу отобрать только те документы, которые нуждаются в корректировке. Разберем сложный, но эффективный пример с группировкой итогов:


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

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

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

В данном примере УПОРЯДОЧИТЬ ПО ИндексСтроки УБЫВ гарантирует нам правильный порядок удаления строк с конца. Использование ИТОГИ ПО Ссылка позволяет удобно сгруппировать все индексы «лишних» строк для одного конкретного документа.

Дополнительные рекомендации по надежности кода

При выполнении массовых операций изменения данных необходимо учитывать аспекты безопасности и производительности, которые часто опускаются в учебных задачах:

  1. Проверка на проведение: Если документ УчебныйДень уже проведен, простое удаление строк и Записать() может привести к тому, что документ останется проведенным, но его движения по регистрам не обновятся. Рекомендуется использовать ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение) для проведенных документов или проверять статус перед записью.
  2. Использование транзакций: Для обеспечения целостности данных оборачивайте цикл обработки в транзакцию. Если на середине процесса произойдет сбой, система откатит все изменения к исходному состоянию.
    
    НачатьТранзакцию();
    Попытка
        // Ваш код обработки
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписатьВЖурналРегистрации("Ошибка при удалении уроков: " + ОписаниеОшибки());
    КонецПопытки;
    
  3. Минимизация записи: Перед вызовом метода Записать() полезно проверить, изменилось ли что-то в объекте. Если строк изначально было 5 или меньше, запись выполнять не нужно. Это снижает нагрузку на сервер и не «засоряет» журнал регистрации лишними событиями.
  4. Блокировки данных: При работе в многопользовательской среде желательно устанавливать блокировку на объект перед его изменением, чтобы избежать конфликтов, если другой пользователь решит отредактировать этот же документ одновременно с вашей обработкой.

Подводя итог, можно сказать, что выбор метода зависит от объема данных. Для разовой задачи на 50 документов подойдет простой цикл, но для серьезной информационной системы использование Запросов и Транзакций является обязательным стандартом разработки.

← На главную