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