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

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

1С: как получить данные из табличной части

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

Табличная часть — это коллекция строк, вложенная в объект: документ, справочник или обработку. Чтобы получить из неё данные, нужно сначала получить сам объект (ссылку и её развёртывание через ПолучитьОбъект() либо данные формы), а затем обратиться к табличной части как к свойству объекта по её имени, например ДокументОбъект.Товары.

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

Ниже разобраны основные приёмы на встроенном языке 1С:Предприятие 8.3 с примерами кода, типичными ошибками и способами проверить, что данные получены корректно.

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

Табличная часть хранится внутри объекта и недоступна напрямую через ссылку. Ссылка — это только идентификатор записи в базе; чтобы прочитать реквизиты и строки, нужно либо получить объект целиком, либо использовать запрос. Имя табличной части задаётся в конфигураторе, типовые варианты — «Товары», «Услуги», «Сотрудники», «Начисления».

Базовое обращение выглядит так:

ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();

ТабЧасть = ДокОбъект.Товары; // коллекция строк табличной части

Сообщить("Строк в табличной части: " + ТабЧасть.Количество());

Если код выполняется на управляемой форме документа, получать объект через ПолучитьОбъект() не нужно — данные уже находятся в реквизите формы. Достаточно обратиться к Объект.Товары в модуле формы. Вызов ПолучитьОбъект() на форме приведёт к чтению данных из базы повторно и может рассинхронизировать форму с объектом.

Перебор строк в цикле

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

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

Сообщить("Номенклатура: " + СтрокаТЧ.Номенклатура

+ ", количество: " + СтрокаТЧ.Количество

+ ", сумма: " + СтрокаТЧ.Сумма);

КонецЦикла;

Для доступа по индексу используется цикл со счётчиком или метод Получить(). Нумерация строк в коллекции начинается с нуля, а свойство НомерСтроки у строки — с единицы. Это частый источник ошибок «на одну строку».

ПерваяСтрока = ДокОбъект.Товары[0]; // первая строка

// или

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

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

КонецЦикла;

Чтение строк на клиенте управляемой формы

В модуле управляемой формы табличная часть представлена коллекцией ДанныеФормыКоллекция. Перебор работает так же, но у строки нет метода Получить() по ссылке, а обращение идёт через Объект.Товары[Индекс]. Директива &НаКлиенте позволяет читать данные без серверного вызова.

Выгрузка табличной части в таблицу значений

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

ТЗ = ДокОбъект.Товары.Выгрузить();

ТЗ.Свернуть("Номенклатура", "Количество,Сумма");

ИтогСумма = ТЗ.Итог("Сумма");

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

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

Цикл Для Каждого

Подходит для построчной обработки и заполнения других структур. Неудобен для группировок и итогов.

Выгрузить() в таблицу значений

Подходит для свёртки, сортировки, подсчёта итогов. Требует памяти под копию данных.

НайтиСтроки()

Подходит для отбора строк по условию без полного перебора. Возвращает массив строк.

Запрос к табличной части

Подходит для чтения без получения объекта и для выборок по многим документам. Работает только на сервере.

Отбор строк через НайтиСтроки

Когда нужны не все строки, а только соответствующие условию, используется метод НайтиСтроки() с параметром типа Структура. Ключ структуры — имя колонки, значение — искомое значение. Сравнение идёт на строгое равенство по всем указанным колонкам одновременно.

Отбор = Новый Структура;

Отбор.Вставить("Номенклатура", ИскомаяНоменклатура);

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

Если НайденныеСтроки.Количество() = 0 Тогда

Сообщить("Строки с такой номенклатурой не найдены");

Иначе

Для Каждого СтрокаТЧ Из НайденныеСтроки Цикл

СтрокаТЧ.Цена = НоваяЦена;

КонецЦикла;

КонецЕсли;

Метод не поддерживает условия вида «больше», «содержит», «не равно». Для таких проверок придётся перебирать строки в цикле с оператором Если или использовать запрос. В типовых конфигурациях также встречается Найти() для поиска первой строки по значению в одной колонке.

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

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Номенклатура КАК Номенклатура,

| СУММА(Товары.Количество) КАК Количество,

| СУММА(Товары.Сумма) КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

|ГДЕ

| Товары.Ссылка = &Ссылка

|СГРУППИРОВАТЬ ПО

