Переход со старых версий платформы (например, «1С:Предприятие 7.7») на управляемое приложение «1С:Предприятие 8.x» часто вызывает трудности у начинающих разработчиков. Одна из самых распространенных проблем — это непонимание разницы между контекстами выполнения кода (клиент и сервер), а также между экранными элементами формы и реальными реквизитами объектов в базе данных.
В этой статье мы подробно разберем, как правильно получать значения реквизитов объектов в модуле менеджера, почему популярный способ получения данных «через точку» является грубой ошибкой, и как корректно передать значение любого флажка или поля с формы в фоновые серверные методы.
Для начала проанализируем ситуацию, с которой часто сталкиваются новички. Попытка напрямую прочитать «флаг на форме» из модуля менеджера всегда будет заканчиваться ошибкой компиляции или выполнения. Выясним причину этого поведения.
Интерфейс платформы «1С:Предприятие 8» строго разделен на уровни:
Модуль менеджера — это класс, который относится к самому прикладному объекту (справочнику, документу и т.д.) и выполняется строго на сервере. Этот модуль описывает логику, общую для всех элементов данного типа (например, регистрацию печати, заполнение по умолчанию или проведение). Модуль менеджера может вызываться системой вообще без открытия каких-либо форм — например, при работе фонового задания, интеграции через API или групповой обработке данных.
Поскольку модуль менеджера ничего не знает о существовании конкретных открытых окон на экране пользователя, напрямую обратиться к элементам формы (например, написать Элементы.МойФлаг.Значение) в нем физически невозможно. Для этого требуются совершенно иные механизмы передачи информации.
В ходе обсуждения на форумах часто предлагают быстрое решение для получения реквизита, например:
Для Каждого Ссылка Из МассивОбъектов Цикл
пЛитраж = Ссылка.Литраж;
Прервать;
КонецЦикла;
Или еще проще:
пЛитраж = МассивОбъектов[0].Литраж;
Рассмотрим подробнее, что происходит на стороне СУБД и сервера приложений, когда мы пишем Ссылка.Литраж. Это классическая ошибка неоптимальной разработки.
Когда платформа встречает конструкцию обращения к реквизиту через точку от ссылки, она выполняет скрытый (неявный) запрос к базе данных. При этом система выбирает не только запрашиваемое поле Литраж, а полностью загружает весь объект целиком со всеми его многочисленными реквизитами, табличными частями и дополнительными свойствами. Полученные данные помещаются в кэш сервера приложений.
Если мы получаем таким образом всего одно число или строку из тяжелого документа (в котором могут быть сотни строк в табличной части «Товары»), система совершает огромный объем лишней работы:
Ниже мы разберем правильные, быстрые и профессиональные способы получения данных.
Если нам необходимо получить значения определенных реквизитов для одного или нескольких объектов (например, документов или элементов справочника), лучшим решением будет написание прямого и легкого запроса. Запрос извлечет из базы данных только те колонки, которые нам действительно нужны, не затрагивая сам объект.
Разберем по шагам пример реализации такого запроса в модуле менеджера:
// Функция получает литраж номенклатуры по переданной ссылке
Функция ПолучитьЛитражНоменклатуры(СсылкаНаОбъект) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Литраж КАК Литраж
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &СсылкаНаОбъект";
Запрос.УстановитьПараметр("СсылкаНаОбъект", СсылкаНаОбъект);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Литраж;
КонецЕсли;
Возврат 0; // Значение по умолчанию, если объект не найден
КонецФункции
В этом примере платформа выполнит точечный и максимально оптимизированный SQL-запрос, который вернет только значение поля Литраж. Данный подход гарантирует высокую скорость работы системы даже под серьезными нагрузками.
Если вы ведете разработку в типовой конфигурации (такой как «1С:ERP», «Управление торговлей», «Бухгалтерия предприятия» или «Комплексная автоматизация»), то в вашем распоряжении уже есть готовые и протестированные инструменты БСП. Разработчики фирмы «1С» заранее написали оптимизированные функции для быстрого чтения полей без лишней нагрузки на СУБД.
Для чтения реквизитов используется общий модуль ОбщегоНазначения.
Вариант А. Получение одного реквизита:
// Быстрое получение значения одного реквизита
ЗначениеЛитража = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаНоменклатуру, "Литраж");
Вариант Б. Получение нескольких реквизитов за один вызов:
Если за один раз требуется прочитать сразу несколько характеристик объекта, функция вернет структуру, где ключами будут имена запрашиваемых полей:
// Получаем структуру с нужными свойствами объекта
ПараметрыНоменклатуры = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаНоменклатуру, "Литраж, Артикул, Код");
// Обращаемся к полученным свойствам структуры
пЛитраж = ПараметрыНоменклатуры.Литраж;
пАртикул = ПараметрыНоменклатуры.Артикул;
Внутри этих функций БСП лежит все тот же оптимизированный механизм динамического построения запросов. Использование БСП сокращает объем вашего кода, делает его более читаемым и защищает от ошибок ручного написания запросов.
Теперь разберем вторую часть проблемы: как все-таки учесть значение флага или настройки, которую пользователь установил на форме, внутри серверного модуля менеджера. Правильный подход заключается в том, чтобы **передать значение флага как параметр** при вызове метода.
Рассмотрим два основных сценария реализации этой задачи.
Обычно в модулях менеджера описывается процедура печати документов. В БСП эта процедура имеет строго определенный формат параметров:
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
Параметр ПараметрыПечати представляет собой структуру. Если на форме печати у вас есть флаг ВыводитьЛитраж, мы должны передать его из формы в эту структуру перед формированием печатной формы:
Код в модуле формы документа (на клиенте/сервере формы):
&НаКлиенте
Процедура ВыполнитьПечать(Команда)
// Подготавливаем структуру параметров печати
ПараметрыПечати = Новый Структура;
// Записываем значение флага с формы (реквизит формы "ФлагВыводитьЛитраж")
ПараметрыПечати.Вставить("ВыводитьЛитраж", Объект.ФлагВыводитьЛитраж);
// Вызываем стандартную команду печати БСП
УправлениеПечатьюКлиент.ВыполнитьКомандуПечати("Документ.РеализацияТоваровУслуг", "МакетЛитраж", МассивСсылок, ЭтотОбъект, ПараметрыПечати);
КонецПроцедуры
Код в модуле менеджера объекта (на сервере):
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Инициализируем переменную значением по умолчанию
ВыводитьЛитраж = Ложь;
// Проверяем, был ли передан параметр с формы, и считываем его
Если ПараметрыПечати <> Неопределено Тогда
ПараметрыПечати.Свойство("ВыводитьЛитраж", ВыводитьЛитраж);
КонецЕсли;
// Используем полученное значение флага в логике формирования макета
Если ВыводитьЛитраж Тогда
// Код вывода колонки с литражом
КонецЕсли;
КонецПроцедуры
Если вы разрабатываете собственную бизнес-процедуру обработки данных в модуле менеджера, просто явно добавьте значение флага в список аргументов метода.
Код в модуле менеджера:
// Экспортная процедура для обработки группы объектов
Процедура ОбработатьЛитражДокументов(МассивДокументов, ИспользоватьОсобыйРежим) Экспорт
// Выполняем обработку с учетом флага ИспользоватьОсобыйРежим
Для Каждого ДокументСсылка Из МассивДокументов Цикл
Если ИспользоватьОсобыйРежим Тогда
// Сложная логика обработки
Иначе
// Простая логика обработки
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Код вызова из формы (передаем состояние элемента формы):
&НаСервере
Процедура ВыполнитьОбработкуНаСервере()
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(Объект.Ссылка);
// Передаем массив документов и значение флага напрямую с формы
Документы.РеализацияТоваровУслуг.ОбработатьЛитражДокументов(МассивСсылок, Объект.ФлагОсобогоРежима);
КонецПроцедуры
Для создания быстрого, стабильного и отказоустойчивого кода в среде 1С всегда следуйте трем золотым правилам работы с реквизитами и формами:
Ссылка.ИмяРеквизита), если есть риск работы с большими объектами или выполнения кода в цикле.ОбщегоНазначения.ЗначенияРеквизитовОбъекта().