Хитрости СКД. Часть 3

13.01.24

Разработка - Универсальные функции

Столкнулся с тем, что мне приходится писать гору отчетов. Во многих приходится использовать повторяющиеся приемы. Решил написать шпаргалку, которая, надеюсь пригодится не только мне. В этой статье: Объединение ячеек в отчете только на определенном уровне иерархии, Постобработка итогов в табличном документе, Скрытие колонок в зависимости от количества месяцев в периоде.

 

1. Объединение ячеек в отчете только на определенном уровне иерархии

 

 

В примере ячейки объединяются на самом нижнем уровне иерархии, что позволяет выводить длинные описания дополнительных расходов в одну строку.

Удобно воспользоваться функцией Максима Гончарова из публикации Получаем уровень группировки ячейки табличного документа (infostart.ru) ПолучитьТаблицуУровнейПоСтрокам

Весь текст модуля:

Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	Настройки = КомпоновщикНастроек.ПолучитьНастройки();
	АдресСхемы = ПоместитьВоВременноеХранилище(СхемаКомпоновкиДанных, Новый УникальныйИдентификатор);
	КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Вставить("АдресСхемы", АдресСхемы);
	МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
	ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных, , ДанныеРасшифровки, Истина);
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
	ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных, Истина);
	ДокументРезультат = ОбработатьТабличныйДокумент(ДокументРезультат);
	ДокументРезультат.ОриентацияСтраницы = ОриентацияСтраницы.Ландшафт;
	ДокументРезультат.АвтоМасштаб = Истина;
КонецПроцедуры

Функция ОбработатьТабличныйДокумент(ТабДок)
	мЗапись = Новый ЗаписьXML;
	мЗапись.УстановитьСтроку("UTF-8");
	СериализаторXDTO.ЗаписатьXML(мЗапись,ТабДок);
	ТекстXML = мЗапись.Закрыть();
	мЧтение = Новый ЧтениеXML;
	мЧтение.УстановитьСтроку(ТекстXML);
	ПострDOM = Новый ПостроительDOM;
	ДанныеDOM = ПострDOM.Прочитать(мЧтение);
	//уровни строк
	ТаблицаУровнейПоСтрокам = ПолучитьТаблицуУровнейПоСтрокам(ДанныеDOM);
	Уровни=ТаблицаУровнейПоСтрокам.Скопировать(,"Уровень");
	Уровни.Свернуть("Уровень");
	ВысотаФ = ТабДок.ФиксацияСверху;
	ШиринаФ = ТабДок.ФиксацияСлева;
	ОбъединяемаяОбласть = Неопределено;
	ОбластьПоискаЗаголовков=ТабДок.Область(1,,ВысотаФ,);
	ОбластьСДР=ТабДок.НайтиТекст("Сумма доп расхода",,ОбластьПоискаЗаголовков);
	Если ОбластьСДР=Неопределено Тогда
		Возврат ТабДок;
	КонецЕсли;
	ОбластьДДЗ=ТабДок.НайтиТекст("Документ доп. расходов",,ОбластьПоискаЗаголовков);
	Если ОбластьДДЗ=Неопределено Тогда
		Возврат ТабДок;
	КонецЕсли;
	ОбластьОписание=ТабДок.НайтиТекст("Описание",,ОбластьПоискаЗаголовков);
	Если ОбластьОписание=Неопределено Тогда
		Возврат ТабДок;
	КонецЕсли;
	Если ОбластьОписание.Лево<ОбластьДДЗ.Лево Тогда
		Возврат ТабДок;
	КонецЕсли;
	СтрокиДопЗатрат= ТаблицаУровнейПоСтрокам.НайтиСтроки(Новый Структура("Уровень",Уровни.Количество()));
	Для Каждого СтрокаТаблицыУровнейПоСтрокам из СтрокиДопЗатрат Цикл
		НомерСтрокиДЗ = СтрокаТаблицыУровнейПоСтрокам.номер;
		ОбластьСтрокиОписания=ТабДок.Область(НомерСтрокиДЗ,ОбластьОписание.Лево, НомерСтрокиДЗ, ОбластьСДР.Лево-1);
		ОбластьСтрокиОписания.Объединить();
	КонецЦикла;
	Возврат ТабДок;
