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