Одной из часто встречающихся задач в 1С:Предприятии является автоматическое оповещение пользователя о важных событиях при запуске программы. Это может быть уведомление о дебиторской задолженности, напоминание о задачах или, как в нашем случае, отчет по истекающим срокам договоров ИТС (или задействовать Универсальный отчет по метаданным). Однако начинающие разработчики часто сталкиваются с тем, что код, написанный для сервера, отказывается открывать окна или выдает загадочную "Ошибку отображения типов" (найти причину подобных проблем поможет Статический анализатор кода проектов). Разберем подробно, почему это происходит и как правильно реализовать такой функционал.
Проанализируем ситуацию: разработчик пытается выполнить запрос на сервере и там же вызвать метод Предупреждение() и Отчет.Сформировать(). В системе 1С:Предприятие 8.3 (особенно в режиме управляемого приложения) существует жесткое разделение на клиентский и серверный контексты.
На сервере «нет экрана». Сервер — это программный код, который работает в оперативной памяти сервера приложений или базы данных. Он не знает о существовании монитора пользователя. Поэтому любые попытки вызвать диалоговые окна (Предупреждение, Вопрос) или открыть форму (ОткрытьФорму) непосредственно из серверного метода приведут к ошибке. Кроме того, объект типа ОтчетОбъект (создаваемый через Отчеты.ИмяОтчета.Создать()) существует только на сервере и не может быть передан на клиент «как есть» — это и вызывает ошибку отображения типов.
Рассмотрим по шагам, как нужно структурировать код, применяя Шаблоны для применения cтандартов и методик разработки. Логика должна быть следующей: клиент запрашивает у сервера проверку условий, сервер возвращает простой ответ (например, Истина или Ложь), и если ответ положительный, клиент открывает форму отчета.
В общем модуле (с галочкой Сервер и Вызов сервера) создаем функцию для проверки условий:
&НаСервере
Функция НужноПоказатьОтчетПоИТС() Экспорт
ТекущаяДата = ТекущаяДата();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| СрокПоИСТ.Ссылка КАК Ссылка
|ИЗ
| Документ.СрокПоИСТ КАК СрокПоИСТ
|ГДЕ
| СрокПоИСТ.ДатаОкончания <= ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, 45)";
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата);
РезультатЗапроса = Запрос.Выполнить();
Возврат Не РезультатЗапроса.Пустой();
КонецФункции
Теперь в модуле управляемого приложения (или в расширении) пропишем логику открытия на клиенте:
&НаКлиенте
Процедура ПриНачалеРаботыСистемы()
Если ОбщийМодульСервер.НужноПоказатьОтчетПоИТС() Тогда
ПоказатьПредупреждение(, "Присутствуют пользователи, у которых заканчивается срок ИТС!");
ПараметрыОтчета = Новый Структура("СформироватьПриОткрытии", Истина);
ОткрытьФорму("Отчет.ОтчетПоСрокамИТС.Форма", ПараметрыОтчета);
КонецЕсли;
КонецПроцедуры
Обратите внимание на параметр СформироватьПриОткрытии. Это стандартный параметр системы компоновки данных (СКД). Вместо того чтобы мучиться с программным вызовом метода СкомпоноватьРезультат, мы просто передаем флаг форме. Платформа сама поймет, что при инициализации окна нужно сразу запустить процесс генерации отчета с настройками по умолчанию.
Выясним причину, почему это лучше ручного формирования:
Разберем ситуацию, когда запрос к базе данных выполняется долго. Если поместить тяжелый запрос непосредственно в ПриНачалеРаботыСистемы, пользователь будет наблюдать пустой экран или «колесико» загрузки слишком долго. Проанализируем, как сделать вход в систему более плавным с помощью ПодключитьОбработчикОжидания.
Этот метод позволяет запустить нашу проверку через пару секунд после того, как основной интерфейс программы уже отрисовался. Рассмотрим пример:
&НаКлиенте
Процедура ПриНачалеРаботыСистемы()
// Подключаем процедуру проверки через 1 секунду после запуска
ПодключитьОбработчикОжидания("ПроверитьСрокиИТС_Обработчик", 1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСрокиИТС_Обработчик()
// Выполняем проверку на сервере
Если ОбщийМодульСервер.НужноПоказатьОтчетПоИТС() Тогда
// Используем асинхронный метод оповещения
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПредупреждения", ЭтотОбъект);
ПоказатьПредупреждение(ОписаниеОповещения, "Истекают сроки ИТС. Сейчас будет открыт отчет.");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеПредупреждения(ДополнительныеПараметры) Экспорт
ОткрытьФорму("Отчет.ОтчетПоСрокамИТС.Форма.ФормаОтчета", Новый Структура("СформироватьПриОткрытии", Истина));
КонецПроцедуры
Важный момент: использование ПоказатьПредупреждение вместо устаревшего Предупреждение критически важно для работы в веб-клиенте и в интерфейсе "Такси". Современная платформа 1С стремится к полной асинхронности, чтобы модальные окна не блокировали работу браузера или других вкладок программы.
Если вы не хотите менять типовую конфигурацию, используйте механизм расширений. Как было предложено в одном из сообщений форума, можно использовать аннотацию &После. Посмотрим на реализацию в расширении:
&После("ПриНачалеРаботыСистемы")
Процедура Расш1_ПриНачалеРаботыСистемы()
// Логика проверки и открытия
// Важно: в расширении также соблюдайте разделение контекста!
Если МойМодульВРасширении.ПроверитьДанные() Тогда
ОткрытьФорму("Отчет.МойОтчет.Форма");
КонецЕсли;
КонецПроцедуры
Проанализируем основные "грабли", на которые наступают разработчики:
ОтчетОбъект нельзя вернуть функцией на клиент. Передавайте только примитивные типы (числа, строки, булево) или структуры с данными.ПолучитьФорму просто создает экземпляр формы в памяти, но не показывает его. Метод ОткрытьФорму выполняет полный цикл — создание, инициализацию и вывод на экран.Таким образом, для решения задачи необходимо вынести тяжелый запрос на сервер, вернуть результат в виде логического значения на клиент, и уже на стороне клиента, используя асинхронные методы и параметры формы, вывести отчет пользователю. Это обеспечит стабильную работу системы без ошибок отображения типов и зависаний интерфейса.