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