Как сохранить несериализуемый объект на управляемой форме и оптимизировать работу со складскими данными

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

В процессе разработки сложных интерфейсов, таких как складские журналы или аналитические панели, программисты часто сталкиваются с необходимостью кэширования данных на стороне сервера между вызовами. Типичная ситуация: мы сформировали сложный запрос с использованием временных таблиц, получили результат и хотим использовать его повторно при навигации пользователя по строкам, не выполняя запрос заново. Однако платформа 1С накладывает жесткое ограничение: такие объекты, как МенеджерВременныхТаблиц, РезультатЗапроса или ВыборкаИзРезультатаЗапроса, являются несериализуемыми. Это означает, что их невозможно передать на клиент или сохранить в реквизите формы. Рассмотрим подробнее, почему это происходит и как элегантно обойти это ограничение, используя современные механизмы платформы 8.3.

Разберем причину ограничения: архитектура «клиент-сервер»

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

Объекты типа МенеджерВременныхТаблиц хранят ссылки на реальные временные таблицы в памяти или в базе данных SQL. Поскольку соединение с БД может быть возвращено в пул или разорвано после завершения вызова, физические таблицы могут быть уничтожены. Именно поэтому платформа запрещает их сериализацию — попытка восстановить такой объект при следующем вызове привела бы к ошибке обращения к несуществующим данным.

Решение 1: Использование временного хранилища для промежуточных данных

Самый распространенный и эффективный способ «эмуляции» сохранения несериализуемого объекта — это выгрузка данных в ТаблицаЗначений и помещение её во временное хранилище. Таблица значений, в отличие от результата запроса, является сериализуемым объектом.

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

  1. При основном формировании данных (нажатие кнопки «Сформировать») выполняем запрос.
  2. Выгружаем результат запроса в таблицу значений.
  3. Помещаем таблицу во временное хранилище, привязывая её к уникальному идентификатору формы.
  4. Сохраняем адрес временного хранилища в реквизите формы (строка).
  5. При активизации строки номенклатуры получаем таблицу из хранилища и накладываем фильтр.

Посмотрим на пример кода для реализации этой логики:


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

&НаСервере
Процедура ПриАктивизацииСтрокиНаСервере(ВыбраннаяНоменклатура)
    // Извлекаем таблицу из хранилища по сохраненному адресу
    ТаблицаДанных = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ТаблицаДанных <> Неопределено Тогда
        // Находим нужные строки через метод НайтиСтроки или используя ПостроительЗапроса
        ПараметрыОтбора = Новый Структура("Номенклатура", ВыбраннаяНоменклатура);
        НайденныеСтроки = ТаблицаДанных.НайтиСтроки(ПараметрыОтбора);
        
        // Заполняем подчиненную таблицу на форме (например, Объект.СерииНоменклатуры)
        Объект.СерииНоменклатуры.Очистить();
        Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
            ЗаполнитьЗначенияСвойств(Объект.СерииНоменклатуры.Добавить(), СтрокаТЗ);
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

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

Решение 2: Динамические списки с параметрами

Если подчиненные таблицы (серии, доп. реквизиты) предназначены только для просмотра, использование динамических списков является «золотым стандартом» платформы 1С. Выясним причину: динамический список не считывает все данные сразу, а запрашивает их порциями, необходимыми для отображения на экране.

Для реализации этого подхода выполним следующие действия:

  1. Добавим на форму реквизиты типа ДинамическийСписок для серий и доп. реквизитов.
  2. В тексте запроса динамического списка пропишем условие: ГДЕ Номенклатура = &ПараметрНоменклатура.
  3. В обработчике ПриАктивизацииСтроки основной таблицы будем программно устанавливать значение параметра.

Рассмотрим пример настройки параметров:


&НаКлиенте
Процедура СписокНоменклатураПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.СписокНоменклатура.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        УстановитьПараметрыСписков(ТекущиеДанные.Ссылка);
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура УстановитьПараметрыСписков(СсылкаНоменклатура)
    // Установка параметра для динамического списка серий
    СписокСерий.Параметры.УстановитьЗначениеПараметра("ПараметрНоменклатура", СсылкаНоменклатура);
    
    // Установка параметра для динамического списка доп. реквизитов
    СписокДопРеквизитов.Параметры.УстановитьЗначениеПараметра("ПараметрНоменклатура", СсылкаНоменклатура);
КонецПроцедуры

Для минимизации задержек при быстром перемещении по списку рекомендуется использовать обработчик ожидания (ПодключитьОбработчикОжидания), чтобы обновление подчиненных списков происходило через 0.3–0.5 секунды после остановки пользователя на конкретной строке.

Решение 3: Программное дозаполнение через ПриПолученииДанныхНаСервере

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

Проанализируем преимущества: мы не тянем из базы 100 000 строк серий, а получаем только 20-30 строк для текущей страницы экрана. Это значительно снижает нагрузку на сеть и сервер БД.

Решение 4: Декомпозиция запросов и индексация

Зачастую проблема «медленного формирования» кроется в попытке получить всё одним огромным запросом (так называемая «портянка»). Если мы разбиваем один тяжелый запрос на несколько легких, выполняемых «по требованию», общая отзывчивость интерфейса повышается.

Важные рекомендации по оптимизации запросов:

Решение 5: Фоновые задания для длительных расчетов

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

Рассмотрим схему работы:

  1. Пользователь нажимает «Сформировать».
  2. Сервер запускает фоновое задание через ФоновыеЗадания.Выполнить().
  3. На клиенте запускается обработчик ожидания, который проверяет статус задания.
  4. По завершении фоновое задание помещает результат (таблицу или структуру) во временное хранилище.
  5. Форма подхватывает адрес и отображает данные.

Это позволяет избежать блокировки интерфейса и сообщения «Программа не отвечает».

Итоги и выбор стратегии

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

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

← На главную