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