Как реализовать расчет суммы выделенных строк в динамическом списке 1С 8.3

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

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

Подготовительный этап: настройка формы списка

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

  1. Режим выделения: Для того чтобы пользователь мог выбрать несколько строк (используя клавиши Ctrl или Shift), у элемента формы Список (тип «Таблица») свойство РежимВыделения должно быть установлено в значение Множественный. В противном случае платформа позволит активировать только одну строку за раз.
  2. Реквизит для итога: Нам нужно создать реквизит формы, в котором будет отображаться результат. Назовем его СуммаВыделенныхДокументов с типом Число (15, 2).
  3. Размещение на форме: Вынесем этот реквизит в шапку или подвал формы списка, установив для него режим Только просмотр, чтобы пользователь видел результат, но не мог его изменить вручную.

Реализация логики на клиенте (простой способ)

Рассмотрим самый очевидный способ реализации задачи. Основное событие, которое нам потребуется — ПриАктивизацииСтроки. Оно срабатывает каждый раз, когда пользователь кликает на строку или меняет выделение.

Разберем пример кода, который перебирает выделенные строки и суммирует значения в колонке СуммаДокумента:


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

Этот метод хорош своей простотой: он выполняется полностью на стороне клиента и не нагружает сервер. Однако у него есть существенное ограничение: метод ДанныеСтроки() возвращает данные только для тех строк, которые в данный момент находятся в кэше клиента (видимы на экране или подгружены рядом). Если пользователь выделит 500 строк «пролистыванием», часть данных может вернуть Неопределено, и сумма будет неверной.

Оптимизация производительности: использование обработчика ожидания

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

Для решения этой проблемы применим методику «отложенного расчета» через ПодключитьОбработчикОжидания. Рассмотрим алгоритм:

  1. В событии ПриАктивизацииСтроки мы не считаем сумму сразу, а лишь даем команду системе: «посчитай через 0.3 секунды».
  2. Если за эти 0.3 секунды пользователь перейдет на другую строку, мы сбросим старый таймер и запустим новый.
  3. Расчет выполнится только тогда, когда пользователь сделает паузу в перемещении.

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

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

Надежный расчет на стороне сервера

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

Посмотрим на пример реализации серверного вызова:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Передаем массив выделенных ссылок на сервер
    МассивСсылок = Новый Массив;
    Для Каждого Идентификатор Из Элементы.Список.ВыделенныеСтроки Цикл
        // Идентификатором в динамическом списке обычно является ссылка
        МассивСсылок.Добавить(Идентификатор);
    КонецЦикла;
    
    СуммаВыделенныхДокументов = ПолучитьСуммуНаСервере(МассивСсылок);
КонецПроцедуры

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

Преимущества серверного метода: он всегда возвращает точный результат, даже если выделено огромное количество строк, и не зависит от того, какие колонки отображены в данный момент в списке. Недостаток: каждый клик создает сетевой вызов к серверу, что при медленном соединении может быть заметно пользователю. Поэтому использование обработчика ожидания здесь становится обязательным.

Работа с группами и иерархией

Проанализируем ситуацию, когда в динамическом списке включено отображение групп. Если пользователь выделит папку, попытка обратиться к ДанныеСтроки.СуммаДокумента может привести к ошибке, так как у группы может не быть такого реквизита, или расчет по группе не имеет смысла.

Чтобы избежать ошибок, рекомендуем добавить проверку типа данных:


Для Каждого Идентификатор Из Элементы.Список.ВыделенныеСтроки Цикл
    Данные = Элементы.Список.ДанныеСтроки(Идентификатор);
    // Проверяем, что это не группа (если применимо)
    Если Данные <> Неопределено И НЕ Данные.ЭтоГруппа Тогда
        Итог = Итог + Данные.СуммаДокумента;
    КонецЕсли;
КонецЦикла;

Заключение

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

Использование подобных механизмов делает работу пользователя в системе 1С:Предприятие 8.3 более комфортной и эффективной, приближая функциональность списков документов к возможностям мощных аналитических отчетов.

← На главную