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