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

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

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

Разбираем проблему: почему сумма считается неверно?

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

Проанализируем типичный ошибочный код, где сумма «затирается» или считается только для последней строки:


// Ошибочный подход
Для Каждого Строка Из ВыделенныеСтроки Цикл
    Сумма = Элементы.Материалы.ДанныеСтроки(Строка).Количество; // Здесь сумма перезаписывается, а не прибавляется
КонецЦикла;
СтрокаТЧ.Количество = Сумма; 

Чтобы исправить это, мы должны использовать классический прием накопления суммы: Сумма = Сумма + ТекущееЗначение.

Решение 1: Суммирование выделенных строк в табличной части

Для корректного расчета суммы выделенных строк нам необходимо выполнить три шага: получить массив идентификаторов выделенных строк, обойти его в цикле и найти данные каждой строки в объекте. Рассмотрим правильный пример реализации на клиенте:


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

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

Решение 2: Суммирование строк с учетом установленного фильтра (отбора)

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

Выясним, как это реализовать:


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

Этот способ крайне удобен, так как нам не нужно программно анализировать сложную структуру ФиксированнаяСтруктура, которая лежит в свойстве ОтборСтрок.

Решение 3: Работа с динамическими списками и серверная оптимизация

Если мы работаем не с табличной частью документа, а с Динамическим списком (например, список всех заказов), то данные на клиенте отсутствуют в полном объеме. В этом случае попытка получить данные через ПолучитьОбъект() в цикле на сервере приведет к катастрофическому падению производительности.

Рассмотрим, как проанализировать ситуацию и оптимизировать расчет:

  1. Передаем массив выделенных идентификаторов на сервер.
  2. На сервере выполняем запрос к базе данных, передавая массив в условие.
  3. Запрос вернет сумму одной операцией, что гораздо быстрее перебора объектов.

Пример эффективного серверного кода:


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

Типичные ошибки и советы по интерфейсу

Разберем еще несколько важных моментов, которые помогут сделать ваш код надежным:

1. Очистка выделения. Если вы программно управляете выделением строк (например, по нажатию кнопки «Выделить все по материалу»), обязательно вызывайте Элементы.Таблица.ВыделенныеСтроки.Очистить() перед добавлением новых ID. В противном случае старые выделенные строки останутся в массиве, и сумма «задвоится» или будет содержать лишние данные.

2. Событие обновления. Расчет суммы логично привязать к событию ПриАктивизацииСтроки. Однако помните, что оно срабатывает очень часто. Чтобы интерфейс не «тормозил», можно использовать ПодключитьОбработчикОжидания. Это позволит запускать расчет суммы с небольшой задержкой (например, 0.2 секунды) после того, как пользователь закончил навигацию по списку.

3. Использование подвала. Если вам нужно видеть итог по всей таблице, не забывайте про стандартное свойство ПутьКДаннымПодвала и АвтоматическийИтог у колонки таблицы. Это работает без единой строчки кода для всей таблицы в целом, но для выделенных строк придется использовать описанные выше программные методы.

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

← На главную