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

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

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

Постановка задачи и анализ ситуации

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

Метод 1. Использование COM-соединения для прямого переноса

Рассмотрим наиболее гибкий программный метод — использование COMConnector. Этот способ позволяет подключиться из одной базы к другой «на лету» и прочитать данные напрямую, хотя для обмена между сильно измененными конфигурациями существуют и более комплексные решения, например, универсальный перенос данных по технологии COM+HTTP. Проанализируем пример кода, который выполняет подключение и выборку данных.

Для начала нам необходимо инициализировать соединение с базой-источником:


ПараметрыСоединения = "Srvr=""ServerName"";Ref=""BaseName"";Usr=""Admin"";Pwd=""Password"";";
COMСоединитель = Новый COMОбъект("V83.COMConnector");
Попытка
    БазаИсточник = COMСоединитель.Connect(ПараметрыСоединения);
Исключение
    Сообщить("Ошибка подключения: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

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


ТекстЗапроса = 
"ВЫБРАТЬ
|   Штрихкоды.ОсновноеСредство.Код КАК КодОС,
|   Штрихкоды.Штрихкод КАК Штрихкод
|ИЗ
|   РегистрСведений.ШтрихкодыОС КАК Штрихкоды";

ЗапросИсточник = БазаИсточник.NewObject("Запрос");
ЗапросИсточник.Текст = ТекстЗапроса;
Выборка = ЗапросИсточник.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    // Поиск объекта в текущей базе по коду
    СсылкаОС = Справочники.ОсновныеСредства.НайтиПоКоду(Выборка.КодОС);
    
    Если Не СсылкаОС.Пустая() Тогда
        // Запись в регистр текущей базы
        МенеджерЗаписи = РегистрыСведений.ШтрихкодыОС.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.ОсновноеСредство = СсылкаОС;
        МенеджерЗаписи.Штрихкод = Выборка.Штрихкод;
        МенеджерЗаписи.Записать();
    КонецЕсли;
КонецЦикла;

Метод 2. Сериализация в XML или JSON (универсальный обмен)

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

Шаг 1. Выгрузка из источника:

Создадим массив структур, где каждая структура — это запись регистра, и запишем его в файл.


ЗаписиДляВыгрузки = Новый Массив;
Выборка = РегистрыСведений.ШтрихкодыОС.Выбрать();
Пока Выборка.Следующий() Цикл
    СтруктураЗаписи = Новый Структура("КодОС, Штрихкод", Выборка.ОсновноеСредство.Код, Выборка.Штрихкод);
    ЗаписиДляВыгрузки.Добавить(СтруктураЗаписи);
КонецЦикла;

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ОткрытьФайл("C:\temp\barcodes.json");
ЗаписатьJSON(ЗаписьJSON, ЗаписиДляВыгрузки);
ЗаписьJSON.Закрыть();

Шаг 2. Загрузка в приемник:

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


ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл("C:\temp\barcodes.json");
МассивДанных = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();

Для Каждого СтрокаДанных Из МассивДанных Цикл
    СсылкаОС = Справочники.ОсновныеСредства.НайтиПоКоду(СтрокаДанных.КодОС);
    Если Не СсылкаОС.Пустая() Тогда
        НаборЗаписей = РегистрыСведений.ШтрихкодыОС.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.ОсновноеСредство.Установить(СсылкаОС);
        
        НоваяЗапись = НаборЗаписей.Добавить();
        НоваяЗапись.ОсновноеСредство = СсылкаОС;
        НоваяЗапись.Штрихкод = СтрокаДанных.Штрихкод;
        
        НаборЗаписей.Записать();
    КонецЕсли;
КонецЦикла;

Метод 3. Перенос подчиненных регистров (технический хак)

Рассмотрим ситуацию, когда регистр ШтрихкодыОС подчинен регистратору. В этом случае мы не можем просто записать МенеджерЗаписи без указания документа. Если документы-регистраторы отсутствуют в новой базе, у нас есть два пути:

  1. Создание технического регистратора: Мы создаем один пустой документ (например, ОперацияБух или специально созданный технический документ ВосстановлениеДанных) и назначаем его регистратором для всех переносимых записей.
  2. Временное изменение режима записи: Если у нас есть доступ к конфигуратору, мы можем временно изменить режим записи регистра на Независимый, загрузить данные и вернуть режим Подчинение регистратору. Однако этот метод требует осторожности, так как при возврате режима записи без регистраторов записи могут «исчезнуть» из видимости системы.

Посмотрим на пример создания записи с техническим регистратором:


ТехРегистратор = Документы.ОперацияБух.СоздатьДокумент();
ТехРегистратор.Дата = ТекущаяДата();
ТехРегистратор.Содержание = "Восстановление штрихкодов ОС";
ТехРегистратор.Записать();

НаборЗаписей = РегистрыСведений.ШтрихкодыОС.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ТехРегистратор.Ссылка);

НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Регистратор = ТехРегистратор.Ссылка;
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.ОсновноеСредство = НайденнаяСсылкаОС;
НоваяЗапись.Штрихкод = ЗначениеШтрихкода;

НаборЗаписей.Записать();

Метод 4. Использование типовых обработок

Выясним, как упростить задачу без написания сложного кода. Существует стандартная обработка ВыгрузкаЗагрузкаДанныхXML — для сложных задач удобнее универсальная синхронизация данных по COM и XML. Она эффективно работает, если метаданные регистра полностью идентичны.

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

Синхронизация по Уникальному Идентификатору (UUID)

Проанализируем проблему «битых ссылок». Если в результате переноса или других действий в базе появляются некорректные указатели на объекты, можно применить обработку для поиска и замены битых ссылок. Если же говорить о первопричине, то когда основные средства в базах создавались независимо, их коды могут совпадать, а внутренние идентификаторы — нет. Чтобы гарантировать корректность, рекомендуется использовать поиск по коду или наименованию, как было показано в примерах выше.

Если же базы являются «родственными» (одна — копия другой), можно использовать UUID:


// В базе источнике
СтрокаUUID = Строка(Выборка.ОсновноеСредство.УникальныйИдентификатор());

// В базе приемнике
НовыйUUID = Новый УникальныйИдентификатор(СтрокаUUID);
СсылкаОС = Справочники.ОсновныеСредства.ПолучитьСсылку(НовыйUUID);

Подведение итогов

Мы рассмотрели несколько способов программного переноса данных регистра сведений. Выбор метода зависит от объема данных и возможности прямого подключения между базами. Наиболее надежным для разового восстановления данных в доработанных регистрах является метод через COM-соединение с поиском ссылок по естественным ключам (код, артикул, наименование).

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

← На главную