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