Модуль объекта в 1С 8.3: в каком контексте работает код и почему не на клиенте

· 9 мин

Содержание

В управляемом приложении 1С 8.3 модуль объекта выполняется на сервере. Дополнительно он доступен во внешнем соединении и в толстом клиенте в режиме обычного приложения. В тонком и веб-клиенте кода модуля объекта на клиенте нет вообще: из клиентской процедуры формы его не вызвать.

Типичная ситуация: программист пишет в форме документа &НаКлиенте вызов Объект.ЗаполнитьТабличнуюЧасть() и получает ошибку «Метод объекта не обнаружен ». Причина в контексте: Объект в форме — это данные формы, а не ДокументОбъект, и место запуска модуля объекта — сервер. Ниже — как устроены контексты, где физически крутится код и как это проверить.

Где физически работает серверный код: три режима

«Сервер» в терминах 1С — это контекст, а не обязательно отдельный компьютер. Где именно выполняется модуль объекта, зависит от варианта работы базы.

Вариант работыГде выполняется серверный код, включая модуль объекта
Клиент-серверныйВ рабочем процессе кластера (rphost) на сервере 1С
Файловый, тонкий или толстый клиентВ процессе клиентского приложения на компьютере пользователя
Файловый, веб-клиентВ процессе веб-сервера, где опубликована база
Внешнее соединение (COM)В процессе, который создал соединение

Из этой таблицы следуют практические выводы:

  • 📁 В файловой базе путь C:\Temp\ в модуле объекта — это папка на машине пользователя. В клиент-серверной — папка на сервере 1С, и прав на неё у пользователя службы может не быть.
  • 🔐 В клиент-серверном режиме код модуля объекта работает от имени учётной записи службы сервера 1С. Отсюда ошибки доступа к сетевым папкам, которые «у меня в файловой работали».
  • 🧪 Ошибка контекста, которая не видна в файловой базе, проявится после перевода базы на сервер. Тестируйте на клиент-серверной копии.

Даже в файловом режиме клиентский и серверный контекст разделены логически. Переменную, объявленную в модуле объекта, из клиентского кода формы вы всё равно не увидите.

Директивы компиляции и инструкции препроцессора в модуле объекта

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

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

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ОбработчикиСобытий

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

	Если ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;

	// Проверки и дозаполнение реквизитов

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

#КонецОбласти

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

Зачем это нужно:

  • ✅ Код не скомпилируется там, где ему не место, и упадёт с понятным текстом, если кто-то создаст объект в толстом клиенте.
  • ✅ Внутри можно дополнительно выделить фрагменты #Если Сервер Тогда, если часть процедур нельзя запускать в обычном приложении на клиенте.
  • ⚠️ Ветка #Иначе срабатывает при компиляции модуля на клиенте, а не при каждом вызове.

Для сравнения — как устроены соседние модули:

МодульГде может выполнятьсяКак управлять контекстом
Модуль объектаСервер, толстый клиент (обычное приложение), внешнее соединениеИнструкции препроцессора
Модуль менеджераТо жеИнструкции препроцессора
Модуль формыКлиент и серверДирективы &НаКлиенте, &НаСервере и др.
Общий модульПо флажкам свойствФлажки «Клиент», «Сервер», «Вызов сервера», «Внешнее соединение»
Модуль набора записейКак модуль объектаИнструкции препроцессора

Тонкий клиент и веб-клиент: почему объект на клиенте недоступен

В тонком и веб-клиенте прикладных объектов нет. Типы ДокументОбъект, СправочникОбъект, ДокументМенеджер на клиенте не существуют. Форма работает с копией данных — ДанныеФормыСтруктура в реквизите Объект.

Отсюда три частых ошибки:

  1. 🚫 Вызов экспортного метода объекта из процедуры &НаКлиенте: Объект.Пересчитать() — метода нет, потому что Объект здесь данные формы.
  2. 🚫 Передача ссылки на клиент с ожиданием, что у неё можно прочитать реквизиты. Ссылка.Контрагент на клиенте не работает : обращение к реквизитам ссылки — это чтение из базы.
  3. 🚫 Попытка вызвать Документы.ПриходТовара.СоздатьДокумент() на клиенте — менеджеры на клиенте тоже недоступны.

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

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

&НаСервере
Процедура ЗаполнитьТоварыНаСервере()

	ДокументОбъект = РеквизитФормыВЗначение("Объект");
	ДокументОбъект.ЗаполнитьТабличнуюЧасть(); // экспортная процедура модуля объекта
	ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");

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

Для такого вызова процедура в модуле объекта должна быть объявлена с ключевым словом Экспорт.

РеквизитФормыВЗначение создаёт объект в памяти сервера, но не записывает его. Изменения попадут в базу только после записи формы пользователем или явного ДокументОбъект.Записать().

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

События модуля объекта: когда и откуда они вызываются

Обработчики модуля объекта — точка входа в его код. Их вызывает платформа на сервере, когда с объектом что-то происходит, независимо от того, откуда пришла команда: из формы, обработки, обмена или регламентного задания.

