Почему регламентное задание в 1С не сохраняет отчет в файл? Разбираем и устраняем серверные ошибки

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Автоматическая выгрузка отчетов по расписанию — одна из самых популярных задач в администрировании и разработке на платформе 1С:Предприятие. Однако очень часто разработчики сталкиваются с ситуацией, когда код, который идеально работает в ручном режиме (через внешнюю обработку), напрочь отказывается выполняться в регламентном задании. При этом система выдает невнятные системные ошибки или просто молча завершает работу сбоем.

В этой статье мы подробно разберем, почему возникает такая проблема, проанализируем неочевидные архитектурные особенности платформы 1С и настроим отказоустойчивый код для регламентного задания, который гарантированно запишет файл в нужное место.

Причина 1. Попытка интерактивного взаимодействия на сервере (Метод Показать)

Давайте внимательно посмотрим на финальную строку в коде автора из первого сообщения темы: в самом конце там написано ключевое слово Показать (подразумевающее метод табличного документа Таблица.Показать()). Это самая критическая ошибка при проектировании серверного кода.

Разберем логику работы платформы. Любое регламентное задание выполняется в фоновом режиме в так называемом серверном контексте. У сервера 1С нет и не может быть графического интерфейса. Он выполняется как служба операционной системы Windows или Linux в изолированном пространстве.

Когда фоновое задание доходит до вызова метода Показать(), платформа пытается инициировать открытие окна на клиенте. Но клиента не существует! В результате сервер генерирует исключение: "Операция недоступна на сервере", и выполнение регламентного задания аварийно прерывается. Файл при этом может даже не успеть записаться, либо запишется, но статус задания в консоли всегда будет "Выполнено с ошибками".

Решение: Полностью исключить любые вызовы диалоговых окон, предупреждений, вопросов и методов визуализации, таких как Показать(), ОткрытьФорму() или Предупреждение(), из кода, предназначенного для работы в регламентных заданиях.

Причина 2. Ошибка конкатенации строк и пропущенный слэш в путях

Проанализируем следующую строку кода, которая изначально использовалась автором:


ПутьКФайлу = "C:\Users\Desktop\Новая папка";
ИмяФайла = "Отчет от" + СОКРЛП(Формат(ТекущаяДата,"ДФ=yyyy-MM-dd-ЧЧ-мм-сс"))+ ".xls";
Файл = ПутьКФайлу + ИмяФайла;

При простом сложении этих строк переменная Файл получит значение:

"C:\Users\Desktop\Новая папкаОтчет от 2020-01-13-12-00-00.xls"

Как мы видим, операционная система воспримет часть строки Новая папкаОтчет от... как единое имя файла, расположенное в каталоге C:\Users\. Если папки с названием Users на диске C нет (или у сервера нет туда прав), запись завершится ошибкой.

Для решения этой проблемы необходимо четко контролировать наличие закрывающего слэша в пути каталога:


Файл = ПутьКФайлу + "\" + ИмяФайла;

А еще лучше использовать специализированные функции для работы с путями, если они доступны в вашей версии БСП, либо самостоятельно проверять наличие символа разделителя на конце строки перед объединением.

Причина 3. Контекст безопасности службы сервера 1С и ошибка 0x80030003

Даже после исправления слэша автор получил следующую ошибку: Ошибка при выполнении файловой операции ... -2147287037(0x80030003): Путь %1 не найден. Этот код ошибки (STG_E_PATHNOTFOUND) является стандартным системным сообщением Windows COM-объектов, указывающим на то, что указанного пути физически не существует в системе.

Почему же папка "C:\Users\Desktop\Новая папка" не находится сервером?

  1. Несуществующий путь по умолчанию. В современных ОС Windows папки C:\Users\Desktop не существует. Рабочий стол конкретного пользователя находится по пути C:\Users\Имя_Пользователя\Desktop.
  2. Разные пользователи операционной системы. Когда вы запускаете отладку вручную под своей учетной записью администратора или разработчика, код видит ваши личные папки. Но регламентное задание выполняется от имени пользователя, под которым запущена служба "Agile 1C Enterprise Server" (обычно это техническая учетная запись USR1CV8). У этого пользователя совершенно другой профиль в системе, и у него физически нет доступа к вашему Рабочему столу.
  3. Физическое расположение сервера. Если вы работаете в клиент-серверном варианте (SQL), то регламентное задание выполняется непосредственно на самом сервере приложений 1С. Это означает, что путь C:\Users\... платформа будет искать на жестком диске серверной машины, а не на вашем персональном компьютере, с которого вы запустили конфигуратор.

