В повседневной работе пользователи часто сталкиваются с необходимостью быстро узнать общую сумму нескольких документов, просто выделив их в списке. В типовых отчетах на системе компоновки данных (СКД) этот функционал реализован на уровне платформы, однако в обычных формах списков документов такую возможность приходится добавлять программно. В этой статье мы подробно разберем, как реализовать динамический расчет суммы выделенных строк, какие подводные камни существуют и как оптимизировать код для стабильной работы интерфейса.
Прежде чем приступать к написанию кода, нам необходимо подготовить визуальную часть и настроить свойства элементов формы. Проанализируем основные требования:
Ctrl или Shift), у элемента формы Список (тип «Таблица») свойство РежимВыделения должно быть установлено в значение Множественный. В противном случае платформа позволит активировать только одну строку за раз.СуммаВыделенныхДокументов с типом Число (15, 2).Только просмотр, чтобы пользователь видел результат, но не мог его изменить вручную.Рассмотрим самый очевидный способ реализации задачи. Основное событие, которое нам потребуется — ПриАктивизацииСтроки. Оно срабатывает каждый раз, когда пользователь кликает на строку или меняет выделение.
Разберем пример кода, который перебирает выделенные строки и суммирует значения в колонке СуммаДокумента:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
Итог = 0;
// Получаем массив идентификаторов выделенных строк
ВыделенныеСтроки = Элементы.Список.ВыделенныеСтроки;
Для Каждого ИдентификаторСтроки Из ВыделенныеСтроки Цикл
// Получаем данные конкретной строки по её идентификатору
ДанныеСтроки = Элементы.Список.ДанныеСтроки(ИдентификаторСтроки);
// Обязательно проверяем, что данные строки подгружены
Если ДанныеСтроки <> Неопределено Тогда
Итог = Итог + ДанныеСтроки.СуммаДокумента;
КонецЕсли;
КонецЦикла;
// Выводим результат в реквизит формы
СуммаВыделенныхДокументов = Итог;
КонецПроцедуры
Этот метод хорош своей простотой: он выполняется полностью на стороне клиента и не нагружает сервер. Однако у него есть существенное ограничение: метод ДанныеСтроки() возвращает данные только для тех строк, которые в данный момент находятся в кэше клиента (видимы на экране или подгружены рядом). Если пользователь выделит 500 строк «пролистыванием», часть данных может вернуть Неопределено, и сумма будет неверной.
Выясним важную деталь: событие ПриАктивизацииСтроки происходит крайне часто. Если пользователь быстро перемещается по списку с помощью стрелок на клавиатуре, код будет выполняться десятки раз в секунду. Это может привести к «подтормаживанию» интерфейса (так называемому фризу).
Для решения этой проблемы применим методику «отложенного расчета» через ПодключитьОбработчикОжидания. Рассмотрим алгоритм:
ПриАктивизацииСтроки мы не считаем сумму сразу, а лишь даем команду системе: «посчитай через 0.3 секунды».
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Отключаем предыдущий вызов, если он был запланирован
ОтключитьОбработчикОжидания("ВыполнитьРасчетСуммы");
// Планируем новый расчет через короткий промежуток времени
ПодключитьОбработчикОжидания("ВыполнитьРасчетСуммы", 0.3, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьРасчетСуммы()
// Здесь размещается код обхода строк, описанный выше
Итог = 0;
Для Каждого Строка Из Элементы.Список.ВыделенныеСтроки Цикл
Данные = Элементы.Список.ДанныеСтроки(Строка);
Если Данные <> Неопределено Тогда
Итог = Итог + Данные.СуммаДокумента;
КонецЕсли;
КонецЦикла;
СуммаВыделенныхДокументов = Итог;
КонецПроцедуры
Если точность расчета критична и мы не можем полагаться на кэш клиента, проанализируем серверный метод. В этом случае мы передаем массив ссылок на документы на сервер и выполняем запрос к базе данных.
Посмотрим на пример реализации серверного вызова:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Передаем массив выделенных ссылок на сервер
МассивСсылок = Новый Массив;
Для Каждого Идентификатор Из Элементы.Список.ВыделенныеСтроки Цикл
// Идентификатором в динамическом списке обычно является ссылка
МассивСсылок.Добавить(Идентификатор);
КонецЦикла;
СуммаВыделенныхДокументов = ПолучитьСуммуНаСервере(МассивСсылок);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСуммуНаСервере(МассивСсылок)
Если МассивСсылок.Количество() = 0 Тогда
Возврат 0;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СУММА(ДокументОбъект.СуммаДокумента) КАК Итог
|ИЗ
| Документ.РеализацияТоваровУслуг КАК ДокументОбъект
|ГДЕ
| ДокументОбъект.Ссылка В(&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Итог;
КонецЕсли;
Возврат 0;
КонецФункции
Преимущества серверного метода: он всегда возвращает точный результат, даже если выделено огромное количество строк, и не зависит от того, какие колонки отображены в данный момент в списке. Недостаток: каждый клик создает сетевой вызов к серверу, что при медленном соединении может быть заметно пользователю. Поэтому использование обработчика ожидания здесь становится обязательным.
Проанализируем ситуацию, когда в динамическом списке включено отображение групп. Если пользователь выделит папку, попытка обратиться к ДанныеСтроки.СуммаДокумента может привести к ошибке, так как у группы может не быть такого реквизита, или расчет по группе не имеет смысла.
Чтобы избежать ошибок, рекомендуем добавить проверку типа данных:
Для Каждого Идентификатор Из Элементы.Список.ВыделенныеСтроки Цикл
Данные = Элементы.Список.ДанныеСтроки(Идентификатор);
// Проверяем, что это не группа (если применимо)
Если Данные <> Неопределено И НЕ Данные.ЭтоГруппа Тогда
Итог = Итог + Данные.СуммаДокумента;
КонецЕсли;
КонецЦикла;
Мы рассмотрели несколько способов реализации подсчета суммы в динамическом списке. Для простых задач и небольших списков вполне подойдет клиентский метод с прямым обходом коллекции ВыделенныеСтроки. Для профессиональных решений, требующих высокой точности и производительности, оптимальным будет сочетание серверного запроса и клиентского обработчика ожидания.
Использование подобных механизмов делает работу пользователя в системе 1С:Предприятие 8.3 более комфортной и эффективной, приближая функциональность списков документов к возможностям мощных аналитических отчетов.