Каждый разработчик 1С рано или поздно сталкивается с мистическим поведением платформы: код в фоновом или регламентном задании отрабатывает абсолютно корректно, отладчик показывает успешное заполнение всех свойств, блок Попытка...Исключение не фиксирует никаких ошибок, а в Журнал регистрации пишется заветное «ОК». Однако при открытии информационной базы в режиме «Предприятие» мы обнаруживаем, что данные в регистре сведений остались прежними.
В этой статье мы подробно проанализируем, почему возникает эта проблема, какие внутренние механизмы платформы «1С:Предприятие» за это отвечают, и разберем по шагам надежные способы решения этой технической неувязки.
Рассмотрим типичный пример кода, который вызывается в общем привилегированном модуле из регламентного задания. Задача процедуры предельно проста — обновить статус документа в независимом регистре сведений:
Процедура ЗаписатьРС(СтруктураДанных) Экспорт
Попытка
НаборЗаписей = РегистрыСведений.ДокументыКОтправке.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(СтруктураДанных.Документ);
НаборЗаписей.Прочитать();
НаборЗаписей.Очистить();
НоваяСтрока = НаборЗаписей.Добавить();
НоваяСтрока.Документ = СтруктураДанных.Документ;
НоваяСтрока.Статус = СтруктураДанных.Статус;
НаборЗаписей.Записать();
ЗаписьЖурналаРегистрации("Запись в РС",
УровеньЖурналаРегистрации.Информация,,, "ОК");
Исключение
ЗаписьЖурналаРегистрации("Запись в РС",
УровеньЖурналаРегистрации.Ошибка,,, "Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
При интерактивном запуске под полными правами данный код отрабатывает без нареканий. Однако при выполнении в фоновом задании происходит странное: в Журнале регистрации мы видим событие со статусом «ОК», отладчик на сервере без проблем проходит строку НаборЗаписей.Записать(), но физически в базе данных запись не меняется.
Выясним истинные причины такого поведения и рассмотрим, как их устранить.
Когда регламентное задание запускается на сервере 1С, платформа создает для него отдельный фоновый сеанс. Если у регламентного задания в свойствах (в консоли заданий или в коде инициализации) не указан конкретный пользователь, то платформа определяет права этого сеанса по свойству конфигурации «Основные роли» (Default Roles).
Здесь кроется важный нюанс:
ФоновыеЗадания.Выполнить(), оно наследует контекст и права того пользователя, который его инициировал. Если у интерактивного пользователя нет прав на этот регистр, то и фоновое задание не сможет выполнить запись.Разработчики часто надеются на то, что код выполняется в общем модуле с установленным флагом «Привилегированный». Однако привилегированный режим может быть проигнорирован платформой в следующих случаях:
УстановитьПривилегированныйРежим(Истина) будут проигнорированы. В этом случае права жестко проверяются по текущему профилю безопасности или базовым ролям.Многих сбивает с толку тот факт, что запись в Журнал регистрации возвращает «ОК», то есть блок Исключение не срабатывает. Это происходит по следующим причинам:
НаборЗаписей.Записать() выполняется успешно в рамках локального кода. Но если вся процедура была вызвана внутри другой, более крупной транзакции, которая впоследствии завершилась ошибкой или была явно отменена платформой на выходе из фонового задания, СУБД выполнит полный откат (rollback). При этом Журнал регистрации пишется вне транзакционной модели базы данных, поэтому запись «ОК» в нем благополучно остается.Записать(). Ограничение может отработать «тихо», просто не зафиксировав изменения в СУБД, либо вызвать откат всей транзакции на этапе ее фиксации (коммита), который происходит уже за рамками блока Попытка...Исключение конкретной процедуры.Для гарантированного решения проблемы рекомендуется применить один из следующих подходов в зависимости от архитектуры вашей базы данных.
Самый надежный способ обойти ограничения прав «Основных ролей» фонового задания (если не используется безопасный режим БСП) — это явное переключение в привилегированный режим непосредственно перед записью в регистр:
Процедура ЗаписатьРС(СтруктураДанных) Экспорт
// Устанавливаем привилегированный режим для обхода ограничений ролей
УстановитьПривилегированныйРежим(Истина);
НаборЗаписей = РегистрыСведений.ДокументыКОтправке.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(СтруктураДанных.Документ);
// Записывать = Истина использовать не нужно, это свойство только для документов
НаборЗаписей.Добавить();
НаборЗаписей[0].Документ = СтруктураДанных.Документ;
НаборЗаписей[0].Статус = СтруктураДанных.Статус;
НаборЗаписей.Записать();
// Возвращаем обычный режим работы сеанса
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Если в вашей конфигурации определены основные роли (свойство конфигурации «Основные роли»), убедитесь, что у тех ролей, которые туда входят, есть явные права на Чтение, Просмотр, Изменение и Запись для проблемного регистра сведений.
Если фоновое задание запускается под конкретным пользователем, проверьте в режиме «1С:Предприятие», назначены ли этому пользователю роли, позволяющие вносить изменения в данный регистр.
Если в процессе отладки вы видите, что сразу после метода Записать() данные внутри этого же сеанса успешно считываются (например, через СоздатьМенеджерЗаписи()), но в параллельных сеансах они отсутствуют — это 100% признак активной транзакции, которая впоследствии откатывается. Проверить это можно, добавив в код логирование состояния транзакции:
Если ТранзакцияАктивна() Тогда
ЗаписьЖурналаРегистрации("ОтладкаТранзакции",
УровеньЖурналаРегистрации.Предупреждение,,, "Код выполняется внутри транзакции!");
КонецЕсли;
В таком случае необходимо локализовать место вызова фонового задания и убедиться, что транзакция успешно завершается методом ЗафиксироватьТранзакцию(), а все возникающие в процессе ошибки обрабатываются корректно без аварийного завершения фонового процесса.