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