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