КонецФункции

//&НаСервереБезКонтекста

Функция ПолучитьТаблицуУровнейПоСтрокам(ДанныеDOMТабДок)
	ТаблицаУровней = Новый ТаблицаЗначений;
	ТаблицаУровней.Колонки.Добавить("Номер",Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(10,0)));
	ТаблицаУровней.Колонки.Добавить("Уровень",Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(10,0)));
	УзлыУровнейDOMТабДок = ДанныеDOMТабДок.ПолучитьЭлементыПоИмени("vg");
	ПредКонецГруппировки = 0;
	Для инд = 0 по УзлыУровнейDOMТабДок.Количество() - 1 Цикл
		УзелГруппы = УзлыУровнейDOMТабДок[инд];
		НачалоГруппировки = 0;
		КонецГруппировки = 0;
		Для н = 0 По УзелГруппы.ДочерниеУзлы.Количество() - 1 Цикл
			ПодУзел = УзелГруппы.ДочерниеУзлы[н];
			Если ПодУзел.ИмяУзла = "b" Тогда
				НачалоГруппировки = Число(СокрЛП(ПодУзел.ТекстовоеСодержимое));
			ИначеЕсли ПодУзел.ИмяУзла = "e" Тогда
				КонецГруппировки = Число(СокрЛП(ПодУзел.ТекстовоеСодержимое));
			КонецЕсли;
		КонецЦикла;
		Если НачалоГруппировки = 0 Тогда Продолжить КонецЕсли;
		// если конца группировки не оказалось, то одна позиция в подгруппе
		Если КонецГруппировки = 0 Тогда
			КонецГруппировки = НачалоГруппировки
		КонецЕсли;
		//конец область увеличить нужно на 1
		КонецГруппировки = КонецГруппировки + 1;
		РодительскийУровень = 0;
		Для Номер = НачалоГруппировки по КонецГруппировки Цикл
			строкаТаблицы = ТаблицаУровней.Найти(Номер, "Номер");
			Если строкаТаблицы = Неопределено Тогда
				строкаТаблицы = ТаблицаУровней.Добавить();
				строкаТаблицы.Номер = Номер;
				строкаТаблицы.Уровень = РодительскийУровень + 1;
				Если Номер = НачалоГруппировки Тогда
					РодительскийУровень = 1;
				КонецЕсли;
			Иначе
				Если Номер = НачалоГруппировки Тогда
					РодительскийУровень = строкаТаблицы.Уровень
				Иначе
					строкаТаблицы.Уровень = РодительскийУровень + 1;
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	Возврат ТаблицаУровней;
КонецФункции

Суть алгоритма проста:

Получаем с помощью функции ПолучитьТаблицуУровнейПоСтрокам таблицу уровней для каждой строки табличного документа, находим в заголовках нужные теги, чтобы сориентироваться какие ячейки в строке объединять, находим строки с нужным уровнем и в каждой объединяем ячейки.

 

2. Постобработка итогов в табличном документе.

Иногда возможностей СКД не хватает, чтоб сформировать особенно хитрые итоги. Выход этой ситуации опять же описан у Максима Гончарова в публикации Получаем уровень группировки ячейки табличного документа (infostart.ru).

Я опишу лишь некоторые дополнения. Вариант, когда итог нужно рассчитать лишь на определенном уровне группировок.

В ресурсах делаем так, чтоб в нужной группировке, вместо чисел стояли литералы “ &Итоги”:

 

 

Код модуля можно взять из верхнего примера. Привожу отличия в коде:


