В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда программный код кажется абсолютно корректным, данные успешно проходят все проверки, но после завершения процедуры изменения в базе данных не сохраняются. При этом в Журнале регистрации появляется лаконичная, но пугающая запись: «Статус транзакции — Отменена». Рассмотрим подробнее, почему это происходит, как работает механизм транзакций в 1С и как найти скрытую ошибку, которая приводит к откату данных.
Проанализируем классический пример, с которым столкнулся автор темы. Представим ситуацию, когда нам необходимо программно связать два элемента справочника Номенклатура. Мы пишем стандартный обработчик:
СпрОсн = Справочники.Номенклатура.НайтиПоКоду(КодОсновной);
СпрПары = Справочники.Номенклатура.НайтиПоКоду(КодПарный);
ОбъектОсн = СпрОсн.ПолучитьОбъект();
ОбъектОсн.НоменклатураПара = СпрПары;
Попытка
ОбъектОсн.Записать();
// На этом этапе код выполняется без ошибок, исключение не срабатывает
Исключение
Сообщить("ОШИБКА ЗАПИСИ " + ОбъектОсн);
КонецПопытки;
На первый взгляд, все верно. Мы получили объект, изменили реквизит и вызвали метод Записать(). Более того, проверка ЗначениеЗаполнено(ОбъектОсн.НоменклатураПара) сразу после записи может показывать, что реквизит в памяти заполнен. Однако, как только выполнение кода завершается, мы открываем справочник и видим пустое поле. Выясним причину этого странного поведения.
Самая распространенная причина «тихого» отката изменений — это явный вызов НачатьТранзакцию() без последующего вызова ЗафиксироватьТранзакцию(). В 1С реализована модель так называемых «плоских» транзакций. Если мы открываем транзакцию, платформа ожидает, что мы либо подтвердим её, либо отменим.
Если процедура заканчивается, а вызванная транзакция так и не была зафиксирована, система автоматически откатывает все изменения, которые были сделаны внутри этого блока. Это механизм защиты целостности данных: 1С не может оставить базу в неопределенном состоянии.
Рассмотрим, как это выглядело в случае автора: при копировании кода в начало процедуры попала строка НачатьТранзакцию();, а в конце не было соответствующей команды. В итоге метод Записать() отрабатывал штатно, но в конце работы модуля вся работа аннулировалась платформой.
Важно понимать, что в 1С все транзакции объединяются в одну общую цепочку. Если вы вызываете НачатьТранзакцию(), а внутри этого блока вызываете метод Записать() для объекта, платформа не создает вторую транзакцию, а «вкладывает» запись объекта в уже существующую.
Критическая особенность: если во вложенном коде (например, в обработчике ПередЗаписью модуля объекта) произойдет ошибка или будет вызван оператор ОтменитьТранзакцию(), вся цепочка помечается как «битая» (невалидная). Даже если основной код в конце попытается выполнить ЗафиксироватьТранзакцию(), платформа выдаст ошибку, так как целостность последовательности уже была нарушена ранее.
Разберем ситуацию, когда в вашем коде вообще нет слов «Транзакция». Тем не менее, запись любого объекта (справочника, документа) всегда открывает неявную транзакцию на уровне СУБД. Проанализируем возможные точки отказа в модуле объекта:
ПередЗаписью или ПриЗаписи переменной Отказ присваивается значение Истина, запись прерывается, и транзакция отменяется.Попытка...Исключение, транзакция внутри метода Записать() уже будет помечена к удалению.Существует коварная ловушка при работе с транзакциями и блоками обработки ошибок. Посмотрим на пример кода, который может привести к проблемам:
НачатьТранзакцию();
Попытка
// Какой-то код, который вызывает ошибку СУБД
ДокументОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
// В этот момент транзакция может быть уже отменена платформой!
Сообщить("Произошла ошибка, но мы пытаемся продолжить...");
// Попытка зафиксировать транзакцию здесь вызовет новую ошибку
КонецПопытки;
Если внутри транзакции произошла ошибка уровня базы данных, транзакция становится неактивной. Попытка вызвать ЗафиксироватьТранзакцию() после этого приведет к исключению «Транзакция уже отменена». Правильный алгоритм действий в блоке Исключение должен всегда включать проверку:
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
Сообщить(ОписаниеОшибки());
КонецПопытки;
Если визуальный анализ кода не помогает, и вы продолжаете видеть статус «Отменена», воспользуемся следующими инструментами:
Попытка просто «проглатывал».DBMSSQL (или DBPOSTGRS) позволит увидеть точный SQL-запрос, который вызвал сбой, и причину отката на уровне СУБД (например, таймаут блокировок или Deadlocks).Подводя итог, отметим: статус «Транзакция отменена» — это всегда следствие незавершенного или ошибочного процесса взаимодействия с базой данных. Тщательная проверка парности операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию(), а также анализ обработчиков событий в модулях объектов позволяют решить эту проблему в большинстве случаев.