понедельник, 13 сентября 2010 г.

1С: Шаблонный вывод

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

// т.к. код пишем мы, а не сосед по парте
// знаем количество изначальных колонок, оно может приходить как параметр
Сверн = ""; // это понадобится для сворачивания итогов и сортировки
Для ц = 1 по СписРодителей.РазмерСписка()
    Если (ц + 5) > ТЗ.КоличествоКолонок() Тогда
        // добавилась подгруппа вносим новую колонку
        ТЗ.НоваяКолонка("Родитель" + ц);
        Сверн = Сверн + "Родитель" + ц + ","
    КонецЕсли;
    ТЗ.УстановитьЗначение(ТЗ.НомерСтроки,"Родитель" + ц,
                       СписРодителей.ПолучитьЗначение(ц);
КонецЦикла;
Кеширование сократило бы время на обработку получения списка родителей.
Ну с сортировкой все понятно:
ТЗ.Сортировать(Сверн+"Товар");
Для удобства итоги я собираю в таблицу, а затем сворачиваю.
Таб = СоздатьОбъект("Таблица");
ТЗИтог = СоздатьОбъект("ТаблицаЗначений");
// Фиксируем родителя
ТЗИтог.НоваяКолонка("Родитель","Справочник.Товары");
// то по чему нужно собирать итог
ТЗИтог.НоваяКолонка("НачОст","Число",15,2); 
ТЗИтог.НоваяКолонка("КонОст","Число",15,2);
ТЗИтог.НоваяКолонка("Приход","Число",15,2);
ТЗИтог.НоваяКолонка("Расход","Число",15,2);
ПредГруппа = "";
СписокРодителей1 = СоздатьОбъект("СписокЗначений");
СписокРодителей2 = СоздатьОбъект("СписокЗначений");

ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    Если ПредГруппа <> ТЗ.Родитель Тогда // родителя я специально внес,
                                  // чтобы не искать среди Родитель1...
        Различие = 0; // позже пригодится
        Если ПредГруппа <> "" Тогда
            // значит уже что-то выводили, нужен итог
            ТЗИтог.Свернуть("Родитель","НачОст,КонОст,Приход,Расход");
            // составляем список расхождения
            // тут бы тоже кеш пригодился
            СписокРодителей1 = ПолучитьСписокРодителей(ПредГруппа);
            СписокРодителей2 = ПолучитьСписокРодителей(ТЗ.Родитель);
            РазмерСписка = Мин(СписокРодителей1.РазмерСписка(),
                              СписокРодителей2.РазмерСписка());
            Для ц = 1 по РазмерСписка Цикл
                // ищем первое расхождение
                Если СписокРодителей1.ПолучитьЗначение(ц) <>
                     СписокРодителей2.ПолучитьЗначение(ц) Тогда
                    Различие = ц;
                    Прервать;
                КонецЕсли;
            КонецЦикла;

            СписокРодителей2.УдалитьВсе(); // нужен пустой список
            Для ц = Различие по СписокРодителей1.РазмерСписка() Цикл
                Родитель = СписокРодителей1.ПолучитьЗначение(ц);
                СписокРодителей2.ДобавитьЗначение(Родитель,Родитель);
            КонецЦикла;

            Для ц = 1 По СписокРодителей2.РазмерСписка() Цикл
                cтрИтогов = 0;
                Родитель = СписокРодителей1.ПолучитьЗначение(ц);
                ИтогДанные.НайтиЗначение(Родитель, СтрИтогов, "Родитель");
                ИтогПо = глВерниТочки(Родитель) + Родитель;
                Таб.ВывестиСекцию("Итог"); // в шаблоне все указано
            КонецЦикла;
        КонецЕсли;
        // выводим шапку
        СписокРодителей2 = ПолучитьСписокРодителей(ТЗ.Родитель);
        Таб.ВывестиСекцию("Шапка");
        Если Различие = 0 Тогда
            Различие = 1;
        КонецЕсли;

        Для ц = Различие По СписокРодителей2.РазмерСписка() Цикл
            печРодитель = глПолучитьТочки(СписокРодителей2.ПолучитьЗначение(ц)) +
                       СписокРодителей2.ПолучитьЗначение(ц);
            Таб.ВывестиСекцию("Родитель");
        КонецЦикла;
        ПредГруппа = ТЗ.Родитель; // финиш
    КонецЕсли; // проверка на смену
    // дальше какая-то полезная работа над товаром
***
    // и конечно же внесение в итоги
    тРодитель = ТЗ.Оборудование.Родитель;
    Пока тРодитель.Выбран() = 1 Цикл
        ТЗИтог.НоваяСтрока();
        ТЗИтог.Родитель = тРодитель;
        ТЗИтог.НачОст = ТЗ.НачОст;
        ТЗИтог.КонОст = ТЗ.КонОст;
        ТЗИтог.Приход = ТЗ.Приход;
        ТЗИтог.Расход = ТЗ.Расход;
        тРодитель = тРодитель.Родитель;
    КонецЦикла;
КонецЦикла;
// просто вывод всех итогов
ТЗИтог.Свернуть("Родитель","НачОст,КонОст,Приход,Расход");
// полюбому хотябы 1 раз вывели
СписокРодителей1 = ПолучитьСписокРодителей(ПредГруппа, 1);
// ну и в цикле
Для ц = 1 по СписокРодителей1.РазмерСписка() Цикл
    СтрИтогов = 0;
    ТЗИтоги.НайтиЗначение(СписокРодителей1.ПолучитьЗначение(ц),
                          СтрИтогов, "Родитель");
    Таб.ВывестиСекцию("Итог"); // в шаблоне все указано
КонецЦикла;
// если есть необходимость всего выводится через итог.
Собственно теперь любой вывод можно сделать через этот шаблон, можно вынести в одтдельную процедуру глобального модуля и все необходимые параметры передавать. Остается невыясненным полезная нагрузка, жаль функции нельзя передавать в качестве паарметров как в Python.

Комментариев нет:

Отправить комментарий