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