Функция ОбработатьТабличныйДокумент(ТабДок)
	мЗапись = Новый ЗаписьXML;
	мЗапись.УстановитьСтроку("UTF-8");
	СериализаторXDTO.ЗаписатьXML(мЗапись,ТабДок);
	ТекстXML = мЗапись.Закрыть();
	мЧтение = Новый ЧтениеXML;
	мЧтение.УстановитьСтроку(ТекстXML);
	ПострDOM = Новый ПостроительDOM;
	ДанныеDOM = ПострDOM.Прочитать(мЧтение);
	//уровни строк
	ТаблицаУровнейПоСтрокам = ПолучитьТаблицуУровнейПоСтрокам(ДанныеDOM);
	Уровни=ТаблицаУровнейПоСтрокам.Скопировать(,"Уровень");
	Уровни.Свернуть("Уровень");
	ВысотаФ = ТабДок.ФиксацияСверху;
	ШиринаФ = ТабДок.ФиксацияСлева;
	ОбъединяемаяОбласть = Неопределено;
	МассивИтоговСальдоСр=Новый Массив(Уровни.Количество()+1);
	Для I=0 по Уровни.Количество() Цикл
		МассивИтоговСальдоСр[I] =0;
	КонецЦикла;
	СписокРядов=Новый СписокЗначений;
	СписокКолонок=Новый СписокЗначений;
	ОбластьПоиска=ТабДок.НайтиТекст("&Итоги");
	Пока ТипЗнч(ОбластьПоиска)<>Тип("Неопределено") Цикл
		ОбработатьОбластьПоиска(ОбластьПоиска, СписокКолонок, СписокРядов,ТабДок);
	КонецЦикла;
	СписокКолонок.СортироватьПоЗначению(НаправлениеСортировки.Возр);
	ТекУровень= Уровни.Количество()-1;
	МаксУровень= ТекУровень;
	ЗапрУровень= ТекУровень+1;
	Для ИндексКолонки=0 По СписокКолонок.Количество()-1 Цикл
		Для I=-ТабДок.ВысотаТаблицы+1 по -ВысотаФ-1 Цикл
			НомерСтроки=-I;
			строкаУровняПоСтрокам = ТаблицаУровнейПоСтрокам.Найти(НомерСтроки,"Номер");
			Если строкаУровняПоСтрокам <> Неопределено Тогда
				УровеньСтроки=строкаУровняПоСтрокам.Уровень;
				Если УровеньСтроки= ЗапрУровень Тогда
					Продолжить;
				ИначеЕсли УровеньСтроки= МаксУровень Тогда
					ЗначениеСтроки= ОбластьВЧисло(СписокКолонок[ИндексКолонки].Значение, НомерСтроки, ТабДок);
					МассивИтоговСальдоСр[УровеньСтроки-1]=МассивИтоговСальдоСр[УровеньСтроки-1]+ЗначениеСтроки;
					ТекУровень= УровеньСтроки;
				ИначеЕсли УровеньСтроки< ТекУровень Тогда
					ТекУровень= УровеньСтроки;
					ЗначениеСтроки=МассивИтоговСальдоСр[УровеньСтроки];
					МассивИтоговСальдоСр[УровеньСтроки-1]=МассивИтоговСальдоСр[УровеньСтроки-1]+ЗначениеСтроки;
					МассивИтоговСальдоСр[УровеньСтроки]=0;
					ТабДок.Область(НомерСтроки, СписокКолонок[ИндексКолонки].Значение, НомерСтроки, СписокКолонок[ИндексКолонки].Значение).ГоризонтальноеПоложение=ГоризонтальноеПоложение.Право;
					ТабДок.Область(НомерСтроки, СписокКолонок[ИндексКолонки].Значение, НомерСтроки, СписокКолонок[ИндексКолонки].Значение).Текст=Формат(ЗначениеСтроки,"ЧДЦ=2");
				КонецЕсли
			Иначе
				ЗначениеСтроки= ОбластьВЧисло(СписокКолонок[ИндексКолонки].Значение, НомерСтроки, ТабДок);
				МассивИтоговСальдоСр[0]=МассивИтоговСальдоСр[0]+ЗначениеСтроки;
			КонецЕсли;
		КонецЦикла;
		ТабДок.Область(ТабДок.ВысотаТаблицы, СписокКолонок[ИндексКолонки].Значение, ТабДок.ВысотаТаблицы, СписокКолонок[ИндексКолонки].Значение).ГоризонтальноеПоложение=ГоризонтальноеПоложение.Право;
		ТабДок.Область(ТабДок.ВысотаТаблицы, СписокКолонок[ИндексКолонки].Значение, ТабДок.ВысотаТаблицы, СписокКолонок[ИндексКолонки].Значение).Текст=Формат( МассивИтоговСальдоСр[0],"ЧДЦ=2");
		МассивИтоговСальдоСр[0]=0;
	КонецЦикла;
	Возврат ТабДок;
КонецФункции

