Как правильно скопировать документ Операция и его проводки в 1С

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

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

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

Анализ ошибки «Не установлен отбор по регистратору»

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


Проводки2 = РегистрыБухгалтерии.Типовой.СоздатьНаборЗаписей();
Проводки2.Отбор.Регистратор.Установить(НовДок);
Проводки2.Загрузить(НовыеПроводки);
Проводки2.Записать();

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

Важный момент: Чтобы исправить эту конкретную ошибку, необходимо передавать НовДок.Ссылка. Однако, как мы увидим далее, этот подход не является оптимальным для документов типа «Операция».

Оптимальный способ копирования движений

В системе 1С:Предприятие 8 каждый объект документа имеет свойство Движения. Это коллекция наборов записей всех регистров, по которым данный документ может делать движения. Использование этой коллекции — самый правильный и «чистый» путь. Выясним, почему это лучше:

  1. Автоматическая связь: Набор записей в коллекции Движения уже жестко связан с текущим документом-объектом. Вам не нужно вручную устанавливать отбор по регистратору.
  2. Целостность данных: При записи объекта документа платформа может автоматически записывать и его движения, если установлены соответствующие флаги.
  3. Производительность: Вам не нужно создавать лишние экземпляры объектов через РегистрыБухгалтерии...СоздатьНаборЗаписей().

Рассмотрим правильный алгоритм копирования:


// 1. Получаем объект исходного документа и копируем его
ИсходныйОбъект = ДокументСсылка.ПолучитьОбъект();
НовДок = ИсходныйОбъект.Скопировать();

// 2. Модифицируем необходимые реквизиты
НовДок.Дата = ТекущаяДата();
НовДок.Комментарий = "Создано программно на основании " + ИсходныйОбъект.Номер;

// 3. Записываем новый документ, чтобы получить ссылку
НовДок.Записать(РежимЗаписиДокумента.Запись);

// 4. Работаем с движениями через коллекцию Движения
// Предположим, имя регистра в вашей конфигурации - "Хозрасчетный"
НаборДвижений = НовДок.Движения.Хозрасчетный;

// Читаем движения исходного документа (если они не подтянулись при копировании)
// В случае с документом ОперацияБух, движения часто копируются вместе с объектом
// Но если мы переносим данные из таблицы значений НовыеПроводки:
НаборДвижений.Загрузить(НовыеПроводки);

// 5. Записываем проводки
НаборДвижений.Записать();

Нюансы работы с методом Загрузить()

Метод Загрузить() крайне удобен для массового заполнения набора записей из таблицы значений. Но при его использовании важно помнить о структуре колонок. Проанализируем важные детали:

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

Субконто: Это самый сложный момент при программной работе с регистром бухгалтерии. Субконто не являются обычными колонками таблицы. Это вложенная коллекция. Если ваша таблица значений НовыеПроводки была получена методом Выгрузить() из другого набора записей, то Загрузить() отработает корректно. Если же вы формируете таблицу вручную, вам придется программно заполнять субконто для каждой записи после загрузки или использовать метод Добавить().

Использование флага «Записывать»

В 1С существует механизм, позволяющий минимизировать количество вызовов метода Записать(). Рассмотрим ситуацию с использованием свойства Записывать коллекции движений. Если установить НовДок.Движения.Хозрасчетный.Записывать = Истина;, то движения будут сохранены автоматически в момент вызова НовДок.Записать(). Это гарантирует атомарность операции: либо запишется и документ, и проводки, либо не запишется ничего.

Пример оптимизированного кода:


НовДок = ДокументОперация.ПолучитьОбъект().Скопировать();
НовДок.Записать(РежимЗаписиДокумента.Запись); // Получаем ссылку

// Загружаем новые проводки в коллекцию движений объекта
НовДок.Движения.Хозрасчетный.Загрузить(НовыеПроводки);

// Указываем системе, что движения нужно сохранить при записи документа
НовДок.Движения.Хозрасчетный.Записывать = Истина;

// Повторная запись объекта сохранит и сам документ, и загруженные движения
НовДок.Записать(РежимЗаписиДокумента.Запись);

Транзакции и безопасность данных

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

Рассмотрим пример с использованием транзакции:


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

Подведем итоги: Для успешного копирования проводок в 1С 8.3 наиболее эффективно использовать свойство Движения самого объекта документа. Это избавляет от необходимости вручную управлять отборами по регистратору и минимизирует риск возникновения ошибок доступа к данным. Всегда проверяйте соответствие типов (объект vs ссылка) и не забывайте про специфику заполнения субконто в бухгалтерских регистрах.

← На главную