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