Как перенести реквизит документа в реквизит создаваемого справочника при конвертации данных 1С

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

При переносе данных между различными конфигурациями 1С с помощью механизма «1С:Конвертация данных 2.1» (КД 2) часто возникает архитектурное несовпадение структур метаданных. Классический пример — перенос кадровых данных из старых версий зарплатных решений (например, ЗУП 1.1 или 2.5) в современные редакции «Бухгалтерии предприятия 3.0» (БП 3.0).

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

---

Анализ типовых ошибок и архитектурных ограничений

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

1. Использование глобальных параметров вместо параметров объекта

Попытка передать значение через конструкцию Параметры.Вставить("ВидЗанятости", ...) в событии ПередВыгрузкой объекта приводит к потере данных. Глобальная структура Параметры в КД 2 предназначена для хранения общих настроек всей сессии выгрузки/загрузки. При обработке коллекции документов значение в глобальной структуре будет постоянно перезаписываться данными последнего выгруженного документа. В момент загрузки конкретного объекта в базе-приемнике эти данные окажутся неактуальными или будут пустыми.

Для связывания временных данных с конкретным конвертируемым объектом необходимо использовать локальную коллекцию ПараметрыОбъекта, которая сериализуется в XML-файл внутри узла данного объекта и гарантированно доходит до обработчика ПослеЗагрузки этого же объекта.

2. Ошибка обращения к значению перечисления

Попытка получить имя перечисления через обращение к свойству Источник.ВидЗанятости.Значение вызывает ошибку платформы:

Поле объекта не обнаружено (Значение)

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

3. Неверный порядок параметров в функции ВыгрузитьПоПравилу()

При использовании правил выгрузки данных (ПВД) с произвольным алгоритмом разработчики часто путают позиции аргументов функции ВыгрузитьПоПравилу(). Синтаксис функции выглядит следующим образом:

ВыгрузитьПоПравилу(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО, ...)

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

4. Особенности записи справочника «Сотрудники» в БП 3.0

Прямое создание элемента справочника через Справочники.Сотрудники.СоздатьЭлемент() в коде обработчика ПослеЗагрузки документа — крайне нерекомендуемый подход для БП 3.0. Современная конфигурация использует сложные механизмы Библиотеки стандартных подсистем (БСП). Без заполнения связанных регистров сведений и вызова специализированных процедур общего модуля созданные сотрудники будут «невидимы» в интерфейсе программы, а кадровый учет по ним нарушится.

---

Решение №1: Использование локальных параметров объекта (ПараметрыОбъекта)

Этот способ оптимален, если вы не хотите переписывать структуру выгрузки и желаете передать данные «на лету» в рамках существующего ПКО документа. Рассмотрим этот метод по шагам.

Шаг 1. В конфигураторе «Конвертация данных» откройте ПКО вашего документа (например, ПриемНаРаботуВОрганизацию). Перейдите на вкладку «Параметры» и добавьте новый параметр с именем ВидЗанятости. Установите для него тип значения (соответствующее перечисление приемника) и обязательно взведите флаг «Передавать в приемник».

Шаг 2. В ПКС (Правила конвертации свойств) для этого созданного параметра укажите соответствующее ПКО перечисления (например, ВидыЗанятости -> ВидыЗанятости). Это позволит КД автоматичеки сопоставить элементы перечисления без написания кода ручного сравнения строк.

Шаг 3. В обработчике события ПередВыгрузкой (или ПриВыгрузке) ПКО документа передайте значение из источника в параметр объекта:


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

Шаг 4. В обработчике события ПослеЗагрузки ПКО документа в базе-приемнике прочитайте переданный параметр и используйте его при создании или обновлении справочника сотрудников. Обратите внимание: в приемнике этот параметр будет доступен в структуре ПараметрыОбъекта.


// Извлекаем переданное значение вида занятости
ВидЗанятостиПриемник = Неопределено;
Если ПараметрыОбъекта <> Неопределено Тогда
    ПараметрыОбъекта.Свойство("ВидЗанятости", ВидЗанятостиПриемник);
КонецЕсли;