Решение: Никогда не использовать жестко прописанные пути (hardcode) к персональным папкам пользователей (Рабочий стол, Документы) в серверном коде.

Причина 4. Выбор формата файла: XLS против XLSX

В первоначальном коде автора запись выполнялась без указания второго параметра метода Записать. По умолчанию платформа 1С записывает табличный документ в своем внутреннем формате MXL, даже если вы принудительно указали расширение файла .xls в строке пути. При попытке открыть такой файл в Microsoft Excel пользователь получит предупреждение о повреждении структуры документа.

В последующих сообщениях автор исправил вызов на:

Таблица.Записать(Файл, ТипФайлаТабличногоДокумента.XLS);

Однако формат XLS (Excel 97-2003) накладывает жесткое ограничение на количество строк на одном листе — не более 65 536 строк. Если ваш отчет по маркетинговому исследованию цен окажется достаточно большим, регламентное задание прервется с ошибкой переполнения листа.

Рекомендация: Всегда используйте современный формат XLSX (Excel 2007 и выше), который поддерживает более миллиона строк, обеспечивает лучшее сжатие данных и стабильно читается всеми современными офисными программами.


Таблица.Записать(Файл, ТипФайлаТабличногоДокумента.XLSX);

Причина 5. Признак Предопределенное у регламентного задания

Как справедливо заметили участники обсуждения на форуме, само регламентное задание может не запускаться по расписанию, если при его создании в конфигураторе не был установлен флаг Предопределенное.

Если этот признак не установлен, регламентное задание создается в базе данных как "шаблон", но его фактический экземпляр не запускается автоматически. В таком случае администратору системы необходимо создавать экземпляр задания вручную через консоль регламентных заданий в режиме "1С:Предприятие". Для автоматического старта сразу после обновления конфигурации базы данных обязательно ставьте этот флажок в свойствах объекта в конфигураторе.


Разбираем правильные варианты реализации по шагам

Рассмотрим несколько надежных и архитектурно верных способов организации выгрузки отчетов в фоновом режиме.

Вариант А. Выгрузка во временную папку сервера и отправка по почте

Если ваша задача — сформировать отчет и передать его пользователям, то самым надежным решением будет сохранение файла во временный каталог сервера с последующей отправкой по электронной почте. Служба сервера 1С гарантированно имеет полные права на чтение и запись в своей временной директории, что исключает любые ошибки доступа.

Проанализируем пример кода для такого решения:


// Сформируем табличный документ по схеме компоновки данных (СКД)
Таблица = Новый ТабличныйДокумент;
СКД = Отчеты.МаркетинговоеИсследованиеЦен.ПолучитьМакет("Макет");
Настройки = СКД.НастройкиПоУмолчанию;

// Установим параметры отчета
ПараметрПериод = Настройки.ПараметрыДанных.Элементы.Найти("Период");
Если ПараметрПериод <> Неопределено Тогда
    ПараметрПериод.Значение = ТекущаяДата();
    ПараметрПериод.Использование = Истина;
КонецЕсли;

Расшифровка = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СКД, Настройки, Расшифровка);

ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , Расшифровка);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Таблица);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных, Истина);

// Шаг 1. Получаем гарантированно доступный путь во временном каталоге сервера
ИмяФайла = "Отчет_цены_" + Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd") + ".xlsx";
ПутьКВременномуФайлу = ПолучитьИмяВременногоФайла("xlsx");

// Шаг 2. Записываем табличный документ в формате XLSX
Таблица.Записать(ПутьКВременномуФайлу, ТипФайлаТабличногоДокумента.XLSX);

// Шаг 3. Отправляем сформированный файл по электронной почте (пример упрощенный)
// Здесь мы используем стандартные механизмы отправки почты 1С
Почта = Новый СистемныеПочтовыеПрофили;
// Настроить профиль отправки...
// Письмо = Новый ПочтовоеСообщение;
// Письмо.Вложения.Добавить(ПутьКВременномуФайлу, ИмяФайла);
// Почта.Отправить(Письмо);

