При разработке в среде 1С:Предприятие 8.3 на управляемых формах программисты часто сталкиваются с необходимостью выполнить какие-либо действия сразу после записи документа. В современных типовых конфигурациях (таких как УТ 11, ERP 2 или КА 2) часто встречается программный паттерн с использованием структуры ПараметрыЗаписи и ключа ДействиеПослеЗаписи. Однако попытка скопировать этот код в самописную конфигурацию или расширение часто приводит к ошибке: "Ошибка преобразования данных XDTO... Отсутствует отображение для типа ОписаниеОповещения". Разберем подробно, почему это происходит и как правильно организовать логику взаимодействия клиента и сервера.
Давайте проанализируем ситуацию: когда мы вызываем метод Записать(ПараметрыЗаписи) на клиенте, платформа инициирует сложный процесс, который включает в себя передачу данных с клиента на сервер. Объект ПараметрыЗаписи — это структура, которая «путешествует» вместе с вызовом. Она попадает в серверные обработчики событий формы, такие как ПередЗаписьюНаСервере, ПриЗаписиНаСервере и ПослеЗаписиНаСервере.
Проблема заключается в том, что тип данных ОписаниеОповещения является исключительно клиентским. Он содержит ссылку на конкретный экземпляр формы в памяти клиентского приложения и имя процедуры, которую нужно вызвать. Этот объект не может быть сериализован (преобразован в формат XML или JSON) для передачи на сервер. Как только платформа пытается упаковать структуру ПараметрыЗаписи для отправки серверному вызову, она натыкается на «непонятный» для сервера объект и выбрасывает исключение XDTO.
Многих разработчиков сбивает с толку тот факт, что в типовой УТ 11 в документе «Заказ на перемещение» или «Заказ клиента» подобная конструкция работает безошибочно. Рассмотрим пример кода, который кажется рабочим:
ОписаниеОповещения = Новый ОписаниеОповещения("ЗакрытьЗаказФрагмент", ЭтотОбъект);
ПараметрыЗаписи = Новый Структура;
ПараметрыЗаписи.Вставить("ДействиеПослеЗаписи", ОписаниеОповещения);
ПараметрыЗаписи.Вставить("РежимЗаписи", РежимЗаписиДокумента.Проведение);
Если Не Записать(ПараметрыЗаписи) Тогда
Возврат;
КонецЕсли;
Секрет «магии» заключается в использовании Библиотеки стандартных подсистем (БСП). В типовых решениях формы документов не являются «чистыми» — они плотно интегрированы с общими модулями БСП. В момент вызова Записать() управление проходит через перехватчики (события формы), где программный код БСП анализирует содержимое структуры ПараметрыЗаписи. Специальный код находит ключ ДействиеПослеЗаписи, временно извлекает (удаляет) его из структуры перед отправкой на сервер, сохраняет в локальной переменной клиента, а после возврата с сервера — программно выполняет оповещение.
Если в вашей форме нет этой «прослойки» из БСП, попытка передать оповещение напрямую в метод Записать() неизбежно приведет к краху.
Выясним, действительно ли нам нужно передавать оповещение внутрь процесса записи. Если ваша задача — просто выполнить действие после успешной записи, и это действие не требует сложной координации с серверными процедурами формы, мы можем упростить код. Рассмотрим исправленный вариант процедуры:
&НаКлиенте
Процедура Распределить(Команда)
Если Модифицированность Или Не ЗначениеЗаполнено(Объект.Ссылка) Тогда
// Вместо передачи оповещения в Записать, мы просто вызываем Записать()
// Если запись прошла успешно (вернула Истина), вызываем следующий этап
Если Записать() Тогда
РаспределитьНаКлиенте();
КонецЕсли;
Возврат;
КонецЕсли;
РаспределитьНаКлиенте();
КонецПроцедуры
В данном случае нам не нужно ОписаниеОповещения, так как метод Записать() на клиенте является синхронным (в обычном понимании) и возвращает булево значение. Код после него выполнится только после того, как сервер завершит работу.
Если логика требует, чтобы решение о вызове процедуры принималось внутри глубоких обработчиков или если вы используете асинхронные вызовы, можно сохранить ОписаниеОповещения в переменную модуля формы на клиенте вместо помещения её в структуру параметров.
Рассмотрим, как это реализовать по шагам:
ПослеЗаписи вызовем это оповещение и очистим переменную.
Перем ОповещениеПослеЗаписи; // Объявляем на уровне модуля
&НаКлиенте
Процедура ВыполнитьДействие(Команда)
ОповещениеПослеЗаписи = Новый ОписаниеОповещения("ЗавершитьДействие", ЭтотОбъект);
Если Модифицированность Тогда
Записать(); // Здесь параметры записи пустые, ошибки не будет
Иначе
ВыполнитьОбработкуОповещения(ОповещениеПослеЗаписи);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Если ОповещениеПослеЗаписи <> Неопределено Тогда
ТекущееОповещение = ОповещениеПослеЗаписи;
ОповещениеПослеЗаписи = Неопределено; // Очищаем, чтобы не сработало повторно
ВыполнитьОбработкуОповещения(ТекущееОповещение);
КонецЕсли;
КонецПроцедуры
Проанализируем еще один надежный способ, который часто применяется в крупных системах. Вместо передачи сложного объекта ОписаниеОповещения, мы передаем в ПараметрыЗаписи простой тип данных (строку или булево), который сервер «переварит» без проблем.
&НаКлиенте
Процедура ВыполнитьРаспределение(Команда)
ПараметрыЗаписи = Новый Структура;
// Передаем простой флаг-признак
ПараметрыЗаписи.Вставить("НужноРаспределитьПослеЗаписи", Истина);
Записать(ПараметрыЗаписи);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
// Проверяем наличие нашего флага в структуре параметров
Если ПараметрыЗаписи.Свойство("НужноРаспределитьПослеЗаписи")
И ПараметрыЗаписи.НужноРаспределитьПослеЗаписи Тогда
РаспределитьНаКлиенте();
КонецЕсли;
КонецПроцедуры
Этот метод хорош тем, что он полностью безопасен с точки зрения XDTO-сериализации. Строка или Булево значение легко передаются на сервер и возвращаются обратно на клиент в обработчик ПослеЗаписи, позволяя вам сохранить контекст вызова.
Подводя итог, при работе с асинхронными вызовами и записью объектов помните о следующих правилах:
ОбщегоНазначенияКлиент.ОформитьЗаписьОбъекта, которая сама умеет обрабатывать оповещения корректно.Записать() в управляемой форме возвращает Истина/Ложь. В большинстве простых сценариев этого достаточно, чтобы понять, можно ли продолжать выполнение кода.Соблюдение этих правил позволит вам избежать трудноотлаживаемых ошибок при обмене данными между клиентом и сервером и сделает код ваших форм более надежным и предсказуемым.