Как перенести Контактные лица в Дополнительные реквизиты Контрагентов в 1С при конвертации данных?

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

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

Для начала, давайте вспомним, что такое дополнительные реквизиты в 1С. Дополнительные реквизиты – это гибкий механизм платформы, позволяющий добавлять пользовательские поля к объектам (справочникам, документам) без изменения структуры конфигурации. Они, как правило, хранятся в специальной табличной части объекта (например, "ДополнительныеРеквизиты") и управляются через специализированные планы видов характеристик, такие как ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения. Для их использования необходимо убедиться, что в вашей конфигурации включена опция "Дополнительные реквизиты и сведения" (обычно в разделе "НСИ и администрирование - Общие настройки").

Теперь давайте перейдем к методам, которые помогут нам перенести контактных лиц в эти самые дополнительные реквизиты.

Метод 1: Использование события "ПослеВыгрузки" с функцией "ВыгрузитьПоПравилу"

Один из самых прямолинейных подходов, предложенный в обсуждении, – это использование функции ВыгрузитьПоПравилу в событии ПослеВыгрузки Правила Конвертации Объекта (ПКО) "Контрагент". Этот метод особенно удобен, когда данные для дополнительного реквизита не являются прямым реквизитом объекта-источника, а, например, хранятся в регистре сведений или являются частью другого объекта.

  1. Настройка ПКО для Контрагента: Мы начинаем с создания или модификации ПКО для справочника "Контрагенты".
  2. Обработчик события "ПослеВыгрузки": Открываем обработчики событий для ПКО "Контрагент" и переходим к событию ПослеВыгрузки. Именно здесь мы будем получать информацию о контактных лицах и "передавать" ее в приемник.
  3. Получение данных о контактных лицах: В этом обработчике нам нужно получить список контактных лиц, связанных с текущим контрагентом. Мы можем использовать для этого запросы к базе данных или обходить табличные части/регистры сведений объекта-источника.
  4. Вызов ВыгрузитьПоПравилу: Для каждого контактного лица или для каждой части информации, которую мы хотим записать в дополнительный реквизит, мы вызываем функцию ВыгрузитьПоПравилу.

Рассмотрим примерный алгоритм действий:

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


// В обработчике события "ПослеВыгрузки" ПКО "Контрагент"
// Параметры: ОбъектИсточник, ОбъектПриемник, Правило

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

// Допустим, мы хотим выгрузить ФИО контактного лица
// и записать его как дополнительный реквизит "КонтактноеЛицо_1", "КонтактноеЛицо_2" и т.д.
// Или просто как один реквизит, если контактное лицо одно.

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   КонтактныеЛицаСрезПоследних.КонтактноеЛицо КАК КонтактноеЛицо
    |ИЗ
    |   РегистрСведений.КонтактныеЛицаКонтрагентов.СрезПоследних(&Дата, Контрагент = &Контрагент) КАК КонтактныеЛицаСрезПоследних";

Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", ОбъектИсточник);

РезультатЗапроса = Запрос.Выполнить().Выбрать();

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

    Индекс = Индекс + 1;
КонецЦикла;

// Теперь, когда у нас есть ТаблицаДопРеквизитов, нам нужно передать ее в приемник.
// Если в приемнике есть отдельное ПКО для табличной части доп. реквизитов
// или мы хотим перезаписать эту ТЧ целиком, мы можем использовать следующую логику.
// Однако, для корректной работы с дополнительными реквизитами, часто требуется
// создавать их через ПВХ.

// Более универсальный подход - передача данных через параметр и обработка в ПослеЗагрузки.
// Но если мы все же хотим использовать ВыгрузитьПоПравилу для прямого заполнения ТЧ:

// Предполагаем, что у нас есть Правило Конвертации Группы Свойств (ПКГС) для ТЧ "ДополнительныеРеквизиты"
// и в нем есть ПКС для "Свойство" и "Значение".
// Тогда мы можем передать нашу ТаблицуДопРеквизитов.

