Как программно скопировать реквизиты и движения документа Расходная накладная

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

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

Этап 1. Копирование основных реквизитов и табличных частей

Первым делом нам необходимо создать новый объект документа и перенести в него данные из исходного. Разберем код, который позволяет это сделать эффективно. Мы будем использовать метод ЗаполнитьЗначенияСвойств, который существенно упрощает перенос данных между объектами с одинаковыми именами реквизитов.

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


&НаСервере
Процедура СкопироватьНаСервере(ИсточникСсылка)
    // Создаем новый объект документа
    НовыйДок = Документы.РасходнаяНакладная.СоздатьДокумент();
    
    // Копируем реквизиты шапки, исключая "Номер" и "Проведен"
    // Мы исключаем "Проведен", чтобы статус документа соответствовал реальности
    ЗаполнитьЗначенияСвойств(НовыйДок, ИсточникСсылка, , "Номер, Проведен");
    
    // Копируем табличную часть "Товары"
    Для Каждого СтрокаИсточника Из ИсточникСсылка.Товары Цикл
        НоваяСтрока = НовыйДок.Товары.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
    КонецЦикла;
    
    // Записываем документ, чтобы получить ссылку (она нужна для движений)
    НовыйДок.Записать(РежимЗаписиДокумента.Запись);
    
    // Теперь переходим к копированию движений
    СкопироватьДвижения(ИсточникСсылка, НовыйДок);
КонецПроцедуры

Этап 2. Программное копирование движений по регистрам

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

Выясним причину, почему нельзя просто вызвать проведение нового документа. В некоторых случаях логика проведения может зависеть от времени или оперативных остатков, которые изменились. Если нам нужно именно скопировать движения (сделать «слепок»), мы должны работать напрямую с коллекцией Движения.

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

  1. Обращаемся к метаданным исходного документа, чтобы узнать, по каким регистрам он может делать записи.
  2. Для каждого такого регистра создаем набор записей.
  3. Читаем записи, созданные исходным документом (регистратором).
  4. Если записи есть, выгружаем их в таблицу значений.
  5. Загружаем эту таблицу в набор записей нового документа.
  6. Устанавливаем для нового набора правильный Регистратор и записываем его.

Процедура СкопироватьДвижения(ИсточникСсылка, ПриемникОбъект)
    
    // Получаем метаданные документа для обхода всех возможных регистров
    МетаданныеДокумента = ИсточникСсылка.Метаданные();
    
    // Перебираем все регистры накопления, в которых может участвовать документ
    Для Каждого МетаданныеРегистра Из МетаданныеДокумента.Движения Цикл
        
        // Получаем имя регистра (например, "ОстаткиНоменклатуры")
        ИмяРегистра = МетаданныеРегистра.Имя;
        
        // Формируем набор записей для источника и приемника
        НаборИсточник = Движения[ИмяРегистра].СоздатьНаборЗаписей();
        НаборИсточник.Отбор.Регистратор.Установить(ИсточникСсылка);
        НаборИсточник.Прочитать();
        
        Если НаборИсточник.Количество() > 0 Тогда
            // Создаем набор для нового документа
            НаборПриемник = Движения[ИмяРегистра].СоздатьНаборЗаписей();
            НаборПриемник.Отбор.Регистратор.Установить(ПриемникОбъект.Ссылка);
            
            // Выгружаем записи в таблицу значений
            ТаблицаДвижений = НаборИсточник.Выгрузить();
            
            // Важно: так как мы копируем записи, в таблице в колонке Регистратор 
            // все еще старая ссылка. Но метод Загрузить() при записи набора 
            // автоматически подставит правильного регистратора из отбора.
            
            НаборПриемник.Загрузить(ТаблицаДвижений);
            
            // Записываем движения в базу данных
            НаборПриемник.Записать();
        КонецЕсли;
        
    КонецЦикла;

    // Устанавливаем статус проведенности, если источник был проведен
    Если ИсточникСсылка.Проведен Тогда
        ПриемникОбъект.Проведен = Истина;
        ПриемникОбъект.Записать(РежимЗаписиДокумента.Запись);
    КонецЕсли;
    
КонецПроцедуры

Важные технические нюансы

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

1. Порядок записи. Вы не можете создать записи в регистре для документа, который еще не сохранен в базе данных. Сначала мы вызываем НовыйДок.Записать(), чтобы система присвоила ему уникальную ссылку (UUID), и только потом используем эту ссылку для установки отбора в наборе записей регистра.

2. Режим записи. Обратите внимание, что мы используем РежимЗаписиДокумента.Запись. Если мы используем РежимЗаписиДокумента.Проведение, 1С запустит стандартный обработчик ОбработкаПроведения из модуля объекта, который может сформировать движения по своей логике, удалив наши скопированные данные.

3. Поле "Период". Обычно записи в регистрах имеют поле Период. При копировании оно сохраняется из исходного документа. Если новый документ создается другой датой, рассмотрите необходимость обновления поля Период в таблице значений перед загрузкой в новый набор записей.

4. Универсальность через метаданные. Использование коллекции Метаданные().Движения делает ваш код устойчивым к изменениям конфигурации. Если завтра в систему добавят новый регистр для «Расходной накладной», ваш алгоритм подхватит его автоматически без правки кода.

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

← На главную