Как вывести сумму из табличной части документа в динамический список

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке интерфейсов на платформе 1С:Предприятие 8.3 часто возникает задача отображения итоговых данных из табличных частей документов непосредственно в списке (журнале). Например, в документе ЧекККМ может потребоваться вывести общую сумму ручной скидки по всем строкам товара. Поскольку динамический список работает с основной таблицей базы данных, прямое обращение к табличной части через точку невозможно. Разберем, как решить эту задачу эффективно и без потери производительности.

Проблема использования агрегатов в основном запросе списка

Первое решение, которое приходит на ум — это использование левого соединения в тексте запроса динамического списка. Мы пытаемся соединить таблицу документов с вложенным запросом, который группирует строки табличной части и суммирует значения. Однако такой подход имеет ряд существенных недостатков:

  1. Падение производительности: Динамический список выполняет запросы порциями. При наличии сложных соединений с группировками СУБД приходится выполнять расчеты для огромного массива данных, что приводит к «подтормаживанию» интерфейса при прокрутке.
  2. Ошибки типов данных: Часто поля сумм в типовых конфигурациях имеют ОпределяемыйТип.ДенежноеПолеЛюбойЗнак или аналогичные. При попытке суммирования таких полей в запросе платформа может выдать ошибку: «Неверные параметры СУММА».
  3. Проблемы с фильтрацией: Поиск и отбор по таким полям в динамическом списке могут работать некорректно или крайне медленно.

Рассмотрим по шагам наиболее правильные и современные методы решения этой задачи.

Метод 1: Использование обработчика ПриПолученииДанныхНаСервере

Это оптимальный вариант, если вам нужно только отображать данные, не настраивая по ним сложные отборы. Суть метода заключается в том, что мы рассчитываем суммы только для тех строк, которые в данный момент видны пользователю на экране (обычно это 30–50 строк).

Шаг 1. Подготовка запроса динамического списка.

Для начала добавим «пустое» поле в основной запрос динамического списка на форме. Это поле послужит контейнером для наших данных. Откроем настройки динамического списка и отредактируем текст запроса:


ВЫБРАТЬ
    ДокументЧекККМ.Ссылка КАК Ссылка,
    ДокументЧекККМ.Номер КАК Номер,
    ДокументЧекККМ.Дата КАК Дата,
    ДокументЧекККМ.СуммаДокумента КАК СуммаДокумента,
    // Добавляем поле-пустышку
    0 КАК СуммаРучнойСкидки
ИЗ
    Документ.ЧекККМ КАК ДокументЧекККМ

Шаг 2. Создание программного обработчика.

В свойствах элемента формы Список (таблица, отображающая данные) найдем событие ПриПолученииДанныхНаСервере и создадим его обработчик. Напишем код, который будет получать ссылки текущих строк и выполнять расчет суммы:


&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Получаем массив ссылок на документы, которые сейчас отображаются на экране
    МассивСсылок = Строки.ПолучитьКлючи();
    
    Если МассивСсылок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Выполняем запрос к табличной части только для этих документов
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЧекККМТовары.Ссылка КАК Ссылка,
        |	СУММА(ВЫРАЗИТЬ(ЧекККМТовары.СуммаРучнойСкидки КАК ЧИСЛО(15, 2))) КАК СуммаРучнойСкидки
        |ИЗ
        |	Документ.ЧекККМ.Товары КАК ЧекККМТовары
        |ГДЕ
        |	ЧекККМТовары.Ссылка В(&МассивСсылок)
        |ГРУППИРОВАТЬ ПО
        |	ЧекККМТовары.Ссылка";
    
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Заполняем данные в строках динамического списка
    Пока Выборка.Следующий() Цикл
        ТекущаяСтрока = Строки.Получить(Выборка.Ссылка);
        Если ТекущаяСтрока <> Неопределено Тогда
            ТекущаяСтрока.Данные.СуммаРучнойСкидки = Выборка.СуммаРучнойСкидки;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Обратите внимание на использование функции ВЫРАЗИТЬ внутри запроса. Это критически важно, когда поле имеет составной тип или базируется на определенном типе. Мы явно приводим его к числу, чтобы функция СУММА отработала корректно на уровне SQL-сервера.

Метод 2: Настройка интерфейса

Часто программисты сталкиваются с тем, что код написан, а колонка в списке не появляется. Разберем типичные ошибки:

Метод 3: Денормализация данных (для высокой нагрузки)

Проанализируем ситуацию: если документов тысячи, а пользователи постоянно хотят видеть итоги и строить по ним отчеты, метод с обработчиком события может не подойти (так как по расчетному полю нельзя сделать стандартный отбор). В таких случаях применяют денормализацию.

Процесс реализации:

  1. Добавим в сам документ ЧекККМ новый реквизит СуммаСкидкиИтого (тип Число).
  2. В модуле объекта документа в процедуре ПередЗаписью пропишем код автоматического расчета этого реквизита на основе данных табличной части.
  3. Запустим обработку для разового заполнения этого поля в старых документах.
  4. В динамическом списке просто выберем этот реквизит как обычное поле.

Этот метод считается «золотым стандартом» в высоконагруженных системах (например, в типовых решениях 1С:ERP или 1С:УТ 11), так как чтение одного готового числа из таблицы заголовков происходит мгновенно.

Выясним причины ошибок в запросах

Если вы все же решили использовать вложенный запрос в динамическом списке, помните о правилах работы с типами. Ошибка «Неверные параметры СУММА» возникает из-за того, что СУБД не может выделить память под результат, если не знает точную размерность числа. Всегда используйте ВЫРАЗИТЬ(Поле КАК ЧИСЛО(15,2)).

Пример корректного соединения во вложенном запросе:


ВЫБРАТЬ
    ДокументЧекККМ.Ссылка,
    ДокументЧекККМ.Номер,
    ВложенныйЗапрос.ИтогоСкидка
ИЗ
    Документ.ЧекККМ КАК ДокументЧекККМ
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ТЧ.Ссылка КАК Ссылка,
            СУММА(ВЫРАЗИТЬ(ТЧ.СуммаРучнойСкидки КАК ЧИСЛО(15,2))) КАК ИтогоСкидка
        ИЗ
            Документ.ЧекККМ.Товары КАК ТЧ
        ГРУППИРОВАТЬ ПО
            ТЧ.Ссылка
    ) КАК ВложенныйЗапрос ПО ДокументЧекККМ.Ссылка = ВложенныйЗапрос.Ссылка

Посмотрим на этот пример внимательно: индексация по полю Ссылка во вложенном запросе происходит автоматически, но при больших объемах данных такое соединение все равно будет медленнее, чем программный расчет через ПриПолученииДанныхНаСервере.

Таким образом, мы рассмотрели основные способы вывода данных из табличных частей в списки. Выбор метода зависит от ваших требований к производительности и необходимости фильтрации данных.

← На главную