К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как обратиться из модуля формы в модуль объекта в 1С

Способы вызова процедур и функций модуля объекта из модуля формы в 1С 8.3: РеквизитФормыВЗначение, ДанныеФормыВЗначение, контекстные вызовы и типичные ошибки.

Задача вызвать процедуру или функцию модуля объекта из модуля формы возникает почти в каждой доработке: нужно заполнить табличную часть по алгоритму, проверить данные перед записью или выполнить расчёт, который логично хранить рядом с объектом, а не с формой. При этом напрямую обратиться к контексту модуля объекта из формы нельзя — это разные контексты исполнения, и платформа сознательно их разделяет.

Причина в архитектуре управляемых форм. Модуль формы работает с данными формы — специальным объектом, который сериализуется и передаётся между клиентом и сервером. Модуль объекта (например, ДокументОбъект или СправочникОбъект) работает с прикладным объектом, который существует только на сервере в момент выполнения серверного вызова. Чтобы «достучаться» до методов объекта, форму нужно сначала преобразовать в объект, вызвать метод, а при необходимости — вернуть изменения обратно в форму.

Ниже разобраны рабочие способы такого обращения, их ограничения на тонком и веб-клиенте, типичные ошибки вроде «Процедура или функция с указанным именем не определена» и критерии выбора между вызовом метода объекта и переносом логики в общий модуль.

Почему из формы нельзя напрямую вызвать модуль объекта

Модуль формы и модуль объекта компилируются и исполняются в разных контекстах. Управляемая форма существует и на клиенте, и на сервере, а её данные представлены типами вроде ДанныеФормыСтруктура. Модуль объекта существует только на сервере и только в момент, когда создан экземпляр прикладного объекта — например, через СоздатьОбъект() или получение объекта по ссылке.

Поэтому конструкция вида Объект.МояПроцедура() в модуле формы не сработает так, как ожидает разработчик: реквизит формы «Объект» — это не ДокументОбъект, а данные формы, у которых нет методов модуля объекта. Попытка вызвать метод приведёт либо к ошибке компиляции, либо к ошибке исполнения «Метод объекта не обнаружен».

Единственный корректный путь — получить настоящий прикладной объект из данных формы, выполнить нужный метод в его контексте и при необходимости вернуть результат. Для этого платформа предоставляет пару методов: РеквизитФормыВЗначение и ЗначениеВРеквизитФормы.

Базовый способ: РеквизитФормыВЗначение и вызов метода объекта

Стандартный сценарий выглядит так. В модуле формы создаётся серверная процедура (с директивой &НаСервере), внутри которой данные формы преобразуются в объект, у объекта вызывается нужный метод, а изменённый объект возвращается в форму:

&НаСервере

Процедура ЗаполнитьПоАлгоритмуНаСервере()

ОбъектДокумента = РеквизитФормыВЗначение("Объект");

ОбъектДокумента.ЗаполнитьТоварыПоОстаткам();

ЗначениеВРеквизитФормы(ОбъектДокумента, "Объект");

КонецПроцедуры

Чтобы вызов прошёл, процедура ЗаполнитьТоварыПоОстаткам в модуле объекта должна быть экспортной — перед ней указывается ключевое слово Экспорт. Без него метод доступен только внутри модуля объекта, и из формы вы получите ошибку «Процедура или функция с указанным именем не определена».

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

Порядок действий
  1. 1
    Откройте модуль объекта и добавьте нужную процедуру или функцию с ключевым словом Экспорт
  2. 2
    В модуле формы создайте серверную процедуру с директивой &НаСервере
  3. 3
    Преобразуйте данные формы: ОбъектДокумента = РеквизитФормыВЗначение("Объект")
  4. 4
    Вызовите метод: ОбъектДокумента.ИмяМетода()
  5. 5
    Если объект изменился, верните данные: ЗначениеВРеквизитФормы(ОбъектДокумента, "Объект")
  6. 6
    Проверьте в пользовательском режиме, что форма отображает актуальные данные

Как вернуть результат из модуля объекта в форму

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

&НаСервере

Функция ПроверитьЗаполнениеПоПравилам()

ОбъектДокумента = РеквизитФормыВЗначение("Объект");

Возврат ОбъектДокумента.ПроверитьПоВнутреннимПравилам();

КонецФункции

