Как перенести документы "Перенос данных" между базами 1С:ЗУП 3.1 с движениями регистров?

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

При переносе данных между информационными базами 1С:Зарплата и управление персоналом 3.1 (ЗУП) программисты часто сталкиваются со специфической проблемой: документы Перенос данных загружаются в базу-приемник абсолютно пустыми. На первый взгляд кажется, что перенос выполнен успешно — сами документы созданы в списке, но внутри них нет никаких данных, а движения по регистрам отсутствуют.

Ситуация значительно усложняется, если исходная база-источник ранее наполнялась из сторонних систем (например, из программы "Камин 2.0" или "ЗиК 7.7"). В этом случае уникальные идентификаторы (UUID/GUID) ссылочных объектов (физических лиц, сотрудников, подразделений, должностей) гарантированно не совпадают с базой-приемником. Использование стандартной обработки ВыгрузкаЗагрузкаДанныхXML становится невозможным, так как она работает строго по GUID и приведет либо к массовому дублированию справочников, либо к появлению битых ссылок вида <Объект не найден> в движениях документов.

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

Архитектурная особенность документа "Перенос данных"

Чтобы понять, почему стандартный перенос не дает нужного результата, проанализируем структуру документа Перенос данных в конфигурации 1С:ЗУП 3.1. Этот документ является техническим "контейнером". В отличие от привычных документов (таких как ПриемНаРаботу или НачислениеЗарплаты), он не содержит классических табличных частей в метаданных, где хранились бы строки с суммами, периодами и кадровыми сведениями.

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

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

Проблема рассинхронизации GUID и настройка полей поиска

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

В правилах обмена (КД 2.1) для ключевых справочников следует настроить следующие правила поиска:

  1. Физические лица (справочник ФизическиеЛица): поиск следует осуществлять по СНИЛС или ИНН. Если эти данные заполнены не у всех, настройте комбинированный поиск по связке ФИО + ДатаРождения.
  2. Сотрудники (справочник Сотрудники): так как у одного физлица может быть несколько мест работы (внутреннее совместительство), искать сотрудников по ФИО напрямую нельзя. Настройте поиск по уникальному Коду сотрудника в пределах организации или по связке ФизическоеЛицо + Организация + ВидЗанятости.
  3. Организации: сопоставление по ИНН и КПП.
  4. Подразделения и Должности: поиск по наименованию (в пределах конкретной организации).
  5. Начисления и удержания (планы видов расчета): сопоставление по наименованию или коду предопределенного элемента.

Только после того, как синхронизация справочников будет полностью отлажена, можно приступать к переносу регистров.

Решение через "Конвертацию данных 2.1": Настройка правил

Рассмотрим подробнее пошаговый процесс настройки правил конвертации объектов (ПКО) для документа Перенос данных в программе "Конвертация данных 2.1".

Шаг 1. Настройка ПКО для документа. Создайте правило конвертации для документа Перенос данных. Перейдите на вкладку "Табличные части" и убедитесь, что табличная часть ТаблицаРегистров переносится корректно. Это обеспечит отображение нужных вкладок в базе-приемнике.

Шаг 2. Сопоставление движений регистров. В КД 2.1 наборы записей регистров, подчиненных регистратору, настраиваются в ПКО во вкладке "Поиск/Запись" или через ручное добавление свойств. Вам необходимо сопоставить все регистры, которые задействованы в исходных документах Перенос данных. К ним обычно относятся:

Для каждого сопоставленного регистра в правилах конвертации свойств должно быть четко указано ПКО соответствующего регистра.

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

Для обхода этого поведения в ПКО документа Перенос данных в обработчике события "После загрузки" необходимо прописать специальный алгоритм. Этот алгоритм временно выгрузит пришедшие движения в память, выполнит запись документа для фиксации его ссылки, а затем принудительно вернет движения и запишет их в базу данных.

Разберем пример кода для обработчика "После загрузки":


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

Этот код надежно защищает перенесенные движения от автоматического очищения платформой в процессе загрузки XML.

Программный метод переноса данных через внешнюю обработку

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

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

Рассмотрим структуру выгрузки наборов записей из источника. Нам нужно перебрать все документы Перенос данных, прочитать их движения по каждому доступному регистру и выгрузить структуру записей, заменив ссылки (Сотрудники, Физлица) на текстовые уникальные ключи (например, СНИЛС или Код).

Посмотрим на пример кода выгрузки данных по регистру сведений КадроваяИсторияСотрудников:


// Создаем структуру для экспорта документа и его движений
ДанныеЭкспорта = Новый Структура;
ДанныеЭкспорта.Вставить("Номер", ДокументОбъект.Номер);
ДанныеЭкспорта.Вставить("Дата", ДокументОбъект.Дата);
ДанныеЭкспорта.Вставить("ПериодРегистрации", ДокументОбъект.ПериодРегистрации);

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

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

ДанныеЭкспорта.Вставить("ДвиженияКадры", МассивЗаписей);

При загрузке в базе-приемнике мы выполняем обратную операцию. Мы находим сотрудника, подразделение и должность по переданным строковым ключам, заполняем набор записей регистра и принудительно записываем его, установив в качестве регистратора созданный документ Перенос данных.

Пример кода загрузки и записи движений в приемнике:


НовыйДокумент = Документы.ПереносДанных.СоздатьДокумент();
НовыйДокумент.Дата = ДанныеЭкспорта.Дата;
НовыйДокумент.Номер = ДанныеЭкспорта.Номер;
НовыйДокумент.ПериодРегистрации = ДанныеЭкспорта.ПериодРегистрации;

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

НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);

// Формируем новый набор записей для загрузки в регистр
НаборЗаписей = РегистрыСведений.КадроваяИсторияСотрудников.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(НовыйДокумент.Ссылка);

Для Каждого ЗаписьИзФайла Из ДанныеЭкспорта.ДвиженияКадры Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Регистратор = НовыйДокумент.Ссылка;
    НоваяЗапись.Период = ЗаписьИзФайла.Period;
    НоваяЗапись.ВидСобытия = ЗаписьИзФайла.ВидСобытия;
    
    // Ищем сотрудника по коду в базе-приемнике
    СсылкаСотрудник = Справочники.Сотрудники.НайтиПоКоду(ЗаписьИзФайла.СотрудникКод);
    Если Не СсылкаСотрудник.Пустая() Тогда
        НоваяЗапись.Сотрудник = СсылкаСотрудник;
    Иначе
        ВызватьИсключение "Не найден сотрудник с кодом " + ЗаписьИзФайла.СотрудникКод;
    КонецЕсли;
    
    // Аналогично ищем подразделение и должность по наименованию
    НоваяЗапись.Подразделение = Справочники.ПодразделенияОрганизаций.НайтиПоНаименованию(ЗаписьИзФайла.ПодразделениеНазвание);
    НоваяЗапись.Должность = Справочники.Должности.НайтиПоНаименованию(ЗаписьИзФайла.ДолжностьНазвание);
КонецЦикла;

// Записываем набор записей регистра с флагом замещения существующего набора
НаборЗаписей.Записать(Истина);

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

Мы проанализировали внутреннее устройство технического документа Перенос данных в 1С:ЗУП 3.1 и выяснили, почему стандартные инструменты обмена часто переносят его незаполненным. Применение правильных подходов гарантирует точный и беспроблемный перенос накопленных данных:

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

← На главную