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