Как программно реализовать копирование документа в 1С:Предприятие 7.7 по кнопке в форме

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

В платформе 1С:Предприятие 7.7 механизм копирования документа реализован как стандартная системная функция, доступная пользователю из журнала документов по клавише F9 или через кнопку с пиктограммой «плюс». Однако часто возникает задача добавить кнопку «Копировать» непосредственно в форму открытого документа. Это удобно для пользователей, которые хотят перевыставить счет или накладную, не закрывая текущий объект.

Основная сложность заключается в том, что обычный вызов ОткрытьФорму создает новый пустой документ, а предопределенная процедура ВводНового не получает признак копирования автоматически. Рассмотрим подробнее несколько способов решения этой задачи: от использования штатного механизма ввода на основании до применения внешних компонент.

Способ 1: Использование механизма «Ввод на основании»

Это самый «чистый» и архитектурно правильный метод в рамках стандартной платформы без использования сторонних расширений. Мы заставим систему думать, что новый документ создается на основании текущего, и перенесем данные в соответствующем обработчике.

Разберем реализацию по шагам:

  1. На форму документа (например, СчетНаОплату) добавим кнопку «Копировать».
  2. В формуле кнопки пропишем вызов функции, которая откроет форму нового документа того же вида.

Проанализируем код обработчика кнопки:


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

    // Открываем форму нового документа того же вида.
    // Третий параметр - текущий документ как основание.
    ОткрытьФорму("Документ.СчетНаОплату", , ТекущийДокумент());
КонецПроцедуры

Теперь нам необходимо настроить модуль самого документа. В платформе 7.7 при передаче третьего параметра в ОткрытьФорму система автоматически вызывает предопределенную процедуру ВводНаОсновании.

Рассмотрим код внутри модуля документа:


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

Способ 2: Универсальное копирование через метаданные

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

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


Процедура глСкопироватьРеквизиты(ДокИсточник, ДокПриемник)
    Перем ВидДок, МетаДок, к, ИдМетаРекв;
    
    ВидДок = ДокИсточник.Вид();
    МетаДок = Метаданные.Документ(ВидДок);
    
    // 1. Копируем общие реквизиты (если есть)
    Для к = 1 по Метаданные.ОбщийРеквизитДокумента() Цикл
        ИдМетаРекв = Метаданные.ОбщийРеквизитДокумента(к).Идентификатор;
        ДокПриемник.УстановитьАтрибут(ИдМетаРекв, ДокИсточник.ПолучитьАтрибут(ИдМетаРекв));
    КонецЦикла;
    
    // 2. Копируем реквизиты шапки
    Для к = 1 по МетаДок.РеквизитШапки() Цикл
        ИдМетаРекв = МетаДок.РеквизитШапки(к).Идентификатор;
        // Важно: не копируем номер и дату, чтобы не нарушить логику системы
        Если (ИдМетаРекв = "НомерДок") Или (ИдМетаРекв = "ДатаДок") Тогда
            Продолжить;
        КонецЕсли;
        ДокПриемник.УстановитьАтрибут(ИдМетаРекв, ДокИсточник.ПолучитьАтрибут(ИдМетаРекв));
    КонецЦикла;
    
    // 3. Копируем табличную часть
    ДокИсточник.ВыбратьСтроки();
    Пока ДокИсточник.ПолучитьСтроку() = 1 Цикл
        ДокПриемник.НоваяСтрока();
        Для к = 1 по МетаДок.РеквизитТабличнойЧасти() Цикл
            ИдМетаРекв = МетаДок.РеквизитТабличнойЧасти(к).Идентификатор;
            ДокПриемник.УстановитьАтрибут(ИдМетаРекв, ДокИсточник.ПолучитьАтрибут(ИдМетаРекв));
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

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

Способ 3: Использование внешней компоненты FormEx

Для продвинутых разработчиков существует способ максимально приблизить программное копирование к интерактивному (как по нажатию F9). Библиотека FormEx предоставляет специализированный метод КопироватьОбъект.

Разберем ситуацию: при использовании этого метода в новом документе признак Скопирован в процедуре ВводНового примет значение 1, а в переменную ДокКоп будет передана ссылка на оригинал.

Код вызова будет выглядеть следующим образом:


// Пример использования FormEx
Сервис = СоздатьОбъект("Сервис");
// КонтекстФормы - сюда вернется контекст открытой формы нового документа
Сервис.КопироватьОбъект(ТекущийДокумент(), КонтекстФормы);

Важное преимущество: в этом случае отрабатывает штатный код в модуле документа, который вы, возможно, уже написали для обработки стандартного копирования из журнала.


Процедура ВводНового(Скопирован, ДокКоп)
    Если Скопирован = 1 Тогда
        // Этот код сработает ПРИ ЛЮБОМ копировании: и через F9, и через кнопку с FormEx
        Сообщить("Создается копия документа " + ДокКоп.НомерДок);
        // Здесь ваше заполнение...
    КонецЕсли;
КонецПроцедуры

Почему не работает простая передача контекста?

Рассмотрим причину, по которой попытка автора (сообщение 0) передать Конт в ОткрытьФорму не приводила к результату. В 1С 7.7 второй параметр функции ОткрытьФорму предназначен для передачи значения в атрибут Форма.Параметр открываемой формы. Это значение можно прочитать в процедуре ПриОткрытии, но оно никак не влияет на предопределенную логику ВводНового.

Если вы хотите использовать именно этот механизм, вам придется вручную разбирать параметры:

  1. В кнопке: ОткрытьФорму("Документ.Счет", ТекущийДокумент()).
  2. В открываемой форме (в процедуре ПриОткрытии):
    
    Процедура ПриОткрытии()
        Если ТипЗначенияСтр(Форма.Параметр) = "Документ" Тогда
            // Заполняем реквизиты из Форма.Параметр...
        КонецЕсли;
    КонецПроцедуры
    

Однако помните, что на момент выполнения ВводНового значение Форма.Параметр еще может быть не инициализировано, поэтому лучше использовать либо Способ 1 (ВводНаОсновании), либо Способ 3 (FormEx).

Резюме и рекомендации

Проанализировав ситуацию, мы видим, что для решения задачи «кнопки для ленивых» оптимальным выбором будет Способ 1. Он не требует сторонних библиотек, использует штатные события платформы и понятен любому программисту 1С 7.7. Если же ваша конфигурация активно использует FormEx, то использование метода КопироватьОбъект будет самым коротким и эффективным решением, которое полностью эмулирует стандартное поведение системы.

← На главную