Как реализовать расшифровку ресурсов до регистратора в СКД и исправить дублирование данных в запросе?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке отчетов на Системе Компоновки Данных (СКД) часто возникает задача: вывести сводные данные по менеджерам (например, сумму заказов и сумму выручки), а при нажатии на конкретную цифру "проваливаться" в список документов, которые эту цифру сформировали. При этом для колонки "Сумма заказа" должны открываться документы "Заказ клиента", а для "Выручки" — документы реализации.

В этой статье мы подробно разберем, почему стандартный подход с соединением таблиц часто приводит к ошибкам, как правильно спроектировать запрос и как реализовать "умную" расшифровку ячеек, что в итоге позволит ускорить формирование отчета "Валовая прибыль предприятия".

Анализ проблемы: Ошибка в структуре запроса

Рассмотрим ситуацию, описанную автором. Имеется отчет, сгруппированный по Менеджерам. В запросе используются две виртуальные таблицы оборотов: РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты и РегистрНакопления.ЗаказыКлиентов.Обороты.

Посмотрим на исходный запрос, который вызывает проблемы (включая потенциальное дублирование данных):


ВЫБРАТЬ
    ...
ИЗ
    РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(...) КАК ВыручкаИСебестоимостьПродажОбороты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыКлиентов.Обороты(...) КАК ЗаказыКлиентовОбороты
        ПО ВыручкаИСебестоимостьПродажОбороты.ПериодМесяц = ЗаказыКлиентовОбороты.ПериодМесяц

В чем здесь критическая ошибка?

Соединение двух таблиц с детализацией (в данном случае по ПериодМесяц) через ЛЕВОЕ СОЕДИНЕНИЕ неизбежно приводит к так называемому "Декартову произведению", если в одном месяце у менеджера было несколько продаж и несколько заказов.

Допустим, в январе было:

  1. 2 Заказа
  2. 3 Реализации

При соединении по условию "Месяц = Месяц" и "Менеджер = Менеджер" база данных соединит каждую строку заказов с каждой строкой реализаций. В итоге мы получим 2 * 3 = 6 строк. Суммы в отчете задвоятся или утроятся. Именно поэтому отчет показывает некорректные данные, и стандартная расшифровка работает непредсказуемо.

Этап 1: Правильное построение запроса через ОБЪЕДИНИТЬ ВСЕ

Чтобы избежать дублирования и подготовить почву для корректной расшифровки, нам нужно отказаться от соединений (JOIN) в пользу объединения (UNION ALL). Этот метод актуален и для движения товаров с резервами (СКД по двум регистрам накопления). Мы должны выбрать данные из первой таблицы, затем из второй, и "сложить" их в один набор данных.

Перепишем запрос правильно. Мы будем выбирать отдельно заказы и отдельно продажи, приводя их к единой структуре полей.


ВЫБРАТЬ
    ТаблицаПродаж.Менеджер КАК Менеджер,
    ТаблицаПродаж.Регистратор КАК Регистратор,
    ТаблицаПродаж.СуммаВыручкиОборот КАК СуммаВыручки,
    0 КАК СуммаЗаказа,
    "Продажа" КАК ТипОперации
ИЗ
    РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ТаблицаПродаж

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ТаблицаЗаказов.Менеджер,
    ТаблицаЗаказов.Регистратор,
    0, -- СуммаВыручки здесь равна 0
    ТаблицаЗаказов.СуммаОборот,
    "Заказ"
ИЗ
    РегистрНакопления.ЗаказыКлиентов.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ТаблицаЗаказов

Преимущества такого подхода:

  1. Отсутствие дублей: Данные не перемножаются.
  2. Единое поле Регистратор: У нас появляется одна колонка Регистратор, которая хранит ссылку либо на Заказ, либо на Реализацию. Это ключевой момент для работы расшифровки.
  3. Простота настройки СКД: Ресурсы СуммаВыручки и СуммаЗаказа просто суммируются по группировкам (также можно реализовать условие: нет данных — нет столбцов в СКД).

Этап 2: Настройка расшифровки в СКД

Теперь, когда у нас есть правильный запрос, разберем, как сделать так, чтобы при клике на сумму заказа открывались заказы, а на выручку — продажи.

В СКД расшифровка работает контекстно. Если вы кликаете на ячейку в группировке "Менеджер", платформа "знает" только о Менеджере. Она не знает, какой регистратор вы хотите увидеть, потому что в одной строке менеджера их сотни.

Способ А: Стандартная расшифровка (без программирования)

