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