К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как обойти табличную часть документа и обратиться к её данным в 1С

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

Табличная часть документа в 1С — это коллекция строк, каждая из которых содержит набор реквизитов: номенклатуру, количество, цену, сумму и другие данные. Во встроенном языке табличная часть представлена объектом типа «ТабличнаяЧасть» (для данных объекта) или «ДанныеФормыКоллекция» (на управляемой форме), и обход её строк — одна из самых частых задач при доработке проведения, заполнения и проверки документов.

Существует несколько способов перебрать строки: цикл «Для Каждого», цикл «Для» по индексу, методы «Найти», «НайтиСтроки», «Выгрузить» и «Итог». Выбор зависит от задачи: нужно ли изменять строки, искать конкретные значения или только суммировать данные. Неправильный выбор способа приводит либо к лишним затратам времени выполнения, либо к ошибкам вроде пропуска строк при удалении внутри цикла.

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

Как устроена табличная часть и что возвращает обращение к ней

К табличной части обращаются по имени, заданному в конфигураторе: ДокументОбъект.Товары, ДокументОбъект.Услуги и так далее. Результат — коллекция строк, где каждая строка имеет тип «СтрокаТабличнойЧасти» и содержит все реквизиты, определённые для этой табличной части, плюс служебное поле «НомерСтроки», которое нумеруется с единицы.

Важно различать контекст обращения. В модуле объекта и в процедурах проведения доступна сама табличная часть объекта — изменения строк напрямую меняют данные документа. На управляемой форме данные лежат в реквизите формы типа «ДанныеФормыКоллекция», и синтаксис обхода немного отличается: у строки есть метод «ПолучитьИдентификатор», а обращение к реквизитам идёт через свойства строки формы.

Обход циклом «Для Каждого» — основной способ

Цикл «Для Каждого» подходит для большинства задач: проверка заполнения, пересчёт сумм, заполнение реквизитов при изменении одного значения. Переменная цикла на каждой итерации содержит ссылку на строку, поэтому присваивание значений её реквизитам изменяет данные документа напрямую.

Для Каждого СтрокаТовары Из Объект.Товары Цикл

Если Не ЗначениеЗаполнено(СтрокаТовары.Номенклатура) Тогда

Продолжить;

КонецЕсли;

СтрокаТовары.Сумма = СтрокаТовары.Количество * СтрокаТовары.Цена;

КонецЦикла;

Обращение к реквизитам строки идёт через точку: СтрокаТовары.Количество, СтрокаТовары.Номенклатура. Если реквизит ссылочного типа, можно обращаться к полям через точку дальше (СтрокаТовары.Номенклатура.Наименование), но помните: каждое такое обращение к реквизиту ссылки — это обращение к базе данных, и в больших циклах это заметно замедляет выполнение. Для массовых операций лучше выгрузить нужные данные запросом.

Обход по индексу и когда он нужен

Цикл «Для» по индексу используют, когда нужен номер итерации, доступ к соседним строкам или обход с конца. Индексы коллекции начинаются с нуля, а количество строк возвращает метод «Количество()».

Для Индекс = 0 По Объект.Товары.Количество() - 1 Цикл

СтрокаТовары = Объект.Товары[Индекс];

// работа со строкой

КонецЦикла;

Обход с конца — стандартный приём при удалении строк внутри цикла. Если удалять строки при прямом обходе, индексы оставшихся элементов сместятся, и часть строк будет пропущена. Обратный цикл решает проблему:

Для Индекс = -Объект.Товары.Количество() + 1 По 0 Цикл

РеальныйИндекс = -Индекс;

Если Объект.Товары[РеальныйИндекс].Количество = 0 Тогда

Объект.Товары.Удалить(РеальныйИндекс);

КонецЕсли;

КонецЦикла;

Альтернатива — собрать строки к удалению в отдельный массив внутри «Для Каждого», а удалять уже после завершения цикла. Этот вариант читается проще и меньше подвержен ошибкам с индексами.

Типичные ошибки
01
Удаление строк внутри прямого цикла «Для Каждого» — часть строк пропускается из-за смещения коллекции
02
Смешение индекса (с нуля) и НомерСтроки (с единицы) — обращение не к той строке или ошибка выхода за границы
03
Обращение к реквизитам ссылок через точку в большом цикле — резкое падение производительности
04
Изменение табличной части объекта, полученного по ссылке, без вызова метода ПолучитьОбъект() — изменения не сохраняются

Поиск строк: Найти, НайтиСтроки и НайтиПоНомеруСтроки

Если перебирать все строки не нужно, а требуется конкретная, используйте методы поиска. Найти(Значение, "Номенклатура") возвращает первую строку с указанным значением реквизита или «Неопределено», если строка не найдена. Результат обязательно проверяйте перед обращением к реквизитам — иначе получите ошибку «Поле объекта не обнаружено».

СтрокаНайденная = Объект.Товары.Найти(НужныйТовар, "Номенклатура");

Если СтрокаНайденная <> Неопределено Тогда

СтрокаНайденная.Количество = СтрокаНайденная.Количество + 1;

Иначе

НоваяСтрока = Объект.Товары.Добавить();

НоваяСтрока.Номенклатура = НужныйТовар;

НоваяСтрока.Количество = 1;

КонецЕсли;

Метод «НайтиСтроки» принимает структуру отбора и возвращает массив всех подходящих строк — удобно, когда нужно обработать группу строк с одинаковым признаком. Метод «НайтиПоНомеруСтроки» возвращает строку по её видимому номеру и полезен при обработке сообщений пользователю, где указывается номер строки.

