При формировании сложных отчетов в среде 1С:Предприятие 7.7 разработчики часто сталкиваются с необходимостью вывода иерархических данных. Идеальный вариант представления такой информации — древовидная структура, аналогичная группировкам в Microsoft Excel, когда пользователь может кликом по кнопке-плюсику [+] развернуть сводные данные по контрагенту или номенклатурной группе и детально изучить входящие в нее строки.
По умолчанию стандартный движок табличных документов 1С 7.7 (так называемый Moxel) не поддерживает интерактивное группирование строк «из коробки». Тем не менее, решить эту задачу можно несколькими способами. В этой статье мы подробно разберем четыре наиболее эффективных метода: от эмуляции стандартными средствами до использования мощной внешней компоненты Йоксель (Yoksel), а также прямого управления COM-объектом Excel.
---
Если в вашей организации запрещено использование сторонних библиотек и внешних компонент (ВК), либо вы хотите сохранить конфигурацию максимально чистой, можно реализовать интерактивность стандартными средствами. Суть метода заключается в полной перерисовке таблицы при клике по управляющему элементу.
Принцип реализации по шагам:
Уровень (число), Развернут (булево/число: 1 — да, 0 — нет), Родитель (ссылка на строку-группировку) и ЭтоГруппа (1 — да, 0 — нет).[+] или [-].ОбработкаЯчейкиТаблицы(). При двойном клике пользователя по «плюсику» управление передается в эту процедуру. Мы считываем параметры из расшифровки, меняем флаг Развернут в служебной ТЗ на противоположный, полностью очищаем текущую печатную форму и формируем её заново на основе обновленных данных ТЗ.Рассмотрим пример программного кода для реализации этого механизма. Сначала напишем процедуру вывода таблицы, которая анализирует состояние свернутости групп:
Процедура ВывестиТаблицу()
Таб.Очистить();
Таб.ИсходнаяТаблица("МакетОтчета");
// Выводим шапку
Таб.ВывестиСекцию("Шапка");
// Обходим подготовленную ТЗ с данными
ТЗДанные.ВыбратьСтроки();
Пока ТЗДанные.ПолучитьСтроку() = 1 Цикл
// Если это детальная строка, и ее родитель свернут — пропускаем вывод
Если ТЗДанные.ЭтоГруппа = 0 Тогда
СтрокаРодителя = 0;
// Ищем строку родителя в ТЗ
Если ТЗДанные.НайтиЗначение(ТЗДанные.Родитель, СтрокаРодителя, "ИдентификаторГруппы") = 1 Тогда
Если ТЗДанные.ПолучитьЗначение(СтрокаРодителя, "Развернут") = 0 Тогда
Продолжить; // Родитель свернут, деталь не выводим
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Определяем, какой маркер выводить для групп
Если ТЗДанные.ЭтоГруппа = 1 Тогда
Если ТЗДанные.Развернут = 1 Тогда
Маркер = "[-]";
Иначе
Маркер = "[+]";
КонецЕсли;
// Готовим расшифровку для клика
Расшифровка = СоздатьОбъект("СписокЗначений");
Расшифровка.Установить("Действие", "ПереключитьГруппу");
Расшифровка.Установить("НомерСтрокиТЗ", ТЗДанные.НомерСтроки);
Таб.ВывестиСекцию("СтрокаГруппы");
Иначе
Маркер = " "; // Для детальных строк отступ без маркера
Расшифровка = "";
Таб.ВывестиСекцию("СтрокаДетали");
КонецЕсли;
КонецЦикла;
Таб.Показать("Интерактивный отчет", "");
КонецПроцедуры
Теперь опишем процедуру перехвата клика в модуле формы отчета:
Процедура ОбработкаЯчейкиТаблицы(ЗначениеЯчейки, ФлагСтандартнойОбработки, Таблица)
Если ТипЗначенияСтр(ЗначениеЯчейки) = "СписокЗначений" Тогда
Действие = ЗначениеЯчейки.Получить("Действие");
Если Действие = "ПереключитьГруппу" Тогда
// Отменяем стандартное открытие карточки субконто или документа
ФлагСтандартнойОбработки = 0;
НомерСтрокиТЗ = ЗначениеЯчейки.Получить("НомерСтрокиТЗ");
// Меняем состояние сворачивания группы
ТекущееСостояние = ТЗДанные.ПолучитьЗначение(НомерСтрокиТЗ, "Развернут");
ТЗДанные.УстановитьЗначение(НомерСтрокиТЗ, "Развернут", 1 - ТекущееСостояние);
// Перерисовываем печатную форму
ВывестиТаблицу();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Преимущества метода: Работает абсолютно на любом компьютере с установленной 1С 7.7, не требует админских прав для регистрации DLL, не вызывает проблем при обновлении платформы.
Недостатки: При больших объемах данных (более 5-10 тысяч строк) полная перерисовка таблицы при каждом клике может вызывать заметные задержки («моргание» экрана). Также этот метод не позволяет перенести структуру группировок при сохранении файла в Excel — там останутся просто текстовые символы [+].
---
Это наиболее элегантный, профессиональный и современный подход к реализации структуры в 1С 7.7. Внешняя компонента Yoksel (также известная как библиотека SpreadSheet.dll) представляет собой альтернативный движок для работы с табличными документами 1С, который полностью заменяет стандартный Moxel.
Ключевые преимущества использования Yoksel:
SpreadSheet.dll в каталог базы данных или каталог исполняемых файлов платформы 1С и загружать динамически в коде.Рассмотрим, как подключить компоненту из каталога базы данных и сформировать отчет с группировками. Для этого проанализируем следующий пример:
// Загружаем внешнюю компоненту динамически из папки базы данных
ПутьКВК = КаталогИБ() + "SpreadSheet.dll";
Если ФС.СуществуетФайл(ПутьКВК) = 1 Тогда
ЗагрузитьВнешнююКомпоненту(ПутьКВК);
Иначе
Предупреждение("Компонента Йоксель (SpreadSheet.dll) не найдена в каталоге базы!");
Возврат;
КонецЕсли;
// Создаем объекты Йокселя
ТабДок = СоздатьОбъект("ТабличныйДокумент");
Конвертер = СоздатьОбъект("КонвертерТаблиц");
// Сначала формируем стандартную таблицу 1С
Таб1С = СоздатьОбъект("Таблица");
Таб1С.ИсходнаяТаблица("Макет");
// Предположим, мы выводим данные по контрагентам и их договорам
Таб1С.ВывестиСекцию("Шапка");
ТЗКонтрагенты.ВыбратьСтроки();
Пока ТЗКонтрагенты.ПолучитьСтроку() = 1 Цикл
// Запоминаем строку начала группы в будущем документе Йокселя
// Учитываем, что высота таблицы 1С увеличивается с каждой секцией
СтрокаНачала = Таб1С.ВысотаТаблицы() + 1;
Таб1С.ВывестиСекцию("СтрокаКонтрагента");
// Выводим детальные строки договоров
ТЗДоговоры.ВыбратьСтроки();
Пока ТЗДоговоры.ПолучитьСтроку() = 1 Цикл
Таб1С.ВывестиСекцию("СтрокаДоговора");
КонецЦикла;
СтрокаКонца = Таб1С.ВысотаТаблицы();
// Важно: Группировать нужно детальные строки (подчиненные)
// Если под контрагентом есть детальные строки, фиксируем их координаты
Если СтрокаКонца > СтрокаНачала Тогда
// Запоминаем координаты для последующей группировки в Йокселе
// Добавляем границы группы в список значений для отложенной обработки
СписокГруппировок.ДобавитьЗначение(СтрокаНачала + 1, СтрокаКонца);
КонецЕсли;
КонецЦикла;
// Конвертируем стандартную таблицу 1С в объект ТабличныйДокумент Йокселя
Конвертер.Конвертировать(Таб1С, ТабДок);
// Теперь расставляем группировки в документе Йокселя
СписокГруппировок.ВыбратьЗначения();
Пока СписокГруппировок.ПолучитьЗначение(Индекс, СтрокаДеталиНачало) = 1 Цикл
СтрокаДеталиКонец = Число(СписокГруппировок.ПолучитьОбъект(Индекс));
// Добавляем группу строк (параметры: НачалоСтроки, КонецСтроки)
ТабДок.ДобавитьГруппуСтрок(СтрокаДеталиНачало, СтрокаДеталиКонец);
КонецЦикла;
// Настраиваем показ структуры в окне просмотра
Фрейм = ТабДок.Показать("Отчет с группировками Йоксель");
Фрейм.ОтображатьСтруктуру(1); // Показывает линии группировки слева
Фрейм.ОтображатьКнопкиУровней(1); // Выводит кнопки уровней "1", "2" вверху панели
Для ускорения разработки существуют уже готовые классы и обработки на профильных ресурсах, которые перехватывают вывод печатной формы на уровне глобального модуля, считывают структуру макета и автоматически расставляют группировки Йокселя на лету. Внедрение такого решения занимает буквально несколько минут.
---
Если конечной целью вашего отчета является отправка готового файла клиенту или руководству, нет необходимости программировать сложный интерфейс в самой 1С 7.7. Мы можем выгрузить данные напрямую в приложение MS Excel, используя механизм ActiveX / COM, и применить встроенные функции группировки табличного процессора Excel.
Выясним, как это реализовать на практике. Разберем по шагам код выгрузки и группировки:
// Создаем COM-объект Excel
Попытка
Эксель = СоздатьОбъект("Excel.Application");
Исключение
Предупреждение("Microsoft Excel не установлен на данном компьютере!");
Возврат;
КонецПопытки;
// Добавляем новую книгу и выбираем активный лист
Книга = Эксель.Workbooks.Add();
Лист = Книга.ActiveSheet;
// Заполняем шапку
Лист.Cells(1, 1).Value = "Контрагент / Договор";
Лист.Cells(1, 2).Value = "Сумма взаиморасчетов";
СтрокаЭкселя = 2;
ТЗКонтрагенты.ВыбратьСтроки();
Пока ТЗКонтрагенты.ПолучитьСтроку() = 1 Цикл
СтрокаГруппыНачало = СтрокаЭкселя;
// Записываем данные контрагента (итоговая строка)
Лист.Cells(СтрокаЭкселя, 1).Value = ТЗКонтрагенты.Наименование;
Лист.Cells(СтрокаЭкселя, 2).Value = ТЗКонтрагенты.ИтогСумма;
Лист.Cells(СтрокаЭкселя, 1).Font.Bold = 1; // Делаем шрифт жирным
СтрокаЭкселя = СтрокаЭкселя + 1;
СтрокаДеталейНачало = СтрокаЭкселя;
// Выводим подчиненные договоры
ТЗДоговоры.ВыбратьСтроки();
Пока ТЗДоговоры.ПолучитьСтроку() = 1 Цикл
Лист.Cells(СтрокаЭкселя, 1).Value = " " + ТЗДоговоры.НомерДоговора;
Лист.Cells(СтрокаЭкселя, 2).Value = ТЗДоговоры.Сумма;
СтрокаЭкселя = СтрокаЭкселя + 1;
КонецЦикла;
СтрокаДеталейКонец = СтрокаЭкселя - 1;
// Если у контрагента были детальные строки, группируем их
Если СтрокаДеталейКонец >= СтрокаДеталейНачало Тогда
ДиапазонГруппировки = Лист.Rows(СтрокаДеталейНачало & ":" & СтрокаДеталейКонец);
ДиапазонГруппировки.Group(); // Группируем строки средствами Excel
КонецЕсли;
КонецЦикла;
// Настраиваем отображение кнопок группировки сверху над подчиненными строками (по желанию)
Лист.Outline.SummaryRow = 0; // Показывает "плюсик" напротив строки контрагента (сверху)
// Сворачиваем все группы до 1-го уровня по умолчанию
Лист.Outline.ShowLevels(1);
// Делаем Excel видимым пользователю
Эксель.Visible = 1;
Преимущества метода: Идеально подходит для предоставления внешней отчетности. Пользователь получает готовый и привычный файл Excel с уже настроенной структурой, формулами и оформлением.
Недостатки: Требует обязательного наличия установленного пакета Microsoft Office на компьютере пользователя. Работает медленнее, чем вывод в стандартный табличный документ 1С или использование Йокселя.
---
Проанализируем все рассмотренные варианты и выберем оптимальный под конкретные задачи:
SpreadSheet.dll в папку с базой данных и настроить автоподключение в глобальном модуле при начале работы системы.