// Пример передачи всей таблицы для заполнения ТЧ "ДополнительныеРеквизиты":
Если ТаблицаДопРеквизитов.Количество() > 0 Тогда
    // Создаем "фиктивный" объект, который будет содержать наши доп. реквизиты
    // и передаем его в приемник через ВыгрузитьПоПравилу
    // Это сложный вариант, проще передать через параметры.

    // Прямое использование ВыгрузитьПоПравилу для записи в ТЧ ДополнительныеРеквизиты
    // требует создания ПКО для этой ТЧ и передачи в него каждой строки.
    // Это может быть избыточно для простых случаев и чревато перезаписью.

    // Альтернатива - сохранить ТаблицуДопРеквизитов в ПараметрыОбъекта
    // и обработать ее в ПослеЗагрузки ПКО контрагента.
    Если ПараметрыОбъекта.Свойство("ДополнительныеРеквизитыДляЗагрузки") Тогда
        ПараметрыОбъекта.ДополнительныеРеквизитыДляЗагрузки.Очистить();
        ПараметрыОбъекта.ДополнительныеРеквизитыДляЗагрузки.Загрузить(ТаблицаДопРеквизитов);
    Иначе
        ПараметрыОбъекта.Вставить("ДополнительныеРеквизитыДляЗагрузки", ТаблицаДопРеквизитов);
    КонецЕсли;
КонецЕсли;

Важный нюанс этого метода: если вы пытаетесь напрямую заполнить табличную часть "ДополнительныеРеквизиты" через Правила Конвертации Группы Свойств (ПКГС) или аналогичным способом, существует риск перезаписи всех существующих дополнительных реквизитов в объекте-приемнике. Это означает, что если в приемнике уже были какие-либо дополнительные реквизиты, введенные пользователем или другими обменами, они могут быть удалены. Это критично, если в приемнике могут быть свои специфические доп. реквизиты, которые не должны быть затронуты обменом. Об этом предупреждает участник форума в сообщении 5, а сообщение 8 объясняет важность сохранения таких данных.

Метод 2: Применение Правила Конвертации Группы Свойств (ПКГС) с программным заполнением

Этот метод подразумевает создание ПКГС для табличной части ДополнительныеРеквизиты объекта-приемника. Однако, вместо того чтобы указывать источник данных напрямую, мы программно формируем необходимые строки в обработчике события ПередОбработкой этого ПКГС.

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

Примерный алгоритм:


// В обработчике "ПередОбработкой" ПКГС для табличной части "ДополнительныеРеквизиты"
// Параметры: ОбъектИсточник, ОбъектПриемник, ТаблицаЗначений, Правило

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

