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