Функция ОбластьВЧисло(НомерКолонки, НомерСтроки, Знач ТабДок)
	Перем ПД, ТекстПД;
	ТекстПД=ТабДок.Область(НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки).Текст;
	ПД=?(ТекстПД="",0,Число(ТекстПД));
	Возврат ПД;
КонецФункции

Процедура ОбработатьОбластьПоиска( ОбластьПоиска, Знач СписокКолонок, Знач СписокРядов,ТабДок);
	Если ТипЗнч(ОбластьПоиска)<>Тип("Неопределено") и ОбластьПоиска.ШиринаКолонки>0 Тогда
		Если СписокРядов.НайтиПоЗначению(ОбластьПоиска.Верх)=Неопределено Тогда
			СписокРядов.Добавить(ОбластьПоиска.Верх);
		КонецЕсли;
		Если СписокКолонок.НайтиПоЗначению(ОбластьПоиска.Лево)=Неопределено Тогда
			СписокКолонок.Добавить(ОбластьПоиска.Лево);
		КонецЕсли;
	КонецЕсли;
	ОбластьПоиска_Право= ОбластьПоиска.Право;
	ОбластьПоиска_Низ= ОбластьПоиска.Низ;
	Если ОбластьПоиска_Право+1>ТабДок.ШиринаТаблицы Тогда
		ОбластьПоиска_Низ= ОбластьПоиска_Низ+1;
	Иначе
		ОбластьПоиска_Право= ОбластьПоиска.Право+1;
	КонецЕсли;
	ОбластьПоиска=ТабДок.НайтиТекст("&СальдоСр",ТабДок.Область(ОбластьПоиска_Низ, ОбластьПоиска_Право, ОбластьПоиска_Низ, ОбластьПоиска_Право));
КонецПроцедуры

 

Алгоритм простой. Получаем уровни для каждой строки как и раньше, находим наши литералы и для нужных строк и колонок считаем наши итоги и помещаем их вместо литералов. Сразу отвечу на вопрос, функции СКД ВычислитьВыражение( ) для расчета таких итогов не получилось использовать из за их использовании на предыдущем уровне вложенности.

 

3. Скрытие колонок в зависимости от количества месяцев в периоде

 

Нужно добавить поле “ЭтоОбщиеИтоги” в Вычисляемые поля.

 

 

Также добавить дважды это поле в Ресурсы для общего итога и остальных группировок:

 

 

В параметры нужно добавить реквизит КМ (количество месяцев)

 

 

В условное оформление нужно поместить примерно следующее:

 

 

В этом примере колонки ОтклПроц,ОтклРуб,Комментарий скрываются во всех месяцах, кроме общих итогов. Колонки ОтклПроц,ОтклРуб скрываются везде если период отчета не равен 2 месяцам, что позволяет их сравнить.

Поле текст надо устанавливать=””, для корректной работы функции суммирования выделенных ячеек.

Таким образом мы получаем отчет с разверткой по месяцам, где в итогах всегда присутствует колонка комментарий и появляются отклонения исли период равен 2 месяцам.

 

4. Полезные ссылки:

Хитрости компоновки данных: произвольная иерархия для внешней таблицы данных в отчетах на СКД. (infostart.ru)

Хитрости компоновки данных: программное формирование заголовков отчета и колонок без использования макетов. (infostart.ru)

 

Другие разработки автора:

Подсистема штрихкодирования серий номенклатуры (УТ 11, КА, ERP)Подсистема штрихкодирования серий номенклатуры (УТ 11, КА, ERP)
Подсистема 'Входной контроль' для ERP ,КА , УТ 11
Подсистема 'Входной контроль' для ERP ,КА , УТ 11
Электронный обходной лист (расширение) для ERP, ЗУП, КА
Отправка электронных писем по задолженностям клиентов (ERP, УТ 11)Отправка электронных писем по задолженностям клиентов (ERP, УТ 11)
Excel Studio for 1CExcel Studio for 1C
Настраиваемый управленческий балансНастраиваемый управленческий баланс
Динамические отчеты СКД
Динамические отчеты СКД
Фоновая отправка уведомлений
Генератор кодаГенератор кода

 