// Предполагаем, что ОбъектИсточник - это текущий Контрагент из исходной базы.
// Мы используем его для получения связанных контактных лиц.

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   КонтактныеЛицаСрезПоследних.КонтактноеЛицо КАК КонтактноеЛицо
    |ИЗ
    |   РегистрСведений.КонтактныеЛицаКонтрагентов.СрезПоследних(&Дата, Контрагент = &Контрагент) КАК КонтактныеЛицаСрезПоследних
    |ГДЕ
    |   КонтактныеЛицаСрезПоследних.КонтактноеЛицо.Имя <> """; // Проверим, что имя не пустое

Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", ОбъектИсточник);

РезультатЗапроса = Запрос.Выполнить().Выбрать();

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

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

    // Колонка "Свойство" в ТЧ ДополнительныеРеквизиты – это ссылка на ПВХ.
    // Чтобы получить эту ссылку, нам нужно либо заранее убедиться, что соответствующий
    // элемент ПВХ существует в приемнике, либо создать его.
    // Для примера, возьмем Наименование контактного лица как Значение,
    // а "КонтактноеЛицо" как наименование вида свойства.

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

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

    Индекс = Индекс + 1;
КонецЦикла;

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

Метод 3: Передача данных через параметры в ПКС и обработка в "ПослеЗагрузки" ПКО

Этот метод считается одним из наиболее гибких и безопасных, поскольку он позволяет сначала передать "сырые" данные в приемник в виде параметров, а затем программно обработать их в событии ПослеЗагрузки ПКО, учитывая уже существующие данные и бизнес-логику. Этот подход особенно актуален для предотвращения перезаписи существующих дополнительных реквизитов, о чем было упомянуто в сообщениях 5 и 8.

  1. Передача данных в параметр:
    • В Правиле Конвертации Свойств (ПКС), связанном с выгрузкой Контрагента, или в отдельном ПКС, которое вы создадите для контактных лиц, установите флаг "Передавать данные в параметр".
    • В качестве источника для этого ПКС может выступать, например, запрос, который возвращает коллекцию контактных лиц, связанных с текущим контрагентом.
    • Результат этого ПКС (например, ТаблицаЗначений с данными о контактных лицах) будет передан в коллекцию ПараметрыОбъекта ПКО.
  2. Обработка в "ПослеЗагрузки" ПКО Контрагента:
    • После того как объект "Контрагент" будет загружен в приемник, но еще не записан в базу данных, будет вызван обработчик события ПослеЗагрузки его ПКО.
    • Здесь мы получаем доступ к ОбъектПриемник (экземпляр объекта в приемнике) и к коллекции ПараметрыОбъекта, в которой находятся переданные данные о контактных лицах.
    • Мы можем обойти табличную часть ДополнительныеРеквизиты объекта-приемника, чтобы проверить наличие уже существующих реквизитов.
    • Затем мы добавляем новые реквизиты или обновляем существующие, основываясь на данных из ПараметрыОбъекта.

Рассмотрим подробный пример кода для обработчика "ПослеЗагрузки" ПКО "Контрагент":


// В обработчике события "ПослеЗагрузки" ПКО "Контрагент"
// Параметры: ОбъектПриемник, ОбъектИсточник, Отказ, ПараметрыОбъекта, Правило

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

Если ТаблицаКонтактныхЛиц = Неопределено Или ТаблицаКонтактныхЛиц.Количество() = 0 Тогда
    Возврат; // Нет данных для добавления
КонецЕсли;

// 2. Получаем ссылку на ПланВидовХарактеристик "ДополнительныеРеквизитыИСведения"
// Это очень важно, т.к. поле "Свойство" в ТЧ ДополнительныеРеквизиты является ссылкой на элемент этого ПВХ.
// Убедитесь, что все необходимые виды свойств (например, "КонтактноеЛицо") существуют в приемнике.
// Если их нет, вы можете создать их программно или заранее загрузить через другие ПКО/ПВХ.

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

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


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

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

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

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

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

Преимущества этого метода:

  1. Безопасность данных: Вы полностью контролируете процесс добавления/обновления дополнительных реквизитов, что позволяет избежать случайной перезаписи существующих данных в приемнике. Мы можем явно проверить наличие реквизита по его <СтрокаКЛ.Имя> Свойству и <СтрокаКЛ.Должность> Значению.
  2. Гибкость: Можно реализовать сложную логику, например, добавлять несколько контактных лиц как отдельные реквизиты, комбинировать информацию, применять условия и правила.
  3. Отложенная обработка: Данные обрабатываются в момент загрузки объекта, что позволяет использовать весь контекст загружаемого объекта.

Общие рекомендации и важные моменты

Независимо от выбранного метода, мы рекомендуем учитывать следующие моменты:

  1. Предварительная настройка видов дополнительных реквизитов: Убедитесь, что все необходимые ВидыДополнительныхРеквизитовИСведений (например, "ФИО КонтактногоЛица", "Должность КонтактногоЛица") существуют в базе-приемнике. Их можно либо создать вручную, либо выгрузить через отдельные ПКО.
  2. Типы значений: Внимательно выбирайте тип значения для дополнительного реквизита в ПВХ.ДополнительныеРеквизитыИСведения. Если вы хотите хранить ссылку на справочник "ФизическиеЛица" или "КонтактныеЛица", то тип должен быть соответствующим. Если это просто строка (ФИО), то тип "Строка".
  3. Производительность: Для больших объемов данных запросы и циклы в обработчиках могут повлиять на производительность обмена. Оптимизируйте запросы и старайтесь минимизировать количество операций в циклах.
  4. Логирование: Добавляйте сообщения в журнал регистрации или механизм сообщений "Конвертации данных", чтобы отслеживать процесс добавления/обновления реквизитов и легко выявлять ошибки.

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

← На главную