Как программно найти и заменить документ по GUID в 1С:Предприятии 8?

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

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

Почему возникает ошибка уникальности?

Разберем ситуацию: метод УстановитьСсылкуНового предназначен исключительно для новых объектов, которые еще ни разу не были записаны в базу данных. Когда мы передаем в этот метод УникальныйИдентификатор, платформа готовит запись для вставки. Однако, если в таблице СУБД уже существует запись с таким же первичным ключом (ссылкой), при вызове метода Записать() происходит конфликт на уровне базы данных. Система не может создать второй документ с тем же GUID, так как это нарушит целостность данных.

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

Алгоритм «Поиск или Создание»

Самый надежный способ — это получение ссылки по имеющемуся GUID и последующая попытка получить объект. Проанализируем правильную последовательность действий на примере кода:


// Предположим, СсылкаДокументID — это наш GUID в формате строки или объекта УникальныйИдентификатор
// искомыйдокумент — строковое имя метаданных, например, "РеализацияТоваровУслуг"

GUIDОбъекта = Новый УникальныйИдентификатор(СсылкаДокументID);
ДокСсылка = Документы[искомыйдокумент].ПолучитьСсылку(GUIDОбъекта);

// Пытаемся получить объект из базы данных
ДокОбъект = ДокСсылка.ПолучитьОбъект();

Если ДокОбъект = Неопределено Тогда
    // Если объект не найден, создаем новый
    ДокОбъект = Документы[искомыйдокумент].СоздатьДокумент();
    // Назначаем ему нужный нам GUID
    ДокОбъект.УстановитьСсылкуНового(ДокСсылка);
КонецЕсли;

// Теперь ДокОбъект — это либо найденный старый документ, либо новый
// Заполняем реквизиты
ДокОбъект.Дата = ТекущаяДата();
ДокОбъект.СуммаДокумента = НужнаяСумма;

// Важно: устанавливаем режим загрузки
ДокОбъект.ОбменДанными.Загрузка = Истина;

ДокОбъект.Записать();

Рассмотрим подробнее нюансы этого кода. Метод ПолучитьСсылку не обращается к базе данных, он просто создает ссылку «в памяти». Реальную проверку выполняет метод ПолучитьОбъект: если он возвращает Неопределено, значит, записи в базе физически не существует.

Обязательное использование ОбменДанными.Загрузка

При замене документов в рамках обмена данными крайне важно устанавливать свойство Объект.ОбменДанными.Загрузка = Истина. Выясним причину, почему это необходимо:

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

Очистка табличных частей при обновлении

Одна из распространенных ошибок при «замене» документа — это забытая очистка табличных частей. Если вы нашли существующий документ и просто начали добавлять в него строки (например, товары), то старые строки никуда не исчезнут. В итоге данные в документе удвоятся или перемешаются.

Правильный подход при обновлении найденного объекта подразумевает полную очистку всех коллекций перед заполнением:


Если ДокОбъект.Выбран() Тогда
    // Если документ существовал, очищаем его содержимое
    ДокОбъект.Товары.Очистить();
    ДокОбъект.Услуги.Очистить();
    // И так для всех табличных частей
КонецЕсли;

// Заполняем новыми данными...
НоваяСтрока = ДокОбъект.Товары.Добавить();
// Заполнение полей строки

Риски использования метода Удалить()

В обсуждениях иногда встречается совет: «Сначала физически удалить старый документ, а потом создать новый с тем же GUID». Мы настоятельно не рекомендуем использовать этот метод по нескольким причинам:

Транзакционность и массовая загрузка

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

Проанализируем пример структуры массовой загрузки:


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

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

Таким образом, правильная замена документа по GUID — это не удаление и создание заново, а получение объекта по существующей ссылке и полная перезапись его свойств с обязательным использованием режима загрузки данных.

← На главную