При работе с системой "Конвертация данных" в 1С часто возникает задача переноса непрямых или связанных данных из объекта-источника в дополнительные реквизиты объекта-приемника. Рассмотрим типичную ситуацию: необходимо выгрузить справочник "Контрагенты" и в качестве дополнительных реквизитов для каждого контрагента записать информацию о его контактных лицах. Это достаточно простая, но требующая понимания механизмов "Конвертации данных" и работы с дополнительными реквизитами задача. Разберем различные подходы к ее решению.
Для начала, давайте вспомним, что такое дополнительные реквизиты в 1С. Дополнительные реквизиты – это гибкий механизм платформы, позволяющий добавлять пользовательские поля к объектам (справочникам, документам) без изменения структуры конфигурации. Они, как правило, хранятся в специальной табличной части объекта (например, "ДополнительныеРеквизиты") и управляются через специализированные планы видов характеристик, такие как ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения. Для их использования необходимо убедиться, что в вашей конфигурации включена опция "Дополнительные реквизиты и сведения" (обычно в разделе "НСИ и администрирование - Общие настройки").
Теперь давайте перейдем к методам, которые помогут нам перенести контактных лиц в эти самые дополнительные реквизиты.
Один из самых прямолинейных подходов, предложенный в обсуждении, – это использование функции ВыгрузитьПоПравилу в событии ПослеВыгрузки Правила Конвертации Объекта (ПКО) "Контрагент". Этот метод особенно удобен, когда данные для дополнительного реквизита не являются прямым реквизитом объекта-источника, а, например, хранятся в регистре сведений или являются частью другого объекта.
ПослеВыгрузки. Именно здесь мы будем получать информацию о контактных лицах и "передавать" ее в приемник.ВыгрузитьПоПравилу: Для каждого контактного лица или для каждой части информации, которую мы хотим записать в дополнительный реквизит, мы вызываем функцию ВыгрузитьПоПравилу.Рассмотрим примерный алгоритм действий:
Предположим, у нас есть регистр сведений, в котором хранятся контактные лица контрагента. Нам нужно для каждого контрагента найти его контактные лица и каждое из них записать в отдельную строку табличной части "Дополнительные реквизиты" объекта-приемника.
// В обработчике события "ПослеВыгрузки" ПКО "Контрагент"
// Параметры: ОбъектИсточник, ОбъектПриемник, Правило
// Создаем временную таблицу, чтобы собрать данные для доп. реквизитов
// Это может быть ТаблицаЗначений, содержащая "Свойство" и "Значение"
ТаблицаДопРеквизитов = Новый ТаблицаЗначений;
ТаблицаДопРеквизитов.Колонки.Добавить("Свойство");
ТаблицаДопРеквизитов.Колонки.Добавить("Значение");
ТаблицаДопРеквизитов.Колонки.Добавить("ТипЗначения"); // Опционально, если нужно передавать тип
// Допустим, мы хотим выгрузить ФИО контактного лица
// и записать его как дополнительный реквизит "КонтактноеЛицо_1", "КонтактноеЛицо_2" и т.д.
// Или просто как один реквизит, если контактное лицо одно.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КонтактныеЛицаСрезПоследних.КонтактноеЛицо КАК КонтактноеЛицо
|ИЗ
| РегистрСведений.КонтактныеЛицаКонтрагентов.СрезПоследних(&Дата, Контрагент = &Контрагент) КАК КонтактныеЛицаСрезПоследних";
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", ОбъектИсточник);
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Индекс = 1;
Пока РезультатЗапроса.Следующий() Цикл
СтрокаДопРеквизита = ТаблицаДопРеквизитов.Добавить();
СтрокаДопРеквизита.Свойство = "КонтактноеЛицо_" + Индекс;
СтрокаДопРеквизита.Значение = РезультатЗапроса.КонтактноеЛицо.Наименование; // Или ФИО, если оно есть
// Если нужно передать ссылку на объект, а не строку, то нужно соответствующее ПКО для КонтактногоЛица
// и передавать его через ВыгрузитьПоПравилу с указанием объекта-приемника для дополнительного реквизита.
Индекс = Индекс + 1;
КонецЦикла;
// Теперь, когда у нас есть ТаблицаДопРеквизитов, нам нужно передать ее в приемник.
// Если в приемнике есть отдельное ПКО для табличной части доп. реквизитов
// или мы хотим перезаписать эту ТЧ целиком, мы можем использовать следующую логику.
// Однако, для корректной работы с дополнительными реквизитами, часто требуется
// создавать их через ПВХ.
// Более универсальный подход - передача данных через параметр и обработка в ПослеЗагрузки.
// Но если мы все же хотим использовать ВыгрузитьПоПравилу для прямого заполнения ТЧ:
// Предполагаем, что у нас есть Правило Конвертации Группы Свойств (ПКГС) для ТЧ "ДополнительныеРеквизиты"
// и в нем есть ПКС для "Свойство" и "Значение".
// Тогда мы можем передать нашу ТаблицуДопРеквизитов.
// Пример передачи всей таблицы для заполнения ТЧ "ДополнительныеРеквизиты":
Если ТаблицаДопРеквизитов.Количество() > 0 Тогда
// Создаем "фиктивный" объект, который будет содержать наши доп. реквизиты
// и передаем его в приемник через ВыгрузитьПоПравилу
// Это сложный вариант, проще передать через параметры.
// Прямое использование ВыгрузитьПоПравилу для записи в ТЧ ДополнительныеРеквизиты
// требует создания ПКО для этой ТЧ и передачи в него каждой строки.
// Это может быть избыточно для простых случаев и чревато перезаписью.
// Альтернатива - сохранить ТаблицуДопРеквизитов в ПараметрыОбъекта
// и обработать ее в ПослеЗагрузки ПКО контрагента.
Если ПараметрыОбъекта.Свойство("ДополнительныеРеквизитыДляЗагрузки") Тогда
ПараметрыОбъекта.ДополнительныеРеквизитыДляЗагрузки.Очистить();
ПараметрыОбъекта.ДополнительныеРеквизитыДляЗагрузки.Загрузить(ТаблицаДопРеквизитов);
Иначе
ПараметрыОбъекта.Вставить("ДополнительныеРеквизитыДляЗагрузки", ТаблицаДопРеквизитов);
КонецЕсли;
КонецЕсли;
Важный нюанс этого метода: если вы пытаетесь напрямую заполнить табличную часть "ДополнительныеРеквизиты" через Правила Конвертации Группы Свойств (ПКГС) или аналогичным способом, существует риск перезаписи всех существующих дополнительных реквизитов в объекте-приемнике. Это означает, что если в приемнике уже были какие-либо дополнительные реквизиты, введенные пользователем или другими обменами, они могут быть удалены. Это критично, если в приемнике могут быть свои специфические доп. реквизиты, которые не должны быть затронуты обменом. Об этом предупреждает участник форума в сообщении 5, а сообщение 8 объясняет важность сохранения таких данных.
Этот метод подразумевает создание ПКГС для табличной части ДополнительныеРеквизиты объекта-приемника. Однако, вместо того чтобы указывать источник данных напрямую, мы программно формируем необходимые строки в обработчике события ПередОбработкой этого ПКГС.
ДополнительныеРеквизиты объекта "Контрагент" в приемнике. Не указывайте источник для этого ПКГС.ПередОбработкой вы получаете данные из источника (например, через запрос, как в предыдущем примере) и программно заполняете эту временную таблицу значений.ДополнительныеРеквизиты создайте Правила Конвертации Свойств (ПКС) для полей "Свойство", "Значение" и, возможно, других (например, "ВидСвойства", "НомерСтроки" и т.д.), указывая их соответствие колонкам вашей программно сформированной таблицы значений.Примерный алгоритм:
// В обработчике "ПередОбработкой" ПКГС для табличной части "ДополнительныеРеквизиты"
// Параметры: ОбъектИсточник, ОбъектПриемник, ТаблицаЗначений, Правило
// ТаблицаЗначений - это уже созданная временная таблица, которую мы будем заполнять
// Она автоматически создается механизмом КД, нам нужно ее только наполнить.
// Предполагаем, что ОбъектИсточник - это текущий Контрагент из исходной базы.
// Мы используем его для получения связанных контактных лиц.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КонтактныеЛицаСрезПоследних.КонтактноеЛицо КАК КонтактноеЛицо
|ИЗ
| РегистрСведений.КонтактныеЛицаКонтрагентов.СрезПоследних(&Дата, Контрагент = &Контрагент) КАК КонтактныеЛицаСрезПоследних
|ГДЕ
| КонтактныеЛицаСрезПоследних.КонтактноеЛицо.Имя <> """; // Проверим, что имя не пустое
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", ОбъектИсточник);
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Индекс = 1;
Пока РезультатЗапроса.Следующий() Цикл
// Добавляем новую строку во временную ТаблицуЗначений
СтрокаТЗ = ТаблицаЗначений.Добавить();
// Заполняем "Свойство"
// Здесь мы должны найти соответствующий элемент ПланаВидовХарактеристик "ДополнительныеРеквизитыИСведения"
// или создать его, если его нет.
// Для примера используем просто строку, но в реальной системе это должна быть ссылка.
// На практике, сначала нужно выгрузить сами ВидыДополнительныхРеквизитов.
// Для простоты примера, представим, что мы ищем существующий вид свойства по наименованию
// Или создаем его, если он не найден
// Например: Справочники.ВидыДополнительныхРеквизитов.НайтиПоНаименованию("КонтактноеЛицо")
// В рамках ПКГС, если у нас есть ПКС для "Свойство" и "Значение",
// мы просто заполняем колонки ТаблицыЗначений, которые будут отображены на эти ПКС.
// Колонка "Свойство" в ТЧ ДополнительныеРеквизиты – это ссылка на ПВХ.
// Чтобы получить эту ссылку, нам нужно либо заранее убедиться, что соответствующий
// элемент ПВХ существует в приемнике, либо создать его.
// Для примера, возьмем Наименование контактного лица как Значение,
// а "КонтактноеЛицо" как наименование вида свойства.
// Реальный код для получения ссылки на вид свойства будет сложнее и потребует
// работы с ПланомВидовХарактеристик.
// Например:
// ВидСвойства = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("КонтактноеЛицо");
// Если ВидСвойства = Неопределено Тогда
// ВидСвойства = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
// ВидСвойства.Наименование = "КонтактноеЛицо";
// ВидСвойства.ТипЗначения = Новый ОписаниеТипов("Строка"); // Или СправочникСсылка.ФизическиеЛица
// ВидСвойства.Записать();
// КонецЕсли;
// СтрокаТЗ.Свойство = ВидСвойства; // Это правильный подход для поля "Свойство"
СтрокаТЗ.Вставить("Свойство", "КонтактноеЛицо_" + Индекс); // Для простоты примера, как строковое представление
СтрокаТЗ.Вставить("Значение", РезультатЗапроса.КонтактноеЛицо.Наименование); // ФИО или наименование
// Если "Значение" должно быть ссылкой, то нужно будет создать ПКС для этого поля
// и настроить его соответствующим образом.
Индекс = Индекс + 1;
КонецЦикла;
Как и в первом методе, данный подход может привести к перезаписи существующей табличной части "ДополнительныеРеквизиты", если не предусмотрена специальная логика для сохранения или обновления. Это важный аспект, который необходимо учитывать при проектировании обмена.
Этот метод считается одним из наиболее гибких и безопасных, поскольку он позволяет сначала передать "сырые" данные в приемник в виде параметров, а затем программно обработать их в событии ПослеЗагрузки ПКО, учитывая уже существующие данные и бизнес-логику. Этот подход особенно актуален для предотвращения перезаписи существующих дополнительных реквизитов, о чем было упомянуто в сообщениях 5 и 8.
ПараметрыОбъекта ПКО.ПослеЗагрузки его ПКО.ОбъектПриемник (экземпляр объекта в приемнике) и к коллекции ПараметрыОбъекта, в которой находятся переданные данные о контактных лицах.ДополнительныеРеквизиты объекта-приемника, чтобы проверить наличие уже существующих реквизитов.ПараметрыОбъекта.Рассмотрим подробный пример кода для обработчика "ПослеЗагрузки" ПКО "Контрагент":
// В обработчике события "ПослеЗагрузки" ПКО "Контрагент"
// Параметры: ОбъектПриемник, ОбъектИсточник, Отказ, ПараметрыОбъекта, Правило
// 1. Получаем переданные данные о контактных лицах из ПараметрыОбъекта
// Предположим, мы передали ТаблицуЗначений с колонками "Имя" и "Должность"
ТаблицаКонтактныхЛиц = Неопределено;
Если ПараметрыОбъекта.Свойство("КонтактныеЛицаДляДобавления") Тогда
ТаблицаКонтактныхЛиц = ПараметрыОбъекта.КонтактныеЛицаДляДобавления;
КонецЕсли;
Если ТаблицаКонтактныхЛиц = Неопределено Или ТаблицаКонтактныхЛиц.Количество() = 0 Тогда
Возврат; // Нет данных для добавления
КонецЕсли;
// 2. Получаем ссылку на ПланВидовХарактеристик "ДополнительныеРеквизитыИСведения"
// Это очень важно, т.к. поле "Свойство" в ТЧ ДополнительныеРеквизиты является ссылкой на элемент этого ПВХ.
// Убедитесь, что все необходимые виды свойств (например, "КонтактноеЛицо") существуют в приемнике.
// Если их нет, вы можете создать их программно или заранее загрузить через другие ПКО/ПВХ.
// Пример получения или создания вида свойства "КонтактноеЛицо"
// В реальной системе эти виды свойств должны быть заранее выгружены или созданы.
// Допустим, мы создаем вид свойства для "ФИО КонтактногоЛица"
ВидСвойстваФИО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ФИО КонтактногоЛица");
Если ВидСвойстваФИО = Неопределено Тогда
ВидСвойстваФИО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
ВидСвойстваФИО.Наименование = "ФИО КонтактногоЛица";
ВидСвойстваФИО.ТипЗначения = Новый ОписаниеТипов("Строка");
ВидСвойстваФИО.ОбъектНазначения.Добавить(ОбъектПриемник.Метаданные()); // Указываем, для какого объекта
Попытка
ВидСвойстваФИО.Записать();
Исключение
Сообщить("Ошибка создания вида свойства 'ФИО КонтактногоЛица': " + ОписаниеОшибки());
Возврат;
КонецПопытки;
КонецЕсли;
// Аналогично для "Должность КонтактногоЛица"
ВидСвойстваДолжность = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Должность КонтактногоЛица");
Если ВидСвойстваДолжность = Неопределено Тогда
ВидСвойстваДолжность = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
ВидСвойстваДолжность.Наименование = "Должность КонтактногоЛица";
ВидСвойстваДолжность.ТипЗначения = Новый ОписаниеТипов("Строка");
ВидСвойстваДолжность.ОбъектНазначения.Добавить(ОбъектПриемник.Метаданные());
Попытка
ВидСвойстваДолжность.Записать();
Исключение
Сообщить("Ошибка создания вида свойства 'Должность КонтактногоЛица': " + ОписаниеОшибки());
Возврат;
КонецПопытки;
КонецЕсли;
// 3. Обходим переданные контактные лица и добавляем/обновляем доп. реквизиты
Для Каждого СтрокаКЛ Из ТаблицаКонтактныхЛиц Цикл
// Проверяем, существуют ли уже такие доп. реквизиты в объекте-приемнике
// Мы ищем по имени свойства, чтобы не дублировать
// Для ФИО:
НайденоФИО = Ложь;
Для Каждого ДопРек Из ОбъектПриемник.ДополнительныеРеквизиты Цикл
Если ДопРек.Свойство = ВидСвойстваФИО И ДопРек.Значение = СтрокаКЛ.Имя Тогда
НайденоФИО = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не НайденоФИО Тогда
// Добавляем новый дополнительный реквизит для ФИО
НовыйДопРек = ОбъектПриемник.ДополнительныеРеквизиты.Добавить();
НовыйДопРек.Свойство = ВидСвойстваФИО;
НовыйДопРек.Значение = СтрокаКЛ.Имя;
КонецЕсли;
// Для Должности:
НайденоДолжность = Ложь;
Если СтрокаКЛ.Свойство("Должность") И ЗначениеЗаполнено(СтрокаКЛ.Должность) Тогда
Для Каждого ДопРек Из ОбъектПриемник.ДополнительныеРеквизиты Цикл
Если ДопРек.Свойство = ВидСвойстваДолжность И ДопРек.Значение = СтрокаКЛ.Должность Тогда
НайденоДолжность = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не НайденоДолжность Тогда
// Добавляем новый дополнительный реквизит для Должности
НовыйДопРек = ОбъектПриемник.ДополнительныеРеквизиты.Добавить();
НовыйДопРек.Свойство = ВидСвойстваДолжность;
НовыйДопРек.Значение = СтрокаКЛ.Должность;
КонецЕсли;
КонецЕсли;
// В более сложных случаях можно обновлять значения, если они изменились
// Для этого нужно найти существующий доп. реквизит по "Свойству"
// и затем обновить его "Значение".
// Например:
// ДопРеквизитДляОбновления = ОбъектПриемник.ДополнительныеРеквизиты.Найти(ВидСвойстваФИО, "Свойство");
// Если ДопРеквизитДляОбновления <> Неопределено Тогда
// ДопРеквизитДляОбновления.Значение = СтрокаКЛ.Имя; // Обновляем
// Иначе
// // Добавляем, если не найден
// КонецЕсли;
КонецЦикла;
Преимущества этого метода:
Свойству и <СтрокаКЛ.Должность> Значению.Независимо от выбранного метода, мы рекомендуем учитывать следующие моменты:
ВидыДополнительныхРеквизитовИСведений (например, "ФИО КонтактногоЛица", "Должность КонтактногоЛица") существуют в базе-приемнике. Их можно либо создать вручную, либо выгрузить через отдельные ПКО.ПВХ.ДополнительныеРеквизитыИСведения. Если вы хотите хранить ссылку на справочник "ФизическиеЛица" или "КонтактныеЛица", то тип должен быть соответствующим. Если это просто строка (ФИО), то тип "Строка".В итоге, хотя автор темы и решил свою задачу с использованием метода, близкого к первому или второму (сообщение 7), наиболее надежный и гибкий подход, особенно для сохранения существующих данных, – это передача контактных лиц через параметры в ПКС и последующая программная обработка в событии ПослеЗагрузки ПКО. Этот метод позволяет тонко настроить логику добавления дополнительных реквизитов, избегая нежелательной перезаписи.