Как преобразовать строки отчета СКД с общим количеством в отдельные строки по каждой единице товара с уникальным постфиксом?

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

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

Предположим, у нас есть исходные данные следующего вида:


Штрихкод заказа	          Количество
M-00167507			  3
M-00167507			  2

Наша цель — трансформировать их в такой вид, где каждая единица товара представлена отдельной строкой, а штрихкод дополнен последовательным постфиксом:


Штрихкод заказа	         Количество
M-00167507-1			 1
M-00167507-2			 1
M-00167507-3			 1
M-00167507-4			 1
M-00167507-5			 1

Мы разберем несколько ключевых методов реализации этой задачи, каждый из которых имеет свои преимущества и области применения.

1. Программная обработка результата отчета (ПриКомпоновкеРезультата или набор данных "Объект")

Один из самых гибких и мощных способов решения подобных задач — это программная обработка данных уже после их получения из запроса СКД. Этот подход позволяет реализовать практически любую, даже самую сложную логику, которую трудно или невозможно выразить в запросе. Мы можем использовать либо событие ПриКомпоновкеРезультата схемы компоновки данных, либо сначала получить данные запросом, обработать их в коде, а затем передать в СКД как набор данных типа "Объект".

Суть метода:

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

Рассмотрим шаги по реализации с использованием набора данных "Объект":

  1. Создание исходного запроса: В схеме СКД создайте набор данных "Запрос", который будет получать штрихкод и общее количество.
  2. Получение данных в коде: В модуле отчета (например, в функции, которая формирует отчет) выполните этот запрос и получите данные в ТаблицаЗначений.
  3. Обработка и разворачивание данных:

// Пример получения исходных данных
// Предполагаем, что у вас есть готовый объект СКД (СхемаКомпоновкиДанных)
// и настройки для его выполнения.

Процедура СформироватьОтчетСРазвернутымиДанными()

    // 1. Получаем исходные агрегированные данные
    КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
    КомпоновщикНастроек.Инициализировать(ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных"));
    // Здесь могут быть применены пользовательские настройки и параметры

    Настройки = КомпоновщикНастроек.ПолучитьНастройки();

    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(КомпоновщикНастроек.ПолучитьСхемуКомпоновкиДанных(), Настройки, ДанныеРасшифровки);

    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);

    РезультатВыполнения = Новый ТаблицаЗначений;
    РезультатВыполнения.Колонки.Добавить("ШтрихкодЗаказа");
    РезультатВыполнения.Колонки.Добавить("Количество"); // Это будет количество, которое мы разворачиваем

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    // Можно использовать ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений
    // для более удобного получения ТЗ, если мы не выводим сразу в ТабличныйДокумент

    // Для примера, имитируем получение исходной ТЗ
    ИсходнаяТЗ = Новый ТаблицаЗначений;
    ИсходнаяТЗ.Колонки.Добавить("ШтрихкодЗаказа");
    ИсходнаяТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

    НоваяСтрока = ИсходнаяТЗ.Добавить();
    НоваяСтрока.ШтрихкодЗаказа = "M-00167507";
    НоваяСтрока.Количество = 3;

    НоваяСтрока = ИсходнаяТЗ.Добавить();
    НоваяСтрока.ШтрихкодЗаказа = "M-00167507";
    НоваяСтрока.Количество = 2;
    
    // 2. Создаем новую ТаблицуЗначений для развернутых данных
    РазвернутаяТЗ = Новый ТаблицаЗначений;
    РазвернутаяТЗ.Колонки.Добавить("ШтрихкодЗаказа", Новый ОписаниеТипов("Строка"));
    РазвернутаяТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число")); // Всегда 1

    // 3. Цикл по исходным данным для "разворачивания"
    Для Каждого СтрокаИсходнойТЗ Из ИсходнаяТЗ Цикл
        Для Счетчик = 1 По СтрокаИсходнойТЗ.Количество Цикл
            НоваяРазвернутаяСтрока = РазвернутаяТЗ.Добавить();
            НоваяРазвернутаяСтрока.ШтрихкодЗаказа = СтрокаИсходнойТЗ.ШтрихкодЗаказа + "-" + Счетчик;
            НоваяРазвернутаяСтрока.Количество = 1;
        КонецЦикла;
    КонецЦикла;

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

    // Пример вывода РазвернутойТЗ в ТабличныйДокумент напрямую (для демонстрации)
    // Эта часть обычно не нужна, если выводим через СКД
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.Очистить();
    Для Каждого Колонка Из РазвернутаяТЗ.Колонки Цикл
        ТабДок.Область(1, Колонка.Индекс + 1).Текст = Колонка.Имя;
    КонецЦикла;
    Для Каждого Строка Из РазвернутаяТЗ Цикл
        Для Каждого Колонка Из РазвернутаяТЗ.Колонки Цикл
            ТабДок.Область(Строка.Индекс + 2, Колонка.Индекс + 1).Текст = Строка[Колонка.Имя];
        КонецЦикла;
    КонецЦикла;
    ТабДок.Показать();

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