Основные события:

  • 📝 ОбработкаЗаполнения — при создании нового объекта на основании или с данными заполнения.
  • 🔍 ОбработкаПроверкиЗаполнения — при проверке заполнения перед записью из формы или по вызову ПроверитьЗаполнение().
  • 💾 ПередЗаписью и ПриЗаписи — до и после записи в базу, в той же транзакции.
  • 📊 ОбработкаПроведения и ОбработкаУдаленияПроведения — у документов, формирование и очистка движений.
  • 📄 ПриКопировании — при копировании элемента или документа.
  • 🗑 ПередУдалением — перед непосредственным удалением объекта.

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

  1. ПередЗаписью формы — на клиенте.
  2. ПередЗаписьюНаСервере формы — на сервере, с параметром ТекущийОбъект.
  3. ПередЗаписью модуля объекта — на сервере.
  4. Запись, проведение, ПриЗаписи модуля объекта.
  5. ПриЗаписиНаСервере и ПослеЗаписиНаСервере формы.
  6. ПослеЗаписи формы — снова на клиенте.

Шаги 2–5 идут за один серверный вызов и в одной транзакции: если в ПередЗаписью модуля объекта установить Отказ = Истина, запись отменится, а форма покажет сообщения, выведенные на сервере. Это удобный приём: в ПередЗаписьюНаСервере положите параметр в ТекущийОбъект.ДополнительныеСвойства, а в ПередЗаписью модуля объекта прочитайте его через ДополнительныеСвойства. Так форма передаёт объекту флаг «пересчитать цены», не заводя лишний реквизит.

Проверку Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли; ставьте в начало ПередЗаписью, ПриЗаписи и ПередУдалением. При загрузке через обмен данные должны записаться как есть, без бизнес-проверок. В режиме загрузки платформа не вызывает ОбработкаПроведения и ОбработкаПроверкиЗаполнения, поэтому туда проверку можно не добавлять.

Проверьте себя
Где выполняется ПередЗаписью модуля документа при записи из тонкого клиента в клиент-серверной базе?
Можно ли в модуле объекта использовать директиву &НаКлиенте?
Где окажется файл, записанный в модуле объекта по пути C:\Temp\, в файловой базе с тонким клиентом?

Вызовы сервера и передача данных: где теряется скорость

Каждый переход из &НаКлиенте в &НаСервере — это серверный вызов. В клиент-серверной базе с медленным каналом он стоит десятки миллисекунд, а в веб-клиенте через интернет — ещё больше. Модуль объекта сам по себе вызовов не порождает, но обращение к нему из формы — всегда вызов сервера.

Что замедляет работу:

  • 🐢 Цикл на клиенте, внутри которого на каждой строке вызывается серверная процедура. 200 строк — 200 вызовов.
  • 🐢 &НаСервере там, где не нужен контекст формы. Такая процедура передаёт на сервер и обратно все данные формы, включая табличные части.
  • 🐢 Лишние РеквизитФормыВЗначение и ЗначениеВРеквизитФормы с большой табличной частью: каждое преобразование копирует данные.
  • 🐢 Чтение реквизитов через точку от ссылки в модуле объекта в цикле. Каждое Строка.Номенклатура.ЕдиницаИзмерения — отдельное обращение к базе. Лучше один запрос.

Как ускорить:

  1. Собирайте данные на клиенте в одну структуру и отправляйте на сервер одним вызовом.
  2. Где контекст формы не нужен, используйте &НаСервереБезКонтекста и передавайте только нужные параметры.
  3. Тяжёлую логику держите в модуле объекта или в общем серверном модуле, а форма пусть вызывает её один раз.
  4. Нужные для клиента значения возвращайте функцией или кладите в реквизиты формы заранее, в ПриСозданииНаСервере.
Директива в формеДанные формы на серверКогда выбирать
&НаСервереПередаютсяНужны Объект, реквизиты формы, РеквизитФормыВЗначение
&НаСервереБезКонтекстаНе передаютсяНужно только прочитать или рассчитать по параметрам
&НаКлиентеНаСервереБезКонтекстаНе передаютсяЧистые вычисления без обращения к базе

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

Понять, где выполнится конкретная строка, можно четырьмя способами.

1. Синтаксис-помощник. У каждого типа и метода указано, где он доступен: «Сервер, толстый клиент, внешнее соединение» или «Тонкий клиент, веб-клиент, сервер». Если в списке нет тонкого клиента, на клиенте управляемой формы этим пользоваться нельзя.

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

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

Включение отладки на рабочем сервере требует перезапуска службы сервера 1С. Все сеансы пользователей при этом оборвутся. Делайте это на тестовом сервере или вне рабочего времени.

4. Счётчик вызовов сервера. В Конфигураторе в Сервис → Параметры → Запуск 1С:Предприятия на вкладке дополнительных настроек включите показ количества вызовов сервера. В запущенном клиенте счётчик покажет, сколько обращений даёт одна команда. Для детального разбора используйте замер производительности из меню Отладка: он разделяет клиентское и серверное время.

Перед переносом кода в модуль объекта
Выполнено: 0 / 6

Если код из модуля объекта ведёт себя не так

Короткая диагностика по симптомам, которые чаще всего связаны именно с местом выполнения.

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

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

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