Как рассчитать сумму по колонке для выделенных строк динамического списка

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке интерфейсов на управляемых формах часто возникает задача отображения итогов по выбранным позициям. Например, пользователь выделяет несколько документов в списке, а система должна мгновенно показать общую сумму, вес или объем этих документов. Несмотря на кажущуюся простоту, реализация этой функции в ДинамическомСписке имеет ряд подводных камней, связанных с клиент-серверным взаимодействием и особенностями кэширования данных. В этой статье мы подробно разберем, как правильно реализовать такой расчет, избежав типичных ошибок и «подтормаживания» интерфейса.

Проблема получения данных из выделенных строк

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

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

Использование метода ДанныеСтроки на клиенте

Метод ДанныеСтроки() позволяет получить данные, которые в данный момент доступны на клиенте в кэше динамического списка. Это работает быстро, так как не требует дополнительного обращения к базе данных. Разберем пример реализации в обработчике события ПриАктивизацииСтроки:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    
    ИтоговаяСумма = 0;
    
    Для Каждого ИдентификаторСтроки Из Элементы.Список.ВыделенныеСтроки Цикл
        
        ДанныеСтроки = Элементы.Список.ДанныеСтроки(ИдентификаторСтроки);
        
        // Обязательно проверяем на Неопределено, так как данные могут быть не подгружены
        Если ДанныеСтроки <> Неопределено Тогда
            ИтоговаяСумма = ИтоговаяСумма + ДанныеСтроки.СуммаДокумента;
        КонецЕсли;
        
    КонецЦикла;
    
    // Присваиваем результат реквизиту формы
    ОбщаяСуммаВыделенных = ИтоговаяСумма;
    
КонецПроцедуры

Важный нюанс: Метод ДанныеСтроки() вернет Неопределено, если строка еще не была отрисована на экране или была вытеснена из кэша. Если пользователь выделит 500 строк (например, через Ctrl+A), расчет по этому методу будет неполным. В таких случаях необходимо комбинировать методы или переходить к серверному расчету.

Почему зацикливается обновление интерфейса?

Выясним причину одной из самых коварных ошибок: когда при изменении суммы событие ПриАктивизацииСтроки начинает срабатывать бесконечно или дважды. Это часто происходит, если для отображения итога используется Декорация (вид — Надпись), и разработчик программно меняет её Заголовок.

Проанализируем ситуацию: изменение заголовка декорации заставляет форму перерисовываться. В некоторых версиях платформы или при определенных настройках состава командной панели это инициирует обновление состояния элементов, что платформа интерпретирует как повод снова вызвать ПриАктивизацииСтроки. Получается замкнутый круг: изменение заголовка -> перерисовка -> активация строки -> изменение заголовка.

Решение проблемы: Вместо декорации следует использовать обычный Реквизит формы (тип — Число или Строка). Этот реквизит нужно вынести на форму как Поле ввода, установив ему вид Поле надписи. Изменение значения реквизита формы не вызывает таких агрессивных побочных эффектов, как прямое изменение свойств элементов формы (декораций).

Оптимизация через обработчик ожидания

Событие ПриАктивизацииСтроки срабатывает очень часто — при каждом перемещении курсора. Если расчет сложный или требует обращения к серверу, интерфейс будет «зависать» при прокрутке списка. Посмотрим, как применить механизм «дребезга» (debouncing) с помощью обработчика ожидания.

Суть метода заключается в том, чтобы запустить расчет не мгновенно, а с небольшой задержкой (например, 0.1 – 0.3 секунды). Если за это время пользователь перейдет на другую строку, старый расчет отменяется и запускается новый отсчет.


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Отключаем предыдущий вызов, если он еще не сработал
    ОтключитьОбработчикОжидания("ВыполнитьРасчетИтогов");
    // Запускаем новый таймер на 200 миллисекунд
    ПодключитьОбработчикОжидания("ВыполнитьРасчетИтогов", 0.2, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьРасчетИтогов()
    // Здесь выполняем суммирование
    РассчитатьСуммуНаСервере();
КонецПроцедуры

Гарантированный расчет на сервере

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


&НаСервереБезКонтекста
Функция ПолучитьСуммуВыделенныхНаСервере(МассивСсылок)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   СУММА(Док.СуммаДокумента) КАК Итого
        |ИЗ
        |   Документ.РеализацияТоваровУслуг КАК Док
        |ГДЕ
        |   Док.Ссылка В(&МассивСсылок)";
    
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат ?(Выборка.Итого = NULL, 0, Выборка.Итого);
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

Этот метод является самым надежным. Чтобы минимизировать количество серверных вызовов, используйте его совместно с описанным выше обработчиком ожидания.

Важные рекомендации

  1. Флаг «Использовать всегда»: Если вы рассчитываете сумму на клиенте через ДанныеСтроки(), убедитесь, что у соответствующих полей в настройках ДинамическогоСписка на форме установлен флаг Использовать всегда. В противном случае, если пользователь скроет колонку через «Изменить форму», её значения перестанут передаваться на клиент, и сумма станет нулевой.
  2. Основная таблица: Проверьте, чтобы у динамического списка была четко определена Основная таблица. Без этого состав ВыделенныхСтрок может работать некорректно, особенно в сложных произвольных запросах.
  3. Суммирование по всем фильтрам: Если ваша задача — посчитать итог не по выделенным строкам, а вообще по всем, что попали в текущий отбор списка, используйте метод ИсполняемаяСхемаКомпоновкиДанных. Это позволит программно выполнить тот же запрос, что и список, включая все пользовательские фильтры.

Подводя итог, можно сказать, что для простых списков и небольшого количества строк вполне достаточно клиентского метода ДанныеСтроки() в связке с реквизитом формы. Для профессиональных интерфейсов, устойчивых к большим объемам данных, оптимальным решением будет связка Обработчик ожидания + Серверный запрос по массиву ссылок.

← На главную