Почему удаляются записи в регистре при повторном проведении документа через подписку

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

При разработке механизмов автоматизации в 1С:Предприятии 8.3 программисты часто используют подписки на события, чтобы не вносить изменения в типовые конфигурации (например, в «1С:Бухгалтерию»). Однако при работе с движениями регистров в подписке на событие ОбработкаПроведения часто возникает неприятная ситуация: при первом проведении документа записи в регистр попадают, а при повторном — бесследно исчезают. Проанализируем, почему это происходит, и как правильно организовать запись данных.

Разбор типичной ошибки в коде

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


// Ошибочный подход в подписке
Движения = РегистрыНакопления.Проекты.СоздатьНаборЗаписей();
Движения.Отбор.Регистратор.Установить(Источник.Ссылка);
Движения.Прочитать();
Движения.Очистить();
НоваяСтрока = Движения.Добавить();
НоваяСтрока.Проект = Источник.Проект;
// ... заполнение других полей
Движения.Записать();

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

Причина №1: Конфликт с коллекцией «Движения» объекта

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

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

  1. Запускается транзакция проведения.
  2. Выполняется код модуля документа.
  3. Выполняются все подписки на событие ОбработкаПроведения.
  4. Завершение транзакции: Платформа берет коллекцию Источник.Движения и для каждого набора записей проверяет свойство Записывать. Если в метаданных документа установлено «Записывать выбранные», а в коллекции документа набор записей регистра «Проекты» пуст и флаг Записывать равен Ложь (или даже Истина, но набор пуст), платформа может «синхронизировать» состояние базы со своей внутренней памятью.

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

Причина №2: Свойство документа «Удаление движений»

Проанализируем настройки метаданных документа. На вкладке «Движения» есть свойство «Удаление движений». Рассмотрим два варианта:

Если в документе стоит режим «Записывать выбранные», то платформа ожидает, что программист будет управлять свойством Записывать каждого конкретного регистра в коллекции Движения.

Правильное решение: Работа через коллекцию Источника

Вместо того чтобы создавать отдельный объект НаборЗаписей через менеджер регистра, мы должны использовать уже существующий набор в объекте документа. Рассмотрим, как переписать код правильно:


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

// Если нужно гарантированно перезаписать данные
НаборДвижений.Очистить(); 

НоваяСтрока = НаборДвижений.Добавить();
НоваяСтрока.Проект = Источник.Проект;
// Важно заполнить обязательные реквизиты, такие как Период
НоваяСтрока.Период = Источник.Дата;

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

В этом случае нам не нужно вызывать метод Записать(). Платформа сама увидит флаг Записывать = Истина и корректно зафиксирует движения в конце транзакции, не конфликтуя с другими процессами.

Нюансы использования имен переменных

Разберем еще один важный стилистический момент, упомянутый в обсуждении. Использование имени переменной Движения внутри подписки технически допустимо, но крайне нежелательно. Посмотрим почему:

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

Рекомендуется использовать более точные имена, например: НаборПроекты или ДвиженияПоПроектам.

Лишние операции в коде

Проанализируем еще одну ошибку в исходном примере: использование Движения.Прочитать() перед Движения.Очистить(). Рассмотрим ситуацию:

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

Итоговый алгоритм действий для стабильной работы

Чтобы движения в подписке не удалялись при повторном проведении, придерживайтесь следующих шагов:

  1. Убедитесь, что регистр накопления (или сведений) указан в списке «Движения» документа в конфигураторе.
  2. В коде подписки обращайтесь к регистру через Источник.Движения.ИмяРегистра.
  3. Заполняйте необходимые строки.
  4. Всегда устанавливайте Источник.Движения.ИмяРегистра.Записывать = Истина;.
  5. Не вызывайте метод Записать() вручную, позвольте платформе завершить транзакцию автоматически.
  6. Не забывайте заполнять стандартные реквизиты записей: Период, Активность и, если требуется, ВидДвижения.

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

← На главную