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