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