Почему документ 1С не проводится, но в списке отображается как проведенный?

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

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

Суть проблемы и механизм «Оптимистической блокировки»

Для начала проанализируем ситуацию с технической точки зрения. Ошибка «несоответствия версии» — это классическое проявление механизма оптимистической блокировки. В платформе 1С каждый объект в базе данных (документ, справочник) имеет скрытый реквизит — ВерсияДанных. Этот параметр изменяется при каждой записи объекта.

Рассмотрим стандартный сценарий работы:

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

Почему в списке документ «Проведен», а в форме — нет?

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

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

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

Поиск причины: двойная запись в коде

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


// ОПАСНЫЙ КОД в модуле документа или подписке
Процедура ПриЗаписи(Отказ)
    Если ЭтотОбъект.ДополнительныеСвойства.Свойство("НужноСоздатьСФ") Тогда
        // Программист пытается создать связанный документ
        НоваяСФ = Документы.СчетФактураВыданный.СоздатьДокумент();
        НоваяСФ.ДокументОснование = ЭтотОбъект.Ссылка;
        // ... заполнение реквизитов ...
        НоваяСФ.Записать(); 
        
        // ВНИМАНИЕ: ОШИБКА ЗДЕСЬ
        // Если внутри логики СФ или здесь вызывается:
        ЭтотОбъект.Записать(); // Это вызовет конфликт версий!
    КонецЕсли;
КонецПроцедуры

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

Алгоритм отладки и решения проблемы

Если вы столкнулись с такой ситуацией, выполним следующие шаги по поиску «лишней» записи:

  1. Замер производительности: Запустите отладчик, включите замер производительности и нажмите «Провести». В результатах замера найдите метод Записать. Если вы видите, что он вызывается более одного раза для вашего документа — вы нашли виновника.
  2. Глобальный поиск: Проанализируйте все вхождения текста .Записать( в модуле документа, модуле менеджера и в Подписках на события. Особое внимание уделите подпискам ПередЗаписью и ПриЗаписи.
  3. Анализ связанных объектов: Проверьте, не создаются ли при проведении другие документы (например, печатные формы УПД, записи в регистры сведений с ведущим объектом). Часто ошибка кроется в коде, который пытается «подтянуть» данные из еще не записанного до конца документа.
  4. Остановка по ошибке: В Конфигураторе выберите меню Отладка -> Остановка по ошибке. Установите галочку «Останавливаться по ошибке». При возникновении конфликта версий отладчик замрет на той строке кода, которая вызвала исключение. Проанализируйте Стек вызовов, чтобы понять, как вы попали в эту точку.

Правильная реализация записи связанных данных

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


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

Если же необходимо создать связанный объект, делайте это в процедуре ПриЗаписи, используя ссылку на текущий документ, но никогда не вызывайте Записать() для исходного объекта ЭтотОбъект внутри его же транзакции записи.

Особенности при копировании документов

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

Резюме

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

← На главную