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