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