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