Акция! Вы можете скачать архив всех моих разработок, которые я предлагаю за StartMone, по Специальной  цене:  //infostart.ru/public/960899/#archive

PS: Надеюсь вам понравится эта и другие мои разработки на //infostart.ru/profile/48714/.

Очень жду ваших комментариев  и пожеланий.

Молочников Олег Spb. 2024

СКД Объединение ячеек Постобработка Скрытие колонок

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    139741    767    391    

800

Сканер штрих-кода Пользователь Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Стандартные конфигурации ERP, КА, УТ 11 позволяют работать с штрихкодированием серий. Есть только одно суровое ограничение – на упаковке должна быть этикетка для номенклатуры и отдельно для серии. Во многих случаях это критически неудобно.

12000 руб.

19.04.2021    21659    62    43    

56

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    11637    dimanich70    80    

124

СКД WEB-интеграция Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Долгое время поддерживаю web-портал, в котором появилась необходимость создавать отчеты. Просмотрев различные фреймворки на js, я решил сделать свое решение, которое позволяло бы быстро разрабатывать и добавлять новые отчеты на web-портал.

2 стартмани

11.12.2023    9408    21    John_d    25    

124

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    11422    YA_418728146    7    

153

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2821    43    progmaster    8    

4
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. echo77 1865 28.05.22 15:03 Сейчас в теме
(0) А оформите, пожалуйста код, как код1С и в сниппеты их - так он будет выглядеть компактно и сама публикация станет лучше. Сейчас вижу кучу пустых строк - вроде, кода мало, а выглядит растянуто. Спасибо
DrAku1a; oldcopy; Shmell; ixijixi; RustIG; anatsen; mrChOP93; t278; json; artbear; rpgshnik; +11 Ответить
2. artbear 1558 28.05.22 15:57 Сейчас в теме
(1) Да, код вообще не отформатирован и нечитабелен (
DrAku1a; paybaseme; oldcopy; anatsen; t278; +5 Ответить
11. DrAku1a 1730 04.06.22 08:06 Сейчас в теме
(2) Код читабелен, хотя и не оформлен по стандартам 1С, и развлеченье автора с именами переменных для циклов.
Для I=-ТабДок.ВысотаТаблицы+1 по -ВысотаФ-1 Цикл
			НомерСтроки=-I;
Еще одна вариация "Обратного цикла" (когда нужен шаг -1, а есть только 1)
3. milkers 2893 30.05.22 08:16 Сейчас в теме
(1) (2) - Хорошо, переделаю
4. zqzq 25 30.05.22 09:16 Сейчас в теме
Вариант, когда итог нужно рассчитать лишь на определенном уровне группировок.
Можно в Конструкторе СКД добавить 2 раза ресурс:
1) по умолчанию пустое значение ""
2) указать выражение расчета и указать в "рассчитывать по" нужные группировки.

По сути в статье это и есть (за минусом "&Итоги"), непонятен смысл постобработки. Возможно задача неясно сформулирована в статье.
5. oldcopy 174 30.05.22 12:22 Сейчас в теме
Автор, ну нельзя так код выкладывать, пожалейте читателей, абсолютно нечитабельно.
6. hudyakov74 30.05.22 15:34 Сейчас в теме
это столько букв чтобы объединить что то типа пары ячеек?
7. milkers 2893 30.05.22 21:24 Сейчас в теме
(4) Если вам интересно, прочитайте здесь https://infostart.ru/1c/articles/1101676/?
про использование внутренних функций системы компоновки данных: Вычислить, ВычислитьВыражение, ВычислитьВыражениеСГруппировкойМассив, ВычислитьВыражениеСГруппировкойТаблицаЗначений.

Частный случай когда система не дает использовать вложенные функции такого рода.
10. zqzq 25 31.05.22 10:46 Сейчас в теме
(7) Про эти функции не только читал, но и использовал на практике. В чём у вас с ними за проблема, так и не понял. Но согласен, что они ведут себя иногда неочевидно.
8. milkers 2893 30.05.22 21:25 Сейчас в теме
(5) Исправил
mrChOP93; EvgeniyOlxovskiy; oldcopy; +3 Ответить
9. milkers 2893 30.05.22 21:25 Сейчас в теме
(6) Всегда готов узнать про более оптимальный вариант.
Оставьте свое сообщение