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