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