Почему не получается обратиться к реквизиту объекта через точку в табличной части 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке печатных форм в среде 1С:Предприятие 8 начинающие разработчики часто сталкиваются с ситуацией, когда попытка получить данные «через точку» (например, СсылкаНаОбъект.Товары.Номенклатура.Код) приводит к ошибке или возвращает пустое значение. Проблема заключается в непонимании того, как платформа работает с коллекциями (табличными частями) и ссылочными типами данных. Разберем эту ситуацию подробно, проанализируем причины ошибки и найдем наиболее эффективные способы решения задачи.

Анализ ошибки: Коллекция vs Строка

Рассмотрим ситуацию, описанную автором темы. Попытка обратиться к реквизиту СсылкаНаОбъект.Товары.Номенклатура.ЕдиницаИзмерения напрямую ошибочна по своей сути. Объект Товары является табличной частью, то есть коллекцией строк. У коллекции нет одного конкретного кода или одной единицы измерения, так как в документе может быть множество строк с разными товарами. Система не «понимает», данные какой именно строки вы хотите получить, если вы обращаетесь ко всей таблице сразу.

Важный момент: если вы используете конструкцию вне цикла, вы обращаетесь к пустому или неопределенному контексту коллекции. Чтобы получить данные конкретного товара, нам необходимо обратиться к конкретной строке этой коллекции.

Решение 1: Правильный обход коллекции в цикле

Для того чтобы вывести данные в печатную форму, мы должны перебрать все строки табличной части. Внутри цикла переменная, представляющая текущую строку, дает нам доступ к реквизитам именно этого товара. Проанализируем исправленный код:


ОбластьСтрокаТаблицы = МакетОбработки.ПолучитьОбласть("СтрокаТаблицы");

Для Каждого ТекущаяСтрока Из СсылкаНаОбъект.Товары Цикл
    // Заполняем параметры макета из реквизитов, которые есть прямо в строке (Номенклатура, Количество и т.д.)
    ЗаполнитьЗначенияСвойств(ОбластьСтрокаТаблицы.Параметры, ТекущаяСтрока);
    
    // Обращаемся к вложенным реквизитам через текущую строку
    ОбластьСтрокаТаблицы.Параметры.ЕдиницаИзмерения = ТекущаяСтрока.Номенклатура.ЕдиницаИзмерения;
    ОбластьСтрокаТаблицы.Параметры.Код = ТекущаяСтрока.Номенклатура.Код;
    
    // Выводим заполненную область в табличный документ
    ТабличныйДокумент.Вывести(ОбластьСтрокаТаблицы);
КонецЦикла;

В данном примере мы используем метод ЗаполнитьЗначенияСвойств. Это эффективный инструмент, который автоматически сопоставляет имена реквизитов строки документа с именами параметров в макете. Однако, поскольку Код и ЕдиницаИзмерения являются реквизитами справочника Номенклатура, а не самой строки таблицы, их приходится присваивать вручную, обращаясь через точку к объекту Номенклатура.

Решение 2: Оптимизация производительности (Проблема N+1)

Хотя вышеуказанный код работает, с точки зрения профессиональной разработки он имеет серьезный недостаток — низкую производительность. Каждое обращение вида ТекущаяСтрока.Номенклатура.Код заставляет систему выполнять отдельный микро-запрос к базе данных для получения этого кода. Если в документе 200 строк, система выполнит 200 лишних запросов.

Рассмотрим более правильный подход с использованием запроса. Проанализируем, как можно получить все данные одним махом:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЗаказКлиентаТовары.Номенклатура.Код КАК Код,
    |	ЗаказКлиентаТовары.Номенклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |	ЗаказКлиентаТовары.Количество КАК Количество,
    |	ЗаказКлиентаТовары.Цена КАК Цена
    |ИЗ
    |	Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
    |ГДЕ
    |	ЗаказКлиентаТовары.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    ОбластьСтрокаТаблицы.Параметры.Заполнить(ВыборкаДетальныеЗаписи);
    ТабличныйДокумент.Вывести(ОбластьСтрокаТаблицы);
КонецЦикла;

Преимущества этого метода: мы получаем все данные (включая вложенные реквизиты номенклатуры) за один запрос. Это критически важно для высоконагруженных систем и при работе в облачных сервисах.

Решение 3: Получение максимального значения из таблицы

В процессе обсуждения возник дополнительный вопрос: как получить максимальную дату или другое экстремальное значение из таблицы? В теме предложено решение через сортировку:


ТаблицаТоваров = СсылкаНаОбъект.Товары.Выгрузить();
ТаблицаТоваров.Сортировать("Дата СУБ"); // Сортировка по убыванию
Если ТаблицаТоваров.Количество() > 0 Тогда
    МаксДата = ТаблицаТоваров[0].Дата;
КонецЕсли;

Однако, давайте выясним, есть ли более элегантные способы. Если мы уже используем запросы (как в Решении 2), гораздо проще добавить в запрос агрегатную функцию МАКСИМУМ(Дата). Это избавит нас от необходимости выгружать таблицу в память и сортировать её программно.

Если же таблица уже находится в памяти в виде объекта ТаблицаЗначений, мы можем использовать метод ВыгрузитьКолонку() для получения массива и последующего поиска в нем, но запрос остается эталонным способом работы с данными в 1С.

Полезные советы и рекомендации

  1. Проверка на заполнение: Всегда проверяйте, заполнено ли ссылочное поле, прежде чем обращаться к его реквизитам. Если Номенклатура в строке пустая, обращение к .Код может вызвать ошибку в некоторых контекстах или просто вернуть пустоту, что испортит вид документа. Используйте ЗначениеЗаполнено().
  2. Использование имен параметров: Чтобы метод ЗаполнитьЗначенияСвойств работал максимально эффективно, называйте параметры в макете точно так же, как они называются в конфигурации или в вашем запросе.
  3. Контекст печати: В современных конфигурациях (на базе БСП) старайтесь не писать логику печати непосредственно в форме. Лучшее место для этого — модуль менеджера объекта, процедура Печать. Это позволит вашей печатной форме работать при групповой печати нескольких документов.

Подводя итог, можно сказать, что главная ошибка автора заключалась в попытке обращения к коллекции как к одиночному объекту. Использование цикла Для Каждого полностью решает эту проблему, а использование механизмов запросов позволяет сделать решение профессиональным и быстрым.

← На главную