Почему возникает ошибка "Отсутствует отображение для типа" при передаче клиентских объектов внешних компонент в серверные процедуры 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Розница Управленческий учет Торговля и дистрибуция
← К списку

При разработке решений на платформе 1С:Предприятие мы часто сталкиваемся с необходимостью взаимодействия между клиентской и серверной частями приложения. Одной из распространенных проблем, вызывающих затруднения, является ошибка "Ошибка отображения типов: Отсутствует отображение для типа...", которая возникает при попытке передать с клиента на сервер объект, специфичный только для клиентского окружения. Давайте вместе разберем эту ситуацию и найдем эффективное решение.

Разберем ситуацию: клиентский объект на сервере

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

Суть проблемы: После получения этих данных на клиенте, мы пытаемся передать переменную ПараметрыККТ в серверную процедуру, например, ДанныеККМ.ЗаписатьДанныеКассыВРегистр(ПараметрыККТ). В этот момент платформа 1С выдает ошибку, указывая, что для типа, связанного с внешним компонентом (например, AddIn.С2acc436546435442ac7fdb42e47acbf.ATOL_KKT_1C_V10), отсутствует отображение на сервере. Сама серверная процедура при этом даже не начинает свое выполнение.

Посмотрим на пример кода, где возникла проблема:

Клиентская часть (фрагмент общего модуля ПодключаемоеОборудованиеДрайверСинхронноКлиент с галкой "Клиент"):


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

Серверная часть (фрагмент общего модуля ДанныеККМ с галкой "Вызов сервера" или "Сервер"):


Процедура ЗаписатьДанныеКассыВРегистр(Знач ПараметрыККТ) Экспорт 
   // Сюда выполнение даже не доходит, ошибка возникает раньше
   А=1; // Эта строка не выполняется
			
КонецПроцедуры

Выясним причину ошибки

Причина этой ошибки кроется в архитектуре платформы 1С:Предприятие, которая является клиент-серверной. Клиентское приложение и сервер 1С — это два отдельных процесса, которые могут работать на разных компьютерах. Для обмена данными между ними используется механизм сериализации.

  1. Контекст выполнения: Объекты, созданные на клиенте (например, экземпляры внешних компонент, элементы управляемых форм), существуют только в контексте клиентского приложения. Сервер 1С не имеет доступа к ним напрямую.
  2. Сериализация данных: При вызове серверной процедуры из клиентского кода, платформа пытается сериализовать (преобразовать в поток байтов) все параметры, передаваемые на сервер. Сервер затем десериализует эти данные.
  3. Отсутствие отображения типов: Если в параметрах передается объект, который является специфичным для клиента (как объект внешней компоненты AddIn), платформа на сервере не может найти соответствующее "отображение" или описание для этого типа. Сервер просто не "знает", что это за объект, поскольку внешняя компонента, как правило, установлена и зарегистрирована только на клиентской машине. Это и приводит к ошибке "Ошибка отображения типов: Отсутствует отображение для типа...".

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

Как решить проблему: Передача сериализуемых данных

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

Рассмотрим подробнее шаги, которые мы предпримем для исправления ситуации:

  1. Получение данных на клиенте: Мы по-прежнему взаимодействуем с внешней компонентой на стороне клиента, получаем от нее все необходимые данные (версии прошивки, заводские номера, ИНН кассы и т.д.).
  2. Преобразование данных в структуру: Вместо того чтобы передавать сам объект или его внутреннее представление, мы извлекаем из него только необходимые для сервера значения и помещаем их в простую структуру 1С. Эта структура будет содержать только сериализуемые типы (строки, числа и т.д.).
  3. Передача структуры на сервер: Мы передаем эту "очищенную" структуру на сервер. Платформа без проблем сериализует и десериализует такую структуру.
  4. Обработка данных на сервере: Серверная процедура получает структуру и может использовать содержащиеся в ней данные для своих целей, например, для записи в регистр.

Пример кода реализации

Давайте посмотрим, как это выглядит на практике. Мы модифицируем клиентскую часть, чтобы подготовить данные, и серверную часть, чтобы их принять.

1. Клиентская часть: извлечение и упаковка данных

В клиентском общем модуле (например, ПодключаемоеОборудованиеДрайверСинхронноКлиент) после получения данных от внешней компоненты ОбъектДрайвера, мы создадим новую структуру и заполним ее необходимыми параметрами.


// ... в процедуре общего модуля ПодключаемоеОборудованиеДрайверСинхронноКлиент
// ... после успешного получения ПараметрыККТ от внешнего компонента
// (где ПараметрыККТ - это переменная, содержащая данные, полученные от драйвера)

// Создаем структуру для передачи на сервер
ПередаваемыеПараметрыККТ = Новый Структура();

// Предположим, что ПараметрыККТ содержит нужные нам поля,
// или мы можем извлечь их из ОбъектДрайвера.
// Например, если ПараметрыККТ - это уже структура, но с потенциально несериализуемыми полями
// или если нам нужно извлечь конкретные данные:

