При разработке групповых обработок, автоматических выгрузок или регламентных заданий в среде «1С:Предприятие 8.3» программисты часто сталкиваются с неприятной проблемой: стандартный код документов или отчетов пытается взаимодействовать с пользователем. Всплывающие окна с вопросами вроде «Данные были изменены. Сохранить изменения?» или «Перед выгрузкой отчета требуется сохранить введенную информацию. Продолжить?» полностью блокируют автоматический процесс, требуя ручного клика «Да» или «Нет».
В этой статье мы подробно разберем, почему возникают подобные ситуации, проанализируем ошибки в коде и рассмотрим несколько эффективных способов обхода интерактивных диалогов без потери функционала записи данных.
Давайте проанализируем ситуацию, с которой столкнулся автор темы. В коде управляемой формы налоговой накладной была попытка применить метод ЗаписатьВФорме(). В результате система выдавала ошибку выполнения.
Причина кроется в архитектурных различиях между обычным и управляемым приложением:
ЗаписатьВФорме() принадлежит исключительно расширению обычной (неуправляемой) формы. Он использовался в старых версиях платформы (1С 8.2 и более ранних в режиме толстого клиента).Записать(). Попытка вызвать несуществующий метод предсказуемо приводила к аварийному завершению работы скрипта.Кроме того, сама по себе практика открытия интерактивных форм в цикле при групповой обработке документов (через метод ОткрытьФорму()) является серьезным антипаттерном. Это создает колоссальную нагрузку на клиентский компьютер, замедляет работу системы и заставляет платформу выполнять множество лишних операций по отрисовке интерфейса.
Ниже мы рассмотрим три практических сценария, как решить задачу обхода диалогов корректно и безопасно.
Если логика конфигурации спроектирована так, что выгрузка обязательно должна инициироваться через открытие формы, мы можем передать в форму специальный параметр, сообщающий ей о том, что визуальные вопросы задавать не нужно.
Давайте разберем по шагам, как это реализовать.
Шаг 1. При программном вызове формы в модуле нашей групповой обработки мы инициализируем структуру параметров и передаем туда наш флаг, например, БезДиалогов:
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("БезДиалогов", Истина);
ПараметрыФормы.Вставить("Ключ", СсылкаНаДокумент); // Если открываем существующий документ
ВыбФормаОтчета = ОткрытьФорму("Отчет.НалоговаяНакладная.Форма.ФормаОтчета", ПараметрыФормы);
Шаг 2. В модуле самой управляемой формы (в процедурах, где происходит проверка модифицированности и вызов функции Вопрос()) мы проверяем наличие переданного параметра:
ПроводитьИнтерактивнуюПроверку = Истина;
Если Параметры.Свойство("БезДиалогов") И Параметры.БезДиалогов Тогда
ПроводитьИнтерактивнуюПроверку = Ложь;
КонецЕсли;
Если Модифицированность Тогда
Если ПроводитьИнтерактивнуюПроверку Тогда
// Стандартный интерактивный сценарий с вопросом пользователю
ТекстВопроса = НСтр("ru='Перед выгрузкой отчета требуется сохранить введенную информацию. Продолжить?'");
Если Вопрос(ТекстВопроса, РежимДиалогаВопрос.ДаНет) <> КодВозвратаДиалога.Да Или Не СохранитьНаКлиенте() Тогда
Возврат;
КонецЕсли;
Иначе
// Программный сценарий без вызова окон: просто сохраняем данные формы
Если Не СохранитьНаКлиенте() Тогда
ВызватьИсключение "Не удалось сохранить данные формы при автоматической выгрузке!";
КонецЕсли;
КонецЕсли;
КонецЕсли;
Этот подход позволяет сохранить оригинальную бизнес-логику формы для обычных пользователей, но дает возможность программному коду проскакивать опасные участки без зависания сессии.
Часто проверка и выдача вопросов происходит не в самой форме, а на уровне модуля объекта (например, в обработчиках событий ПередЗаписью или ПриЗаписи). Чтобы передать флаг «тихой» работы с клиента на сервер внутрь записываемого объекта, разработчики используют стандартное свойство ДополнительныеСвойства.
Это специальная структура, которая существует "в памяти" у конкретного экземпляра объекта базы данных во время его жизненного цикла и не записывается в саму СУБД.
Рассмотрим, как применить этот механизм. В коде обработки перед записью документа мы пишем следующий код:
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// Заполняем структуру свойств флагом отключения диалогов
ДокументОбъект.ДополнительныеСвойства.Вставить("ОтключитьДиалогиИПроверки", Истина);
// Выполняем необходимые изменения
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Теперь перейдем в модуль объекта нашего документа (или подписки на событие ПередЗаписью) и считаем это свойство:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, запущен ли процесс в режиме групповой обработки
ЭтоГрупповаяОбработка = Ложь;
Если ДополнительныеСвойства.Свойство("ОтключитьДиалогиИПроверки") Тогда
ЭтоГрупповаяОбработка = ДополнительныеСвойства.ОтключитьДиалогиИПроверки;
КонецЕсли;
Если Не ЭтоГрупповаяОбработка Тогда
// Здесь выполняются интерактивные проверки и вызовы диалогов
// ...
КонецЕсли;
КонецПроцедуры
Использование свойства ДополнительныеСвойства — один из самых гибких и надежных способов управления поведением алгоритмов проведения и записи документов «на лету».
Если вам необходимо полностью отключить любые проверки, триггеры, автозаполнения и интерактивные вопросы при записи объекта в базу данных (например, при импорте или жесткой групповой перезаписи), можно задействовать платформенный механизм ОбменДанными.Загрузка.
Когда этот флаг установлен в значение Истина, платформа 1С понимает, что объект переносится "как есть", и подавляет большинство стандартных проверок в модулях.
Посмотрим на пример его реализации:
ДокументОбъект = ВыбраннаяСсылка.ПолучитьОбъект();
ДокументОбъект.ОбменДанными.Загрузка = Истина;
// Платформа запишет объект без вызова стандартных проверок заполнения и транзакционных вопросов
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Важное предупреждение: Применение свойства ОбменДанными.Загрузка = Истина требует особой осторожности. Вместе с вопросами вы отключаете также и важные бизнес-проверки конфигурации (контроль остатков, заполнение обязательных реквизитов, сверку дат). Пользуйтесь этим методом только тогда, когда вы абсолютно уверены в корректности записываемых данных.
Иногда возникают ситуации, когда данные формы фактически уже сохранены или их не требуется сохранять вовсе, но форма всё равно считает себя измененной (например, после выполнения программного заполнения полей на клиенте). При попытке закрыть форму платформа видит признак Модифицированность = Истина и автоматически задает вопрос пользователю.
Чтобы мгновенно обойти это поведение без изменения логики записи, мы можем принудительно "обмануть" форму, сбросив признак ее модифицированности перед закрытием:
// Выполняем действия с формой
// ...
// Сбрасываем признак модифицированности: форма считает, что изменений не было
ВыбФормаОтчета.Модифицированность = Ложь;
ВыбФормаОтчета.Закрыть();
В этом случае форма закроется мгновенно, не вызывая никаких штатных предупреждений и вопросов.
В завершение разберем наиболее правильный с точки зрения стандартов разработки 1С подход. Любая массовая обработка документов (будь то проведение, заполнение или экспорт в XML) должна выполняться **исключительно на сервере**, минуя создание и открытие клиентских форм.
Вся логика формирования XML-файла выгрузки должна быть вынесена в экспортный метод общего серверного модуля или в модуль менеджера документа. Наш групповой обработчик должен выглядеть примерно так:
&НаСервере
Процедура ВыполнитьГрупповуюВыгрузкуНаСервере(МассивСсылок)
Для Каждого ТекущаяСсылка Из МассивСсылок Цикл
// Получаем объект напрямую на сервере без открытия интерфейса
ДокументОбъект = ТекущаяСсылка.ПолучитьОбъект();
// Выполняем заполнение или расчет параметров
// ДокументОбъект.ЗаполнитьПоУмолчанию();
// Передаем признак автоматической обработки
ДокументОбъект.ДополнительныеСвойства.Вставить("ГрупповаяОбработка", Истина);
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
// Вызываем серверный метод формирования XML, передавая туда ссылку
ВыгрузкаДокументовСервер.ВыгрузитьВXML(ДокументОбъект.Ссылка);
Исключение
ЗаписьЖурналаРегистрации("ГрупповаяВыгрузка", УровеньЖурналаРегистрации.Ошибка,,,
"Ошибка при обработке документа " + ТекущаяСсылка + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Этот подход гарантирует: