Возможно ли получить реквизит управляемой формы без открытия самой формы?

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

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

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

Архитектурная проблема: почему реквизиты формы не хранятся в базе данных

Для начала проанализируем структуру данных управляемого приложения. Реквизиты управляемой формы — это элементы её внутреннего контекста. Они создаются в оперативной памяти сервера приложений и передаются на клиент только на время «жизни» этой конкретной формы. Как только пользователь закрывает окно, контекст формы уничтожается.

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

Попытка вызвать метод ПолучитьФорму() или открыть форму программно ради одного реквизита заставляет платформу выполнять колоссальный объем лишней работы:

  1. Создавать тяжелый объект формы в памяти сервера.
  2. Инициализировать схему компоновки или динамические списки, если они есть на форме.
  3. Выполнять все обработчики событий создания и чтения данных.
  4. Передавать контекст формы между сервером и клиентом.

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

Выясняем причину: где и как заполняется реквизит?

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

  1. Платформа считывает данные объекта из базы данных.
  2. Вызывается обработчик события ПриЧтенииНаСервере. В этот момент сам прикладной объект уже прочитан, и его данные доступны через параметр ТекущийОбъект.
  3. Вызывается обработчик события ПриСозданииНаСервере. Здесь происходит основная программная настройка элементов интерфейса и заполнение дополнительных реквизитов формы.

Как правило, именно в обработчике ПриЧтенииНаСервере разработчики размещают алгоритмы расчета связанных показателей. Посмотрим на типовой пример того, как это выглядит в модуле формы:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Алгоритм расчета процента брака на основе данных документа
    Если ТекущийОбъект.КоличествоВыпуска > 0 Тогда
        ЭтотОбъект.ПроцентБрака = (ТекущийОбъект.КоличествоБрака / ТекущийОбъект.КоличествоВыпуска) * 100;
    Иначе
        ЭтотОбъект.ПроцентБрака = 0;
    КонецЕсли;
КонецПроцедуры

Разобравшись с тем, откуда берутся данные, рассмотрим детальные сценарии решения задачи получения этого показателя без обращения к форме.

Решение 1. Вынос бизнес-логики расчета в модуль менеджера или общий модуль

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

Выполним это по шагам:

  1. Найдем алгоритм расчета в модуле формы (например, в процедуре ПриЧтенииНаСервере).
  2. Создадим экспортную функцию в модуле менеджера нашего документа (или в серверном общем модуле, например, ОбщиеМеханизмыСервер).
  3. Перенесем туда алгоритм вычисления. На вход функция должна принимать ссылку на объект или структуру готовых данных.

Посмотрим на пример реализации такой функции в модуле менеджера документа:


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

Теперь в нашей внешней обработке или отчете мы можем легко получить этот процент в цикле, не открывая форму:


Для Каждого СтрокаДокумента Из СписокДокументов Цикл
    ПроцентБрака = Документы.ВыпускПродукции.РассчитатьПроцентБрака(СтрокаДокумента.Ссылка);
    // Дальнейшая обработка результата
КонецЦикла;

Решение 2. Оптимизация через пакетный запрос (Самый быстрый способ)

Несмотря на то, что Решение 1 избавляет нас от инициализации формы, вызов функции в цикле все равно порождает проблему N+1 запросов к базе данных. Если в списке 1000 документов, система сделает 1000 отдельных запросов к СУБД, что негативно скажется на производительности.

Проанализируем ситуацию: если алгоритм расчета процента брака можно описать математически, мы можем получить данные для всей выборки документов одним-единственным запросом.

Рассмотрим пример пакетного запроса, который выбирает список документов и сразу рассчитывает процент брака:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВыпускПродукции.Ссылка КАК ДокументСсылка,
    |	ВЫБОР
    |		КОГДА ВыпускПродукции.КоличествоВыпуска > 0
    |			ТОГДА (ВыпускПродукции.КоличествоБрака / ВыпускПродукции.КоличествоВыпуска) * 100
    |		ИНАЧЕ 0
    |	КОНЕЦ КАК ПроцентБрака
    |ИЗ
    |	Документ.ВыпускПродукции КАК ВыпускПродукции
    |ГДЕ
    |	ВыпускПродукции.Ссылка В (&МассивСсылок)";

Запрос.УстановитьПараметр("МассивСсылок", МассивДокументов);
РезультатЗапроса = Запрос.Выполнить();

// Выгружаем данные в таблицу значений для быстрой работы в памяти
ТаблицаРезультатов = РезультатЗапроса.Выгрузить();
ТаблицаРезультатов.Индексировать("ДокументСсылка");

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

Решение 3. Работа с дополнительными реквизитами и сведениями (БСП)

Довольно часто разработчики путают реквизиты формы с Дополнительными реквизитами, настроенными в режиме «1С:Предприятие» с помощью Библиотеки стандартных подсистем (БСП). Пользователь видит такие реквизиты непосредственно на форме документа, но в конфигураторе в структуре самого документа их действительно нет.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ДопРеквизиты.Значение КАК ПроцентБрака
    |ИЗ
    |	Документ.ВыпускПродукции.ДополнительныеРеквизиты КАК ДопРеквизиты
    |ГДЕ
    |	ДопРеквизиты.Ссылка = &Ссылка
    |	И ДопРеквизиты.Свойство.Имя = ""ПроцентБрака""";

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ДопСведения.Значение КАК ПроцентБрака
    |ИЗ
    |	РегистрСведений.ДополнительныеСведения КАК ДопСведения
    |ГДЕ
    |	ДопСведения.Объект = &Ссылка
    |	И ДопСведения.Свойство.Имя = ""ПроцентБрака""";

Решение 4. Чтение из настроек формы (Хранилище настроек)

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

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


КлючОбъекта = "Документ.ВыпускПродукции.Форма.ФормаДокумента/СохраняемыеЗначения";
КлючНастройки = "ПроцентБракаПоУмолчанию";
ИмяПользователя = Пользователи.ТекущийПользователь().Имя; // Или конкретное имя

ЗначениеНастройки = ХранилищеОбщихНастроек.Получить(КлючОбъекта, КлючНастройки, , ИмяПользователя);

Если ЗначениеНастройки <> Неопределено Тогда
    ПроцентБрака = ЗначениеНастройки;
КонецЕсли;

Этот способ позволяет извлекать пользовательские настройки мгновенно, не затрагивая интерфейсные модули и не инициализируя саму форму.

Подведем итоги

Мы детально разобрали особенности работы с реквизитами управляемых форм. Главный вывод: никогда не получайте форму объекта ради чтения ее данных в цикле. Стремитесь разделять интерфейс и бизнес-логику. Переносите тяжелые расчеты в модули менеджеров, используйте пакетные запросы СУБД или механизмы БСП. Это позволит вашим решениям работать стабильно, быстро и выдерживать высокие нагрузки при масштабировании систем на платформе «1С:Предприятие 8».

← На главную