При разработке на управляемых формах в 1С часто возникает задача серверной обработки данных, находящихся в табличной части документа или справочника. Например, необходимо удалить строки по условию, пересчитать суммы или выполнить поиск. Однако попытки обратиться к табличной части напрямую часто приводят к путанице с типами данных.
В этой статье мы подробно разберем, как правильно работать с табличными частями при конвертации данных формы, почему возникает тип ДанныеФормыКоллекция и как решать задачи по изменению данных, например, удалению строк с нулевой ценой.
В первом сообщении темы автор столкнулся с классической ситуацией: попытка получить табличную часть отдельно от объекта.
Важно понимать архитектуру платформы:
ДанныеФормыКоллекция. Это облегченная структура, предназначенная для отображения данных интерфейсе. У нее нет методов прикладного объекта (например, методов модуля объекта), и она не является полноценной ТабличнойЧастью.ТабличнаяЧасть существует только как свойство прикладного объекта (ДокументОбъект, СправочникОбъект). Она не может существовать "сама по себе", в отрыве от родителя.ТаблицаЗначений — это отдельный тип, в который можно выгрузить данные для произвольной обработки.Поэтому вызов РеквизитФормыВЗначение("Объект.Товары") вернет вам не табличную часть объекта, а коллекцию данных формы, либо вызовет ошибку, если платформа не сможет интерпретировать путь.
Самый надежный и "классический" способ получить доступ к "настоящей" табличной части — это восстановить весь прикладной объект из данных формы. Это позволяет работать с методами объекта и его метаданными.
Рассмотрим алгоритм действий:
Пример кода:
&НаСервере
Процедура ОбработатьТабличнуюЧастьНаСервере()
// 1. Получаем прикладной объект из данных формы
// В этот момент в памяти сервера создается копия объекта с данными из формы
ТекущийОбъект = РеквизитФормыВЗначение("Объект");
// 2. Теперь ТекущийОбъект.Товары — это именно ТабличнаяЧасть
// Мы можем использовать методы объекта или перебирать ее
Для Каждого СтрокаТЧ Из ТекущийОбъект.Товары Цикл
// Какая-то логика обработки
СтрокаТЧ.Сумма = СтрокаТЧ.Цена * СтрокаТЧ.Количество;
КонецЦикла;
// 3. Записываем изменения обратно в данные формы, чтобы пользователь увидел их
ЗначениеВРеквизитФормы(ТекущийОбъект, "Объект");
КонецПроцедуры
В ходе обсуждения (сообщения 5, 9, 14) был предложен и использован метод выгрузки данных в ТаблицуЗначений. Это удобно, если вам нужно использовать специфические методы ТЗ (например, сложные свертки, поиск строк или выгрузка в другую подсистему), которые недоступны в табличной части напрямую, или если вы хотите избежать передачи всего "тяжелого" объекта.
Разберем этот подход:
&НаСервере
Процедура УдалитьНулевыеЦеныЧерезТЗ()
// Вариант А: Через объект (как обсуждалось в теме)
ТекущийОбъект = РеквизитФормыВЗначение("Объект");
// Выгружаем ТЧ в Таблицу Значений
ТаблицаТоваров = ТекущийОбъект.Товары.Выгрузить();
// ... здесь проводим манипуляции с ТЗ ...
// Загружаем обратно
ТекущийОбъект.Товары.Загрузить(ТаблицаТоваров);
// Возвращаем на форму
ЗначениеВРеквизитФормы(ТекущийОбъект, "Объект");
КонецПроцедуры
Важно: Метод Загрузить() полностью перезаписывает содержимое табличной части. Это может привести к потере привязок строк (если логика завязана на идентификаторы строк формы) или сбросу текущей строки в интерфейсе.
Автор темы обозначил конкретную задачу: удалить из списка товары, у которых цена не заполнена (равна нулю). Разберем, как это сделать правильно, не выгружая данные в ТЗ, а работая напрямую с объектом. Это часто бывает оптимальнее по производительности.
При удалении строк из коллекции в цикле нужно помнить главное правило: удалять следует при обходе с конца. Если идти с начала, индексы строк смещаются после удаления, и цикл "перепрыгивает" через строки.
Рассмотрим правильный код решения задачи автора:
&НаСервере
Процедура ОчиститьСтрокиСНезаполненнойЦенойНаСервере()
// Конвертируем данные формы в объект
ДокументОбъект = РеквизитФормыВЗначение("Объект");
// Получаем ссылку на табличную часть для удобства
ТЧТовары = ДокументОбъект.Товары;
// Обходим коллекцию с конца к началу
// Индексы в 1С начинаются с 0, поэтому верхняя граница: Количество() - 1
Индекс = ТЧТовары.Количество() - 1;
Пока Индекс >= 0 Цикл
СтрокаТЧ = ТЧТовары.Получить(Индекс);
// Проверяем условие
Если СтрокаТЧ.Цена = 0 Тогда
ТЧТовары.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// Возвращаем измененный объект на форму
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Если условий удаления немного и они простые (на равенство), можно использовать метод поиска, что сделает код более читаемым. Этот метод работает как с ТаблицейЗначений, так и с ТабличнойЧастью.
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Цена", 0);
// Находим все строки, подходящие под условие
НайденныеСтроки = ДокументОбъект.Товары.НайтиСтроки(ПараметрыОтбора);
// Удаляем их
Для Каждого УдаляемаяСтрока Из НайденныеСтроки Цикл
ДокументОбъект.Товары.Удалить(УдаляемаяСтрока);
КонецЦикла;
РеквизитФормыВЗначение("Объект") создает серверный прикладной объект. Обращение к его табличным частям (Объект.Товары) дает тип ТабличнаяЧасть.ТабличнуюЧасть отдельно от объекта. Если вам нужно передать только таблицу в процедуру общего модуля, используйте выгрузку в ТаблицаЗначений.ЗначениеВРеквизитФормы.Загрузить() при работе с формами — он полностью обновляет коллекцию, что может повлиять на пользовательский опыт (сброс выделения, прокрутки).