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