При формировании управленческой отчетности по продажам или взаиморасчетам руководители и менеджеры часто сталкиваются с ситуацией: в отбор отчета добавили список ключевых клиентов (например, 20 контрагентов на контроле), а при формировании сформировались строки только по 18 из них. Клиенты, по которым за выбранный период не было ни одной отгрузки или оплаты, попросту не попадают в итоговую таблицу. Однако для контроля важно видеть именно тех, кто перестал покупать, пусть даже со значением ноль.
Разберем, почему типовые отчеты конфигурации 1С:Управление торговлей 10.3 ведут себя подобным образом и как технически решить эту задачу.
Большинство типовых отчетов в УТ 10.3 («Продажи», «Ведомость по взаиморасчетам с контрагентами» и др.) построены на базе механизма Универсального отчета, использующего объект платформы ПостроительОтчета. По умолчанию типовой отчет обращается напрямую к виртуальной таблице: РегистрНакопления.Продажи.Обороты.
Виртуальная таблица оборотов платформы 1С устроена так, что возвращает записи только по тем комбинациям измерений, по которым за указанный период были фактические движения (регистраторы). Если продаж контрагенту не было, в таблице оборотов физически отсутствуют строки по нему. Пользовательский отбор в отчете лишь накладывает фильтр на уже полученную выборку, отсекая лишнее, но он не может сгенерировать отсутствующие данные. В объекте ПостроительОтчета нет стандартного флажка «Выводить строки без движений», поэтому задачу приходится решать переработкой структуры запроса.
Создание нового внешнего отчета на базе СКД — наиболее надежный и быстрый путь. Схема компоновки данных берет на себя автоматическое оформление, группировки и вывод ресурсов.
Рассмотрим реализацию через Левое соединение в запросе. Чтобы в отчет попали все контрагенты из отбора независимо от наличия движений, в качестве ведущей (левой) таблицы берем Справочник.Контрагенты, а к ней присоединяем обороты регистра.
Разберем по шагам текст запроса для набора данных СКД:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Контрагент,
ЕСТЬNULL(ПродажиОбороты.КоличествоОборот, 0) КАК Количество,
ЕСТЬNULL(ПродажиОбороты.СтоимостьОборот, 0) КАК СуммаВыручки
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты(
&НачалоПериода,
&КонецПериода,
Период,
) КАК ПродажиОбороты
ПО Контрагенты.Ссылка = ПродажиОбороты.ДоговорКонтрагента.Владелец
ГДЕ
НЕ Контрагенты.ЭтоГруппа
{ГДЕ
Контрагенты.Ссылка.* КАК Контрагент}
Проанализируем критически важные нюансы этого запроса:
ЕСТЬNULL(): для клиентов без движений правая таблица вернет значение NULL. Если не заменить его на 0, строки останутся пустыми, а агрегатные формулы ресурсов могут работать некорректно.{ГДЕ Контрагенты.Ссылка.* КАК Контрагент} обязательно должно быть наложено на поле справочника (левую таблицу). Если случайно поместить отбор на поле ПродажиОбороты.ДоговорКонтрагента.Владелец, СУБД автоматически трансформирует ЛЕВОЕ СОЕДИНЕНИЕ во внутреннее (INNER JOIN), и контрагенты с нулями снова пропадут из отчета.Также в СКД можно применить альтернативный подход: создать два набора данных. Первый набор выбирает список контрагентов из справочника, а второй — обороты продаж. Затем на вкладке «Связи наборов данных» связываем их по полю Контрагент. В этом случае СКД автоматически выведет все записи из первого набора.
Если требуется сохранить привычный пользователям интерфейс обычных форм УТ 10.3, модифицируем типовой отчет. В модуле объекта типового отчета «Продажи» найдем процедуру УстановитьНачальныеНастройки(), которая отвечает за конфигурацию построителя.
Посмотрим на пример переопределения логики отчета:
Процедура УстановитьНачальныеНастройки(ЛожьЗаполнять = Ложь) Экспорт
// Отключаем автоматическое обращение к регистру
УниверсальныйОтчет.мВыбиратьИмяРегистра = Ложь;
// Задаем собственный текст запроса с левым соединением
ТекстЗапроса =
"ВЫБРАТЬ
| СпрКонтрагенты.Ссылка КАК Контрагент,
| ЕСТЬNULL(Вложенный.КоличествоОборот, 0) КАК Количество,
| ЕСТЬNULL(Вложенный.СтоимостьОборот, 0) КАК Стоимость
|{ВЫБРАТЬ
| Контрагент.*,
| Количество,
| Стоимость}
|ИЗ
| Справочник.Контрагенты КАК СпрКонтрагенты
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, Авто, , ) КАК Вложенный
| ПО СпрКонтрагенты.Ссылка = Вложенный.ДоговорКонтрагента.Владелец
|ГДЕ
| НЕ СпрКонтрагенты.ЭтоГруппа
|{ГДЕ
| СпрКонтрагенты.Ссылка.* КАК Контрагент}
|{СГРУППИРОВАТЬПО
| Контрагент.*}";
ПостроительОтчета.Текст = ТекстЗапроса;
// Инициализируем доступные поля и показатели отчета
УниверсальныйОтчет.ДобавитьПолеГруппировка("Контрагент", "Контрагент", "Контрагент");
УниверсальныйОтчет.ДобавитьПоказатель("Стоимость", "Сумма продажи", Истина, "ЧЦ=15; ЧДЦ=2");
УниверсальныйОтчет.ДобавитьПоказатель("Количество", "Количество", Истина, "ЧЦ=15; ЧДЦ=3");
КонецПроцедуры
Обратите внимание на директивы {ВЫБРАТЬ ...}, {ГДЕ ...} и {СГРУППИРОВАТЬПО ...}. Они необходимы объекту ПостроительОтчета для того, чтобы на форму выводились стандартные панели отборов и группировок.
Если внесение правок в код нежелательно, проанализируем смежные типовые отчеты блока CRM в УТ 10.3, предназначенные для контроля клиентской базы: