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