Разработка и Конфигурация · 11 мин
Как перебрать табличную часть документа в 1С
Как перебрать строки табличной части документа в 1С: циклы Для Каждого, обращение по индексу, изменение и удаление строк, типичные ошибки и примеры кода.
Табличная часть документа в 1С — это коллекция строк, и перебрать её можно несколькими способами: циклом Для Каждого ... Цикл, обращением по индексу через цикл Для ... Цикл, выгрузкой в таблицу значений или запросом. Выбор зависит от задачи: простое чтение данных, изменение строк, удаление части строк или сложная фильтрация.
Чаще всего используется конструкция Для Каждого СтрокаТЧ Из Объект.Товары Цикл, где внутри цикла доступны все реквизиты текущей строки. Этот способ подходит для чтения и изменения данных, но имеет ограничение: удалять строки внутри такого цикла напрямую нельзя — это приведёт к нарушению перебора.
Ниже разобраны основные сценарии: чтение и изменение строк, удаление, работа через индексы, выгрузка в таблицу значений и перебор через запрос, а также типичные ошибки, которые приводят к некорректным результатам.
Базовый перебор циклом «Для Каждого»
Самый распространённый способ — цикл Для Каждого. Он подходит, когда нужно прочитать или изменить данные каждой строки без удаления элементов коллекции.
Для Каждого СтрокаТЧ Из ДокументОбъект.Товары Цикл
// Чтение данных строки
СуммаСтроки = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
// Изменение реквизита строки
СтрокаТЧ.Сумма = СуммаСтроки;
КонецЦикла;
Переменная цикла (в примере — СтрокаТЧ) содержит объект строки табличной части. Изменение её реквизитов напрямую меняет данные документа — отдельно «записывать» строку не нужно, достаточно записать сам документ после цикла.
Если перебор выполняется в модуле формы документа, обращение идёт через Объект.Товары. В модуле объекта или во внешней обработке — через переменную, содержащую объект документа, например ДокументОбъект.Товары.
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЦикла;Перебор по индексу и когда он нужен
Обращение к строкам по индексу используется, когда нужен номер строки, обратный порядок обхода или пропуск/удаление элементов. Индексация начинается с нуля, количество строк возвращает метод Количество().
ВсегоСтрок = ДокументОбъект.Товары.Количество();
Для Индекс = 0 По ВсегоСтрок - 1 Цикл
СтрокаТЧ = ДокументОбъект.Товары[Индекс];
Сообщить("Строка " + (Индекс + 1) + ": " + СтрокаТЧ.Номенклатура);
КонецЦикла;
У каждой строки также есть свойство НомерСтроки — оно начинается с единицы и соответствует видимому номеру в форме документа. Не путайте его с индексом: Товары[0] — это строка с НомерСтроки = 1.
Обратный обход по индексам — стандартный приём для безопасного удаления строк: при движении с конца коллекции сдвиг индексов не влияет на ещё не обработанные элементы.
Удаление строк при переборе
Удалять строки методом Удалить() внутри цикла Для Каждого нельзя: коллекция изменяется во время итерации, и часть строк будет пропущена. Есть два корректных подхода.
Первый — обратный цикл по индексам:
Для Индекс = -ДокументОбъект.Товары.Количество() + 1 По 0 Цикл
РеальныйИндекс = -Индекс;
СтрокаТЧ = ДокументОбъект.Товары[РеальныйИндекс];
Если СтрокаТЧ.Количество = 0 Тогда
ДокументОбъект.Товары.Удалить(РеальныйИндекс);
КонецЕсли;
КонецЦикла;
Второй — метод НайтиСтроки(), который возвращает массив строк по отбору. Найденные строки можно удалить в отдельном цикле, поскольку массив уже не привязан к перебору коллекции:
Отбор = Новый Структура("Количество", 0);
СтрокиКУдалению = ДокументОбъект.Товары.НайтиСтроки(Отбор);
Для Каждого СтрокаТЧ Из СтрокиКУдалению Цикл
ДокументОбъект.Товары.Удалить(СтрокаТЧ);
КонецЦикла;
Выгрузка в таблицу значений для сложной обработки
Если нужно сгруппировать данные, свернуть дубли, отсортировать по произвольному условию или многократно искать строки, удобнее выгрузить табличную часть в таблицу значений методом Выгрузить():
ТЗ = ДокументОбъект.Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура", "Количество, Сумма");
Для Каждого СтрокаТЗ Из ТЗ Цикл
Сообщить(СтрокаТЗ.Номенклатура);
КонецЦикла;
Важно понимать: таблица значений — это копия данных. Изменения в ней не попадают в документ автоматически. Чтобы вернуть результат, используют ДокументОбъект.Товары.Загрузить(ТЗ) — при этом старая табличная часть полностью заменяется новой.
Метод Выгрузить() принимает параметры: массив или список строк и строку с именами выгружаемых колонок. Это позволяет выгрузить только нужные строки и реквизиты, что полезно при больших табличных частях.
Перебор через запрос
Когда документ уже записан в базу и нужно получить данные его табличной части без чтения всего объекта, эффективнее запрос к таблице вида Документ.РеализацияТоваровУслуг.Товары:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура,
| Товары.Количество
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// обработка строки выборки
КонецЦикла;
Запрос — единственный разумный вариант, когда нужно перебрать табличные части сразу многих документов: чтение каждого документа целиком через ПолучитьОбъект() в цикле — типичная причина медленной работы обработок.
Ограничение подхода: через запрос данные доступны только для чтения. Изменить табличную часть можно только через объект документа с последующей записью.
Сравнение способов перебора
Чтение и изменение строк одного объекта. Не подходит для удаления строк внутри цикла.
Нужен номер строки, обратный обход или удаление. Код менее читаем, легко ошибиться с границами.
Группировка, свёртка, сортировка, многократный поиск. Требует Загрузить() для возврата данных в документ.
Чтение данных одного или многих документов без получения объектов. Только чтение, требует записанного документа.
Работа в модуле формы: клиент и сервер
В управляемых формах перебор табличной части часто выполняется на клиенте — например, при интерактивном пересчёте сумм. В этом случае обращение идёт к Объект.Товары, и цикл Для Каждого работает так же, как на сервере.
Если внутри цикла нужны данные, недоступные на клиенте (запросы, привилегированный режим, серверные функции), перебор следует вынести в серверную процедуру. Делать серверный вызов на каждую строку внутри клиентского цикла — грубая ошибка производительности.
Для перебора именно выделенных пользователем строк таблицы формы используется коллекция Элементы.Товары.ВыделенныеСтроки — она содержит идентификаторы строк, по которым строку получают методом Объект.Товары.НайтиПоИдентификатору().
- 1Определите контекст: модуль объекта, модуль формы или внешняя обработка — от этого зависит способ обращения к табличной части.
- 2Выберите способ перебора: Для Каждого для чтения/изменения, обратный цикл по индексам для удаления, запрос для чтения многих документов.
- 3Напишите цикл и внутри него обращайтесь к реквизитам строки через переменную цикла.
- 4Если данные изменялись через объект — запишите документ методом Записать().
- 5Проверьте результат на тестовом документе: количество строк, итоговые суммы, отсутствие пропущенных строк.
Типичные ошибки и ограничения
Самая частая ошибка — попытка перебрать табличную часть по ссылке на документ: ДокументСсылка.Товары недоступно, потому что ссылка не содержит данных табличной части. Нужно либо получить объект (ДокументСсылка.ПолучитьОбъект()), либо использовать запрос.
Вторая группа ошибок связана с удалением строк в прямом цикле и с путаницей между индексом (с нуля) и номером строки (с единицы). Обе приводят к пропуску строк или обращению к несуществующему элементу.
Также стоит помнить, что после Удалить() или Очистить() ранее полученные переменные-строки могут стать недействительными, а при работе в форме — что Объект в управляемой форме и данные на сервере синхронизируются платформой, но прямое обращение к реквизитам объекта вне контекста формы работать не будет.
Как проверить результат
Счётчик в цикле равен Товары.Количество() до удалений
Счётчик меньше — часть строк пропущена, вероятно удаление в прямом цикле
Суммы пересчитаны по каждой строке и сходятся с ожидаемым итогом
Есть строки со старыми значениями — цикл завершился досрочно
Документ записан, изменения видны при повторном открытии
Изменения пропали — объект не был записан или правилась только копия в таблице значений
Подведём итог: для большинства задач достаточно цикла Для Каждого, удаление выполняется обратным циклом или через НайтиСтроки(), сложная аналитика — через Выгрузить() или запрос. Проверяйте результат на копии базы, если обработка затрагивает много документов.
Что важно запомнить
Основной способ перебора — «Для Каждого СтрокаТЧ Из Объект.Товары Цикл». Удаление — только обратным циклом по индексам или через НайтиСтроки. По ссылке табличная часть недоступна: нужен объект или запрос. Выгрузить() даёт копию, изменения возвращаются через Загрузить().
Частые вопросы
Как узнать номер текущей строки при переборе «Для Каждого»?
У переменной цикла есть свойство НомерСтроки: СтрокаТЧ.НомерСтроки. Оно начинается с единицы и совпадает с номером, который видит пользователь в форме документа. Заводить отдельный счётчик не требуется.
Можно ли перебрать табличную часть, не открывая документ?
Да, если документ записан в базе — используйте запрос к таблице Документ.ИмяДокумента.ИмяТабличнойЧасти с отбором по ссылке. Это быстрее, чем ПолучитьОбъект(), но данные доступны только для чтения.
Почему в цикле пропускаются строки?
Почти всегда причина — удаление или вставка строк внутри цикла «Для Каждого» по той же коллекции. При изменении состава коллекции итератор смещается. Решение: обратный цикл по индексам или сначала собрать строки через НайтиСтроки(), затем удалять.
Как перебрать только строки с определённым условием?
Два варианта: проверять условие внутри общего цикла через «Если ... Тогда», либо получить готовый массив строк методом НайтиСтроки(Отбор), где Отбор — структура с именами и значениями реквизитов. НайтиСтроки сравнивает на точное равенство; для условий «больше», «содержит» подходит только проверка в цикле или запрос.
Чем отличается перебор в модуле объекта и в модуле формы?
В модуле объекта табличная часть доступна как свойство самого объекта (Товары). В модуле управляемой формы — через реквизит Объект (Объект.Товары). Логика циклов одинакова, но в форме добавляются ограничения клиентского контекста: на клиенте недоступны запросы и часть серверных методов.
Как полностью очистить табличную часть?
Перебор не нужен — используйте метод Очистить(): ДокументОбъект.Товары.Очистить(). После этого табличная часть станет пустой, и изменения зафиксируются при записи документа.