// Шаг 4. Обязательно удаляем временный файл после отправки, чтобы не засорять диск сервера
Попытка
    УдалитьФайлы(ПутьКВременномуФайлу);
Исключение
    ЗаписьЖурналаРегистрации("РегламентноеЗадание.ВыгрузкаОтчета",
        УровеньЖурналаРегистрации.Предупреждение, , ,
        "Не удалось удалить временный файл: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Вариант Б. Запись в сетевой каталог (UNC-путь) с проверкой прав

Если отчет необходимо складывать в общую папку в локальной сети компании для доступа других сотрудников, используйте сетевые пути в формате UNC (например, "\\server-storage\SharedReports\").

Важное условие безопасности: Учетная запись службы Windows, под которой запущен сервер 1С (тот самый USR1CV8), должна обладать правами на запись ("Запись", "Изменение") в эту сетевую папку на уровне общего доступа и безопасности NTFS. Прав вашей личной учетной записи Windows для этого недостаточно!

Посмотрим на пример кода с предварительной проверкой доступности каталога и автоматическим его созданием при необходимости:


СетевойКаталог = "\\server-storage\SharedReports\Marketing\";
ИмяФайла = "Отчет_" + Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd") + ".xlsx";
ПолныйПуть = СетевойКаталог + ИмяФайла;

// Проверяем существование сетевой папки
КаталогНаДиске = Новый Файл(СетевойКаталог);
Если Не КаталогНаДиске.Существует() Тогда
    Попытка
        СоздатьКаталог(СетевойКаталог);
    Исключение
        ЗаписьЖурналаРегистрации("РегламентноеЗадание.ВыгрузкаОтчета",
            УровеньЖурналаРегистрации.Ошибка, , ,
            "Ошибка создания каталога: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат; // Завершаем выполнение, так как папка недоступна
    КонецПопытки;
КонецЕсли;

// Записываем файл
Попытка
    Таблица.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.XLSX);
Исключение
    ЗаписьЖурналаРегистрации("РегламентноеЗадание.ВыгрузкаОтчета",
        УровеньЖурналаРегистрации.Ошибка, , ,
        "Не удалось записать файл отчета на сетевой ресурс: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Вариант В. Альтернативный подход: сохранение в базу данных 1С

Часто файлы пытаются сохранить на диск сервера просто из-за отсутствия альтернатив. Однако наиболее прогрессивным методом является сохранение сформированного отчета непосредственно в саму базу данных 1С — например, в регистр сведений или справочник "Присоединенные файлы".

Пользователи смогут открыть и скачать этот файл в любой момент времени из интерфейса программы (в том числе при работе через Веб-клиент в обычном браузере), при этом вам не придется настраивать сетевые права доступа на сервере приложений.

Для реализации этого подхода выполните следующие шаги:


// Сохраняем табличный документ во временный файл на сервере
ВременныйФайл = ПолучитьИмяВременногоФайла("xlsx");
Таблица.Записать(ВременныйФайл, ТипФайлаТабличногоДокумента.XLSX);

// Помещаем файл в двоичные данные
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ВременныйФайл);

// Создаем хранилище значения для записи в базу данных
ХранилищеФайла = Новый ХранилищеЗначения(ДвоичныеДанныеФайла, Новый СжатиеДанных(9));

// Записываем хранилище в справочник ХранимыеОтчеты или регистр сведений
НовыйОтчет = Справочники.ХранимыеОтчеты.СоздатьЭлемент();
НовыйОтчет.Наименование = "Отчет по ценам от " + Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy HH:mm");
НовыйОтчет.ДанныеФайла = ХранилищеФайла;
НовыйОтчет.Расширение = "xlsx";
НовыйОтчет.Записать();

// Чистим временный файл на диске сервера
УдалитьФайлы(ВременныйФайл);

Используя эти паттерны программирования, мы полностью обезопасим наши регламентные задания от сбоев файловой системы, разграничения прав доступа и ограничений интерактивного интерфейса платформы 1С:Предприятие.

← На главную