Управляемые формы. Итоги в динамических списках

Опубликовал Петр Андреев (argut) в раздел Обработки - Универсальные обработки

Многие задаются вопросами расчета итогов динамических списков в управляемых формах. Здесь мы не будем претендовать на академичность, оставим споры, что мол это не нужно, что это все равно что считать бородатых мужиков на движущемся эскалаторе, что это дополнительная нагрузка. Все это понимают, Кто не понимает - поймет на практике. Поэтому основываемся на... "снегопад, снегопад, если женщина просит..." или "..а мня плевать - мне очень хочется.." Я попытался решить практические проблемы при расчете и выводе итогов. В приложении примеры расчета итогов в иерархическом справочнике и журнале документов.

Рассмотрим на примере неких колонок Поступило и Списано в динамическом списке

1. Размещаем реквизиты итогов

На форму добавляем числовые реквизиты ИтогиПоступилоПодвал и ИтогиСписноПодвал и назначаем их в подвал таблицы к полям Поступило и Списано в качестве ПутьКДаннымПодвала

2. Отлавливаем изменения на клиенте

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

Пока нерешенной проблемой остается изменение поиска. Стоит заметить, что если поиск выполняется средствами СУБД (не через индекс полнотекстового поиска), то настройки компоновки содержат отбор, соответствующий поиску. Поэтому, если использоватья явный вызов расчета на сервере, то итоги будут рассчитаны с учетом поиска. Но вот отследить на клиенте когда нужно вызвать сервер при поиске пока у меня не получилось.

Добавляем на форме строковый реквизит НастройкаСписка, который и будет нашим флагом. Запускаем обработчик ожидания и следим за изменениями. Если что-то поменялось - обращаемся на сервер за расчетом итогов

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	НастройкиСписка = "...";//при первом вызове посчитаем
	ПодключитьОбработчикОжидания("ПроверкаИзменения",1);
КонецПроцедуры

&НаКлиенте
Процедура ПроверкаИзменения()
	
	НастройкиСпискаТек = "";
	Для Каждого ЭлементПользНастроек Из	Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
		//тут отборы и сортировка - отловим их
		НастройкиСпискаТек = НастройкиСпискаТек + СокрЛП(ЭлементПользНастроек) + СокрЛП(Элементы.Список.Период.ДатаНачала) + СокрЛП(Элементы.Список.Период.ДатаОкончания);
	КонецЦикла;
	//тут добавим в ловушку период списка (если это список документов)
	НастройкиСпискаТек = НастройкиСпискаТек + СокрЛП(Элементы.Список.Период.ДатаНачала) + СокрЛП(Элементы.Список.Период.ДатаОкончания);
	
	Если не НастройкиСписка = НастройкиСпискаТек Тогда	
	 
	 	//чей-то поменялось
		НастройкиСписка = НастройкиСпискаТек;
		
		СчитаемИтогиНаСервере();	
		
	КонецЕсли;
	
КонецПроцедуры

3. Расчитываем реквизиты итогов на сервере

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

Вот пример для журнала банковских выписок:

&НаСервере
Процедура СчитаемИтогиНаСервере()
	
	//получаем схему компоновки списка
	СКД = Элементы.Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
	
	//добавляем ресурсы
	ПолеИтога = СКД.ПоляИтога.Добавить();
	ПолеИтога.ПутьКДанным 	= "Поступление";
	ПолеИтога.Выражение 	= "СУММА(Поступление)";
	
	ПолеИтога = СКД.ПоляИтога.Добавить();
	ПолеИтога.ПутьКДанным 	= "Списание";
	ПолеИтога.Выражение 	= "СУММА(Списание)";
	
	//получаем настройки компоновки списка (в ней уже есть отборы и поиск)
	НастройкаСКД 	= Элементы.Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
	
	//нам не нужна вся таблица, а только итог
	//очищаем поля основной группировки - получаем в итоге группировку всего запроса по "неопределено" и на выходе одну строку в результате
	НастройкаСКД.Структура[0].ПоляГруппировки.Элементы.Очистить();
	
	//нам не нужны все поля, а только наши ресурсы
	НастройкаСКД.Структура[0].Выбор.Элементы.Очистить();
	Выб = НастройкаСКД.Структура[0].Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Выб.Поле 			= Новый ПолеКомпоновкиДанных("Поступление");
	Выб.Использование 	= Истина;
	
	Выб = НастройкаСКД.Структура[0].Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Выб.Поле 			= Новый ПолеКомпоновкиДанных("Списание");
	Выб.Использование 	= Истина;
	
	//отбор проведенных
	ЭО = НастройкаСКД.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭО.ЛевоеЗначение	= Новый ПолеКомпоновкиДанных("Проведен");
	ЭО.ВидСравнения 	= ВидСравненияКомпоновкиДанных.Равно;
	ЭО.ПравоеЗначение	= Истина;
	ЭО.Использование	= Истина;
	
	//поехали за результатом
	КомпоновщикМакетаКомпоновкиДанных = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновкиДанных = КомпоновщикМакетаКомпоновкиДанных.Выполнить(СКД, НастройкаСКД,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений")) ;
	
	ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных);
   
   	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	
	Результат = ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
	
	//заполняем реквизиты итогов подвала
	ИтогиПоступилоПодвал 	= Результат.Итог("Поступление");
	ИтогиСписаноПодвал 		= Результат.Итог("Списание");
КонецПроцедуры

зы:

Для иерархичеких списков выявилась одна особенность: СКД не содержит отбора по текущему родителю (ну может я его не нашел). Поэтому в случае со справочником можно доработать настройки типа так:

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

ну и для справочника можно добавить вот это:

