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