Как вывести количество, цену и сумму по разным товарам в отдельные колонки в 1С СКД без схлопывания строк?

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

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

Однако задача существенно усложняется, если в один и тот же день одному и тому же контрагенту один товар мог продаваться по разным ценам несколькими документами реализации. Стандартная таблица СКД при группировке по дате и контрагенту автоматически объединит (схлопнет) эти продажи в одну строку, рассчитав среднюю или средневзвешенную цену. Нам же требуется сохранить детальность строк и вывести каждую уникальную продажу отдельно. В этой статье мы подробно рассмотрим два эффективных архитектурных решения этой проблемы.

Почему стандартная кросс-таблица СКД здесь не работает?

Прежде чем перейти к решениям, проанализируем поведение стандартного механизма кросс-таблиц СКД. Если мы попытаемся решить задачу «в лоб» через структуру «Таблица» в настройках СКД (где в строках указаны Дата и Контрагент, в колонках — Номенклатура, а в ресурсах — Количество, Цена и Сумма), то столкнемся со следующими проблемами:

  1. Неудобный порядок колонок. СКД по умолчанию группирует ресурсы под колонками номенклатуры. Мы получим иерархию вида: Товар 1 -> Количество, Цена, Сумма | Товар 2 -> Количество, Цена, Сумма. Сделать так, чтобы сначала шли все колонки с количеством (Количество Т1, Количество Т2), а затем все цены и суммы, штатными средствами настройки кросс-таблицы крайне затруднительно.
  2. Автоматическая агрегация. Без уникального ключа в группировке строк (например, без документа-регистратора) СКД неизбежно объединит строки за один день по одному контрагенту. Вместо детальных строк с уникальными ценами мы получим одну строку со средним арифметическим или средневзвешенным значением цены, что искажает реальные финансовые показатели.

Для обхода этих ограничений мы разберем два альтернативных метода.


Решение 1: Транспонирование на уровне запроса (условная агрегация)

Если список интересующих товаров относительно стабилен и фиксирован (например, это 4 ключевых позиции, которые практически не меняются), наиболее производительным и простым решением будет разворот данных непосредственно внутри SQL-запроса с помощью конструкции ВЫБОР КОГДА. Это аналог классического оператора PIVOT.

Чтобы решить проблему схлопывания строк с разными ценами в рамках одного дня, мы добавим в группировку запроса уникальный идентификатор записи — документ Регистратор (или саму рассчитываемую цену), но при выводе в СКД скроем его от пользователя.

Рассмотрим пример построения такого запроса на основе регистра накопления ВыручкаИСебестоимостьПродаж:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Выручка.Период, ДЕНЬ) КАК Дата,
    Выручка.Регистратор.Контрагент КАК Контрагент,
    Выручка.Регистратор КАК Регистратор, // Поле-ключ для разделения детальных строк
    ВЫБОР 
        КОГДА Выручка.Количество <> 0 
        ТОГДА Выручка.СуммаВыручки / Выручка.Количество 
        ИНАЧЕ 0 
    КОНЕЦ КАК ЦенаГруппировки, // Дополнительный ключ разделения, если в одном документе разные цены

    // Показатели для Товара 1
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар1 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) КАК КоличествоТ1,
    ВЫБОР 
        КОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар1 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) <> 0
        ТОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар1 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) / 
              СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар1 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ)
        ИНАЧЕ 0 
    КОНЕЦ КАК ЦенаТ1,
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар1 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) КАК СуммаТ1,

    // Показатели для Товара 2
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар2 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) КАК КоличествоТ2,
    ВЫБОР 
        КОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар2 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) <> 0
        ТОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар2 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) / 
              СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар2 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ)
        ИНАЧЕ 0 
    КОНЕЦ КАК ЦенаТ2,
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар2 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) КАК СуммаТ2,

    // Показатели для Товара 3
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар3 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) КАК КоличествоТ3,
    ВЫБОР 
        КОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар3 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) <> 0
        ТОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар3 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) / 
              СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар3 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ)
        ИНАЧЕ 0 
    КОНЕЦ КАК ЦенаТ3,
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар3 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) КАК СуммаТ3,

    // Показатели для Товара 4
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар4 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) КАК КоличествоТ4,
    ВЫБОР 
        КОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар4 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ) <> 0
        ТОГДА СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар4 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) / 
              СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар4 ТОГДА Выручка.Количество ИНАЧЕ 0 КОНЕЦ)
        ИНАЧЕ 0 
    КОНЕЦ КАК ЦенаТ4,
    СУММА(ВЫБОР КОГДА Выручка.АналитикаУчетаНоменклатуры.Номенклатура = &Товар4 ТОГДА Выручка.СуммаВыручки ИНАЧЕ 0 КОНЕЦ) КАК СуммаТ4,

    // Общие итоги по всей строке
    СУММА(Выручка.Количество) КАК ИтогоКоличество,
    СУММА(Выручка.СуммаВыручки) КАК ИтогоСумма
ИЗ
    РегистрНакопления.ВыручкаИСебестоимостьПродаж КАК Выручка
ГДЕ
    Выручка.Период МЕЖДУ &НачДата И &КонДата
    И Выручка.АналитикаУчетаНоменклатуры.Номенклатура В (&СписокНоменклатуры)
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(Выручка.Период, ДЕНЬ),
    Выручка.Регистратор.Контрагент,
    Выручка.Регистратор,
    ВЫБОР 
        КОГДА Выручка.Количество <> 0 
        ТОГДА Выручка.СуммаВыручки / Выручка.Количество 
        ИНАЧЕ 0 
    КОНЕЦ

