Как в СКД вывести сквозную нумерацию строк в кросс-таблице по каждому покупателю

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Рассмотрим подробнее несколько эффективных способов решения этой задачи, начиная от классических запросов и заканчивая современными возможностями платформы 1С.

Метод 1. Использование оконных функций (Платформа 8.3.20 и выше)

Проанализируем самый современный и производительный способ. Если ваша версия платформы позволяет использовать оконные функции в языке запросов, задача решается одной строкой. Оконная функция ПОРЯДКОВЫЙ_НОМЕР() позволяет пронумеровать записи внутри определенного окна (в нашем случае — в разрезе покупателя).

Разберем пример запроса:


ВЫБРАТЬ
    Продажи.Покупатель КАК Покупатель,
    Продажи.Количество КАК Количество,
    ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕДИНИТЬ ПО Продажи.Покупатель УПОРЯДОЧИТЬ ПО Продажи.Дата КАК НомерПокупки
ИЗ
    Документ.РеализацияТоваровУслуг КАК Продажи
ГДЕ
    Продажи.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания

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

Метод 2. Внутреннее соединение таблицы самой с собой

Если вы работаете на более ранних версиях платформы, где оконные функции недоступны, можно воспользоваться классическим методом нумерации через соединение. Мы присоединяем таблицу продаж к самой себе по условию «Покупатель тот же, а Дата или Ссылка меньше или равна текущей».

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


ВЫБРАТЬ
    Т1.Покупатель КАК Покупатель,
    Т1.Ссылка КАК Документ,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.Ссылка) КАК НомерПокупки
ИЗ
    Документ.РеализацияТоваровУслуг КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Т2
        ПО Т1.Покупатель = Т2.Покупатель
            И Т1.Дата >= Т2.Дата
ГДЕ
    Т1.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
СГРУППИРОВАТЬ ПО
    Т1.Покупатель,
    Т1.Ссылка

Важный момент: этот метод может работать медленно на больших объемах данных, так как количество соединений растет в геометрической прогрессии. Однако для небольших отчетов это надежное и проверенное решение.

Метод 3. Реализация через внешний набор данных (Объект)

Выясним причину, почему иногда проще подготовить данные в коде 1С, чем писать сложный запрос. Если логика нумерации зависит от множества условий, можно выгрузить результат запроса в ТаблицуЗначений, обработать ее в цикле и передать обратно в СКД.

Посмотрим на алгоритм реализации в модуле отчета:

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

Пример кода на языке 1С:


ТекущийПокупатель = Неопределено;
Счетчик = 0;

Для Каждого Строка Из ТаблицаРезультат Цикл
    Если Строка.Покупатель <> ТекущийПокупатель Тогда
        ТекущийПокупатель = Строка.Покупатель;
        Счетчик = 1;
    Иначе
        Счетчик = Счетчик + 1;
    КонецЕсли;
    Строка.НомерПокупки = Счетчик;
КонецЦикла;

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

Метод 4. Использование функции «АВТОНОМЕРЗАПИСИ»

Начиная с версии 8.3.13, в языке запросов появилась функция АВТОНОМЕРЗАПИСИ(). Она позволяет получить инкрементальный номер для каждой записи результирующей таблицы. Однако стоит учитывать, что она просто нумерует строки результата «подряд».

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

Почему системное поле «НомерПоПорядку» не подходит?

Проанализируем типичную ошибку начинающих разработчиков. Поле НомерПоПорядку в настройках компоновки (раздел «Выбранные поля») вычисляется процессором вывода в самом конце. Если вы добавите его в кросс-таблицу, оно просто пронумерует визуальные строки от 1 до N. Если Иванов купил товары 3 раза, а Петров 1 раз, вы получите таблицу с тремя строками, но данные Петрова «размажутся» или схлопнутся некорректно, так как у системы не будет ключа связи для помещения Петрова именно в первую строку рядом с первой покупкой Иванова.

Запомните: для правильной работы кросс-таблицы порядковый номер должен быть физическим полем в наборе данных (из запроса или таблицы значений), по которому осуществляется группировка в строках отчета.

Итоговая настройка СКД

После того как вы получили поле НомерПокупки одним из вышеперечисленных методов, выполните следующие шаги в настройках СКД:

  1. Перейдите на закладку Настройки.
  2. Выберите тип основного обхода — Таблица.
  3. В Строки таблицы добавьте группировку по полю НомерПокупки.
  4. В Колонки таблицы добавьте группировку по полю Покупатель.
  5. В Выбранные поля таблицы (в область пересечения) добавьте Количество, Сумма или другой нужный ресурс.

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

← На главную