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

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

В процессе разработки в среде «1С:Предприятие 8» часто возникает задача синхронизации документов. Представим ситуацию: при определенных условиях создается копия некоторого документа-источника. В дальнейшем пользователь или система может изменить исходный документ. Нам необходимо сделать так, чтобы все изменения оперативно и корректно переносились в уже существующую копию. При этом важно не нарушить ссылочную целостность базы данных и обеспечить высокую скорость работы алгоритма.

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

Почему идея «удалить копию и создать заново» ошибочна?

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

  1. Потеря уникального идентификатора (UUID): При удалении документа безвозвратно теряется его уникальная ссылка в базе данных.
  2. Появление битых ссылок: Если на этот документ-копию уже успели сослаться другие объекты системы (например, подчиненные документы, бизнес-процессы, задачи или записи регистров), то после удаления вместо ссылки они будут отображать неприятный текст «<Объект не найден>».
  3. Проблемы с историей и версионированием: Если в системе включено версионирование объектов, история изменений по данному документу будет полностью стерта, и начнется заново для нового объекта.

Правильный и профессиональный путь — это поиск существующего документа-копии по ссылке и перезапись его внутренних реквизитов и табличных частей.

Шаг 1. Копирование реквизитов шапки с помощью ЗаполнитьЗначенияСвойств

Для копирования простых реквизитов (шапки документа) стандартным решением является использование встроенного метода 1С ЗаполнитьЗначенияСвойств. Однако использовать его «в лоб» без параметров нельзя.

Если мы попытаемся скопировать абсолютно все свойства из источника в приемник, то система выдаст ошибку, так как поле Ссылка доступно только для чтения. Кроме того, затрутся уникальный номер и дата документа-копии, которые должны оставаться уникальными.

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


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

// Заполняем реквизиты шапки, исключая ключевые системные поля
ИсключаемыеСвойства = "Ссылка, Номер, Дата, Проведен, ПометкаУдаления";
ЗаполнитьЗначенияСвойств(ДокументКопияОбъект, ДокументИсточникОбъект, , ИсключаемыеСвойства);

Используя этот подход, мы гарантируем, что перезапишем только пользовательские реквизиты (например, организацию, контрагента, договор, сумму), но сохраним уникальные реквизиты самого документа-копии.

Шаг 2. Эффективное копирование табличных частей

Вторым этапом нам необходимо перенести данные табличных частей. Здесь существует два подхода в зависимости от структуры документов.

Вариант А. Структура табличных частей полностью совпадает

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


// Очищаем существующую табличную часть в копии
ДокументКопияОбъект.Товары.Очистить();

// Выгружаем данные из источника и загружаем в копию
ТаблицаТоваров = ДокументИсточникОбъект.Товары.Выгрузить();
ДокументКопияОбъект.Товары.Загрузить(ТаблицаТоваров);

Вариант Б. Структура табличных частей различается

Если вы копируете данные между разными типами документов, где состав реквизитов табличных частей может не совпадать, то следует использовать построчный обход в цикле с применением все той же функции ЗаполнитьЗначенияСвойств:


// Очищаем табличную часть приемника
ДокументКопияОбъект.Товары.Очистить();

// Переносим строки построчно
Для Каждого СтрокаИсточника Из ДокументИсточникОбъект.Товары Цикл
    НоваяСтрока = ДокументКопияОбъект.Товары.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
КонецЦикла;

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

Шаг 3. Синхронизация статусов проведения и удаления

Важно синхронизировать не только данные, но и состояние документов в системе. Копия должна чутко реагировать на действия пользователя с оригиналом:

Реализуем эту логику при записи объекта-копии:


// Синхронизируем пометку удаления
ДокументКопияОбъект.ПометкаУдаления = ДокументИсточникОбъект.ПометкаУдаления;

// Определяем режим записи в зависимости от состояния источника
Если ДокументИсточникОбъект.Проведен Тогда
    РежимЗаписи = РежимЗаписиДокумента.Проведение;
Иначе
    РежимЗаписи = РежимЗаписиДокумента.Запись;
КонецЕсли;

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

Архитектурные рекомендации

Где лучше всего разместить этот код? Чтобы система работала стабильно и не требовала постоянного вмешательства при обновлении конфигурации, придерживайтесь следующих правил:

1. Использование подписок на события. Не пишите код синхронизации непосредственно в модуле объекта исходного документа. Создайте общую Подписку на события для события ПриЗаписи нужных документов. Это позволит сохранить конфигурацию максимально приближенной к типовой и упростит ее поддержку.

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

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

← На главную