Этот подход очень надежен, когда требуется сложная логика трансформации или когда необходимо интегрировать данные из нескольких источников, которые трудно объединить напрямую в запросе. Однако для очень больших объемов данных он может быть менее производительным, чем чисто запросными решениями, так как требует передачи всех данных на сторону клиентского/серверного кода 1С для обработки.

2. Решение на уровне запроса СКД (соединение с таблицей чисел)

Метод "соединения с таблицей чисел" является одним из наиболее элегантных и производительных способов решения задачи полностью на уровне запроса. Он позволяет "размножить" строки исходного набора данных без необходимости программной обработки в коде 1С.

Суть метода:

  1. Мы используем вспомогательную "таблицу чисел", которая представляет собой набор данных, содержащий последовательность целых чисел (1, 2, 3, ...).
  2. Мы соединяем наш основной запрос (который выдает штрихкод и общее количество) с этой таблицей чисел.
  3. Условие соединения формулируется таким образом, что число из таблицы чисел должно быть меньше или равно полю "Количество" из основного запроса.
  4. В результате такого соединения каждая исходная строка будет дублироваться столько раз, каково ее значение в поле "Количество". Каждому дублю будет соответствовать свое число из таблицы чисел, которое мы затем используем как постфикс.

Рассмотрим шаги по реализации:

  1. Создание таблицы чисел: Существует несколько способов получить таблицу чисел:
    • Использование временной таблицы в запросе: Самый гибкий способ. Мы можем сгенерировать таблицу чисел "на лету" с помощью рекурсивного общего табличного выражения (ОТЕ) или серии ОБЪЕДИНИТЬ ВСЕ.
    • Использование существующего регистра/справочника: Если в базе есть регистр или справочник с большим количеством элементов, которые можно использовать для получения последовательности чисел (например, измерение с числовым кодом, если их достаточно). Это менее универсально и не всегда применимо.
  2. Соединение с основной таблицей: Выполняем ВНУТРЕННЕЕ СОЕДИНЕНИЕ или ЛЕВОЕ СОЕДИНЕНИЕ основного запроса с таблицей чисел.
  3. Формирование нового штрихкода: В поле выбора запроса мы конкатенируем исходный штрихкод, разделитель (например, "-") и число из таблицы чисел.

Пример запроса с использованием ОТЕ для таблицы чисел:


// Определяем Общее Табличное Выражение (ОТЕ) для генерации чисел
ВЫБРАТЬ
    1 КАК Номер
ПОМЕСТИТЬ Числа
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    Номер + 1
ИЗ
    Числа
ГДЕ
    Номер < 100 // Максимальное количество товара, которое может быть развернуто

ИНДЕКСИРОВАТЬ ПО Номер
;