// Пример извлечения данных из ПараметрыККТ (если это структура, полученная от ВК)
Если ТипЗнч(ПараметрыККТ) = Тип("Структура") Тогда
    Для Каждого КлючЗначение Из ПараметрыККТ Цикл
        // Проверяем, что значение сериализуемо (строка, число, булево, дата)
        // и добавляем в новую структуру
        Если ТипЗнч(КлючЗначение.Значение) = Тип("Строка") ИЛИ
           ТипЗнч(КлючЗначение.Значение) = Тип("Число") ИЛИ
           ТипЗнч(КлючЗначение.Значение) = Тип("Булево") ИЛИ
           ТипЗнч(КлючЗначение.Значение) = Тип("Дата") Тогда
            ПередаваемыеПараметрыККТ.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
        Иначе
            // Если есть несериализуемые типы, их нужно преобразовать или исключить.
            // Например, для сложных объектов можно взять их строковое представление.
            // В данном случае, если ПараметрыККТ содержит ссылку на ОбъектДрайвера,
            // мы ее просто не будем добавлять в ПередаваемыеПараметрыККТ.
            // Для примера, возьмем строковое представление, если это не объект драйвера
            Если ТипЗнч(КлючЗначение.Значение) <> Тип("AddIn") Тогда
                ПередаваемыеПараметрыККТ.Вставить(КлючЗначение.Ключ, Строка(КлючЗначение.Значение));
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
Иначе
    // Если ПараметрыККТ - это не структура, а, например, просто ссылка на клиентский объект,
    // то нам нужно извлечь данные напрямую из ОбъектДрайвера или ПараметрыККТ
    // (если ПараметрыККТ уже содержит нужные данные в простом виде).
    // Допустим, нам нужны ВерсияФН и ЗаводскойНомер из ОбъектДрайвера
    Попытка
        // Здесь мы предполагаем, что ОбъектДрайвера имеет методы/свойства для получения этих данных
        ПередаваемыеПараметрыККТ.Вставить("ВерсияФН", ОбъектДрайвера.ПолучитьВерсиюФН());
        ПередаваемыеПараметрыККТ.Вставить("ЗаводскойНомер", ОбъектДрайвера.ПолучитьЗаводскойНомер());
        // ... и другие нужные параметры
    Исключение
        // Обработка ошибок получения данных от драйвера
    КонецПопытки;
КонецЕсли;

// Теперь вызываем серверную процедуру, передавая нашу новую структуру
// Мы используем внеконтекстный вызов, чтобы явно управлять передаваемыми параметрами.
// Предполагаем, что ДанныеККМ - это серверный общий модуль.
ДанныеККМ.ЗаписатьДанныеКассыВРегистр(ПередаваемыеПараметрыККТ);

// ...

2. Серверная часть: прием и обработка структуры

Серверная процедура в общем модуле ДанныеККМ теперь будет принимать структуру с простыми типами данных. Обратите внимание на использование ключевого слова Знач для параметров, чтобы избежать передачи по ссылке и возможных проблем с мутабельными значениями.


// В общем модуле ДанныеККМ (с галкой "Сервер" или "Вызов сервера")

Процедура ЗаписатьДанныеКассыВРегистр(Знач ПараметрыККТСтруктура) Экспорт 
	
	// Теперь мы успешно получаем структуру на сервере
	// и можем использовать ее поля для записи в регистр или других операций.
	
	// Пример записи в регистр сведений
	Попытка
		// Мы можем обращаться к полям структуры по их именам
		ВерсияФН        = ПараметрыККТСтруктура.ВерсияФН;
		ЗаводскойНомер  = ПараметрыККТСтруктура.ЗаводскойНомер;
		// ... другие параметры
		
		// Например, запишем в регистр сведений "НастройкиККТ"
		НаборЗаписей = РегистрыСведений.НастройкиККТ.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.КлючКассы.Установить(ПараметрыККТСтруктура.КлючКассы); // Если есть ключ кассы в структуре
		НаборЗаписей.Прочитать();
		
		Если НаборЗаписей.Количество() = 0 Тогда
			НоваяЗапись = НаборЗаписей.Добавить();
		Иначе
			НоваяЗапись = НаборЗаписей[0];
		КонецЕсли;
		
		НоваяЗапись.ВерсияФискальногоНакопителя = ВерсияФН;
		НоваяЗапись.ЗаводскойНомерККТ = ЗаводскойНомер;
		// ... заполняем другие поля регистра
		
		НаборЗаписей.Записать();
		
	Исключение
		// Обработка ошибок записи в регистр
		ОписаниеОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		// Можно записать ошибку в журнал регистрации
		ЗаписатьСобытиеЖурнала("ЗаписьПараметровККТ", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки);
		ВызватьИсключение "Ошибка при записи параметров ККТ: " + ОписаниеОшибки;
	КонецПопытки;
	
КонецПроцедуры

Таким образом, мы успешно обходим проблему с передачей клиентских объектов на сервер, используя промежуточную структуру с сериализуемыми данными. Этот подход обеспечивает надежное и корректное взаимодействие между клиентской и серверной частями вашего приложения 1С.

Дополнительные рекомендации

Мы надеемся, что это подробное объяснение поможет вам избежать подобных ошибок и строить более надежные и эффективные решения на платформе 1С:Предприятие!

← К списку