В процессе автоматизации периодических задач программисты 1С часто сталкиваются со следующей ситуацией: написанная обработка или процедура выгрузки данных отлично работает при ручном запуске в интерактивном режиме («под собой»), но при переносе в регламентное (фоновое) задание мгновенно завершается с невыясненной ошибкой. Особенную сложность это вызывает в конфигурациях на базе обычных форм (например, Управление торговлей 10.3), где механизмы построения отчетов тесно связаны с интерфейсными общими модулями.
В этой статье мы подробно разберем, почему серверный контекст накладывает жесткие ограничения на выполнение подобных задач, выясним причины сбоев при работе с сетевыми ресурсами и научимся правильно диагностировать и исправлять ошибки выполнения фоновых заданий.
---
Для начала проанализируем разницу в средах выполнения. Когда мы запускаем обработку вручную через тонкий, толстый или веб-клиент, код выполняется в контексте текущего пользователя Windows. Этот пользователь имеет свой рабочий стол, сетевое окружение, доступ к смонтированным сетевым дискам и права на запись в сетевые папки.
Регламентные и фоновые задания выполняются исключительно на стороне сервера 1С:Предприятия. Процесс сервера (обычно это rphost.exe) функционирует в неинтерактивной сессии Windows (так называемая «Сессия 0»). У него нет доступа к графическому интерфейсу пользователя, а также к сетевым дискам, которые вы привыкли видеть в проводнике.
Давайте подробно разберем три основные причины, по которым процедура автоматической выгрузки отчета из примера автора завершается аварийно, и пошагово решим каждую из них.
---
В представленном коде автора для сохранения файла используется следующий подход:
Результат = "net use t: \\gate.saks.ru\translate\service\1c\_1с.ут.отчёты Amozhetbyttakoyparol7 /user:OFFICE\1cut";
ЗапуститьПриложение(Результат,,Истина);
ПутьКФайлуДискПодключен = "t:\" + ИмяФайла + ".xlsx";
Этот метод обречен на ошибку в фоновом задании по двум причинам:
T:, монтируемый через команду net use, привязывается к идентификатору безопасности (SID) сессии Windows, в которой была выполнена команда. Поскольку фоновое задание выполняется в изолированной служебной сессии, созданный диск просто не будет виден серверному процессу 1С при попытке записи файла через метод ТабДок.Записать().usr1cv8). Этот пользователь не является доменным и не имеет прав для выхода в локальную сеть, даже если в скрипте авторизации указаны логин и пароль. Попытка вызова консольных утилит в неинтерактивном режиме часто блокируется операционной системой из соображений безопасности.Чтобы сервер 1С мог беспрепятственно сохранять файлы в сетевые папки других серверов, нам необходимо полностью отказаться от монтирования дисков буквами (вроде T:, Z:) и перейти на использование прямых сетевых путей формата UNC (Universal Naming Convention), например: \\gate.saks.ru\translate\service\1c\_1с.ут.отчёты\.
Разберем по шагам, как настроить права операционной системы для сервера 1С:
services.msc и нажмите Enter).1C:Enterprise 8 Server Agent). Откройте ее свойства и перейдите на вкладку Вход в систему (Log On)..\usr1cv8, служба не сможет получить доступ к шаре на другом сервере. Нам необходимо переключить запуск службы на специально созданную доменную учетную запись (например, ту самую OFFICE\1cut, которая использовалась в коде автора).\\gate.saks.ru\translate\service\1c\_1с.ут.отчёты\ на удаленном сервере.Теперь в коде 1С мы можем напрямую обращаться к сетевому адресу без выполнения внешних команд операционной системы:
ИмяФайла = СтрокаПоиска.НаименованиеОтчета;
ПутьКФайлу = "\\gate.saks.ru\translate\service\1c\_1с.ут.отчёты\" + ИмяФайла + ".xlsx";
// Запись файла выполняется напрямую по сети
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
---
Вторая и наиболее частая проблема при переносе кода из обработок в регламентные задания — это попытка инициализации объектов, предназначенных для работы на стороне клиента (в интерфейсе). В коде автора мы видим использование типового Универсального отчета:
УниверсальныйОтчет = Отчеты.УниверсальныйОтчет.Создать();
// ...
УниверсальныеМеханизмы.ПолучитьНастройку(Structure);
// ...
Отчет.ВосстановитьНастройкиИзСтруктуры(СтруктураНастройки.СохраненнаяНастройка);
Отчет.СформироватьОтчет(ТабДок);
Универсальный отчет в конфигурациях уровня УТ 10.3 / УПП 1.3 очень сильно завязан на общие модули клиентского приложения. Например, общий модуль УниверсальныеМеханизмы может иметь флаг «Клиент» и отсутствовать на сервере, либо функции внутри него могут обращаться к интерактивным методам (вызывать окна предупреждений, открывать формы ввода дат, обращаться к глобальным переменным сессии типа глТекущийПользователь).
При выполнении фонового задания на сервере попытка вызвать процедуру, содержащую элементы GUI, приводит к немедленному падению потока с ошибкой «Интерактивное получение формы не разрешено» или «Переменная не определена».
Рассмотрим подробнее, как адаптировать формирование отчета для выполнения в фоновом режиме:
УниверсальныеМеханизмы и проверим его свойства. У него обязательно должен стоять флаг Сервер. Если в модуле присутствуют вызовы клиентских процедур (например, Предупреждение() или ОткрытьФорму()), их необходимо обернуть в инструкции препроцессора:
#Если Клиент Тогда
ПоказатьПредупреждение(,"Произошла ошибка при получении настроек!");
#КонецЕсли
ВосстановитьНастройкиИзСтруктуры() или СформироватьОтчет() внутри самого отчета не инициализирует форму. Если отчет не может сформироваться без открытия своей формы, то правильнее будет отказаться от использования тяжелого Универсального отчета в пользу прямого программного выполнения схемы компоновки данных (СКД) или Построителя отчета, на котором он базируется.Проанализируем пример того, как можно программно выполнить СКД на сервере без привлечения интерфейсных механизмов:
Функция СформироватьОтчетНаСервере(ИмяОтчета, Настройки) Экспорт
ТабДок = Новый ТабличныйДокумент;
// Получаем сам отчет из метаданных
ОтчетОбъект = Отчеты[ИмяОтчета].Создать();
СхемаКомпоновки = ОтчетОбъект.СхемаКомпоновкиДанных;
// Инициализируем компоновщик настроек
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, Настройки);
// Выполняем компоновку данных
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
// Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТабДок;
КонецФункции
---
Самая большая трудность при работе с регламентными заданиями — это их «молчаливое» падение. Если в обычном сеансе при возникновении ошибки система выдает понятное окно с описанием проблемы и указанием строки кода, то в фоновом задании выполнение просто прерывается. В консоли заданий статус меняется на «Ошибка», но деталей там практически нет.
Разберем два эффективных метода локализации ошибок в регламентных заданиях.
Обязательно оборачивайте весь исполняемый код регламентного задания в блок Попытка ... Исключение. Это позволит перехватить критическую ошибку, не даст заданию аварийно завершить работу всей службы и запишет подробную информацию (включая имя модуля и номер строки) в системный лог 1С.
Модернизируем процедуру автора, добавив в нее обработчик ошибок:
Процедура ВыгрузитьОтчеты() Экспорт
Попытка
ПараметрыПоиска = ПолучитьСписокОтчетов();
Для каждого СтрокаПоиска из ПараметрыПоиска цикл
// Проверка периодичности
Если СтрокаПоиска.Неделя Тогда
Если ДеньНедели(ТекущаяДата()) <> 1 Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
Период = ПолучитьПериод(СтрокаПоиска.PeriodStart, СтрокаПоиска.PeriodEnd);
// Логика формирования отчета
ТабДок = СформироватьТабличныйДокумент(СтрокаПоиска, Период);
// Определение пути и сохранение
ИмяФайла = СтрокаПоиска.НаименованиеОтчета;
ПутьКФайлу = "\\gate.saks.ru\translate\service\1c\_1с.ут.отчёты\" + ИмяФайла + ".xlsx";
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
// Фиксируем успешное выполнение в журнале
ЗаписьЖурналаРегистрации("ВыгрузкаОтчетов",
УровеньЖурналаРегистрации.Информация,
,
,
"Успешно выгружен отчет: " + ИмяФайла);
КонецЦикла;
Исключение
// Перехватываем ошибку и пишем подробный стек в журнал регистрации
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("ВыгрузкаОтчетов",
УровеньЖурналаРегистрации.Ошибка,
,
,
"Ошибка при выгрузке отчетов: " + ТекстОшибки);
КонецПопытки;
КонецПроцедуры
Теперь в случае любого сбоя достаточно открыть в режиме предприятия меню Сервис -> Журнал регистрации, установить отбор по событию «ВыгрузкаОтчетов» и прочитать понятное техническое описание ошибки со ссылкой на строку кода.
Если записи в журнале регистрации недостаточно, мы можем подключиться отладчиком конфигуратора напрямую к выполняющемуся фоновому заданию на сервере.
Для этого выполним следующие действия:
ragent и добавьте параметр -debug в строку запуска службы. После этого перезапустите службу сервера 1С.ВыгрузитьОтчеты()).---
Для создания надежных механизмов автоматического экспорта данных из 1С придерживайтесь следующих архитектурных правил:
net use в фоновых процессах. Всегда работайте через прямые UNC-пути.Попытка ... Исключение с выводом информации об ошибке в Журнал регистрации. Это сбережет вам массу времени при последующем сопровождении системы.