Как на сервере вызвать форму выбора модально?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе «1С:Предприятие» многие, особенно начинающие программисты, сталкиваются с необходимостью запросить данные у пользователя (например, выбрать характеристику номенклатуры) прямо в момент выполнения серверного кода. Интуитивное желание — вызвать форму выбора прямо внутри серверной процедуры. Однако при попытке сделать это возникает ошибка: «Интерактивные операции недоступны».

Давайте подробно разберем, почему возникает эта проблема, как устроена архитектура платформы и какими методами нужно решать подобные задачи.

Суть проблемы и анализ кода

Рассмотрим ситуацию, описанную автором. Имеется серверная процедура, которая выполняется, например, после сканирования штрихкода. Если номенклатура имеет характеристики, программист хочет сразу же показать окно выбора.

Пример кода, который вызывает ошибку:


&НаСервере
&Перед("ПоискПоШтрихкодуЗавершениеНаСервере")
Процедура ДПоискПоШтрихкодуЗавершениеНаСервере1(ДополнительныеПараметры)
    // Попытка получить форму на сервере
    ФормаВыбора = Справочники.ХарактеристикиНоменклатуры.ПолучитьФормуВыбора();
    
    ФормаВыбора.Отбор.Владелец.Установить(ДополнительныеПараметры.КэшированныеЗначения.Номенклатура);
    ФормаВыбора.ПараметрВыборГруппИЭлементов = ИспользованиеГруппИЭлементов.Элементы;
    
    // Попытка открыть форму МОДАЛЬНО на сервере
    ВыбранноеЗначение = ФормаВыбора.ОткрытьМодально(); // <-- Здесь возникает ошибка
    
    Сообщить(СокрЛП(ВыбранноеЗначение));
    ДополнительныеПараметры.КэшированныеЗначения.Характеристика = ВыбранноеЗначение;
КонецПроцедуры

При выполнении этого кода система выдает исключение. Давайте разберемся в причинах.

Почему формы недоступны на сервере?

Чтобы понять причину ошибки, необходимо обратиться к основам клиент-серверной архитектуры «1С:Предприятие 8».

  1. Разделение контекстов: Платформа четко разделяет код, отвечающий за отображение (Клиент), и код, отвечающий за обработку данных (Сервер).
    • Клиент — это приложение, запущенное на компьютере пользователя (Тонкий клиент, Веб-клиент). У него есть экран, клавиатура, мышь. Он умеет рисовать окна.
    • Сервер — это программа (служба rphost), работающая где-то в серверной стойке или в облаке. У сервера нет монитора, нет пользователя, сидящего перед ним. Это «безголовый» вычислитель.
  2. Физическая невозможность: Как метко заметили участники обсуждения, сервер может находиться «в соседней галактике» или просто в другом здании. Код, выполняющийся на сервере, не имеет технической возможности нарисовать окно на мониторе конкретного пользователя, который сидит за сотни километров. Сервер только принимает запросы и отдает данные.
  3. Блокировка процессов: Метод ОткрытьМодально останавливает выполнение кода и ждет действий пользователя. Если бы это сработало на сервере, рабочий процесс сервера «завис» бы в ожидании, пока пользователь нажмет кнопку. А поскольку сервер обслуживает множество пользователей одновременно, это могло бы привести к отказу в обслуживании всей системы.

Вывод: На сервере форм не существует. Совсем. Никаких. Любые методы, связанные с интерфейсом (ПолучитьФорму, Сообщить, Предупреждение, ОткрытьФорму), должны вызываться только на клиенте.

Путаница с методом Выбрать()

Иногда возникает путаница между визуальным выбором и программной выборкой данных. В обсуждении был предложен вариант использования метода Выбрать() на сервере.

Давайте разграничим понятия:

Пример использования Выбрать() (это НЕ то, что нужно автору, но полезно для понимания):


// Это работает на сервере, но ничего не показывает пользователю
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
    // Обработка данных
КонецЦикла;

Как правильно решать задачу выбора данных?

Поскольку мы выяснили, что «зависнуть» на сервере и спросить пользователя нельзя, нам нужно изменить архитектуру решения. Процесс должен быть разбит на этапы, и управление должно передаваться между клиентом и сервером.

