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