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