В процессе разработки часто возникает необходимость программно создавать задачи для пользователей. Обычно это простая операция, но иногда разработчики сталкиваются с ситуацией, когда код создания задачи выполняется без ошибок, но сама задача в базе данных не появляется. Особенно часто это происходит при попытке создать задачу в обработчиках проведения или отмены проведения документов.
Рассмотрим типичный случай: нужно создать задачу-напоминание для пользователя, если отменяется проведение важного документа. Вы пишете код, запускаете его, форма задачи может даже открыться на экране, но после закрытия она бесследно исчезает. Проанализируем, почему так происходит и как решить эту проблему.
Основная причина такого "мистического" поведения кроется в механизме транзакций. Любые операции с базой данных, связанные с проведением или отменой проведения документа (код в модуле объекта в процедурах ОбработкаПроведения и ОбработкаОтменыПроведения), выполняются внутри одной большой транзакции.
Что это значит на практике? Платформа 1С "запоминает" все изменения, которые вы делаете в базе данных в рамках этой операции. Если в конце процедуры проведения выставляется флаг Отказ = Истина или происходит какая-либо ошибка, платформа отменяет абсолютно все изменения, сделанные с момента начала транзакции. Она как бы "откатывает" базу данных к состоянию, в котором та была до начала операции.
Именно в эту ловушку и попадает создание нашей задачи. Посмотрим на типичный код:
// Этот код находится, например, в модуле документа
// в процедуре ОбработкаОтменыПроведения(Отказ)
// ... какая-то логика ...
Отказ = Истина; // Симулируем отмену операции
// Пытаемся создать задачу
НоваяЗадача = Задачи.ЗадачиПользователя.СоздатьЗадачу();
НоваяЗадача.Наименование = "Проверить отмененный документ!";
НоваяЗадача.Исполнитель = АвторДокумента;
НоваяЗадача.Объект = СсылкаНаОбъект;
// ... заполнение других реквизитов ...
Попытка
НоваяЗадача.Записать(); // В этот момент задача записывается ВНУТРИ ТРАНЗАКЦИИ
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
// После выхода из процедуры, из-за "Отказ = Истина",
// вся транзакция будет отменена, включая запись новой задачи.
В результате код отрабатывает, но так как вся транзакция отменяется, запись о новой задаче тоже стирается из базы данных. Вы не видите ни ошибок, ни результата.
Чтобы задача гарантированно сохранилась, ее создание и запись нужно вынести за пределы "проблемной" транзакции. Самый надежный и правильный способ сделать это — использовать фоновые задания.
Фоновое задание запускается в отдельном сеансе и, соответственно, в своей собственной, независимой транзакции. Если основная транзакция документа откатится, это никак не повлияет на выполнение фонового задания.
Разберем по шагам, как это реализовать:
// Общий модуль, например, "РаботаСЗадачамиСервер"
Процедура СоздатьЗадачуНапоминаниеВФоне(Знач ПараметрыЗадачи) Экспорт
НоваяЗадача = Задачи.ЗадачиПользователя.СоздатьЗадачу();
НоваяЗадача.Наименование = ПараметрыЗадачи.Наименование;
НоваяЗадача.Исполнитель = ПараметрыЗадачи.Исполнитель;
НоваяЗадача.Объект = ПараметрыЗадачи.Объект;
НоваяЗадача.Описание = ПараметрыЗадачи.Описание;
НоваяЗадача.Дата = ТекущаяДата();
НоваяЗадача.СрокИсполнения = КонецДня(ТекущаяДата());
НоваяЗадача.Записать();
КонецПроцедуры
ОбработкаОтменыПроведения), мы не создаем ее напрямую, а инициируем запуск нашей процедуры в фоновом режиме.
// Модуль объекта документа, процедура ОбработкаОтменыПроведения(Отказ)
// ... какая-то логика ...
Если НужноСоздатьЗадачу Тогда
// 1. Готовим параметры для передачи в фоновое задание
ПараметрыЗадачи = Новый Структура;
ПараметрыЗадачи.Вставить("Наименование", "Документ " + ЭтотОбъект.Ссылка + " не проведен");
ПараметрыЗадачи.Вставить("Исполнитель", ЭтотОбъект.Ответственный);
ПараметрыЗадачи.Вставить("Объект", ЭтотОбъект.Ссылка);
ПараметрыЗадачи.Вставить("Описание", "Необходимо проверить причину отмены проведения документа.");
// 2. Формируем массив параметров для метода Выполнить()
ПараметрыВыполнения = Новый Массив;
ПараметрыВыполнения.Добавить(ПараметрыЗадачи);
// 3. Запускаем фоновое задание
ФоновыеЗадания.Выполнить("РаботаСЗадачамиСервер.СоздатьЗадачуНапоминаниеВФоне", ПараметрыВыполнения);
КонецЕсли;
Отказ = Истина;
// Теперь откат транзакции не повлияет на создание задачи,
// так как она будет создана в другом сеансе.
Этот подход является наиболее универсальным и надежным для выполнения любых действий, которые должны произойти независимо от исхода основной транзакции.
В ходе обсуждения подобных проблем часто возникает путаница между объектами конфигурации Задача и Бизнес-процесс. Давайте проясним этот момент.
В 1С можно создать объект метаданных Задача, который не будет привязан ни к одному бизнес-процессу. Это идеальный вариант для создания простых напоминаний или поручений. Именно такой подход мы и использовали в примерах выше.
Задачи.<ИмяЗадачи>.СоздатьЗадачу().Важный момент: если вы создали задачу, а она не видна пользователю, проверьте, куда он смотрит. Возможно, он использует какой-то специфический отчет или форму списка, настроенную на отображение задач только из конкретного бизнес-процесса. Прежде чем утверждать, что задача не создалась, проверьте ее наличие через консоль запросов.
Если задача является шагом в какой-то сложной логике (согласование, обработка заказа и т.д.), то она, скорее всего, привязана к бизнес-процессу. В этом случае создавать объект задачи вручную — неправильно.
Вместо этого нужно создавать и запускать сам бизнес-процесс. Платформа 1С, двигаясь по карте маршрута, сама создаст все необходимые задачи в нужные моменты времени.
Посмотрим на пример создания задачи через запуск бизнес-процесса "Задание":
// Создаем не задачу, а сам бизнес-процесс
НовыйБП = БизнесПроцессы.Задание.СоздатьБизнесПроцесс();
// Заполняем реквизиты бизнес-процесса
НовыйБП.Автор = ТекущийПользователь;
НовыйБП.Важность = Перечисления.ВариантыВажностиЗадачи.Высокая;
НовыйБП.Дата = ТекущаяДата();
НовыйБП.СрокИсполнения = КонецДня(ТекущаяДата());
НовыйБП.Исполнитель = НужныйПользователь;
НовыйБП.Наименование = "Обработать запрос клиента";
НовыйБП.Предмет = СсылкаНаДокумент;
// Записываем бизнес-процесс
НовыйБП.Записать();
// Стартуем его! Именно в этот момент создастся первая задача по маршруту
НовыйБП.Старт();
// После этого пользователю "НужныйПользователь" будет назначена новая задача
Главное правило: если вам нужно простое напоминание — создавайте независимую задачу. Если нужна часть сложного процесса — создавайте и запускайте бизнес-процесс.