При формировании нестандартных отчетов в 1С:Предприятии 7.7 разработчики часто сталкиваются с необходимостью объединить данные за несколько периодов или из разных источников в единую Таблицу Значений (ТЗ). Однако стандартный метод ТЗ.Сортировать() при попытке упорядочить данные по покупателям и номенклатуре перемешивает элементы, ломая древовидную структуру каталога.
Проанализируем причины такого поведения платформы и разберем практические способы правильного вывода данных с сохранением иерархии папок и товаров.
Разберем, почему стандартные подходы не дают желаемого результата:
ТЗ.Сортировать("Покупатель,Номенклатура") сравнивает элементы по их строковому представлению (наименованию или коду). При этом родительские связи игнорируются, и все позиции выстраиваются в общий плоский алфавитный список.ТЗ.Сортировать("Покупатель,*Номенклатура") выполняет сортировку по внутреннему 36-ричному идентификатору (ID) записи в базе данных. Этот идентификатор присваивается объектам хронологически в момент создания и не имеет отношения к иерархической структуре дерева справочника.Самый простой штатный способ сохранить дерево подчинения внутри Таблицы Значений — добавить колонку с полным путем элемента в справочнике и выполнять сортировку по ней.
В платформе 1С 7.7 у объектов справочников есть метод ПолноеНаименование(), который возвращает строку со всеми вышестоящими уровнями иерархии, разделенными символом косой черты (/).
Рассмотрим по шагам реализацию этого подхода:
ПолныйПуть.Посмотрим на пример кода:
ТЗ.НоваяКолонка("ПолныйПуть", "Строка");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Если ПустоеЗначение(ТЗ.Номенклатура) = 0 Тогда
ТЗ.ПолныйПуть = ТЗ.Номенклатура.ПолноеНаименование();
КонецЕсли;
КонецЦикла;
ТЗ.Сортировать("Покупатель,ПолныйПуть");
При таком упорядочивании строки выстроятся по веткам дерева каталога, так как путь родительской папки будет являться префиксом для всех вложенных подгрупп и элементов (например: Бытовая техника, затем Бытовая техника/Холодильники, затем Бытовая техника/Холодильники/Модель 1).
Обратите внимание: Если в наименованиях самих папок или товаров уже содержится символ /, это может нарушить порядок сортировки. В таком случае надежнее использовать метод ПолныйКод(), если в справочнике включена уникальность кодов и задана фиксированная длина с лидирующими нулями.
Если задача сводится к получению данных по нескольким произвольным периодам из регистра «Продажи», вместо выполнения трех запросов в цикле и последующего объединения в ТЗ эффективнее использовать один штатный объект Запрос. Платформа 1С 7.7 позволяет рассчитывать агрегатные функции по условию Когда и автоматически строит правильную иерархию.
Рассмотрим пример формирования такого запроса:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(ПродажиПоПериодам)
|Период С (МинДата) По (МаксДата);
|Покупатель = Регистр.Продажи.Покупатель;
|Номенклатура = Регистр.Продажи.Номенклатура;
|Сумма = Регистр.Продажи.ПродСтоимость;
|Док = Регистр.Продажи.ТекущийДокумент;
|
|Функция СуммаПериод1 = Сумма(Сумма) Когда (Док.ДатаДок >= ДатаНач1) И (Док.ДатаДок <= ДатаКон1);
|Функция СуммаПериод2 = Сумма(Сумма) Когда (Док.ДатаДок >= ДатаНач2) И (Док.ДатаДок <= ДатаКон2);
|Функция СуммаПериод3 = Сумма(Сумма) Когда (Док.ДатаДок >= ДатаНач3) И (Док.ДатаДок <= ДатаКон3);
|
|Группировка Покупатель Без Групп;
|Группировка Номенклатура Иерархия;
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка(1) = 1 Цикл
// Обработка покупателя
Пока Запрос.Группировка(2) = 1 Цикл
// Вложенный обход номенклатуры с автоматическим выводом групп и элементов
Если Запрос.ЭтоГруппа("Номенклатура") = 1 Тогда
// Вывод заголовка группы
Иначе
// Вывод строки товара
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЕсли;
Такой способ полностью снимает проблему ручной сортировки, так как движок запросов 1С 7.7 берет на себя восстановление родительских веток и подсчет промежуточных итогов по уровням.
Если в проекте уже используется внешняя компонента 1С++, для сложных манипуляций с данными рекомендуется применять класс ИндексированнаяТаблица (ИТЗ). Он существенно быстрее стандартной ТЗ и поддерживает встроенную иерархическую группировку.
Разберем механизм работы с ИТЗ:
& перед именем поля справочника.Посмотрим на пример использования:
ИТЗ = СоздатьОбъект("ИндексированнаяТаблица");
// Загружаем плоские данные из обычной ТЗ
ИТЗ.Загрузить(ТЗ);
// Группируем по покупателю и с иерархией по номенклатуре (&Номенклатура)
ИТЗ.Группировать("Покупатель,&Номенклатура", "Сумма1,Сумма2,Сумма3");
// Выполняем обход готового дерева
ИТЗ.ВыбратьСтроки();
Пока ИТЗ.ПолучитьСтроку() = 1 Цикл
// Данные уже упорядочены по веткам иерархии с учетом родительских папок
КонецЦикла;
Использование ИТЗ избавляет от необходимости вручную формировать строковые ключи и восстанавливать недостающие папки в отчете.