&НаКлиенте
Процедура ДинамическийСписокПриСменеТекущегоРодителя(Элемент)
	НастройкиСписка = "..."; //сбросили - расчитаем на сервере на след шаге обработчика ожидания
КонецПроцедуры

К статье приложил пару файлов.

Скачать файлы

Наименование Файл Версия Размер
Обработка для 1С:Бугалтерия 3.0. Остатки номенклатуры. Иерархический список и итогом.
.epf 8,99Kb
15.12.16
2
.epf 8,99Kb 2 Скачать
Расширение для 1С:Бугалтерия 3.0. Итоги в журнале банковских выписок
.cfe 41,26Kb
15.12.16
4
.cfe 41,26Kb 4 Скачать

См. также

Комментарии
1. Владимир Лагутин (Lukich66) 79 21.12.16 08:37 Сейчас в теме
из текста "Тут нужно сказать спасибо разработчикам ..."- года 2 назад в длинные новогоднии каникулы
"развлекся" нечто подобным (итогами в журнале документов учета путевых листов- бац оперативно посмотреть в отборе циферки гсм,РВ и т.п.), когда столкнулся с непреодолимыми на моем уровне проблемами- то б/удовольствия стал смотреть на УФ и ДС. Вот почему некоторый создатель не желает совершенствовать свой -же продукт?
2. Петр Андреев (argut) 59 21.12.16 13:25 Сейчас в теме
Совершенствуют потихоньку :) Поиск более менее нормальный вот не так давно запилили и экспериментируют с ним. Может и события появятся.
3. Дмитрий К. (Dementor) 9 21.12.16 16:56 Сейчас в теме
оставим споры, что мол это не нужно


Управляемые формы - это просто песня и легкость разработки. Вот у меня клиент пару месяцев назад в УТ10 в журнале заказов заказал нумерацию списка - что бы сбоку были порядковые номера. При этом при смене периода журнала, при наложении фильтров, при смене сортировок, что бы эта нумерация пересчитывалась и была актуальной: нашел по поиску нужный документ и видишь, что он 15345, а вверх и вниз идет уменьшение и увеличение счетчика. Для УФ это было бы легко, но тут обычная форма. Пытался я его отговорить, но ему нужно, а иначе он работать не может.... Пришлось сделать, даже не тормозило при прокрутке :)

Кстати, использование данных для вывода значения в подвале - правильное решение. Если пробовать менять текст в свойстве ТекстПодвала, то это будет приводить к пересозданию формы и лишнему серверному вызову.
4. EvgeniuXP EvgeniuXP (EvgeniuXP) 21.12.16 23:57 Сейчас в теме
(3) дополнение к последнему абзацу: а если на форме еще есть горизонтальные и вертикальные сплитеры - то они будут сбрасываться на первоначальное положение в форме - что опять же будет не удобно пользователю.
5. Яков Коган (Yashazz) 1987 06.02.17 18:49 Сейчас в теме
Мдя. Не понял причин ажиотажа. Задача тривиальная, решение так себе, ресурсоёмкое и не очень-то универсальное.

За пассажи вроде получения результата в таблицу значений и потом итог по колонке вообще сразу минусовать хочется. Автор, видимо, не в курсе, что умеет СКД в части агрегирования данных и вообще итогов. И уж точно автор не представляет, что будет с сеансовыми данными при солидной выборке. Так что - учить матчасть.

Ну и ещё: во-первых, и без всяких ПолучитьИсполняемуюСхемуКомпоновкиДанных() можно обойтись, хотя и неудобнее; во-вторых, очень любопытно, что будет с таким подходом, если юзер возьмётся всяко группировать динамический список (потому и "отбор по текущему родителю" может быть бессмысленным), и в третьих, такие вещи делаются архитектурно иначе. Если клиенту нужны итоги, надо организовать их подачу отдельной механикой, а не лепить экселеобразный итог по колонке, простихосспади.

Впрочем, это хорошая публикация. Чем больше их будет, тем больше нам потом заработка на оптимизации отчаянно тормозящей системы)))
6. Яков Коган (Yashazz) 1987 06.02.17 18:54 Сейчас в теме
(3) "Управляемые формы - это просто песня и легкость разработки"? Это вы или иронизируете, или грустно шутите, наверное. Всерьёз такое сказануть вряд ли можно.
7. Дмитрий К. (Dementor) 9 06.02.17 22:48 Сейчас в теме
(6) Вы совершенно верно все подметили - это была грустная шутка для тех, кто в теме.
С одной стороны некоторые вещи стали проще чем были в обычных формах (те же гибкие ДинамическиеСписки вместо дубовых объектов СправочникСписок и ДокументСписок), но с другой - многое бесит. Невозможность управлять самопоявляющимися отступами на форме, недоступность изменения свойств заголовков элементов, слетающие условные оформления на сложных списках и так далее...
8. Петр Андреев (argut) 59 07.02.17 07:01 Сейчас в теме
(5)
Автор, видимо, не в курсе, что умеет СКД в части агрегирования данных и вообще итогов.

Ну так тут СКД и формирует таблицу из одной строки уже сагрегированную.

(5)
И уж точно автор не представляет, что будет с сеансовыми данными при солидной выборке.

Для этого и обсуждаем чтобы люди, которые опытнее могли помочь менее опытным.
9. Яков Коган (Yashazz) 1987 07.02.17 10:35 Сейчас в теме
(8) Одну сагрегированную? Ну это наивные надежды, что, грохнув группировки, мы добьёмся от СКД нужного результата. Это ж надо нормальную пустую группировку создать, авто-поле, все дела. СКД понимает, только когда ей всё жёстко "в лоб" указано.