При разработке в 1С часто возникает задача, когда заказчик желает видеть интерактивное подтверждение (окно типа "Да/Нет") перед проведением документа при определенных условиях. Однако эта задача усложняется, когда проведение документа осуществляется не только из открытой формы документа, но и из формы списка (например, нажатием кнопки "Провести" на панели или через контекстное меню), а также при групповом проведении. Мы проанализируем эту ситуацию и рассмотрим доступные методы решения.
Основная сложность заключается в том, что платформа 1С по-разному обрабатывает события, происходящие в форме документа, и события, относящиеся к самому объекту документа, особенно когда речь идет о клиент-серверном взаимодействии и модальных окнах.
Чтобы понять, почему стандартные методы могут не работать в нужных нам сценариях, давайте разберем ключевые различия в обработке событий:
ПередЗаписью() в модуле формы документа:
Вопрос() (хотя в управляемых формах это считается устаревшей практикой) или асинхронную функцию ПоказатьВопрос().ПараметрыЗаписи для передачи дополнительной информации, такой как режим записи или проведения документа.ПередЗаписью() в модуле объекта документа:
Отказ = Истина.ПередЗаписьюНаСервере формы документа:
ТекущийОбъект). Изменения, внесенные в ТекущийОбъект, будут записаны, в отличие от изменений в основном реквизите формы Объект, которые могут быть потеряны.ОбработкаПроведения(Отказ, РежимПроведения) модуля объекта документа:
ПередЗаписью() объекта, она выполняется на сервере и не может выводить интерактивные диалоги. Установка Отказ = Истина в этой процедуре отменит проведение.Как мы выяснили, если пользователь нажимает кнопку "Провести" на панели формы списка, не открывая документ, то процедура ПередЗаписью() формы документа не вызывается. Вместо этого вызывается процедура ПередЗаписью() объекта документа, которая выполняется на сервере. Если мы поместим там вызов Вопрос(), это приведет к блокировке или ошибке, особенно при попытке провести сразу несколько документов из списка – пользователю посыплются модальные окна, что недопустимо.
Таким образом, основная проблема заключается в том, что платформа 1С:Предприятие 8.3 ориентирована на работу без модальных окон, особенно в веб-клиенте и на мобильных устройствах. Для взаимодействия с пользователем рекомендуется использовать асинхронную модель.
Поскольку прямое использование модальных окон в серверных процедурах объекта невозможно и нежелательно, мы рассмотрим несколько подходов, которые помогут элегантно обойти это ограничение.
Этот подход является одним из наиболее гибких, хотя и требует некоторой доработки конфигурации. Он позволяет полностью контролировать логику проведения и интерактивного взаимодействия.
Видимость) или через программное изменение доступности команд в процедурах ПриСозданииНаСервере или ПриОткрытии.В обработчике команды "Провести (с вопросом)" формы документа мы можем использовать асинхронный вызов ПоказатьВопрос(). Мы установим Отказ = Истина в ПередЗаписью формы, а затем зададим вопрос. Если пользователь ответит "Да", мы повторно вызовем стандартную процедуру записи/проведения документа, но уже без вопросов.
// Пример в модуле формы документа
&НаКлиенте
Процедура МояКомандаПровести(Команда)
// Сохраним состояние, чтобы при повторном вызове не задавать вопрос
Если НЕ ДополнительныеСвойства.Свойство("ОтвеченоНаВопросПроведения") Тогда
// ПередЗаписью на форме может быть перехвачено
Оповещение = Новый ОписаниеОповещения("ОбработатьОтветНаВопросПроведения", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Вы действительно хотите провести документ?", РежимДиалогаВопрос.ДаНет, , "Подтверждение проведения");
Иначе
// Если уже ответили "Да", то проводим
Записать(РежимЗаписиДокумента.Проведение);
ДополнительныеСвойства.Удалить("ОтвеченоНаВопросПроведения"); // Очищаем флаг
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьОтветНаВопросПроведения(РезультатВопроса, ДополнительныеПараметры) Экспорт
Если РезультатВопроса = КодВозвратаДиалога.Да Тогда
// Устанавливаем флаг, что вопрос уже задан и отвечен положительно
// Это нужно, чтобы при повторном вызове записи через команду
// процедура ПередЗаписью() или ПередЗаписьюНаСервере() не задавала вопрос снова.
ДополнительныеСвойства.Вставить("ОтвеченоНаВопросПроведения", Истина);
// Повторно вызываем проведение
Записать(РежимЗаписиДокумента.Проведение);
Иначе
// Пользователь отказался
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ПараметрыЗаписи)
Если НЕ ДополнительныеСвойства.Свойство("ОтвеченоНаВопросПроведения") Тогда
// Здесь можно определить условие, при котором нужно задавать вопрос
Если УсловиеДляВопроса Тогда // Ваше условие
Отказ = Истина; // Отменяем запись
// Далее на клиенте будет вызван ПоказатьВопрос
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В обработчике команды "Провести (с вопросом)" формы списка мы можем проверить количество выделенных документов. Если выделен только один документ, мы можем открыть его форму и вызвать нашу команду проведения с вопросом. Если выделено несколько документов, мы можем либо провести их без вопроса, либо выдать предупреждение о невозможности интерактивного подтверждения для группы, либо вовсе отказать в групповом проведении через эту команду.
// Пример в модуле формы списка документов
&НаКлиенте
Процедура МояКомандаПровестиИзСписка(Команда)
ВыбранныеЭлементы = Элементы.Список.ВыделенныеСтроки;
Если ВыбранныеЭлементы.Количество() = 1 Тогда
ИДДокумента = ВыбранныеЭлементы[0];
// Получим ссылку на документ
ДокументСсылка = Документы.МойДокумент.ПолучитьСсылку(ИДДокумента);
// Открываем форму документа и вызываем там нашу команду проведения
ФормаДокумента = ПолучитьФорму("Документ.МойДокумент.ФормаДокумента", Новый Структура("Ключ", ДокументСсылка));
// Передаем параметр, чтобы форма знала, что нужно задать вопрос
ФормаДокумента.ДополнительныеСвойства.Вставить("ТребуетсяПодтверждениеПроведения", Истина);
ФормаДокумента.Открыть();
// В самой форме документа нужно будет вызвать команду проведения после открытия,
// если ТребуетсяПодтверждениеПроведения == Истина.
// Или можно передать в ПараметрыОткрытия, чтобы обработать в ПриСозданииНаСервере
// и вызвать асинхронный вопрос.
ИначеЕсли ВыбранныеЭлементы.Количество() > 1 Тогда
ПоказатьПредупреждение(Новый ОписаниеОповещения(""), "Для группового проведения интерактивное подтверждение невозможно. Документы будут проведены без вопроса.");
// Здесь вызываем серверную процедуру для группового проведения без вопросов
ПровестиГруппуДокументовНаСервере(ВыбранныеЭлементы);
Иначе
ПоказатьПредупреждение(Новый ОписаниеОповещения(""), "Не выбрано ни одного документа для проведения.");
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПровестиГруппуДокументовНаСервере(ИДДокументов)
Для Каждого ИД Из ИДДокументов Цикл
ДокументОбъект = Документы.МойДокумент.ПолучитьОбъект();
ДокументОбъект.Прочитать(); // или сразу получить по ссылке
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
КонецПроцедуры
Важный нюанс: в типовых конфигурациях 1С часто можно обнаружить "свои" кнопочки "Провести", "Записать", "Провести и закрыть" в формах документа. Это подтверждает, что такой подход не является чем-то "неэлегантным", а скорее практичным решением для обеспечения специфической логики.
Если мы не хотим полностью переписывать команды, но хотим хотя бы избежать блокировки при групповом проведении, можно в процедуре ПередЗаписью() или ОбработкаПроведения() модуля объекта документа попытаться определить контекст. Однако это очень сложно и ненадежно, так как серверный код не знает, из какого именно места был вызван метод. Мы не можем точно знать, сколько документов пытается провести пользователь, если это не явно передано в параметрах.
Единственный надежный способ отличить "индивидуальное проведение из формы" от "группового" на сервере — это использовать флаги, передаваемые через параметры, или полагаться на то, что только индивидуальное проведение из формы может инициировать асинхронный диалог. Если мы не управляем вызовом из клиента, то на сервере мы всегда должны предполагать, что диалог невозможен.
Как уже упоминалось, платформа 1С 8.3 для управляемых форм рекомендует использовать ПоказатьВопрос() вместо модальных функций Вопрос(). Это особенно актуально, если мы хотим сохранить возможность интерактивного диалога при проведении из открытой формы документа.
Отказ = Истина: В процедуре ПередЗаписьюНаСервере формы (если мы хотим, чтобы вопрос задавался перед записью) мы можем установить Отказ = Истина, если условия для вопроса выполнены.ПоказатьВопрос() на клиенте: После отмены записи на сервере, управление вернется на клиент. Здесь мы можем вызвать ПоказатьВопрос(), передав в него ОписаниеОповещения.ОписаниеОповещения, мы проверяем ответ пользователя. Если пользователь ответил "Да", мы повторно вызываем стандартную процедуру записи/проведения документа, но уже с флагом, который сообщит серверу (или ПередЗаписьюНаСервере), что вопрос уже был задан и ответ положительный, чтобы избежать повторного вызова вопроса.
// В модуле формы документа
&НаКлиенте
ПеременнаяФормы_ТребуетсяПодтверждениеПроведения = Ложь;
&НаКлиенте
Процедура ПриЗаписи(Отказ)
Если ПеременнаяФормы_ТребуетсяПодтверждениеПроведения Тогда
Возврат; // Пропускаем проверку, если уже ответили на вопрос
КонецЕсли;
// Здесь можно реализовать проверку условий, при которых нужен вопрос
Если УсловияДляВопросаВыполнены() Тогда
Отказ = Истина; // Отменяем текущую запись
Оповещение = Новый ОписаниеОповещения("ПослеВопросаОПроведении", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Вы действительно хотите провести документ?", РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопросаОПроведении(РезультатВопроса, ДополнительныеПараметры) Экспорт
Если РезультатВопроса = КодВозвратаДиалога.Да Тогда
ПеременнаяФормы_ТребуетсяПодтверждениеПроведения = Истина; // Устанавливаем флаг
Записать(РежимЗаписиДокумента.Проведение); // Повторно вызываем проведение
ПеременнаяФормы_ТребуетсяПодтверждениеПроведения = Ложь; // Сбрасываем флаг
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция УсловияДляВопросаВыполнены()
// Здесь ваша логика определения, нужен ли вопрос
// Например, если какой-то реквизит имеет определенное значение
Возврат Объект.Сумма > 100000;
КонецФункции
Прежде чем реализовывать сложные технические решения, всегда полезно вернуться к исходной бизнес-задаче. Почему заказчику нужен такой вопрос? Возможно, есть более системные и менее интрузивные способы обеспечить контроль:
Мы настоятельно рекомендуем избегать любых попыток вывода модальных окон (например, Вопрос()) из процедур, выполняющихся на сервере (таких как ПередЗаписью() или ОбработкаПроведения() модуля объекта). Это приведет к ошибкам в веб-клиенте, в фоновых заданиях, при групповой обработке и при интерактивной работе в тонком клиенте, блокируя выполнение и создавая неудобства для пользователей.
Решение задачи по выводу интерактивного подтверждения перед проведением документа, особенно при проведении из формы списка или групповом проведении, требует глубокого понимания архитектуры 1С:Предприятие и клиент-серверного взаимодействия. Универсального "элегантного" решения, которое бы одинаково хорошо работало во всех сценариях, не существует. Нам предстоит идти на компромиссы, выбирая между кастомизацией интерфейса (создание своих команд), использованием асинхронных диалогов и, возможно, пересмотром бизнес-логики. Самым надежным и контролируемым подходом часто оказывается создание собственных команд проведения с детальной обработкой контекста вызова.