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