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

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

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

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

Проанализируем ситуацию на примере. Допустим, у нас есть иерархическая структура:

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

Способ 1: Фильтрация по минимальному уровню группировки

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

Для начала нам необходимо перебрать все выделенные области и определить, какой минимальный индекс уровня группировки встречается в выделении. В 1С уровни строк начинаются с 1 (или 0 в зависимости от контекста обращения, но чаще мы оперируем понятием УровеньГруппировкиСтрок).

Рассмотрим пример функции, которая выполняет такой расчет:


Функция ПолучитьСуммуВыделенныхЯчеек(ТабличныйДокумент) Экспорт
    
    ПеременнаяСумма = 0;
    ВыделенныеОбласти = ТабличныйДокумент.ВыделенныеОбласти;
    
    // Шаг 1: Определяем минимальный уровень группировки в выделении
    МинУровень = 99; // Задаем заведомо большое число
    
    Для Каждого Область Из ВыделенныеОбласти Цикл
        Для НомерСтроки = Область.Верх По Область.Низ Цикл
            // Проверяем только видимые строки
            Если ТабличныйДокумент.Область(НомерСтроки, , НомерСтроки, ).Видимость Тогда
                ТекущийУровень = ТабличныйДокумент.Область(НомерСтроки, , НомерСтроки, ).УровеньГруппировкиСтрок;
                Если ТекущийУровень < МинУровень Тогда
                    МинУровень = ТекущийУровень;
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    
    // Шаг 2: Суммируем ячейки, соответствующие этому уровню
    Для Каждого Область Из ВыделенныеОбласти Цикл
        Для НомерСтроки = Область.Верх По Область.Низ Цикл
            СтрокаТД = ТабличныйДокумент.Область(НомерСтроки, , НомерСтроки, );
            
            // Складываем только если строка видима и её уровень совпадает с минимальным в выделении
            Если СтрокаТД.Видимость И СтрокаТД.УровеньГруппировкиСтрок = МинУровень Тогда
                Для НомерКолонки = Область.Лево По Область.Право Цикл
                    Ячейка = ТабличныйДокумент.Область(НомерСтроки, НомерКолонки);
                    Если ТипЗнч(Ячейка.Текст) = Тип("Число") Тогда
                         ПеременнаяСумма = ПеременнаяСумма + Ячейка.Содержимое; // Или Ячейка.Текст, если там число
                    Иначе
                         // Попытаемся преобразовать текст в число, если это необходимо
                         Попытка
                             ПеременнаяСумма = ПеременнаяСумма + Число(Ячейка.Текст);
                         Исключение
                             // Игнорируем нечисловые значения
                         КонецПопытки;
                    КонецЕсли;
                КонецЦикла;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    
    Возврат ПеременнаяСумма;
    
КонецФункции

Способ 2: Учет горизонтальных группировок (колонки)

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

Выясним причину ошибки: если мы выделили область, включающую итоги по строкам и итоги по колонкам, то на их пересечении находится «общий итог». Для корректного расчета нужно суммировать только те ячейки, уровень строки которых минимален для выделения И уровень колонки которых также минимален для выделения.

Проанализируем алгоритм для этого случая:

  1. Проходим по всем выделенным областям и находим МинУровеньСтрок и МинУровеньКолонок.
  2. Вторым проходом суммируем только те ячейки, у которых УровеньГруппировкиСтрок == МинУровеньСтрок И УровеньГруппировкиКолонок == МинУровеньКолонок.
  3. Обязательно проверяем свойства Видимость как для строк, так и для колонок.

Важные нюансы работы с объектом Область

При работе с ТабличныйДокумент важно помнить, что обращение к каждой ячейке через метод Область(НомерСтроки, НомерКолонки) в больших циклах может работать медленно. Рассмотрим несколько советов по оптимизации:

Событие ПриАктивизацииОбласти

Чтобы сумма обновлялась «на лету», как в Excel, код расчета необходимо разместить в обработчике события ПриАктивизацииОбласти табличного документа. Посмотрим, как это выглядит в модуле формы:


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

&НаКлиенте
Процедура РассчитатьИтогиВыделения()
    
    СуммаВыделения = ПолучитьСуммуВыделенныхЯчеек(Элементы.Результат);
    // Выводим результат в реквизит формы
    Объект.СуммаПоCalculated = СуммаВыделения;
    
КонецПроцедуры

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

Использование методов БСП

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

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

← На главную