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