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