Возвращать из серверной процедуры формы на клиент можно только сериализуемые типы: примитивы, структуры, массивы, таблицы значений через соответствующие обёртки. Сам прикладной объект (ДокументОбъект) на клиент передать нельзя — он не сериализуется. Если метод объекта должен вернуть сложные данные, упакуйте их в структуру.

Для табличных частей учитывайте, что ЗначениеВРеквизитФормы полностью заменяет данные формы данными объекта. Если пользователь в этот момент редактировал строку, несохранённые в данные формы изменения могут потеряться. Поэтому серверный вызов с перезаполнением лучше выполнять до начала ручного редактирования или предупреждать пользователя.

Ограничения на клиенте и директивы компиляции

РеквизитФормыВЗначение и ЗначениеВРеквизитФормы доступны только на сервере. В процедурах с директивой &НаКлиенте эти методы вызовут ошибку. Поэтому типовой шаблон такой: клиентская процедура (обработчик команды или кнопки) вызывает серверную, а та уже работает с объектом.

Директива &НаСервереБезКонтекста для этой задачи не подходит: в бесконтекстных серверных процедурах недоступны данные формы и методы РеквизитФормыВЗначение. Там объект придётся получать по ссылке через Ссылка.ПолучитьОбъект(), что допустимо только для уже записанных объектов и создаёт отдельный экземпляр, не связанный с формой.

Ещё одно ограничение касается толстого клиента и обычных форм: там модуль объекта и модуль формы тоже разделены, но в обычной форме доступен метод ПолучитьОбъект(), который возвращает объект напрямую. В управляемых формах этого метода нет — только преобразование данных формы.

Сравнение вариантов

РеквизитФормыВЗначение в форме

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

ПолучитьОбъект() по ссылке

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

Общий модуль

Подходит, если логика нужна и форме, и другим потребителям; объект туда передаётся параметром, но вызов всё равно только на сервере.

Модуль менеджера

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

Альтернатива: перенести логику в общий модуль

Не всегда метод обязан жить в модуле объекта. Если процедура не использует внутреннее состояние объекта, а принимает данные параметрами, разумнее разместить её в общем серверном модуле. Тогда из формы достаточно одного серверного вызова общего модуля без преобразования данных формы в объект.

Критерий выбора простой: если логика неразрывно связана с объектом (заполнение по правилам документа, проверка его реквизитов, пересчёт табличных частей) и вызывается также при программной записи, обмене, проведении — ей место в модуле объекта. Если логика универсальна и нужна разным объектам или только форме — ей место в общем модуле.

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

Маршрут решения

Как выбрать действие

Метод работает с реквизитами и табличными частями конкретного объектаРазместите экспортный метод в модуле объекта и вызывайте через РеквизитФормыВЗначение
Логика универсальна и не привязана к одному виду объектовВынесите её в общий серверный модуль и вызывайте напрямую из формы
Нужно обработать уже записанный объект без открытой формыИспользуйте Ссылка.ПолучитьОбъект() в серверной процедуре или общем модуле
Код нужен на клиенте без серверного вызоваМетоды модуля объекта недоступны — перенесите расчёт в клиентский общий модуль или в модуль формы

Типичные ошибки и их признаки

Ошибка «Процедура или функция с указанным именем не определена» почти всегда означает одно из двух: в модуле объекта метод объявлен без Экспорт, либо разработчик пытается вызвать метод у реквизита формы, а не у преобразованного объекта. Проверяйте оба места: сигнатуру в модуле объекта и наличие РеквизитФормыВЗначение перед вызовом.

Вторая частая ситуация — изменения «не сохраняются»: метод объекта отработал, но форма показывает старые данные. Причина в отсутствии ЗначениеВРеквизитФормы после вызова метода. Симметричная ошибка — разработчик вызвал ЗначениеВРеквизитФормы, но забыл, что метод объекта ничего не изменил, потому что у объекта был вызван не тот метод или объект не был модифицирован.

Третья группа ошибок связана с модифицированностью. После ЗначениеВРеквизитФормы форма обычно становится модифицированной, и при закрытии пользователь получит вопрос о сохранении. Если преобразование выполняется при открытии формы только для чтения данных, это раздражает пользователей — в таких случаях либо не возвращайте объект в форму, либо сбрасывайте модифицированность осознанно.

