Почему в 1С можно было вызывать неэкспортные процедуры модуля формы и как правильно вызывать их сейчас?

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

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

Рассмотрим пример такого вызова из подсистемы управления контактной информацией:


// Вызов из клиентского общего модуля:
Форма.Подключаемый_ОбновитьКонтактнуюИнформацию(Результат);

В модуле самой управляемой формы процедура объявлена следующим образом:


&НаСервере
Процедура Подключаемый_ОбновитьКонтактнуюИнформацию(Результат)  
    УправлениеКонтактнойИнформацией.ОбновитьКонтактнуюИнформацию(ЭтотОбъект, Объект, Результат);
КонецПроцедуры

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

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

Проанализируем архитектуру вызова, чтобы понять физику процесса. В платформе «1С:Предприятие 8.3» управляемая форма разделена на клиентскую и серверную части. На клиенте разработчик оперирует объектом-ссылкой УправляемаяФорма, который выступает в роли клиентского прокси.

В версиях платформы до 8.3.12 включительно существовала специфика диспетчеризации контекстных серверных вызовов:

  1. Клиентское обращение к прокси-объекту: При вызове Форма.ИмяМетода() с клиента среда исполнения определяла, что указанный метод не зарегистрирован в таблице клиентских экспортов формы, и автоматически транслировала вызов в контекстный серверный запрос.
  2. Отсутствие валидации на сервере: При получении пакета на стороне сервера платформа восстанавливала серверный контекст формы и направляла вызов напрямую во внутреннюю процедуру модуля формы. На этом этапе валидация флага Экспорт не выполнялась, если метод имел директиву компиляции &НаСервере.
  3. Односторонняя специфика: Вызвать клиентскую процедуру без директивы Экспорт с клиента было нельзя — на клиенте таблица экспортных методов проверялась строго. Также было невозможно вызвать внутренний метод формы из серверного модуля, так как на сервере форма не существует в виде отдельного доступного COM- или API-объекта вне своего контекста.

Таким образом, возможность вызова неэкспортных серверных процедур формы являлась недокументированной особенностью работы прокси-объекта управляемой формы при переходе контекста «клиент — сервер».

Изменения в платформе 8.3.13 и актуальных редакциях БСП

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

Теперь при попытке внешнего обращения к неэкспортной процедуре управляемой формы платформа незамедлительно генерирует штатное исключение:

Метод объекта не обнаружен (<ИмяМетода>)

Вслед за платформой изменилась и библиотека БСП. В версиях БСП 3.0 и выше документация и шаблоны внедрения подсистем были скорректированы: все подключаемые обработчики форм (такие как Подключаемый_ОбновитьКонтактнуюИнформацию, Подключаемый_ВыполнитьКомандуНаСервере и др.) теперь в обязательном порядке содержат ключевое слово Экспорт.

Стандарты разработки 1С и архитектура форм

С точки зрения стандартов разработки фирмы «1С», управляемая форма не должна выступать поставщиком API для внешних механизмов. Модуль формы предназначен исключительно для управления элементами интерфейса и связи пользователя с прикладными данными.

Размещение процедур с директивами &НаСервере Экспорт в модуле формы считается нежелательной практикой по следующим причинам:

  1. Тяжелый контекст: Каждый такой вызов пересылает между клиентом и сервером полный контекст управляемой формы (все реквизиты, элементы управления и параметры), что создает повышенную нагрузку на сеть и сервер.
  2. Сложность тестирования и поддержки: Логика, запертая в модуле формы, не может быть эффективно протестирована модульными автотестами или переиспользована в фоновых заданиях и внешних обработках.

Рекомендованные варианты реализации взаимодействия

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

Способ 1. Явное объявление процедуры экспортной (для дорабатываемых форм)

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


&НаСервере
Процедура Подключаемый_ОбновитьКонтактнуюИнформацию(Результат) Экспорт
    УправлениеКонтактнойИнформацией.ОбновитьКонтактнуюИнформацию(ЭтотОбъект, Объект, Результат);
КонецПроцедуры

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


&НаКлиенте
Процедура ОбновитьДанныеФормы(Результат) Экспорт
    ОбновитьДанныеФормыНаСервере(Результат);
КонецПроцедуры

&НаСервере
Процедура ОбновитьДанныеФормыНаСервере(Результат)
    // Серверная обработка данных без прямого экспорта во внешний мир
КонецПроцедуры

Способ 2. Вынос логики в общие модули или модули менеджеров

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


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

// В модуле формы или внешней обработке:
РасчетЦенСервер.ПересчитатьЦены(Объект.Товары, Новый Структура("Валюта", Объект.Валюта));

Такой код одинаково доступен как для управляемой формы документа, так и для внешних обработок заполнения или регламентных заданий без обращения к контексту открытых окон.

Способ 3. Взаимодействие форм через оповещения

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

  1. В вызывающей форме отправляем событие: Оповестить("ОбновленаКонтактнаяИнформация", Результат, ЭтотОбъект);
  2. В целевой форме обрабатываем событие в штатном обработчике ОбработкаОповещения:
    
    &НаКлиенте
    Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
        Если ИмяСобытия = "ОбновленаКонтактнаяИнформация" Тогда
            ОбновитьКонтактнуюИнформациюНаСервере(Параметр);
        КонецЕсли;
    КонецПроцедуры
    

Использование оповещений полностью устраняет жесткую связность между модулями и исключает ошибки поиска методов.

Способ 4. Доработка типовых форм на поддержке через расширения

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

  1. Подключаем форму объекта к расширению.
  2. Добавляем в расширение собственный экспортный метод либо перехватываем штатную серверную процедуру аннотациями &После или &Вместо:
    
    &Вместо("УдалитьТоварыБезИзмененныхЦенНаСервере")
    Процедура Расш_УдалитьТоварыБезИзмененныхЦенНаСервере()
        // Вызов оригинального метода формы
        ПродолжитьВызов();
        // Дополнительная логика расширения
    КонецПроцедуры
    

Такой подход обеспечивает устойчивость доработок при последующих обновлениях типовой конфигурации и гарантирует корректную работу на актуальных версиях платформы 1С.

← На главную