Вызов экспортных методов модуля объекта: из формы, общего модуля и обработки

· 9 мин

Содержание

Чтобы обратиться к функции в модуле объекта 1С, объявите её с ключевым словом Экспорт, получите сам объект (не ссылку!) и вызовите метод через точку: ДокОбъект.РассчитатьИтог(). Внутри того же модуля функцию вызывают просто по имени, без Экспорт и без точки.

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

Экспорт и область видимости методов модуля объекта

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

Что видно снаружи, решает одно слово:

  • 🔒 Без Экспорт — процедура или функция служебная. Её можно вызвать только из этого же модуля.
  • 🔓 С Экспорт — метод становится методом объекта. Его видит любой код, у которого в руках экземпляр объекта.
  • 📦 Переменная модуля с Экспорт — Перем КэшНастроек Экспорт; в начале модуля. Она тоже доступна через точку, как свойство: ДокОбъект.КэшНастроек.
// Модуль объекта документа «РеализацияТоваров»

Перем РежимПересчета Экспорт;

Функция РассчитатьИтог() Экспорт
	Возврат Товары.Итог("Сумма") - СуммаСкидкиПоДокументу();
КонецФункции

Функция СуммаСкидкиПоДокументу()
	// служебная: снаружи не видна
	Возврат Товары.Итог("СуммаСкидки");
КонецФункции

Внутри модуля к реквизитам и табличным частям обращаются напрямую: Товары, Дата, Контрагент. Сам экземпляр доступен через ЭтотОбъект — это пригодится, когда объект нужно передать параметром в общий модуль.

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

В типовых конфигурациях текст модуля объекта обычно обёрнут в инструкцию препроцессора:

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	// процедуры и функции модуля
#Иначе
	ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли

Директивы &НаКлиенте и &НаСервере в модуле объекта не используются — они нужны в модулях форм и команд. Место исполнения модуля объекта определяется контекстом, в котором создан экземпляр.

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

Внутри модуля метод вызывают по имени. Префикс ЭтотОбъект. не нужен, хотя и не запрещён для экспортных методов.

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
	СуммаДокумента = РассчитатьИтог();
	Если СуммаДокумента <= 0 Тогда
		Отказ = Истина;
	КонецЕсли;
КонецПроцедуры

Несколько правил, которые экономят время на отладке:

  1. Порядок объявления не важен. В 8.3 можно вызвать функцию, описанную ниже по тексту модуля. Предварительное объявление, как в 7.7, не требуется.
  2. Функцию можно вызвать как процедуру. Строка РассчитатьИтог(); без присваивания допустима — результат просто отбросится.
  3. Процедуру нельзя вызвать как функцию. Х = ЗаполнитьТовары(); для процедуры даст ошибку компиляции Использование процедуры как функции.
  4. Имена уникальны в пределах модуля. Перегрузки по числу параметров в языке 1С нет. Две функции с одним именем — ошибка компиляции.

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

Обращение к функции модуля объекта из другого модуля

Главное правило: методы модуля объекта есть у объекта, а не у ссылки. Тип ДокументСсылка.РеализацияТоваров их не знает. Сначала получите объект одним из способов ниже.

Существующий элемент или документ по ссылке

// общий модуль с флажком «Сервер» или серверная процедура
ДокОбъект = ДокументСсылка.ПолучитьОбъект();
Итог = ДокОбъект.РассчитатьИтог();

ПолучитьОбъект() читает из базы весь объект с табличными частями. Для одного-двух полей это дорого: если нужно только значение реквизита, прочитайте его запросом или через ДокументСсылка.ИмяРеквизита.

Новый объект

НовыйДок = Документы.РеализацияТоваров.СоздатьДокумент();
НовыйДок.Дата = ТекущаяДатаСеанса();
НовыйДок.ЗаполнитьПоУмолчанию(); // экспортная функция модуля объекта
НовыйДок.Записать();

Для справочника — Справочники.Номенклатура.СоздатьЭлемент(), для группы — СоздатьГруппу().

Обработка или отчёт из конфигурации

Обработка = Обработки.ЗагрузкаПрайса.Создать();
Обработка.ИмяФайла = "C:\Обмен\price.xlsx";
Результат = Обработка.ВыполнитьЗагрузку();

У обработки нет записи в базу, поэтому её модуль объекта часто служит «библиотекой» с экспортными функциями. Это самый частый на практике случай.

Внешняя обработка

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

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

Из внешнего соединения

Через COM-соединение (V83.COMConnector) экспортные методы модуля объекта вызываются той же цепочкой: Соединение.Документы.РеализацияТоваров.НайтиПоНомеру(...).ПолучитьОбъект().РассчитатьИтог(). Условие — модуль скомпилирован для внешнего соединения. Если в препроцессорной обёртке нет ВнешнееСоединение, метод окажется недоступен.

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

