Как запретить открытие формы после программного копирования элемента справочника и правильно скопировать иерархию

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

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

Выясним причину: почему открывается форма?

Для начала проанализируем ситуацию. Платформа 1С разделяет интерактивные действия пользователя и программные методы. Когда пользователь нажимает кнопку в списке, срабатывает стандартная команда. Алгоритм платформы следующий:

  1. Создается новый объект в памяти.
  2. Вызывается обработчик ПриКопировании в модуле объекта.
  3. Инициализируется создание формы нового объекта.
  4. Вызывается обработчик ПриСозданииНаСервере в модуле формы.
  5. Форма отображается пользователю.

Если вы пытаетесь писать код копирования внутри модуля объекта (например, в событии ПриКопировании), вы работаете уже внутри процесса, который инициировала платформа. Создание еще одного элемента методом СоздатьЭлемент() внутри этого события — грубая ошибка, которая может привести к бесконечной рекурсии или созданию лишних дублей, при этом первая «стандартная» копия все равно попытается открыться.

Решение №1: Использование обработчика в модуле формы

Если вам нужно оставить стандартную кнопку «Скопировать», но предотвратить открытие формы, мы можем использовать «жесткий», но действенный метод в модуле формы создаваемого элемента. Рассмотрим этот код:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, что элемент новый и он создается на основании другого (копирование)
    Если Объект.ЭтоНовый() И ЗначениеЗаполнено(Параметры.ЗначениеКопирования) Тогда
        // Здесь можно выполнить дополнительные действия по записи объекта
        // Объект.Записать(); 
        
        // Устанавливаем Отказ в Истина, чтобы форма не открылась
        Отказ = Истина;
    КонецЕсли;
    
КонецПроцедуры

В данном случае, когда платформа попытается открыть форму после копирования, срабатывает Отказ = Истина, и процесс визуализации прерывается. Однако стоит помнить, что этот метод является «обходным». Правильнее управлять процессом там, где он начинается.

Решение №2: Правильное программное копирование

Чтобы избежать открытия форм в принципе, нужно отказаться от использования стандартных интерактивных команд и реализовать копирование полностью программно. В этом случае форма даже не будет инициализироваться. Рассмотрим ситуацию, когда нам нужно скопировать элемент справочника. Вместо ручного присвоения каждого реквизита типа Спр.Наименование = Источник.Наименование, используем встроенный метод Заполнить().

Проанализируем пример кода для программного копирования:


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

Решение №3: Копирование иерархии (групп и элементов)

Задача усложняется, когда нужно скопировать не один элемент, а целую ветку справочника (группу со всеми вложенными элементами и подгруппами). Здесь нам поможет рекурсия. Посмотрим на алгоритм: мы должны создать новую группу, а затем найти всех «детей» старой группы и для каждого вызвать ту же самую функцию копирования, передав в качестве родителя только что созданную группу.

Разберем по шагам реализацию рекурсивного копирования иерархии:


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

Важные рекомендации и тонкости

При выполнении массового копирования или работы с иерархией, следует обратить внимание на несколько важных моментов, которые помогут избежать ошибок и повысить производительность:

Рассмотрим пример перехвата в форме списка:


&НаКлиенте
Процедура СписокПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа)
    
    Если Копирование Тогда
        Отказ = Истина; // Отменяем стандартное создание формы
        ТекущаяСсылка = Элементы.Список.ТекущаяСтрока;
        Если ЗначениеЗаполнено(ТекущаяСсылка) Тогда
            // Вызываем серверный метод для копирования
            СкопироватьПрограммноНаСервере(ТекущаяСсылка);
            // Обновляем список, чтобы увидеть результат
            Элементы.Список.Обновить();
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Таким образом, мы проанализировали ситуацию и выяснили, что наиболее надежным способом избежать открытия формы является программное создание объекта вне стандартного потока интерактивных команд. Использование метода Заполнить() обеспечивает корректный перенос данных, а рекурсивный обход позволяет сохранить целостность иерархии справочника.

← На главную