Почему при проведении документа в 1С создается только одно движение из двух?

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

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

Основная задача — создать документ, который будет делать два движения в регистре накопления "Баланс":

  1. Расход — списание суммы со счета-отправителя.
  2. Приход — зачисление этой же суммы на счет-получателя.

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

Анализ ошибочного кода

Давайте посмотрим на исходный код, который приводит к такому поведению. Он размещен в модуле объекта документа, в процедуре ОбработкаПроведения.


Процедура ОбработкаПроведения(Отказ, Режим)
    Расход = Движения.Баланс.Добавить();
    Расход.Счёт = Отправитель;
    Расход.ВидДвижения = ВидДвиженияНакопления.Расход;
    Расход.Период = Дата;
    Расход.Сумма = Сумма;
     
    Движения.Баланс.Очистить(); // <-- Ключевая ошибка здесь!
     
    Приход = Движения.Баланс.Добавить();
    Приход.Счёт = Получатель;     
    Приход.ВидДвижения = ВидДвиженияНакопления.Приход;
    Приход.Период = Дата;
    Приход.Сумма = Сумма;           
КонецПроцедуры

Проанализируем по шагам, что происходит при выполнении этого кода:

  1. Расход = Движения.Баланс.Добавить(); — платформа создает в памяти новый объект записи для регистра "Баланс" и добавляет его в коллекцию движений этого документа.
  2. Далее заполняются свойства этой записи: Счёт, ВидДвижения, Период, Сумма. На этом этапе в памяти у нас есть одна готовая к записи строка — расход.
  3. Движения.Баланс.Очистить(); — а вот и причина проблемы. Этот метод полностью очищает всю коллекцию движений по регистру "Баланс", которая была подготовлена в памяти. Только что созданная запись о расходе безвозвратно удаляется.
  4. Приход = Движения.Баланс.Добавить(); — в теперь уже пустую коллекцию движений добавляется новая запись.
  5. Заполняются свойства для записи о приходе.
  6. КонецПроцедуры — процедура завершается. Платформа 1С берет коллекцию движений (которая на данный момент содержит только одну запись о приходе) и записывает ее в базу данных.

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

Правильное формирование движений документа

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

Корректный код для процедуры проведения:


Процедура ОбработкаПроведения(Отказ, Режим)
    // Устанавливаем флаг, чтобы при перепроведении документа
    // старые движения этого документа автоматически удалялись.
    Движения.Баланс.Записывать = Истина;

    // 1. Формируем движение по списанию средств (Расход)
    Расход = Движения.Баланс.Добавить();
    Расход.ВидДвижения = ВидДвиженияНакопления.Расход;
    Расход.Период = Дата;
    Расход.Счёт = Отправитель; // Реквизит документа "Отправитель"
    Расход.Сумма = Сумма;      // Реквизит документа "Сумма"

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

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

Разбор ключевых методов и свойств

Чтобы закрепить понимание, давайте подробнее остановимся на объектах и свойствах, с которыми мы работали.

Свойство Движения.ИмяРегистра.Записывать

Это свойство — ваш лучший друг при разработке проведения документов. Установка Движения.Баланс.Записывать = Истина; говорит платформе: "Перед тем как записать новый набор движений этого документа, пожалуйста, найди и удали все старые движения, которые этот же документ сделал по этому же регистру ранее".

Метод Движения.ИмяРегистра.Добавить()

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

Метод Движения.ИмяРегистра.Очистить()

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

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

← На главную