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

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

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

Выясняем причину: почему исчезают движения?

Рассмотрим ситуацию, с которой часто сталкиваются разработчики. Для записи движений при проведении документа пишется код, использующий создание независимого набора записей напрямую через менеджер регистра:


Набор = РегистрыНакопления.НовыйРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
// Заполнение набора записями...
Набор.Записать();

Этот код действительно записывает движения в базу данных в момент вызова метода Записать(). Однако сразу после завершения процедуры ОбработкаПроведения эти записи бесследно исчезают. Почему это происходит?

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

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

Стандартный и правильный способ записи движений при проведении

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

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

Шаг 2. Заполнение движений в цикле. Мы обходим строки табличной части документа и добавляем новые записи в коллекцию.

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

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


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // Очищаем движения по нашему регистру в памяти
    Движения.НовыйРегистр.Очистить();
    
    // Обходим табличную часть документа "Товары"
    Для Каждого ТекСтрока Из Товары Цикл
        
        // Проверяем необходимое условие проведения
        Если ТекСтрока.Количество > 0 Тогда
            
            // Добавляем новую запись напрямую в коллекцию движений документа
            Движение = Движения.НовыйРегистр.Добавить();
            
            // Заполняем обязательные свойства и измерения
            Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
            Движение.Период      = Дата;
            Движение.Номенклатура = ТекСтрока.Номенклатура;
            Движение.Склад        = Справочники.Склады.НайтиПоКоду("000000056"); // Пример статического склада
            Движение.Количество   = ТекСтрока.Количество;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Сигнализируем платформе о необходимости записать этот регистр в БД
    Движения.НовыйРегистр.Записывать = Истина;
    
КонецПроцедуры

При таком подходе вызывать метод Записать() вручную внутри кода проведения не нужно. Платформа 1С выполнит запись набора в базу данных автоматически и максимально оптимально в рамках общей транзакции проведения документа.

Как влияют настройки свойств документа на запись движений

Поведение платформы при завершении транзакции проведения во многом определяется свойствами документа, заданными в конфигураторе на вкладке "Движения":

  1. Свойство «Запись движений при проведении»:
    • Записывать выбранные (рекомендуемое значение): Платформа запишет только те регистры, у которых в коде было явно установлено свойство Движения.ИмяРегистра.Записывать = Истина.
    • Записывать модифицированные: Платформа самостоятельно отслеживает изменения в коллекциях и записывает только те наборы, в которые были добавлены или изменены строки.
  2. Свойство «Удаление движений»:
    • Удалять автоматически: Перед началом проведения платформа сама очищает все старые движения этого документа по всем регистрам в базе данных.
    • Не удалять автоматически: Старые движения не удаляются автоматически. Разработчик должен сам управлять очисткой старых записей. Это свойство часто применяется при использовании "новой" методики проведения для оптимизации производительности СУБД.

Когда действительно нужно использовать СоздатьНаборЗаписей?

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

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


// Создаем независимый набор записей для конкретного документа
Набор = РегистрыНакопления.НовыйРегистр.СоздатьНаборЗаписей();

// Устанавливаем жесткий отбор по регистратору (конкретному документу)
Набор.Отбор.Регистратор.Установить(ДокументСсылка);

// Если нужно дополнить существующие движения, сначала читаем их из БД
// Если нужно полностью перезаписать новыми — этот шаг можно пропустить
Набор.Прочитать();

// Очищаем старые записи, если требуется полная перезапись
Набор.Очистить();

// Добавляем новую запись
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Регистратор  = ДокументСсылка;
НоваяЗапись.Период       = ДокументСсылка.Дата;
НоваяЗапись.ВидДвижения  = ВидДвиженияНакопления.Приход;
НоваяЗапись.Номенклатура = ВыбраннаяНоменклатура;
НоваяЗапись.Количество   = 10;

// Рекомендуется использовать режим обмена данными при программной корректировке
Набор.ОбменДанными.Загрузка = Истина;

// Записываем набор. Параметр "Истина" означает замещение старых движений новыми
Набор.Записать(Истина);

Разбор критической ошибки: Ручные транзакции при проведении

Проанализируем еще одну частую ошибку из практики — использование конструкций НачатьТранзакцию() и ЗафиксироватьТранзакцию() внутри обработчика проведения.

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

Поэтому никогда не используйте явное управление транзакциями внутри обработчиков проведения документов.

← На главную