Как реализовать вычисляемую колонку с пересчетом данных в динамическом списке 1С

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

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

Вариант 1. Использование события «ПриПолученииДанныхНаСервере»

Начиная с версии платформы 8.3.10, для динамического списка доступно серверное событие ПриПолученииДанныхНаСервере. Это событие вызывается после того, как порция данных для отображения на клиенте уже получена из базы данных, но до передачи ее в форму. Оно позволяет программно модифицировать данные или добавить значения в произвольные колонки формы.

Рассмотрим по шагам правильный алгоритм работы с данным событием:

  1. В конфигураторе добавляем в динамический список пользовательскую колонку (например, добавив в текст запроса поле-пустышку: ВЫРАЗИТЬ(0 КАК ЧИСЛО(15, 2)) КАК СуммаВРублях).
  2. Для ключевых полей, которые понадобятся для расчета (например, Ссылка, Дата, ВалютаДокумента, СуммаДокумента), устанавливаем свойство ИспользоватьВсегда = Истина в свойствах динамического списка. Если этого не сделать, платформа передаст в коллекцию строк только видимые на экране поля и ключевые поля списка.
  3. В обработчике события выполняем пакетную выборку данных одним запросом для всей переданной порции строк, исключая выполнение запросов в цикле.
  4. Заполняем вычисленные значения в коллекции Строки.

Посмотрим на пример реализации серверного обработчика:


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

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

Вариант 2. Получение среза последних на каждую дату в тексте запроса

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

Для решения этой задачи в запросе соединяются с реальной таблицей регистра сведений:

  1. Выполняют левое соединение таблицы документов с реальной таблицей РегистрСведений.КурсыВалют по условию совпадения валюты и Курсы.Период <= Документы.Дата.
  2. С помощью группировки по документу находят максимальную дату курса: МАКСИМУМ(Курсы.Период).
  3. Повторно соединяют результат с реальной таблицей регистра по валюте и найденному максимальному периоду для получения значений ресурсов Курс и Кратность.

Ограничение по производительности: сложные подзапросы, группировки и множественные соединения реальных таблиц внутри динамического списка могут привести к тому, что платформа отключит постраничную динамическую подгрузку данных (динамическое считывание). В результате СУБД будет сканировать всю таблицу, что вызовет ощутимые задержки и «залипание» формы при прокрутке списка пользователем.

Вариант 3. Архитектурное хранение рассчитанных значений в документе или регистре

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

Рассмотрим рекомендуемые архитектурные решения:

  1. Хранение курса и суммы в реквизитах документа: в документ добавляются реквизиты КурсДокумента, Кратность и СуммаРегл. Они заполняются при выборе валюты/даты или при проведении документа. В этом случае динамический список считывает готовое поле без дополнительных соединений и вычислений, сохраняя максимальное быстродействие и возможность поиска/сортировки.
  2. Использование данных учетных регистров: если документ формирует проводки или движения по регистрам накопления, в них уже содержится готовая сумма в валюте регламентированного учета. В качестве источника данных для динамического списка можно использовать виртуальные таблицы остатков/оборотов или реальные таблицы движений.
  3. Вспомогательный регистр сведений: если объект нельзя изменять напрямую (например, типовая конфигурация на поддержке без возможности редактирования реквизитов), создается непериодический регистр сведений с ведущим измерением Документ и ресурсом СуммаВРублях. Заполнение выполняется через подписку на событие ОбработкаПроведения или ПриЗаписи, а в динамическом списке настраивается простое левое соединение 1:1 по ссылке.
← На главную