Разберем, как правильно настроить схему компоновки данных для этого запроса:

  1. Создаем обычный отчет типа «Список» (а не кросс-таблицу).
  2. В структуре настроек добавляем следующую иерархию группировок: Дата -> Контрагент -> Детальные записи.
  3. Переходим на вкладку «Выбранные поля» для группировки «Детальные записи» и добавляем туда наши вычисляемые ресурсы: КоличествоТ1, ЦенаТ1, СуммаТ1... и так далее до ИтогоСумма.
  4. Важно: поля Регистратор и ЦенаГруппировки мы не добавляем в выбранные поля. Они выполняют чисто технологическую функцию в запросе, разделяя записи с разными ценами. На выходе у нас получится плоская, красивая и легко читаемая таблица ровно в той структуре, которую просил автор.

Решение 2: Паттерн «Таблица показателей» (для динамического списка товаров)

Рассмотренный выше первый метод идеален, если список номенклатурных позиций статичен. Но что делать, если пользователи регулярно расширяют список интересующей продукции через настройки отчета? Переписывать каждый раз текст запроса, добавляя новые блоки ВЫБОР КОГДА — это тупиковый путь поддержки информационной системы.

Для таких случаев в архитектуре запросов 1С используется продвинутый паттерн проектирования — «Таблица показателей». Его идея состоит в том, что мы искусственно дублируем исходные записи в запросе для каждого вида выводимого показателя (Количество, Цена, Сумма), идентифицируя их строковой константой.

Выясним, как построить такой динамический запрос по шагам:

Шаг 1. Сначала мы выбираем исходные сгруппированные данные по продажам во временную таблицу, сохраняя уникальность по документу продажи и цене:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Выручка.Период, ДЕНЬ) КАК Дата,
    Выручка.Регистратор.Контрагент КАК Контрагент,
    Выручка.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
    Выручка.Регистратор КАК Регистратор,
    СУММА(Выручка.Количество) КАК Количество,
    СУММА(Выручка.СуммаВыручки) КАК СуммаВыручки,
    ВЫБОР 
        КОГДА СУММА(Выручка.Количество) <> 0 
        ТОГДА СУММА(Выручка.СуммаВыручки) / СУММА(Выручка.Количество) 
        ИНАЧЕ 0 
    КОНЕЦ КАК Цена
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
    РегистрНакопления.ВыручкаИСебестоимостьПродаж КАК Выручка
ГДЕ
    Выручка.Период МЕЖДУ &НачДата И &КонДата
    И Выручка.АналитикаУчетаНоменклатуры.Номенклатура В (&СписокНоменклатуры)
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(Выручка.Период, ДЕНЬ),
    Выручка.Регистратор.Контрагент,
    Выручка.АналитикаУчетаНоменклатуры.Номенклатура,
    Выручка.Регистратор
;

Шаг 2. Теперь мы выполняем разворот показателей, объединяя выборки по типам выводимых данных с помощью конструкции ОБЪЕДИНИТЬ ВСЕ:


ВЫБРАТЬ
    Дата,
    Контрагент,
    Номенклатура,
    Регистратор,
    Цена КАК ЦенаГруппировки,
    "1. Количество" КАК Показатель,
    Количество КАК Значение
ИЗ
    ВТ_ИсходныеДанные

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

ВЫБРАТЬ
    Дата,
    Контрагент,
    Номенклатура,
    Регистратор,
    Цена,
    "2. Цена",
    Цена
ИЗ
    ВТ_ИсходныеДанные

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

ВЫБРАТЬ
    Дата,
    Контрагент,
    Номенклатура,
    Регистратор,
    Цена,
    "3. Сумма",
    СуммаВыручки
ИЗ
    ВТ_ИсходныеДанные

Шаг 3. Настройка структуры и ресурсов в СКД:

Теперь вся магия происходит на уровне настроек компоновки данных. Мы создаем структуру отчета типа «Таблица»:

  1. Строки таблицы: Добавляем иерархическую структуру группировок: Дата -> Контрагент -> Регистратор -> ЦенаГруппировки. Чтобы отчет выглядел чисто, у группировок Регистратор и ЦенаГруппировки в свойствах (на вкладке «Другие настройки») устанавливаем параметр «Расположение группировок» = «Нет». Они будут невидимы для пользователя, но сохранят структуру строк без нежелательного объединения.
  2. Колонки таблицы: Добавляем группировку по полю Показатель. Внутрь нее (как подчиненную группировку колонок) добавляем поле Номенклатура. Благодаря префиксам в строках ("1. Количество", "2. Цена", "3. Сумма") колонки в отчете выстроятся строго по типам показателей, а не перемешаются.
  3. Настройка ресурсов: Единственным числовым ресурсом нашего отчета становится поле Значение. Но для него нужно корректно прописать формулу расчета на вкладке «Ресурсы» СКД. Формула должна зависеть от контекста выводимого показателя:

Выбор 
    Когда Показатель = "2. Цена" 
    Тогда Максимум(Значение) 
    Иначе Сумма(Значение) 
Конец

Этот подход гарантирует, что количество и сумма будут корректно суммироваться в итогах, а для цен СКД не станет вычислять бессмысленную математическую сумму, оставив точное значение.

Проанализировав оба метода, мы видим, что СКД предоставляет колоссальные возможности для кастомизации вывода информации. Первый метод на базе условной агрегации в запросе проще в понимании и отлично подходит для статичных структур. Второй метод на базе паттерна показателей — это профессиональный инструмент 1С-разработчика, позволяющий гибко масштабировать отчет при любых изменениях справочника номенклатуры пользователями.

← На главную