При разработке обработок и переносе данных в платформе 1С:Предприятие 7.7 нередко возникает задача переподчинить элемент одного справочника другому родителю (владельцу). На первый взгляд операция проста: найти элемент, указать нового владельца через соответствующий реквизит и вызвать метод записи. Однако разработчики часто сталкиваются с типовыми ошибками: «Неверный тип вышестоящего справочника!» или «Не выбран элемент!». Рассмотрим детально, почему они возникают и как правильно сменить владельца подчинённого справочника.
Разберём типичный пример исходного ошибочного кода:
цеха = СоздатьОбъект("Справочник.Цеха");
подр = СоздатьОбъект("Справочник.Подразделения");
подр.НайтиПоКоду("0000015");
цеха.НайтиПоКоду("12");
подр.Владелец = цеха.ТекущийЭлемент();
подр.Записать();
В данном сценарии возникают две фундаментальные проблемы:
Владелец неподчинённого справочника (или справочника, подчиненного другой структуре) ссылку на несовместимый тип вызывает исключение: «Неверный тип вышестоящего справочника!». В метаданных именно справочник Цеха подчинён справочнику Подразделения, а не наоборот. Следовательно, менять владельца необходимо у объекта Цеха.НайтиПоКоду("12") по умолчанию ищет элемент только внутри уже установленного контекста владельца. Если владелец не был явно задан через ИспользоватьВладельца(), метод возвращает 0 (элемент не найден). Попытка прочитать или записать свойства неполученного элемента немедленно вызывает ошибку: «Не выбран элемент!».Самый надёжный и прямолинейный подход для пользовательских обработок — разместить на форме два поля выбора соответствующих типов либо воспользоваться интерактивным выбором. Когда у нас уже есть готовые ссылки на конкретный переносимый элемент и его нового владельца, мы избегаем проблем с коллизиями кодов и контекстом подчинения.
Рассмотрим пример кода для обработки с реквизитами диалога ВыбЦех (тип Справочник.Цеха) и ВыбПодразделение (тип Справочник.Подразделения):
цеха = СоздатьОбъект("Справочник.Цеха");
Если цеха.НайтиЭлемент(ВыбЦех) = 1 Тогда
цеха.Владелец = ВыбПодразделение;
цеха.Записать();
Сообщить("Владелец успешно изменён!");
Иначе
Сообщить("Не удалось спозиционироваться на выбранном элементе!");
КонецЕсли;
Если требуется запросить элементы у пользователя без создания дополнительных полей на форме диалога, применим функцию ВвестиЗначение:
Перем ВыбЦех, ВыбПодразделение;
Если ВвестиЗначение(ВыбПодразделение, "Выберите новое подразделение", "Справочник.Подразделения") = 1 Тогда
Если ВвестиЗначение(ВыбЦех, "Выберите перемещаемый цех", "Справочник.Цеха") = 1 Тогда
цеха = СоздатьОбъект("Справочник.Цеха");
Если цеха.НайтиЭлемент(ВыбЦех) = 1 Тогда
цеха.Владелец = ВыбПодразделение;
цеха.Записать();
Сообщить("Элемент успешно перемещен к новому владельцу.");
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если обработка выполняется в пакетном режиме и поиск происходит исключительно по строковым кодам, обратим внимание на второй параметр функции НайтиПоКоду. Передача значения 0 отключает ограничение по владельцу и указывает платформе искать элемент по всей таблице справочника.
Разберём программную реализацию с обязательной проверкой результатов выполнения поиска:
цеха = СоздатьОбъект("Справочник.Цеха");
подр = СоздатьОбъект("Справочник.Подразделения");
Если подр.НайтиПоКоду("0000015") = 0 Тогда
Сообщить("Новое подразделение с указанным кодом не найдено!");
Возврат;
КонецЕсли;
// Второй параметр 0 отключает поиск в пределах подчинения
Если цеха.НайтиПоКоду("12", 0) = 1 Тогда
цеха.Владелец = подр.ТекущийЭлемент();
цеха.Записать();
Сообщить("Цех успешно переподчинен новому подразделению!");
Иначе
Сообщить("Цех с кодом 12 не найден в информационной базе!");
КонецЕсли;
Важно: если нумерация подчинённого справочника не является уникальной во всей базе данных (то есть коды уникальны только в рамках конкретного подразделения), поиск с параметром 0 найдёт первый попавшийся элемент с таким кодом. В таких условиях необходимо точно знать исходного владельца.
Если старый владелец элемента известен заранее, наиболее корректно сначала спозиционировать объект на старом владельце с помощью метода ИспользоватьВладельца, найти нужный элемент и только потом переназначать реквизит Владелец.
Посмотрим на реализацию этого алгоритма:
цеха = СоздатьОбъект("Справочник.Цеха");
подрСтарое = СоздатьОбъект("Справочник.Подразделения");
подрНовое = СоздатьОбъект("Справочник.Подразделения");
Если (подрСтарое.НайтиПоКоду("0000010") = 1) И (подрНовое.НайтиПоКоду("0000015") = 1) Тогда
// Задаем контекст старого владельца для точного поиска
цеха.ИспользоватьВладельца(подрСтарое.ТекущийЭлемент());
Если цеха.НайтиПоКоду("12", 1) = 1 Тогда
цеха.Владелец = подрНовое.ТекущийЭлемент();
цеха.Записать();
Сообщить("Элемент успешно перенесен!");
Иначе
Сообщить("Указанный цех у старого подразделения не найден!");
КонецЕсли;
КонецЕсли;
Записать() платформа выполняет системную проверку. Если у нового владельца уже существует цех с кодом "12", а в конфигураторе включён контроль уникальности в пределах подчинения, операция завершится ошибкой дублирования кода.Записать() вызовет исключение блокировки. При выполнении групповых изменений всегда оборачивайте массовые операции в транзакции через НачатьТранзакцию() и ЗафиксироватьТранзакцию() с обработкой ошибок.