Как сделать полную копию незаписанного документа и защитить его от изменений в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке гибких алгоритмов, таких как пользовательские фильтры, правила согласования или динамическая маршрутизация, часто возникает необходимость передать данные еще не записанного в базу документа в процедуру исполнения произвольного алгоритма (например, через функцию Выполнить() или внешний модуль). При этом критически важно гарантировать, что исполняемый алгоритм не сможет случайно или намеренно изменить реквизиты исходного объекта либо записать его в базу данных.

Разберем, почему стандартные механизмы копирования в данном случае не работают напрямую, какие ограничения накладывает платформа 1С:Предприятие и какими способами можно безопасно передать копию незаписанного объекта.

Ограничения стандартных механизмов платформы

Проанализируем типичные ошибки и заблуждения, с которыми сталкиваются разработчики при попытке создать копию объекта в оперативной памяти:

  1. Модификатор передачи параметра Знач: ключевое слово Знач в заголовке процедуры создает физическую копию значения только для простых примитивных типов (Число, Строка, Дата, Булево). Для ссылочных и объектных типов (таких как ДокументОбъект, Структура, ТаблицаЗначений) по значению передается лишь ссылка на экземпляр в памяти. Любое изменение реквизитов или строк табличной части внутри вызываемой процедуры изменит исходный документ.
  2. Метод Скопировать(): данный метод существует только у ссылок (ДокументСсылка), но отсутствует у типа ДокументОбъект. Если документ еще ни разу не был записан в информационную базу, получить ссылку и вызвать данный метод невозможно.
  3. Метод ЗаполнитьЗначенияСвойств(): этот метод переносит только реквизиты шапки с совпадающими именами и типами, но полностью игнорирует табличные части документа.

Способ 1. Преобразование объекта документа в структуру данных

Наиболее надежный и рекомендуемый способ защитить объект от модификации и несанкционированной записи — трансформировать его в обычную структуру. У структуры физически отсутствует метод Записать(), что исключает обращение к базе данных.

Рассмотрим функцию, которая на основе метаданных считывает все реквизиты и табличные части незаписанного объекта, формируя независимую структуру:


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

Если требуется абсолютная защита от изменений даже внутри самой структуры (например, чтобы код не менял поля переданной структуры), значения можно упаковать в неизменяемые типы: ФиксированнаяСтруктура и ФиксированныйМассив.

Способ 2. Глубокое клонирование незаписанного объекта через сериализацию

Если исполняемый код все же требует взаимодействия именно с интерфейсом объекта документа (например, обращение к методам или свойствам через точку), можно создать его точный дубликат в оперативной памяти без записи в базу данных. Для этого применим механизмы сериализации платформы.

Вариант с внутренней сериализацией:


// Создание полного независимого клона объекта в оперативной памяти
КопияДокумента = ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(ИсходныйДокументОбъект));

Вариант через XDTO-сериализатор:


// Клонирование через XML-сериализацию
СериализованныйОбъект = СериализаторXDTO.ЗаписатьXDTO(ИсходныйДокументОбъект);
КопияДокумента = СериализаторXDTO.ПрочитатьXDTO(СериализованныйОбъект);

Оба этих метода создают глубокую копию объекта, включая все реквизиты и табличные части. Если исполняемый алгоритм изменит реквизиты КопияДокумента, исходный экземпляр останется нетронутым.

Способ 3. Защита среды исполнения и архитектурные альтернативы

Если задача заключается в вычислении маршрутов согласования, условий отбора или категорий на основе данных документа, использование Выполнить() несет риски безопасности и производительности. Рассмотрим более надежные практики:

  1. Включение безопасного режима: если запуск пользовательского кода через Выполнить() неизбежен, перед его вызовом устанавливайте безопасный режим с помощью конструкции УстановитьБезопасныйРежим(Истина). Это отключит привилегированные права и предотвратит несанкционированные действия с базой.
  2. Использование Схемы компоновки данных (СКД): данные документа можно передать во внешний набор данных СКД (НаборДанныхОбъект) и вычислять маршрутизацию через пользовательские поля и выражения схемы. Это работает быстро и безопасно.
  3. Таблицы решений в регистрах сведений: правила условий (ЦФО, статья, диапазоны сумм) целесообразно хранить в периодическом регистре сведений с приоритетами условий. В таком случае маршрутизация определяется простым параметризованным запросом к регистру без необходимости писать и выполнять программный код.
← На главную