Как ускорить открытие формы документа с большим количеством табличных частей в 1С?

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

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

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

Выясняем причину медленной отрисовки

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

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

Этот процесс включает в себя несколько ресурсоемких этапов:

  1. Чтение из СУБД: Сервер 1С формирует запрос к базе данных для получения всех данных объекта.
  2. Сериализация на сервере: Весь полученный массив данных (включая тысячи строк из 20 таблиц) упаковывается в специальный формат для передачи на клиент.
  3. Передача по сети: Упакованный объект передается с сервера на клиентское приложение.
  4. Десериализация на клиенте: Клиентское приложение распаковывает полученные данные и размещает их в памяти формы.

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

Архитектурное решение: от табличных частей к регистрам

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

Рассмотрим по шагам, как реализовать это решение.

Шаг 1. Меняем структуру хранения данных

Вместо 20 табличных частей в объекте Документ, создадим один или несколько объектов РегистрСведений. Этот регистр должен быть подчинен нашему документу-регистратору.

Что это дает?

Например, если у нас были табличные части "Товары", "Услуги", "Материалы", мы можем создать один регистр сведений "СоставДокумента" с измерением ТипДанных (перечисление: Товары, Услуги, Материалы) или несколько отдельных регистров, если структура колонок сильно отличается.

Шаг 2. Отображаем данные с помощью динамических списков

Теперь, когда данные хранятся в регистре, нам нужно отобразить их на форме. Вместо элемента ТаблицаФормы, который работает с данными объекта, мы будем использовать ДинамическийСписок.

Ключевое преимущество динамического списка — он не загружает все данные сразу. Вместо этого он работает по принципу "порционного чтения":

  1. При открытии формы динамический список отправляет в базу данных запрос на получение только той порции данных, которая помещается в видимую область таблицы на экране (например, первые 20-30 строк).
  2. Когда пользователь начинает прокручивать таблицу вниз, список автоматически запрашивает следующую порцию данных.

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

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


ВЫБРАТЬ
    МойРегистрСведений.Номенклатура,
    МойРегистрСведений.Количество,
    МойРегистрСведений.Цена,
    МойРегистрСведений.Сумма
ИЗ
    РегистрСведений.МойРегистрСведений КАК МойРегистрСведений
ГДЕ
    МойРегистрСведений.Регистратор = &Ссылка

Параметр запроса &Ссылка будет автоматически заполнен ссылкой на текущий открытый документ.

Шаг 3. Реализуем "отложенную" загрузку данных для разных вкладок

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

Для этого используется механизм отложенной загрузки. Настроим его:

  1. В процедуре-обработчике события формы ПриСозданииНаСервере мы не устанавливаем основной отбор для динамических списков. Вместо этого можно установить заведомо невыполнимое условие (например, Регистратор = НЕОПРЕДЕЛЕНО), чтобы при открытии не было выполнено ни одного запроса.
  2. Создаем обработчик события ПриАктивизацииСтраницы для элемента формы ГруппаСтраницы, на котором расположены наши таблицы.
  3. В этом обработчике мы анализируем, какая страница стала активной, и только для динамического списка на этой странице устанавливаем правильный отбор по регистратору.

Пример кода для отложенной загрузки:


&НаКлиенте
Процедура ПанельТаблицПриАктивизацииСтраницы(Элемент, ТекущаяСтраница)
    // Вызываем серверную процедуру для установки отбора
    УстановитьОтборДляАктивнойСтраницыНаСервере(ТекущаяСтраница.Имя);
КонецПроцедуры

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

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

Пример из типовых конфигураций: Корректировка регистров

Описанный подход не является чем-то экзотическим. Он активно используется в типовых конфигурациях 1С. Яркий пример — документ Корректировка регистров в 1С:ERP. В конфигураторе у этого документа всего одна табличная часть ВыбранныеРегистры. Однако на форме пользователь может добавить любое количество регистров для корректировки, и для каждого из них будет динамически создана своя страница с таблицей движений. Эти таблицы являются динамическими списками, которые "смотрят" напрямую в таблицы соответствующих регистров, обеспечивая высокую производительность.

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

← На главную