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