В управляемой форме реквизит Объект имеет тип ДанныеФормыСтруктура. Это копия данных для клиента, а не сам объект. Методов модуля объекта у неё нет, поэтому Объект.РассчитатьИтог() в клиентской процедуре упадёт с ошибкой Метод объекта не обнаружен.

Рабочая схема — серверная процедура формы, которая превращает данные формы в объект:

&НаКлиенте
Процедура Пересчитать(Команда)
	ПересчитатьНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПересчитатьНаСервере()
	ДокОбъект = РеквизитФормыВЗначение("Объект");
	ДокОбъект.ПересчитатьСкидки();          // меняет табличную часть
	ЗначениеВРеквизитФормы(ДокОбъект, "Объект"); // вернуть изменения в форму
КонецПроцедуры

Три момента, на которых ошибаются чаще всего:

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

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

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

Параметры функции и возвращаемое значение

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

Функция СуммаСНДС(Знач Сумма, СтавкаНДС = 20, ОкруглятьДоРубля = Ложь) Экспорт
	Сумма = Сумма * (100 + СтавкаНДС) / 100;
	Если ОкруглятьДоРубля Тогда
		Возврат Окр(Сумма, 0);
	КонецЕсли;
	Возврат Окр(Сумма, 2);
КонецФункции

Варианты вызова этой функции:

ВызовЧто получится
ДокОбъект.СуммаСНДС(1000)1200 — ставка и округление по умолчанию
ДокОбъект.СуммаСНДС(1000, 10)1100
ДокОбъект.СуммаСНДС(1000, , Истина)1200 — второй параметр пропущен запятой
ДокОбъект.СуммаСНДС(1000, 20, Истина, 5)ошибка: слишком много параметров

Что помнить про параметры:

  • ✅ Параметры со значением по умолчанию ставьте в конец списка. Пропущенный параметр без значения по умолчанию получит Неопределено.
  • ✅ Изменяемые коллекции (ТаблицаЗначений, Массив, Структура) передаются ссылкой на один и тот же экземпляр. Даже с Знач изменение содержимого коллекции — добавление строк, удаление элементов — будет видно снаружи. Знач защищает только от присваивания параметру другого значения.
  • ✅ При вызове с клиента на сервер параметры сериализуются. Передавать можно примитивы, ссылки, массивы, структуры, соответствия. Объект (ДокументОбъект) с клиента на сервер не передаётся.

Возврат значения. Функция заканчивается оператором Возврат Значение;. Если до него дело не дошло, функция вернёт Неопределено. Когда нужно вернуть несколько значений, возвращайте структуру:

Функция ПроверитьОстатки() Экспорт
	Результат = Новый Структура("Успех, Сообщение", Истина, "");
	// ... проверка
	Возврат Результат;
КонецФункции

Для передачи данных между обработчиками записи одного экземпляра есть свойство ДополнительныеСвойства. Его удобно заполнять перед Записать() и читать в ПередЗаписью — без лишних экспортных переменных.

Модуль объекта, менеджера или общий модуль: куда положить функцию

Часто функцию кладут в модуль объекта по привычке, хотя ей не нужен конкретный экземпляр. Тогда каждый вызов тянет ПолучитьОбъект() и лишнее чтение из базы.

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

Модуль менеджера открывается там же, где модуль объекта: Открыть модуль менеджера. Его экспортные функции доступны через Справочники.Имя и Документы.Имя без создания экземпляра. Из формы такую функцию удобно вызывать в процедуре &НаСервереБезКонтекста — серверный вызов получается легче, потому что данные формы не передаются.

Хорошее правило: если функции для работы хватает ссылки и запроса — ей место в модуле менеджера. Если ей нужны несохранённые реквизиты экземпляра — в модуле объекта.

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

Ошибки при вызове функции модуля объекта и их отладка

Диагностика: выберите симптом

Как найти проблему быстро:

  1. Синтаксический контроль. В конфигураторе в модуле нажмите Ctrl+F7. Опечатки в именах локальных функций всплывут сразу. Вызовы через точку синтаксический контроль не проверяет — они разрешаются только во время выполнения.
  2. Точка останова. Поставьте её на строку вызова (F9) и запустите отладку (F5). Наведите курсор на переменную или откройте табло: там видно, это ДокументОбъект или ДокументСсылка.
  3. Шаг внутрь. Клавиша F11 заходит в вызываемую функцию модуля объекта. Если отладчик в неё не заходит, а вызов идёт с клиента, — значит, код исполняется не там, где вы ожидаете.
  4. Отладка на сервере. В клиент-серверной базе для серверного кода должна быть включена отладка на сервере 1С. Иначе точки останова в модуле объекта не сработают.

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

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