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

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

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

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

Анализ проблемы: эффект «четного/нечетного» перепроведения

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

Проблема проявляется в том, что в некоторых документах (таких как «Закрытие месяца» или кастомные обмены) каждое второе проведение очищает движения. Выясним причину этого поведения. Проанализируем стек вызовов в момент удаления записей. Часто оказывается, что в момент очистки стек пуст, либо содержит единственный вызов обработчика ПриЗаписи. Это указывает на то, что инициатором записи выступает не ваш программный код, а ядро платформы (внутренний механизм фиксации транзакций).

Причина 1: Конфликт между объектом «Документ» и ручной записью набора

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

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

Этот процесс часто срабатывает именно при перепроведении, так как платформа пытается оптимизировать запись и сравнивает текущее состояние БД с состоянием объекта в памяти.

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

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

Решение 1: Предварительное распроведение документа

Самый простой, хотя и несколько «силовой» метод — это гарантировать, что перед началом проведения документ не имеет движений в проблемном регистре. Рассмотрим пример кода, который можно вставить в начало процедуры проведения или в подписку ПередЗаписью документа:


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

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

Решение 2: Использование флага «Записывать» в коллекции движений

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

Даже если вы записываете регистр своим кодом, добавьте следующую строку:


// В контексте объекта документа (например, в обработке проведения)
Движения.ВашДополнительныйРегистр.Записывать = Истина;

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

Решение 3: Особенности документа «Закрытие месяца»

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

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

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

Чтобы избежать этого, проанализируем использование свойства ОбменДанными.Загрузка и проверку на пустой набор перед записью во второй регистр:


Процедура ПриЗаписиНабораХозрасчетный(Источник, Отказ)
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Проверяем, не пустой ли набор мы пытаемся скопировать
    Если Источник.Количество() = 0 Тогда
        // Если основной набор пуст, нужно быть осторожным, 
        // чтобы не затереть данные, записанные ранее в этой же транзакции
        Возврат;
    КонецЕсли;

    // Логика копирования в ВашДополнительныйРегистр...
КонецПроцедуры

Рекомендации по отладке

Если проблема сохраняется, выполните следующие шаги для выявления «невидимки», очищающего регистр:

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

← На главную