При разработке и сопровождении решений на платформе 1С:Предприятие разработчики нередко сталкиваются с ситуацией: при выполнении внешней обработки или кода в режиме обычного приложения на строке с вызовом ПолучитьОбъект() возникает исключение. Текст ошибки сообщает о том, что обработчик для подписки на событие (например, ПередЗаписью) не найден, хотя явного вызова метода Записать() в коде не производилось.
Разберем подробнее, почему платформа 1С выполняет проверку обработчиков событий еще до фактической записи объекта и как правильно устранить эту проблему.
Причина поведения платформы:
Когда в программном коде вызывается метод ПолучитьОбъект() у ссылки, платформа 1С выполняет создание экземпляра объекта данных в оперативной памяти (например, ДокументОбъект или СправочникОбъект). В момент конструирования этого объекта выполняется внутренняя инициализация и построение таблицы всех обработчиков событий, назначенных для данного объекта метаданных, включая все зарегистрированные объекты ПодпискаНаСобытие.
Платформа проверяет физическое наличие и доступность методов-обработчиков именно в том контексте исполнения, в котором создается объект. Если обработка запускается в толстом клиенте обычного приложения, а модуль обработчика подписки скомпилирован исключительно с признаком Сервер (без флага Толстый клиент (обычное приложение)), платформа не может сопоставить обработчик в текущем контексте и выбрасывает критическую ошибку валидации еще на этапе вызова ПолучитьОбъект().
Рассмотрим классический способ устранения ошибки при сохранении текущей архитектуры вызовов. Если обработчик подписки должен корректно связываться во всех режимах работы конфигурации, настроим свойства общего модуля, в котором расположена процедура подписки.
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
Процедура ДокументПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Основная логика подписки на событие
КонецПроцедуры
#КонецЕсли
Такой подход обеспечивает успешную инициализацию объекта в любом режиме клиента без риска возникновения ошибок несовместимости контекстов.
Проанализируем второй вариант решения, который считается наиболее архитектурно правильным в конфигурациях с управляемыми и смешанными формами. Если конфигурация работает под управлением клиент-серверной архитектуры, создание и изменение прикладных объектов данных не должно выполняться на стороне клиента.
Перенесем логику взаимодействия с объектом в серверный общий модуль с установленным признаком Вызов сервера. В таком случае вызов ПолучитьОбъект() будет выполнен на сервере 1С:Предприятия, где общий модуль с единственным флагом Сервер доступен штатно и связывание подписок пройдет без ошибок.
Разберем ситуацию, когда вызов ПолучитьОбъект() используется исключительно для изменения движений документа по регистрам накопления, бухгалтерии или сведений. В этом случае создавать тяжелый объект ДокументОбъект избыточно.
Инициализация объекта документа приводит к излишним накладным расходам памяти, блокировкам данных и обязательной проверке всех подписок. Чтобы изменить движения документа без обращения к самому объекту документа, воспользуемся прямой записью набора записей регистра:
// Корректировка движений регистра напрямую по ссылке на регистратор
НаборЗаписей = РегистрыНакопления.ВзаиморасчетыСКонтрагентами.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// Читаем существующие движения при необходимости
НаборЗаписей.Прочитать();
// Выполняем модификацию набора и записываем изменения
НаборЗаписей.Записать();
Такой метод полностью изолирует работу с регистрами от самого документа, не инициирует проверку подписок на события документа и выполняется значительно быстрее.
Если метод ПолучитьОбъект() вызывался исключительно для считывания реквизитов документа или строк его табличных частей, заменим его на объект Запрос.
Чтение данных через запрос не создает объект в памяти, не вызывает процедур валидации обработчиков событий и выбирает из базы данных строго необходимые поля, снижая нагрузку на систему.