// Обходим табличную часть документа в приемнике для создания сотрудников
Для Каждого СтрокаТЧ Из Объект.РаботникиОрганизации Цикл
    
    // Проверяем, существует ли уже сотрудник с таким физлицом в базе
    СотрудникСсылка = Справочники.Сотрудники.НайтиПоРеквизиту("ФизическоеЛицо", СтрокаТЧ.ФизическоеЛицо);
    
    Если СотрудникСсылка.Пустая() Тогда
        // Создаем нового сотрудника программно
        НовыйСотрудник = Справочники.Сотрудники.СоздатьЭлемент();
        НовыйСотрудник.ФизическоеЛицо = СтрокаТЧ.ФизическоеЛицо;
        НовыйСотрудник.ГоловнаяОрганизация = Объект.Организация;
        НовыйСотрудник.Наименование = СтрокаТЧ.ФизическоеЛицо.Наименование;
        
        // Заполняем перенесенный вид занятости
        Если ЗначениеЗаполнено(ВидЗанятостиПриемник) Тогда
            НовыйСотрудник.ВидЗанятости = ВидЗанятостиПриемник;
        КонецЕсли;
        
        Попытка
            НовыйСотрудник.Записать();
            СтрокаТЧ.Сотрудник = НовыйСотрудник.Ссылка;
            Сообщить("Успешно создан сотрудник: " + НовыйСотрудник.Наименование);
        Исключение
            Сообщить("Ошибка при записи сотрудника: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
        КонецПопытки;
    Иначе
        // Если сотрудник найден, просто подставляем его в документ
        СтрокаТЧ.Сотрудник = СотрудникСсылка;
    КонецЕсли;
    
КонецЦикла;

---

Решение №2: Использование ПВД с произвольным алгоритмом и Входящих данных

Это наиболее гибкий и профессиональный подход с точки зрения архитектуры КД 2. Мы создаем отдельное ПКО для справочника Сотрудники, у которого нет объекта-источника (поле "Источник" оставляем пустым), а все данные для заполнения передаем через структуру ВходящиеДанные непосредственно из запроса выгрузки документов.

Шаг 1. Создайте ПКО для справочника Сотрудники. Поле «Источник» оставьте пустым, а «Приемник» установите как СправочникСсылка.Сотрудники. В таблице ПКС добавьте реквизиты, которые необходимо заполнить: Код, Наименование, ФизическоеЛицо, ГоловнаяОрганизация, ВидЗанятости. Для каждого из этих свойств установите флаг «Получать из входящих данных».

Шаг 2. Создайте ПВД (Правило выгрузки данных). Выберите тип выгрузки — «Произвольный алгоритм». В обработчике события ПередОбработкой напишите запрос, который выберет все необходимые данные по кадровым документам за период выгрузки:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	ПриемРаботников.ФизЛицо КАК ФизическоеЛицо,
    |	ПриемРаботников.ФизЛицо.Код КАК Код,
    |	ПриемРаботников.ФизЛицо.Наименование КАК Наименование,
    |	ПриемРаботников.Ссылка.Организация КАК Организация,
    |	ПриемРаботников.Ссылка.ВидЗанятости КАК ВидЗанятости
    |ИЗ
    |	Документ.ПриемНаРаботуВОрганизацию.РаботникиОрганизации КАК ПриемРаботников
    |ГДЕ
    |	ПриемРаботников.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
    |	И ПриемРаботников.Ссылка.Проведен = ИСТИНА";

Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    
    // Формируем структуру входящих данных для передачи в ПКО справочника
    ВходящиеДанныеДляСотрудника = Новый Структура;
    ВходящиеДанныеДляСотрудника.Вставить("Код", Выборка.Код);
    ВходящиеДанныеДляСотрудника.Вставить("Наименование", Выборка.Наименование);
    ВходящиеДанныеДляСотрудника.Вставить("ФизическоеЛицо", Выборка.ФизическоеЛицо);
    ВходящиеДанныеДляСотрудника.Вставить("ГоловнаяОрганизация", Выборка.Организация);
    ВходящиеДанныеДляСотрудника.Вставить("ВидЗанятости", Выборка.ВидЗанятости);
    
    // Вызываем выгрузку по правилу конвертации.
    // Обратите внимание на ПРАВИЛЬНЫЙ порядок параметров: 
    // ВходящиеДанные передаются ТРЕТЬИМ аргументом.
    ВыгрузитьПоПравилу(,, ВходящиеДанныеДляСотрудника,, "СозданиеСотрудников");
    
КонецЦикла;

Шаг 3. Чтобы сопоставить перечисление ВидЗанятости без написания сложных условий сравнения строк, обязательно создайте стандартное ПКО для перечислений. В правилах конвертации свойств для реквизита ВидЗанятости укажите созданное ПКО перечисления. Конвертация данных автоматически сопоставит типы источника и приемника на этапе генерации XML.

---

Важные рекомендации по работе с перечислениями

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


// Получение строкового имени значения перечисления
ИмяЗначения = "";
Если ЗначениеЗаполнено(Источник.ВидЗанятости) Тогда
    ИндексЗначения = Перечисления.ВидыЗанятости.Индекс(Источник.ВидЗанятости);
    ИмяЗначения = Метаданные.Перечисления.ВидыЗанятости.ЗначенияПеречисления[ИндексЗначения].Имя;
КонецЕсли;

// Теперь можно безопасно использовать строковое сравнение
Если ИмяЗначения = "ОсновноеМестоРаботы" Тогда
    // Ваша логика
КонецЕсли;

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

← На главную