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

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

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

Рассмотрим на конкретном примере, как добавить колонку с суммой проводок в список документов "Требование-накладная" в конфигурации "1С:Бухгалтерия 3.0", и разберем, как избежать "тормозов" при работе с динамическим списком и виртуальными таблицами регистров.

Анализ проблемы: почему запрос работает очень медленно?

Исходная задача — в форму списка документов Требование-накладная добавить колонку "Сумма", которая будет показывать итог по кредитовым оборотам проводок, сформированных документом. Для этого был изменен запрос динамического списка. Сначала во временную таблицу собирались обороты, а затем она присоединялась к основной таблице документов.

Давайте посмотрим на первоначальный вариант запроса для временной таблицы:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ХозрасчетныйОбороты.Регистратор КАК ТребованиеНакладная,
    СУММА(ЕСТЬNULL(ХозрасчетныйОбороты.СуммаОборотКт, 0)) КАК СуммаОборотКт
ПОМЕСТИТЬ ВТ_Обороты
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(&ДатаДокумента, &ДатаДокумента, Регистратор, ) КАК ХозрасчетныйОбороты
СГРУППИРОВАТЬ ПО
    ХозрасчетныйОбороты.Регистратор
ИНДЕКСИРОВАТЬ ПО
    ТребованиеНакладная

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

  1. Отсутствие фильтра по типу регистратора. Условие Регистратор, в параметрах виртуальной таблицы означает, что система будет анализировать движения, сделанные абсолютно всеми документами в базе, которые делают проводки по регистру Хозрасчетный. Это могут быть поступления, реализации, списания и т.д. Объем данных огромен.
  2. Отсутствие фильтров по счетам и периоду. В параметрах виртуальной таблицы Обороты() не указаны конкретные счета или адекватный период. Из-за этого система пытается поднять данные за весь период существования информационной базы по всем счетам.

В итоге, при каждом открытии или обновлении формы списка система вынуждена была пересчитывать гигантскую временную таблицу, что и приводило к длительным задержкам.

Решение 1: Оптимизация запроса в динамическом списке

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

Шаг 1. Ограничение по типу регистратора.

Самое главное — указать системе, что нас интересуют движения только одного вида документов. Для этого в запрос к виртуальной таблице нужно добавить условие ГДЕ с проверкой типа регистратора.

Шаг 2. Ограничение по счетам.

Если вы знаете, по каким конкретно счетам делает движения документ (например, по счетам учета материалов из табличной части), обязательно добавьте фильтр по ним. Это еще сильнее сократит объем данных для анализа.

С учетом этих правок, оптимизированный запрос для временной таблицы будет выглядеть так:


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

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

Решение 2: Архитектурно правильный подход через регистр сведений

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

Разберем этот подход по шагам:

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

Преимущества этого подхода:

Важное замечание: побочные эффекты

При изменении запросов типовых динамических списков, особенно через расширения, всегда есть риск нарушить работу связанного функционала. В данном случае выяснилось, что после доработки перестал формироваться отчет "Реестр документов" для требований-накладных.

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

Всегда проводите комплексное тестирование после подобных доработок! Проверяйте не только работу самой формы, но и связанных отчетов и обработок.

← На главную