Всем дня. Столкнулся в работе с просьбой клиента разрешить сохранять дерево значений в эксель.
Плевое дело, ведь так? Так и подумал:)
Содержание статьи:
1. Введение
2. Типовые методы решения и их проблемы
Внешний отчет имеет условное оформление (цвета фонов, цвета текста и выделение в некоторых местах другим шрифтом). Необходимо сохранить текущую таблицу в эксель, при этом сохранив все форматирование.
Типовые методы решения и их проблемы
Первым делом решил открыть доступ к типовой команде табличных частей "Вывести список". Сказано - сделано.
Можно выбрать колонки, можно вывести выделенный фрагмент, спрятать подчиненные. Шикарно, пока не увидишь, что при таком выводе условное оформление пропадает :(
В грустных эмоциях начал копать интернет и ничего толкового не нашел. В статьях предлагали костыли по типу "сохранять в эксель" или "формировать сразу в эксель файл".
И если вариант "формировать сразу в эксель" точно не подходит, ибо человеку надо видеть данные (и из 20 раз сохранять всего один), то с "сохранять в эксель после" еще можно подумать. В реализации относительно ничего сложного, но нужно будет читать каждую ячейку и ее свойства и сохранять тоже самое в эксель, используя свойства экселя.
Минусы данного подхода:
- Нужен установленный эксель на компе, где формируют (опять таки, извечная проблема, не всегда в наличии)
- Сугубо мое мнение - костыль
- Если вдруг добавится новое условное оформление нужно дописывать выгрузку
- Надо сопоставить свойство 1С и экселя (натянуто, но все же)
ps. Сопоставить свойства можно с помощью записи макросов.
Немного погуляв вспомнил, что при сохранении отчета из СКД с установленным условным оформлением табличный документ сохраняет свое оформление. Проверил - действительно!
Итак, приступаем к реализации.
1. Формируем дерево значений в обработке
2. На основании дерева создаем схему СКД
3. Формируем отчет СКД
4. Выводим типовыми средствами
Дерево значений с установленным условным оформлением у нас есть. Выводим на форму команду для вывода дерева значений в форму. В функции формируем табличный документ и выкидываем в типовую форму (из нее можно будет сохранить, напечатать, скопировать фрагмент, при этом оформление останется).
&НаКлиенте
Процедура ВывестиСписок(Команда)
СформированныйТабличныйДокумент = СформироватьТабличныйДокументСервер();
КоллекцияПечатныхФорм = УправлениеПечатьюКлиент.НоваяКоллекцияПечатныхФорм("СквознойОтчет");
ПечатнаяФорма = УправлениеПечатьюКлиент.ОписаниеПечатнойФормы(КоллекцияПечатныхФорм, "СквознойОтчет");
ПечатнаяФорма.ТабличныйДокумент = СформированныйТабличныйДокумент;
КлючУникальности = Строка(Новый УникальныйИдентификатор);
ПараметрыОткрытия = Новый Структура("ИмяМенеджераПечати,ИменаМакетов,ПараметрКоманды,ПараметрыПечати");
ПараметрыОткрытия.ПараметрКоманды = Новый Массив;
ПараметрыОткрытия.ПараметрыПечати = Новый Структура("Идентификатор", "");
ПараметрыОткрытия.Вставить("КоллекцияПечатныхФорм", КоллекцияПечатныхФорм);
ОткрытьФорму("ОбщаяФорма.ПечатьДокументов", ПараметрыОткрытия, ВладелецФормы, КлючУникальности);
КонецПроцедуры
&НаСервере
Функция СформироватьТабличныйДокументСервер()
ТабДокумент = Новый ТабличныйДокумент;
ДанныеДерева = РеквизитФормыВЗначение("ДеревоЗначенийДерево");
НастройкиВидимостиКолонок = Новый ТаблицаЗначений;
НастройкиВидимостиКолонок.Колонки.Добавить("Имя");
НастройкиВидимостиКолонок.Колонки.Добавить("Заголовок");
ПорядокКолонокВыводим = "Организация,ЗаказКлиента,СтоимостьЗаказаКлиента,СуммаРеализацийПоЗаказуКлиента,СебестоимостьЗаказаКлиента,СуммаВсехПоступленийПоЗаказуКлиента,
|СуммаДолгаПоЗаказуКлиента,ЗаказПоставщику,СтоимостьЗаказаПоставщику,ОбщаяСтоимостьПоступившихТоваровПоЗаказуПоставщика,ОбщаяСуммаСписанияБСпоЗаказуПоставщика,
|ЗадолжностьПоЗаказуПоставщику,СуммаБлижайшегоЭтапаПлатежаПоЗаказуПоставщика,ДатаБлижайшегоЭтапаПлатежаПоЗаказуПоставщика,КомментарийЗаказа";
ПорядокКолонокМассивВыводим = СтрРазделить(ПорядокКолонокВыводим, ",", Ложь);
Для каждого СтрокаМассива Из ПорядокКолонокМассивВыводим Цикл
НоваяСтрока = НастройкиВидимостиКолонок.Добавить();
НоваяСтрока.Имя = СтрокаМассива;
//ЗАГОЛОВОК ПОЛУЧАЮ ИЗ ДЕРЕВА НА ФОРМЕ
ДеревоНаФормеКолонки = Элементы.ДеревоЗначенийДерево.ПодчиненныеЭлементы;
ТекущаяКолонка = ДеревоНаФормеКолонки.Найти("ДеревоЗначенийДерево" + СтрокаМассива);
НоваяСтрока.Заголовок = ?(ТекущаяКолонка = Неопределено, СтрокаМассива, ТекущаяКолонка.Заголовок);
КонецЦикла;
ИсточникиДанных = Новый Структура("ДанныеДерева", ДанныеДерева);
СхемаКомпоновки = ПолучитьСхемуКомпоновкиПоДереву(ДанныеДерева, НастройкиВидимостиКолонок);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, СхемаКомпоновки.НастройкиПоУмолчанию);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ИсточникиДанных);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДокумент);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТабДокумент;
КонецФункции
Т.к. в дереве есть служебные колонки с данными, которые скрыты в исходном отчете, также скрываю в выводимом отчете.
Дополнительно устанавливаю сортировку в нужном порядке (если обращаюсь Элементы.ИмяДерева) колонки в порядке А-Я. Возможно, просто затупил (если есть другой вариант, напишите, буду благодарен).
И сама функция создания схемы компоновки:
&НаСервере
Функция ПолучитьСхемуКомпоновкиПоДереву(ДанныеДерева, ВыбранныеПоляДерева = Неопределено, ПолеИД = "КлючСвязи", ПолеРодительИД = "КлючСвязиРодителя")
Если ВыбранныеПоляДерева = Неопределено Тогда
ВыбранныеПоляДерева = Новый Массив;
КонецЕсли;
ЗаполнитьВыбранныеПоляДерева = НЕ ВыбранныеПоляДерева.Количество();
СхемаКомпоновки = Новый СхемаКомпоновкиДанных;
НастройкиКомпоновки = СхемаКомпоновки.НастройкиПоУмолчанию;
ИсточникДанных = СхемаКомпоновки.ИсточникиДанных.Добавить();
ИсточникДанных.Имя = "ИсточникДанных";
ИсточникДанных.ТипИсточникаДанных = "Local";
НаборДанных = СхемаКомпоновки.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
НаборДанных.ИмяОбъекта = "ДанныеДерева";
НаборДанных.Имя = "НаборДанных";
НаборДанных.ИсточникДанных = "ИсточникДанных";
Для Каждого КолонкаДерева Из ДанныеДерева.Колонки Цикл
ИмяПоля = КолонкаДерева.Имя;
ЗаголовокПоля = КолонкаДерева.Заголовок;
ПолеНабора = НаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеНабора.Заголовок = ЗаголовокПоля;
ПолеНабора.Поле = ИмяПоля;
Если ЗаполнитьВыбранныеПоляДерева И НЕ (ИмяПоля = ПолеИД ИЛИ ИмяПоля = ПолеРодительИД) Тогда
ВыбранныеПоляДерева.Добавить(Новый Структура("Имя, Заголовок", ИмяПоля, ЗаголовокПоля));
КонецЕсли;
КонецЦикла;
СвязьНабора = СхемаКомпоновки.СвязиНаборовДанных.Добавить();
СвязьНабора.НаборДанныхИсточник = "НаборДанных";
СвязьНабора.НаборДанныхПриемник = "НаборДанных";
СвязьНабора.ВыражениеИсточник = ПолеИД;
СвязьНабора.ВыражениеПриемник = ПолеРодительИд;
СвязьНабора.НачальноеВыражение = "0";
ВыбранныеПоля = НастройкиКомпоновки.Выбор.Элементы;
Для Каждого ПолеДерева Из ВыбранныеПоляДерева Цикл
ПолеОтчет = ВыбранныеПоля.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ПолеОтчет.Поле = Новый ПолеКомпоновкиДанных(ПолеДерева.Имя);
ПолеОтчет.Заголовок = ПолеДерева.Заголовок;
КонецЦикла;
СтруктураГруппировки = НастройкиКомпоновки.Структура;
ЭлементГруппировки = СтруктураГруппировки.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ЭлементГруппировки.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
УсловноеОформлениеСКД = НастройкиКомпоновки.УсловноеОформление.Элементы;
Для каждого ЭлементУсловногоОформления Из ЭтаФорма.УсловноеОформление.Элементы Цикл
НовоеУсловноеОформление = УсловноеОформлениеСКД.Добавить();
//ОФОРМЛЕНИЕ
Для каждого ЭлементОформления Из ЭлементУсловногоОформления.Оформление.Элементы Цикл
Если ЭлементОформления.Использование Тогда
НовоеУсловноеОформление.Оформление.УстановитьЗначениеПараметра(ЭлементОформления.Параметр, ЭлементОформления.Значение);
НовоеУсловноеОформление.Использование = Истина;
КонецЕсли;
КонецЦикла;
//ОТБОР УСЛОВИЕ
Для каждого ОтборОформления Из ЭлементУсловногоОформления.Отбор.Элементы Цикл
ЭлементОтбора = НовоеУсловноеОформление.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЗаполнитьЗначенияСвойств(ЭлементОтбора, ОтборОформления);
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(СтрЗаменить(СтрЗаменить(ОтборОформления.ЛевоеЗначение, "ДеревоЗначенийДерево.", ""), "ДеревоЗначенийДерево", ""));
КонецЦикла;
//ВЫБРАННЫЕ ПОЛЯ
Для каждого ОформляемоеПоле Из ЭлементУсловногоОформления.Поля.Элементы Цикл
НовоеПолеОформления = НовоеУсловноеОформление.Поля.Элементы.Добавить();
НовоеПолеОформления.Использование = Истина;
НовоеПолеОформления.Поле = Новый ПолеКомпоновкиДанных(СтрЗаменить(ОформляемоеПоле.Поле, "ДеревоЗначенийДерево", ""));
КонецЦикла;
КонецЦикла;
Возврат СхемаКомпоновки;
КонецФункции
На основании дерева значений создаем схему компоновки, передаем дерево значений во внешний набор данных, устанавливаем условное оформление (не учитывал, что могут быть группы условий в отборе).
ВАЖНО. Для вывода именно дерева значений необходимо сделать два служебных реквизита (ключ связи и ключ связи родителя - тип число). В данные поля записываем значение ПолучитьИндентификатор() + 1).
Данный реквизит используется для связи строк по уровням (через наборы данных) в компоновке данных.
Вроде все интуитивно просто :) А главное - клиент доволен.