Типичные ошибки
01
Вызов метода без Экспорт в модуле объекта — ошибка «Процедура или функция не определена»
02
Вызов метода у реквизита формы «Объект» вместо преобразованного объекта — ошибка «Метод объекта не обнаружен»
03
Пропуск ЗначениеВРеквизитФормы после изменений — форма показывает устаревшие данные
04
Использование РеквизитФормыВЗначение в &НаКлиенте или &НаСервереБезКонтекста — метод недоступен
05
Попытка вернуть ДокументОбъект на клиент — объект не сериализуется, возвращайте структуру

Как проверить результат и не сломать данные

После внедрения вызова проверьте три сценария. Первый — работа с новым незаписанным объектом: преобразование РеквизитФормыВЗначение корректно работает и для него, но метод объекта не должен внутри себя обращаться к ссылке как к существующей в базе (например, строить запросы по ссылке), иначе для нового объекта результаты будут пустыми.

Второй сценарий — запись и проведение. Убедитесь, что тот же метод объекта корректно отрабатывает при стандартной записи документа, а не только при вызове из формы. Третий — повторный вызов: нажатие кнопки дважды не должно задваивать строки или суммы, если метод не идемпотентен — добавьте очистку табличной части или проверку внутри метода.

Перед изменением типового модуля объекта в конфигурации на поддержке оцените последствия: доработка усложнит обновление. Предпочтительные варианты — расширение конфигурации (в расширении можно добавить свой модуль объекта с экспортными методами) или вынос логики в общий модуль без изменения типового кода.

Контрольные точки

Как проверить результат

Компиляция модуля формы
Норма

Ошибок нет, метод объекта найден

Тревожный признак

«Процедура или функция не определена» — нет Экспорт или вызов идёт не у объекта

Отображение данных после вызова
Норма

Форма показывает изменённые реквизиты и табличные части

Тревожный признак

Данные старые — пропущен ЗначениеВРеквизитФормы

Запись объекта после вызова
Норма

Запись проходит без конфликта версий

Тревожный признак

Ошибка «данные изменены» — объект записывался в обход формы

Повторный вызов метода
Норма

Результат идентичен, без дублирования строк

Тревожный признак

Строки и суммы задваиваются — метод не очищает данные перед заполнением

Частые вопросы

Можно ли вызвать модуль объекта с клиента без серверной процедуры?

Нет. Методы прикладного объекта доступны только на сервере. Минимальная схема: клиентская процедура формы вызывает серверную, внутри неё выполняется РеквизитФормыВЗначение и вызов метода объекта. Полностью избежать серверного вызова нельзя.

Чем отличается РеквизитФормыВЗначение от ДанныеФормыВЗначение?

РеквизитФормыВЗначение — метод формы, который преобразует реквизит по имени и работает с учётом настроек формы. ДанныеФормыВЗначение — глобальный метод, принимающий данные формы как параметр. Для основного реквизита «Объект» чаще используют РеквизитФормыВЗначение; ДанныеФормыВЗначение удобен при работе с произвольными реквизитами составных типов.

Работает ли этот подход для нового, ещё не записанного документа?

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

Как вызвать метод объекта из формы списка или формы выбора?

В форме списка нет реквизита «Объект», поэтому преобразование данных формы не подходит. Для выделенной записи получите ссылку из текущей строки списка, затем в серверной процедуре вызовите Ссылка.ПолучитьОбъект() и метод объекта. Учтите, что если этот объект открыт в другой форме, конфликтов при записи не избежать без дополнительной обработки.

Можно ли добавить метод в модуль объекта через расширение?

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

Почему после вызова метода форма стала модифицированной, хотя ничего не менялось?

ЗначениеВРеквизитФормы заменяет данные формы, и платформа считает это изменением. Если преобразование использовалось только для чтения, не выполняйте обратное преобразование — вызывайте метод-функцию, получайте результат и оставляйте данные формы нетронутыми.

Итог

Что важно запомнить

Прямого вызова модуля объекта из модуля формы не существует: на сервере данные формы преобразуют в объект через РеквизитФормыВЗначение, вызывают экспортный метод и возвращают изменения через ЗначениеВРеквизитФормы. Метод в модуле объекта должен быть с Экспорт, универсальную логику лучше выносить в общий модуль, а запись объекта выполнять штатно через форму, чтобы избежать конфликта версий.