Отбор = Новый Структура("Склад", СкладОтгрузки);

СтрокиСклада = Объект.Товары.НайтиСтроки(Отбор);

Для Каждого СтрокаСклада Из СтрокиСклада Цикл

// обработка отобранных строк

КонецЦикла;

Итоги, Выгрузить и работа с большими объёмами

Для суммирования по числовой колонке не нужен цикл вообще — метод «Итог» считает быстрее и короче: ВсегоПоДокументу = Объект.Товары.Итог("Сумма");. Аналогично «Количество()» возвращает число строк без перебора.

Метод «Выгрузить» преобразует табличную часть в таблицу значений. Это открывает доступ к свёртке, группировкам и сортировке: например, «Свернуть("Номенклатура", "Количество,Сумма")» сгруппирует строки по товару и просуммирует показатели. Также выгруженную таблицу удобно передавать в запрос через временную таблицу для проведения по регистрам — это стандартный приём в типовых конфигурациях.

Сравнение способов обхода

Для Каждого

Универсальный перебор и изменение строк. Не подходит для удаления внутри цикла

Для по индексу

Нужен номер итерации, обход с конца или доступ к соседним строкам. Более громоздкий синтаксис

Найти / НайтиСтроки

Поиск конкретных строк по значению. Не заменяет полный перебор при обработке всех строк

Итог / Выгрузить

Агрегация и группировка без цикла. Выгрузить создаёт копию данных — изменения не попадут в документ автоматически

Обход табличной части на управляемой форме

На управляемой форме документа табличная часть доступна как Объект.Товары, где «Объект» — реквизит формы типа «ДанныеФормыСтруктура». Синтаксис цикла тот же — «Для Каждого», но строка имеет тип «ДанныеФормыЭлементКоллекции». Ключевое отличие: у такой строки есть идентификатор, необходимый для методов формы вроде «Элементы.Товары.ТекущаяСтрока».

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

Порядок действий при доработке обхода
  1. 1
    Определите контекст: модуль объекта, модуль формы или внешняя обработка — от этого зависит тип коллекции
  2. 2
    Выберите способ обхода: Для Каждого для изменений, НайтиСтроки для отбора, Итог для сумм
  3. 3
    Добавьте проверки на Неопределено и пустые значения перед обращением к реквизитам
  4. 4
    Протестируйте на документе с несколькими строками, с одной строкой и пустой табличной частью
  5. 5
    Проверьте, что изменённые данные сохраняются после записи документа

Проверка результата и частые проблемы

После обхода убедитесь, что обработаны все строки: сравните количество обработанных строк со значением Объект.Товары.Количество(). При отладке полезно выводить «НомерСтроки» в сообщения — пользователь сразу видит, к какой строке относится предупреждение, и может перейти к ней по номеру.

Отдельная проблема — работа с документом по ссылке. Запись Документы.РеализацияТоваровУслуг.НайтиПоНомеру(...).Товары вернёт табличную часть только для чтения, и попытка изменить строку ни к чему не приведёт либо вызовет ошибку. Для изменения нужно получить объект: ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект(), выполнить обход, затем вызвать ДокОбъект.Записать().

Контрольные точки

Как проверить результат

Количество обработанных строк
Норма

Совпадает с Объект.Товары.Количество()

Тревожный признак

Меньше — часть строк пропущена, проверяйте условия Продолжить и удаление в цикле

Изменённые значения после записи
Норма

В открытом документе видны новые суммы и реквизиты

Тревожный признак

Данные старые — объект не был получен через ПолучитьОбъект() или не вызван Записать()

Поиск строки через Найти
Норма

Возвращена строка, реквизиты читаются

Тревожный признак

Неопределено — значение отбора не совпадает по типу или строки нет

FAQ

Чем отличается индекс строки от НомерСтроки?

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

Можно ли обойти табличную часть запросом вместо цикла?

Да, для документов, уже записанных в базу, запрос к табличной части (например, «ВЫБРАТЬ ... ИЗ Документ.РеализацияТоваровУслуг.Товары») часто эффективнее цикла, особенно при группировках и соединениях со справочниками. Цикл остаётся нужен для ещё не записанных данных в памяти и при изменении строк.

Как удалить все строки табличной части?

Используйте метод «Очистить()»: Объект.Товары.Очистить();. Это быстрее и надёжнее, чем удаление строк по одной в цикле, и не создаёт проблем со смещением индексов.

Почему изменения строк на форме не видны после обхода?

Чаще всего обход выполнен по выгруженной таблице значений (метод «Выгрузить»), которая является копией, либо изменения сделаны в данных объекта на сервере, а форма не обновилась. Проверьте, что работаете именно с коллекцией формы, и при необходимости обновите отображение или данные формы.

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

Соберите строки к удалению в массив внутри цикла «Для Каждого», а после цикла пройдите по массиву и вызовите «Удалить» для каждой строки. Так коллекция не меняется во время перебора, и ни одна строка не будет пропущена.

Что быстрее для подсчёта суммы — цикл или метод Итог?

Метод «Итог("Сумма")» короче и выполняется встроенными средствами платформы, поэтому предпочтительнее. Цикл оправдан только если одновременно с суммированием нужно выполнять дополнительные действия со строками.

Итог

Что важно запомнить

Основной способ обхода — цикл «Для Каждого» с обращением к реквизитам строки через точку. Для поиска используйте Найти и НайтиСтроки, для сумм — Итог, для группировок — Выгрузить со Свернуть. При удалении строк в цикле перебирайте коллекцию с конца или через промежуточный массив, а для изменения документа по ссылке всегда получайте объект и записывайте его.