Как скопировать или перенести адреса подчиненного справочника с одного контрагента на другого в 1С?

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

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

Разберем типичные ошибки начинающих разработчиков при реализации такой задачи и рассмотрим надежные варианты программного решения.

Анализ типичных ошибок при работе со ссылочными данными

Посмотрим на частые ошибки, которые возникают при попытке перенести или скопировать данные через таблицу результатов запроса:

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

Вариант 1. Копирование элементов через метод Скопировать() (Рекомендуемый)

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

Разберем по шагам реализацию выборки и создания копий элементов:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	битАдреса.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.битАдресаКонтрагентов КАК битАдреса
    |ГДЕ
    |	битАдреса.Владелец = &ИсходныйКонтрагент";

Запрос.УстановитьПараметр("ИсходныйКонтрагент", КонтрагентИсточник);

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

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

Вариант 2. Создание нового объекта через ЗаполнитьЗначенияСвойств()

Рассмотрим альтернативный способ, когда создается чистый элемент справочника, а значения реквизитов копируются вручную или с помощью системной процедуры ЗаполнитьЗначенияСвойств().

Важно помнить: чтобы скопировать реквизиты, источником для ЗаполнитьЗначенияСвойств() должен выступать реальный объект или выборка запроса со всеми полями, а не просто ссылка на элемент:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	битАдреса.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.битАдресаКонтрагентов КАК битАдреса
    |ГДЕ
    |	битАдреса.Владелец = &ИсходныйКонтрагент";

Запрос.УстановитьПараметр("ИсходныйКонтрагент", КонтрагентИсточник);

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

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

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

Вариант 3. Перенос адресов без создания дублей (Смена владельца)

Если задача заключается не в дублировании адресов, а в их полном переносе от одного контрагента к другому, создавать новые элементы не требуется. Достаточно получить объект каждого существующего адреса и перезаписать реквизит Владелец:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	битАдреса.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.битАдресаКонтрагентов КАК битАдреса
    |ГДЕ
    |	битАдреса.Владелец = &ИсходныйКонтрагент";

Запрос.УстановитьПараметр("ИсходныйКонтрагент", КонтрагентИсточник);

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

Пока Выборка.Следующий() Цикл
    ОбъектАдрес = Выборка.Ссылка.ПолучитьОбъект();
    Если ОбъектАдрес <> Неопределено Тогда
        ОбъектАдрес.Владелец = КонтрагентПриемник;
        ОбъектАдрес.Записать();
    КонецЕсли;
КонецЦикла;

Важные технические нюансы при групповой обработке

  1. Контроль уникальности кодов: При использовании метода Скопировать() новый объект получает код исходного элемента. Если в подчиненном справочнике нумерация настроена в пределах всей базы, а не подчинения владельцу, возникнет ошибка нарушения уникальности. Чтобы избежать сбоя, перед записью всегда очищайте реквизит НовыйАдрес.Код = "" — тогда платформа присвоит следующий свободный номер автоматически.
  2. Использование транзакций: Оборачивание цикла в блок НачатьТранзакцию() ... ЗафиксироватьТранзакцию() существенно ускоряет запись сотен и тысяч элементов, так как СУБД фиксирует изменения одной порцией, а не создает отдельную транзакцию на каждый элемент.
  3. Использование выборки вместо выгрузки в ТЗ: Конструкция Запрос.Выполнить().Выбрать() и последующий обход через Пока Выборка.Следующий() потребляет меньше оперативной памяти по сравнению с Выгрузить(), так как данные считываются последовательно.
  4. Отключение лишних проверок: Если в модуле справочника на событиях ПередЗаписью или ПриЗаписи прописаны тяжелые проверки или интеграции с внешними сервисами, при массовом переносе можно включить режим загрузки: НовыйАдрес.ОбменДанными.Загрузка = Истина.
← На главную