При работе с системой компоновки данных (СКД) в 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. Получаем исходные агрегированные данные
КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроек.Инициализировать(ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных"));
// Здесь могут быть применены пользовательские настройки и параметры
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(КомпоновщикНастроек.ПолучитьСхемуКомпоновкиДанных(), Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
РезультатВыполнения = Новый ТаблицаЗначений;
РезультатВыполнения.Колонки.Добавить("ШтрихкодЗаказа");
РезультатВыполнения.Колонки.Добавить("Количество"); // Это будет количество, которое мы разворачиваем
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
// Можно использовать ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений
// для более удобного получения ТЗ, если мы не выводим сразу в ТабличныйДокумент
// Для примера, имитируем получение исходной ТЗ
ИсходнаяТЗ = Новый ТаблицаЗначений;
ИсходнаяТЗ.Колонки.Добавить("ШтрихкодЗаказа");
ИсходнаяТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
НоваяСтрока = ИсходнаяТЗ.Добавить();
НоваяСтрока.ШтрихкодЗаказа = "M-00167507";
НоваяСтрока.Количество = 3;
НоваяСтрока = ИсходнаяТЗ.Добавить();
НоваяСтрока.ШтрихкодЗаказа = "M-00167507";
НоваяСтрока.Количество = 2;
// 2. Создаем новую ТаблицуЗначений для развернутых данных
РазвернутаяТЗ = Новый ТаблицаЗначений;
РазвернутаяТЗ.Колонки.Добавить("ШтрихкодЗаказа", Новый ОписаниеТипов("Строка"));
РазвернутаяТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число")); // Всегда 1
// 3. Цикл по исходным данным для "разворачивания"
Для Каждого СтрокаИсходнойТЗ Из ИсходнаяТЗ Цикл
Для Счетчик = 1 По СтрокаИсходнойТЗ.Количество Цикл
НоваяРазвернутаяСтрока = РазвернутаяТЗ.Добавить();
НоваяРазвернутаяСтрока.ШтрихкодЗаказа = СтрокаИсходнойТЗ.ШтрихкодЗаказа + "-" + Счетчик;
НоваяРазвернутаяСтрока.Количество = 1;
КонецЦикла;
КонецЦикла;
// 4. Теперь РазвернутаяТЗ содержит данные в нужном формате.
// Мы можем передать ее в новый отчет СКД как набор данных "Объект".
// Для этого в схеме СКД создаем набор данных типа "Объект",
// а в параметрах указываем имя этой таблицы значений.
// Например, если имя набора данных "РазвернутыеДанные", то:
// Параметры.Вставить("РазвернутыеДанные", РазвернутаяТЗ);
// Или, если это внешний отчет, то можно передать ее как параметр для основной схемы:
// КомпоновщикНастроек.ПараметрыДанных.УстановитьЗначениеПараметра("МояТЗ", РазвернутаяТЗ);
// Пример вывода РазвернутойТЗ в ТабличныйДокумент напрямую (для демонстрации)
// Эта часть обычно не нужна, если выводим через СКД
ТабДок = Новый ТабличныйДокумент;
ТабДок.Очистить();
Для Каждого Колонка Из РазвернутаяТЗ.Колонки Цикл
ТабДок.Область(1, Колонка.Индекс + 1).Текст = Колонка.Имя;
КонецЦикла;
Для Каждого Строка Из РазвернутаяТЗ Цикл
Для Каждого Колонка Из РазвернутаяТЗ.Колонки Цикл
ТабДок.Область(Строка.Индекс + 2, Колонка.Индекс + 1).Текст = Строка[Колонка.Имя];
КонецЦикла;
КонецЦикла;
ТабДок.Показать();
КонецПроцедуры
Этот подход очень надежен, когда требуется сложная логика трансформации или когда необходимо интегрировать данные из нескольких источников, которые трудно объединить напрямую в запросе. Однако для очень больших объемов данных он может быть менее производительным, чем чисто запросными решениями, так как требует передачи всех данных на сторону клиентского/серверного кода 1С для обработки.
Метод "соединения с таблицей чисел" является одним из наиболее элегантных и производительных способов решения задачи полностью на уровне запроса. Он позволяет "размножить" строки исходного набора данных без необходимости программной обработки в коде 1С.
Суть метода:
Рассмотрим шаги по реализации:
ОБЪЕДИНИТЬ ВСЕ.ВНУТРЕННЕЕ СОЕДИНЕНИЕ или ЛЕВОЕ СОЕДИНЕНИЕ основного запроса с таблицей чисел.Пример запроса с использованием ОТЕ для таблицы чисел:
// Определяем Общее Табличное Выражение (ОТЕ) для генерации чисел
ВЫБРАТЬ
1 КАК Номер
ПОМЕСТИТЬ Числа
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Номер + 1
ИЗ
Числа
ГДЕ
Номер < 100 // Максимальное количество товара, которое может быть развернуто
ИНДЕКСИРОВАТЬ ПО Номер
;
// Основной запрос с исходными данными
ВЫБРАТЬ
ТаблицаЗаказов.ШтрихкодЗаказа,
ТаблицаЗаказов.Количество
ПОМЕСТИТЬ ИсходныеДанные
ИЗ
(ВЫБРАТЬ "M-00167507" КАК ШтрихкодЗаказа, 3 КАК Количество
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ "M-00167507" , 2 ) КАК ТаблицаЗаказов // Имитация получения данных из регистра
ИНДЕКСИРОВАТЬ ПО ШтрихкодЗаказа
;
// Соединение исходных данных с таблицей чисел и формирование результата
ВЫБРАТЬ
ИсходныеДанные.ШтрихкодЗаказа + "-" + Числа.Номер КАК ШтрихкодЗаказаСПостфиксом,
1 КАК Количество
ИЗ
ИсходныеДанные КАК ИсходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Числа КАК Числа
ПО ИсходныеДанные.Количество >= Числа.Номер
ГДЕ
Числа.Номер <= ИсходныеДанные.Количество
УПОРЯДОЧИТЬ ПО
ШтрихкодЗаказаСПостфиксом
Этот метод является предпочтительным для больших объемов данных, так как вся работа по генерации строк выполняется на стороне СУБД, что, как правило, гораздо быстрее, чем программная обработка на стороне 1С. Кроме того, он сохраняет возможность гибкой настройки отчета через СКД.
Функция АвтоНомерЗаписи() в языке запросов 1С предназначена для присвоения порядкового номера строкам результата запроса. Она может показаться привлекательной для решения нашей задачи, однако имеет существенные ограничения, которые делают ее менее подходящей для "разворачивания" количества.
Как работает АвтоНомерЗаписи():
Эта функция присваивает уникальный последовательный номер каждой строке, попадающей в результирующий набор запроса. Нумерация начинается с 1 и увеличивается для каждой следующей строки. Важно понимать, что она не предназначена для дублирования строк по количеству, а лишь нумерует уже существующие строки.
Ограничения:
АвтоНомерЗаписи() не увеличивает количество строк в результате запроса. Если у вас было 2 строки, он просто пронумерует их как 1 и 2. Для нашей задачи нам нужно из одной строки с количеством 5 получить 5 строк.Пример использования (но не для "разворачивания"):
Если бы мы уже каким-то образом получили развернутые строки (например, с помощью таблицы чисел), АвтоНомерЗаписи() можно было бы использовать для добавления постфикса, но только если этот постфикс должен быть сквозным номером, а не номером в рамках группы.
ВЫБРАТЬ
ИсходныеДанные.ШтрихкодЗаказа,
ИсходныеДанные.Количество,
АвтоНомерЗаписи() КАК ПорядковыйНомер
ИЗ
ИсходныеДанные КАК ИсходныеДанные
// Если бы ИсходныеДанные уже были развернуты,
// то ПорядковыйНомер был бы сквозным по всему набору.
Таким образом, хотя АвтоНомерЗаписи() является полезным инструментом для нумерации, она не подходит для основной задачи "разворачивания" агрегированных данных по количеству. Более эффективным и надежным способом для этой цели является соединение с таблицей чисел.
Мы рассмотрели три основных подхода к решению задачи по преобразованию агрегированных строк отчета СКД в детализированные строки с уникальными постфиксами. Каждый из методов имеет свои сильные стороны:
ПриКомпоновкеРезультата) обеспечивает максимальную гибкость и контроль над данными. Этот подход идеален для сложных трансформаций, которые трудно выразить в запросе, или для интеграции данных из разнородных источников. Однако он может быть менее производительным для очень больших объемов данных.АвтоНомерЗаписи(), хотя и предназначено для нумерации, не подходит напрямую для "разворачивания" строк по количеству из-за своих ограничений. Его можно применять только для нумерации уже сформированных строк.Выбор конкретного метода будет зависеть от сложности требуемой логики, объема данных, требований к производительности и предпочтений разработчика. В большинстве случаев мы рекомендуем начать с попытки реализовать решение на уровне запроса с таблицей чисел, как наиболее эффективное и масштабируемое.