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