Благодаря тому, что мы переписали запрос через ОБЪЕДИНИТЬ ВСЕ, у нас есть поле Регистратор в детальных записях. Если пользователь дважды кликнет на ячейку с ресурсом (например, Сумма Заказа), платформа предложит стандартное меню выбора поля для расшифровки.

Если пользователь выберет поле Регистратор, СКД автоматически построит отчет, где под выбранным Менеджером покажется список регистраторов. Поскольку мы разнесли суммы по разным колонкам (0 или Значение), то:

Способ Б: Точечная программная расшифровка (Профессиональный подход)

Чтобы сделать интерфейс максимально удобным (без лишних вопросов пользователю и лишних строк с нулями), мы перехватим событие клика.

1. В форме отчета создайте обработчик события ОбработкаРасшифровки элемента формы Результат (Табличный документ).

2. В коде модуля формы мы проанализируем, на какую колонку нажал пользователь.


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    // Получаем данные о том, куда кликнули
    Перем Данные;
    ЭтотОбъект.ДанныеРасшифровки.Элементы.Получить(Расшифровка).ПолучитьПоля(Данные);
    
    // Ищем поле, по которому кликнули
    ВыбраноПолеЗаказов = Ложь;
    ВыбраноПолеВыручки = Ложь;
    
    Для Каждого Поле Из Данные Цикл
        Если Поле.Поле = "СуммаЗаказа" Тогда
            ВыбраноПолеЗаказов = Истина;
            Прервать;
        ИначеЕсли Поле.Поле = "СуммаВыручки" Тогда
            ВыбраноПолеВыручки = Истина;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    // Если кликнули не по ресурсам, а по группировке Менеджер - оставляем стандартное поведение
    Если НЕ ВыбраноПолеЗаказов И НЕ ВыбраноПолеВыручки Тогда
        Возврат;
    КонецЕсли;

    // Отменяем стандартную обработку, будем делать свою
    СтандартнаяОбработка = Ложь;
    
    // Формируем настройки для нового отчета-расшифровки
    // Нам нужно "провалиться" до регистратора
    
    ПараметрыОткрытия = Новый Структура;
    ПараметрыОткрытия.Вставить("СформироватьПриОткрытии", Истина);
    ПараметрыОткрытия.Вставить("Расшифровка", Новый ОписаниеОбработкиРасшифровки(Расшифровка, ЭтотОбъект.ДанныеРасшифровки, , ));
    
    // Здесь мы программно можем добавить группировку "Регистратор" и отбор
    // Но проще всего открыть этот же отчет с новым вариантом настроек или 
    // использовать механизм "ОткрытьЗначение", если мы хотим открыть конкретный список.
    
    // Более простой вариант для начинающих - открыть форму списка документов с отбором
    // Но для отчета правильнее модифицировать компоновщик настроек.
    
    // Пример логики "на лету" (упрощенно):
    Оповещение = Новый ОписаниеОповещения("ОбработкаВыбораРасшифровки", ЭтотОбъект);
    
    СписокДоступныхДействий = Новый СписокЗначений;
    СписокДоступныхДействий.Добавить("Документы", "Показать документы");
    
    ПоказатьВыборИзМеню(Оповещение, СписокДоступныхДействий);

КонецПроцедуры

Для реализации полноценного "проваливания" в список документов внутри отчета, в процедуре обработки часто используют программную модификацию настроек компоновщика.

Рассмотрим алгоритм действий для реализации "умного" перехода:

  1. Определяем поле (как в примере выше).
  2. Создаем новый объект НастройкиКомпоновкиДанных на основании текущих.
  3. В новых настройках добавляем группировку по полю Регистратор.
  4. Если кликнули на СуммаЗаказа, программно добавляем отбор: ТипОперации = "Заказ" (поле из нашего исправленного запроса).
  5. Если кликнули на СуммаВыручки, добавляем отбор: ТипОперации = "Продажа".
  6. Открываем форму отчета с новыми настройками.

Резюме

Главная проблема, с которой столкнулся автор темы, заключалась не столько в механизме расшифровки, сколько в неправильной архитектуре запроса. Использование ЛЕВОЕ СОЕДИНЕНИЕ для оборотных таблиц недопустимо, если нет гарантии уникальности записей в периоде.

Порядок решения задачи:

  1. Переписать запрос, используя конструкцию ОБЪЕДИНИТЬ ВСЕ. Это создаст единый поток данных с полем Регистратор.
  2. В полях наборов данных СКД убедиться, что поле Регистратор доступно (но не обязательно выбрано в основной группировке).
  3. Если требуется строгая фильтрация (чтобы при клике на заказы не показывались пустые строки продаж), необходимо использовать событие ОбработкаРасшифровки в модуле формы, где программно накладывать отбор на тип документа или специальное поле-признак.
← На главную