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