| Товары.Номенклатура";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить("" + Выборка.Номенклатура + " — " + Выборка.Количество);

КонецЦикла;

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

Порядок действий при чтении запросом
  1. 1
    Определите точное имя документа и табличной части в конфигураторе
  2. 2
    Сформируйте текст запроса к таблице Документ.Имя.ИмяТабличнойЧасти
  3. 3
    Установите параметр отбора по ссылке на документ
  4. 4
    Выполните запрос и обойдите выборку методом Следующий()
  5. 5
    Проверьте, что выборка не пустая, прежде чем обрабатывать результат

Работа с табличной частью на управляемой форме

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

&НаКлиенте

Процедура ПоказатьИтог(Команда)

ИтогКоличество = 0;

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

ИтогКоличество = ИтогКоличество + СтрокаТЧ.Количество;

КонецЦикла;

Сообщить("Итого количество: " + ИтогКоличество);

КонецПроцедуры;

Если требуется серверная обработка — например, запрос к остаткам по каждой строке — передавайте на сервер не всю форму, а только нужные данные. В серверной процедуре &НаСервере доступ к Объект.Товары сохраняется, но контекст формы сериализуется, поэтому тяжёлые операции лучше выносить в процедуры &НаСервереБезКонтекста с передачей выгруженной таблицы значений.

Типичные ошибки и как их проверить

Большинство проблем при работе с табличными частями связано с контекстом выполнения и неверными именами. Ошибка «Поле объекта не обнаружено» почти всегда означает опечатку в имени табличной части или реквизита — сверяйте имена с конфигуратором, а не с отображаемыми синонимами в интерфейсе.

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

Типичные ошибки
01
Обращение к табличной части через ссылку — данные не читаются, нужен объект или запрос
02
Путаница индекса и НомерСтроки: индекс начинается с 0, НомерСтроки — с 1
03
Изменение выгруженной таблицы значений в ожидании, что объект обновится сам — нужен Загрузить()
04
НайтиСтроки с условием «больше/меньше» — метод поддерживает только равенство
05
Вызов ПолучитьОбъект() в модуле формы — повторное чтение из базы и риск конфликта данных
06
Запрос к табличной части без отбора по ссылке — сканирование всех документов и потеря производительности
Контрольные точки

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

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

Совпадает с числом строк в документе на экране

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

Меньше или ноль — проверьте отбор и то, тот ли документ читается

Итог по сумме или количеству
Норма

Совпадает с итогом табличной части в форме документа

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

Расходится — проверьте свёртку и отбор строк

Поведение после изменения строк
Норма

После Записать() изменения видны при повторном открытии

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

Изменения потеряны — объект не был записан или редактировалась копия

Частые вопросы

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

Обратитесь к строке по индексу и затем к реквизиту: Значение = ДокОбъект.Товары[0].Цена;. Перед обращением убедитесь, что строка существует, проверив ДокОбъект.Товары.Количество() > 0, иначе получите ошибку выхода за границы коллекции.

Можно ли прочитать табличную часть без ПолучитьОбъект()?

Да, через запрос к таблице вида Документ.ИмяДокумента.ИмяТабличнойЧасти с отбором по полю Ссылка. Это быстрее и безопаснее для чтения, так как не загружает весь объект в память и не требует прав на изменение.

Почему НайтиСтроки возвращает пустой массив, хотя строка есть?

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

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

Передайте в фоновую процедуру ссылку на документ, внутри неё получите объект или выполните запрос, а затем обычный цикл Для Каждого. Данные формы и таблицы значений с клиента в фоновое задание передавать не следует — сериализуйте простые структуры.

Чем отличается Выгрузить() от Загрузить()?

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

Как узнать имя табличной части в чужой конфигурации?

Откройте конфигуратор, найдите нужный вид документа или справочника и разверните ветку «Табличные части» в дереве метаданных. Именно имя, а не синоним, используется в коде. Альтернатива — посмотреть обращения в типовых процедурах через глобальный поиск по конфигурации.

Итог

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

Табличная часть читается через объект, данные формы или запрос — но не через ссылку. Для перебора используйте Для Каждого, для итогов — Выгрузить(), для отбора — НайтиСтроки(), для чтения без объекта — запрос. Проверяйте имена в конфигураторе и сверяйте итоги с формой документа.