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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда в журнале документов необходимо отобразить данные, которые физически не хранятся в реквизитах самого документа. Классический пример — вывод итоговой суммы по табличной части для документа, где реквизит СуммаДокумента по каким-то причинам отсутствует или не заполняется. Поскольку Журнал документов является стандартным объектом метаданных, агрегирующим данные из разных видов документов, решение этой задачи требует понимания механизмов работы Динамических списков и Управляемых форм.

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

Способ 1. Использование произвольного запроса в динамическом списке

Это основной и наиболее рекомендуемый метод для работы на управляемых формах. Динамический список, который лежит в основе формы журнала, позволяет переопределить стандартную выборку данных на Произвольный запрос. Рассмотрим подробнее, как это реализовать.

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

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


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

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

Способ 2. Программное вычисление через событие ПриПолученииДанныхНаСервере

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

Рассмотрим по шагам процесс реализации:

  1. Добавим в динамический список журнала вычисляемую колонку (путь к данным оставить пустым).
  2. В модуле формы создадим обработчик события СписокПриПолученииДанныхНаСервере.
  3. Внутри обработчика реализуем обход полученных строк.

Пример кода для реализации:


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Подготовим массив ссылок для пакетного запроса
    МассивСсылок = Новый Массив;
    Для Каждого СтрокаСписка Из Строки Цикл
        Если ТипЗнч(СтрокаСписка.Ключ) = Тип("ДокументСсылка.ОсобыйДокумент") Тогда
            МассивСсылок.Добавить(СтрокаСписка.Ключ);
        КонецЕсли;
    КонецЦикла;
    
    Если МассивСсылок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

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

КонецПроцедуры

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

Способ 3. Создание избыточного реквизита (Денормализация)

Как бы ни хотелось избежать изменения структуры метаданных, во многих случаях создание реального реквизита СуммаДокумента является самым правильным решением. Выясним причину:

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Рассчитываем итог по табличной части и записываем в реквизит
    СуммаДокумента = Товары.Итог("Сумма");
КонецПроцедуры

После этого реквизит СуммаДокумента просто включается в состав граф журнала через дерево метаданных, и задача решается стандартными средствами платформы.

Проблема разных валют и итогов

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

  1. Выводить рядом с суммой колонку с валютой документа.
  2. Если требуется подвал с общим итогом по журналу, необходимо предварительно приводить все суммы к валюте регламентированного учета в самом запросе динамического списка.

Рассмотрим пример фрагмента запроса с конвертацией:


ВЫБОР
    КОГДА Журнал.Валюта = &ВалютаУчета 
        ТОГДА ВложенныйЗапрос.ИтогоПоТЧ
    ИНАЧЕ ВложенныйЗапрос.ИтогоПоТЧ * КурсВалюты.Курс / КурсВалюты.Кратность
КОНЕЦ КАК СуммаВРегВалюте

Подведение итогов и выбор решения

Выясним, какой метод выбрать в вашем конкретном случае:

1. Если документов немного и не требуется поиск/сортировка по сумме — используйте ПриПолученииДанныхНаСервере. Это самый быстрый в разработке способ, не требующий изменения структуры базы.

2. Если нужно решение "из коробки" с работающими отборами, но вы не хотите добавлять реквизит — используйте произвольный запрос с ЛЕВЫМ СОЕДИНЕНИЕМ. Помните, что это может замедлить открытие журнала при миллионах записей.

3. Если база данных высоконагруженная и сумма является ключевым показателем для работы пользователя — лучше всего добавить физический реквизит и заполнять его при записи документа. Это обеспечит максимальную скорость работы интерфейса.

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

← На главную