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