Как программно изменить владельца подчинённого справочника в 1С:Предприятии 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке обработок и переносе данных в платформе 1С:Предприятие 7.7 нередко возникает задача переподчинить элемент одного справочника другому родителю (владельцу). На первый взгляд операция проста: найти элемент, указать нового владельца через соответствующий реквизит и вызвать метод записи. Однако разработчики часто сталкиваются с типовыми ошибками: «Неверный тип вышестоящего справочника!» или «Не выбран элемент!». Рассмотрим детально, почему они возникают и как правильно сменить владельца подчинённого справочника.

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

Разберём типичный пример исходного ошибочного кода:


цеха = СоздатьОбъект("Справочник.Цеха");
подр = СоздатьОбъект("Справочник.Подразделения");
подр.НайтиПоКоду("0000015");
цеха.НайтиПоКоду("12");
подр.Владелец = цеха.ТекущийЭлемент();
подр.Записать();

В данном сценарии возникают две фундаментальные проблемы:

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

Способ 1. Использование диалога или интерактивного выбора элементов

Самый надёжный и прямолинейный подход для пользовательских обработок — разместить на форме два поля выбора соответствующих типов либо воспользоваться интерактивным выбором. Когда у нас уже есть готовые ссылки на конкретный переносимый элемент и его нового владельца, мы избегаем проблем с коллизиями кодов и контекстом подчинения.

Рассмотрим пример кода для обработки с реквизитами диалога ВыбЦех (тип Справочник.Цеха) и ВыбПодразделение (тип Справочник.Подразделения):


цеха = СоздатьОбъект("Справочник.Цеха");

Если цеха.НайтиЭлемент(ВыбЦех) = 1 Тогда
    цеха.Владелец = ВыбПодразделение;
    цеха.Записать();
    Сообщить("Владелец успешно изменён!");
Иначе
    Сообщить("Не удалось спозиционироваться на выбранном элементе!");
КонецЕсли;

Если требуется запросить элементы у пользователя без создания дополнительных полей на форме диалога, применим функцию ВвестиЗначение:


Перем ВыбЦех, ВыбПодразделение;

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

Способ 2. Программный поиск по коду по всей таблице справочника

Если обработка выполняется в пакетном режиме и поиск происходит исключительно по строковым кодам, обратим внимание на второй параметр функции НайтиПоКоду. Передача значения 0 отключает ограничение по владельцу и указывает платформе искать элемент по всей таблице справочника.

Разберём программную реализацию с обязательной проверкой результатов выполнения поиска:


цеха = СоздатьОбъект("Справочник.Цеха");
подр = СоздатьОбъект("Справочник.Подразделения");

Если подр.НайтиПоКоду("0000015") = 0 Тогда
    Сообщить("Новое подразделение с указанным кодом не найдено!");
    Возврат;
КонецЕсли;

// Второй параметр 0 отключает поиск в пределах подчинения
Если цеха.НайтиПоКоду("12", 0) = 1 Тогда
    цеха.Владелец = подр.ТекущийЭлемент();
    цеха.Записать();
    Сообщить("Цех успешно переподчинен новому подразделению!");
Иначе
    Сообщить("Цех с кодом 12 не найден в информационной базе!");
КонецЕсли;

Важно: если нумерация подчинённого справочника не является уникальной во всей базе данных (то есть коды уникальны только в рамках конкретного подразделения), поиск с параметром 0 найдёт первый попавшийся элемент с таким кодом. В таких условиях необходимо точно знать исходного владельца.

Способ 3. Поиск с предварительной установкой текущего владельца

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

Посмотрим на реализацию этого алгоритма:


цеха = СоздатьОбъект("Справочник.Цеха");
подрСтарое = СоздатьОбъект("Справочник.Подразделения");
подрНовое = СоздатьОбъект("Справочник.Подразделения");

Если (подрСтарое.НайтиПоКоду("0000010") = 1) И (подрНовое.НайтиПоКоду("0000015") = 1) Тогда
    // Задаем контекст старого владельца для точного поиска
    цеха.ИспользоватьВладельца(подрСтарое.ТекущийЭлемент());
    
    Если цеха.НайтиПоКоду("12", 1) = 1 Тогда
        цеха.Владелец = подрНовое.ТекущийЭлемент();
        цеха.Записать();
        Сообщить("Элемент успешно перенесен!");
    Иначе
        Сообщить("Указанный цех у старого подразделения не найден!");
    КонецЕсли;
КонецЕсли;

Особые нюансы и ограничения платформы

  1. Контроль уникальности кода при записи. При вызове метода Записать() платформа выполняет системную проверку. Если у нового владельца уже существует цех с кодом "12", а в конфигураторе включён контроль уникальности в пределах подчинения, операция завершится ошибкой дублирования кода.
  2. Блокировки объектов. Если переносимый элемент открыт для редактирования другим пользователем системы, метод Записать() вызовет исключение блокировки. При выполнении групповых изменений всегда оборачивайте массовые операции в транзакции через НачатьТранзакцию() и ЗафиксироватьТранзакцию() с обработкой ошибок.
← На главную