Правильный алгоритм действий выглядит так:

  1. Клиент: Пользователь инициирует действие (например, сканирует штрихкод).
  2. Клиент -> Сервер: Вызывается серверная функция для поиска данных.
  3. Сервер: Ищет номенклатуру. Понимает, что нужна характеристика (есть неоднозначность).
    Важно: Сервер не открывает форму. Сервер возвращает на Клиент структуру ответа, в которой сказано: «Найдена номенклатура Х, но нужно уточнить характеристику».
  4. Клиент: Получает ответ от сервера. Видит, что нужно уточнение. Открывает форму выбора (или выпадающий список) для пользователя.
  5. Клиент: Пользователь выбирает характеристику.
  6. Клиент -> Сервер: Снова вызывается сервер (или продолжается логика на клиенте) уже с полным набором данных (Номенклатура + Характеристика).

Рассмотрим схематичный пример реализации этого подхода.

1. Функция на сервере (только поиск)


&НаСервере
Функция НайтиНоменклатуруПоШтрихкодуНаСервере(Штрихкод)
    
    РезультатПоиска = Новый Структура;
    РезультатПоиска.Вставить("Номенклатура", Неопределено);
    РезультатПоиска.Вставить("ТребуетсяХарактеристика", Ложь);
    
    // Логика поиска...
    НайденнаяСсылка = Справочники.Номенклатура.НайтиПоРеквизиту("Штрихкод", Штрихкод);
    
    Если Не НайденнаяСсылка.Пустая() Тогда
        РезультатПоиска.Номенклатура = НайденнаяСсылка;
        // Проверяем, ведется ли учет по характеристикам
        Если НайденнаяСсылка.ВестиУчетПоХарактеристикам Тогда
            РезультатПоиска.ТребуетсяХарактеристика = Истина;
        КонецЕсли;
    КонецЕсли;
    
    Возврат РезультатПоиска;

КонецФункции

2. Процедура на клиенте (управление интерфейсом)


&НаКлиенте
Процедура ОбработкаСканирования(Штрихкод)
    
    // 1. Спрашиваем сервер
    ДанныеПоиска = НайтиНоменклатуруПоШтрихкодуНаСервере(Штрихкод);
    
    Если ДанныеПоиска.Номенклатура = Неопределено Тогда
        Сообщить("Товар не найден");
        Возврат;
    КонецЕсли;
    
    // 2. Анализируем ответ и принимаем решение об интерфейсе
    Если ДанныеПоиска.ТребуетсяХарактеристика Тогда
        
        // Нам нужно открыть форму выбора. Делаем это НА КЛИЕНТЕ.
        ПараметрыФормы = Новый Структура;
        ПараметрыФормы.Вставить("Отбор", Новый Структура("Владелец", ДанныеПоиска.Номенклатура));
        
        // Используем асинхронный метод (рекомендовано в современных версиях)
        Оповещение = Новый ОписаниеОповещения("ПослеВыбораХарактеристики", ЭтотОбъект, ДанныеПоиска);
        ОткрытьФорму("Справочник.ХарактеристикиНоменклатуры.ФормаВыбора", ПараметрыФормы, ЭтотОбъект,,,, Оповещение);
        
    Иначе
        // Характеристика не нужна, сразу добавляем в корзину
        ДобавитьТоварВКорзину(ДанныеПоиска.Номенклатура, Неопределено);
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораХарактеристики(РезультатВыбора, ДополнительныеПараметры) Экспорт
    
    Если РезультатВыбора = Неопределено Тогда
        // Пользователь отказался от выбора
        Возврат;
    КонецЕсли;
    
    // 3. Данные собраны, продолжаем работу
    ДобавитьТоварВКорзину(ДополнительныеПараметры.Номенклатура, РезультатВыбора);

КонецПроцедуры

Отказ от модальности

В теме также затрагивался вопрос «модальности». Стоит отметить, что современный стандарт разработки в 1С (особенно для веб-клиента и мобильных устройств) предполагает отказ от модальных окон (метод ОткрытьМодально) в пользу асинхронных вызовов через ОписаниеОповещения.

Модальные окна блокируют интерфейс всего браузера (в веб-клиенте), что считается плохим тоном UX. Поэтому, даже находясь на клиенте, старайтесь использовать конструкцию с ОписаниеОповещения, как показано в примере выше.

Резюме

  1. Вызвать форму или диалог на сервере невозможно технически.
  2. Серверный код должен быть «чистым» от интерфейса: получил данные -> обработал -> вернул результат.
  3. Если логика требует вмешательства пользователя в середине процесса, процесс необходимо разрывать: возвращать управление на клиент, открывать форму, получать выбор пользователя и затем снова вызывать сервер.
  4. Метод Выбрать() служит для перебора записей базы данных и не имеет отношения к визуальному интерфейсу.
← На главную