// Основной запрос с исходными данными
ВЫБРАТЬ
    ТаблицаЗаказов.ШтрихкодЗаказа,
    ТаблицаЗаказов.Количество
ПОМЕСТИТЬ ИсходныеДанные
ИЗ
    (ВЫБРАТЬ "M-00167507" КАК ШтрихкодЗаказа, 3 КАК Количество
     ОБЪЕДИНИТЬ ВСЕ
     ВЫБРАТЬ "M-00167507" , 2 ) КАК ТаблицаЗаказов // Имитация получения данных из регистра

ИНДЕКСИРОВАТЬ ПО ШтрихкодЗаказа
;

// Соединение исходных данных с таблицей чисел и формирование результата
ВЫБРАТЬ
    ИсходныеДанные.ШтрихкодЗаказа + "-" + Числа.Номер КАК ШтрихкодЗаказаСПостфиксом,
    1 КАК Количество
ИЗ
    ИсходныеДанные КАК ИсходныеДанные
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Числа КАК Числа
        ПО ИсходныеДанные.Количество >= Числа.Номер
ГДЕ
    Числа.Номер <= ИсходныеДанные.Количество

УПОРЯДОЧИТЬ ПО
    ШтрихкодЗаказаСПостфиксом

Этот метод является предпочтительным для больших объемов данных, так как вся работа по генерации строк выполняется на стороне СУБД, что, как правило, гораздо быстрее, чем программная обработка на стороне 1С. Кроме того, он сохраняет возможность гибкой настройки отчета через СКД.

3. Использование функции АвтоНомерЗаписи() (с оговорками)

Функция АвтоНомерЗаписи() в языке запросов 1С предназначена для присвоения порядкового номера строкам результата запроса. Она может показаться привлекательной для решения нашей задачи, однако имеет существенные ограничения, которые делают ее менее подходящей для "разворачивания" количества.

Как работает АвтоНомерЗаписи():

Эта функция присваивает уникальный последовательный номер каждой строке, попадающей в результирующий набор запроса. Нумерация начинается с 1 и увеличивается для каждой следующей строки. Важно понимать, что она не предназначена для дублирования строк по количеству, а лишь нумерует уже существующие строки.

Ограничения:

  1. Не дублирует строки: АвтоНомерЗаписи() не увеличивает количество строк в результате запроса. Если у вас было 2 строки, он просто пронумерует их как 1 и 2. Для нашей задачи нам нужно из одной строки с количеством 5 получить 5 строк.
  2. Нумерация не сбрасывается по группам: Нумерация является сквозной для всего результата запроса или для определенных групп, если используется в контексте агрегатных функций с полем группировки. Она не сбрасывается автоматически для каждой новой группы штрихкодов в том смысле, как нам нужно (т.е., чтобы для каждого "M-00167507" нумерация начиналась с 1).
  3. Ограничения использования: Функция имеет ограничения на использование в некоторых выражениях и условиях запроса.

Пример использования (но не для "разворачивания"):

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


ВЫБРАТЬ
    ИсходныеДанные.ШтрихкодЗаказа,
    ИсходныеДанные.Количество,
    АвтоНомерЗаписи() КАК ПорядковыйНомер
ИЗ
    ИсходныеДанные КАК ИсходныеДанные
// Если бы ИсходныеДанные уже были развернуты,
// то ПорядковыйНомер был бы сквозным по всему набору.

Таким образом, хотя АвтоНомерЗаписи() является полезным инструментом для нумерации, она не подходит для основной задачи "разворачивания" агрегированных данных по количеству. Более эффективным и надежным способом для этой цели является соединение с таблицей чисел.

Заключение

Мы рассмотрели три основных подхода к решению задачи по преобразованию агрегированных строк отчета СКД в детализированные строки с уникальными постфиксами. Каждый из методов имеет свои сильные стороны:

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

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

← На главную