Как выполнить запрос после выбора значения в колонке табличной части 1С?

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

При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда после интерактивного выбора значения в табличной части документа необходимо выполнить сложную логику: проверить остатки, рассчитать цены или заполнить связанные реквизиты. Все эти действия требуют выполнения запроса к базе данных. Однако попытка вызвать процедуру с запросом напрямую из события НачалоВыбора часто приводит к ошибкам компиляции или выполнения. Разберем подробно, почему это происходит и какими способами можно решить эту задачу.

Почему возникает проблема с запросом?

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

Основные причины ограничений:

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

Решение 1: Использование события ПриИзменении

Рассмотрим самый простой и рекомендуемый способ. Вместо того чтобы пытаться выполнить логику в процессе начала выбора, логично сделать это после того, как значение уже выбрано и помещено в ячейку. Для этого идеально подходит событие ПриИзменении соответствующего поля ввода в табличной части.

Разберем алгоритм действий по шагам:

  1. Пользователь выбирает значение в форме списка.
  2. Значение передается в поле ввода табличной части.
  3. Срабатывает клиентское событие ПриИзменении.
  4. Из этого события мы вызываем серверную процедуру, где спокойно выполняем Запрос.

Пример кода в модуле формы:


&НаКлиенте
Процедура ТЧТоварыНоменклатураПриИзменении(Элемент)
    
    // Получаем данные текущей строки
    ТекущиеДанные = Элементы.ТЧТовары.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную функцию для получения данных по запросу
    ДополнительныеДанные = ПолучитьДанныеНаСервере(ТекущиеДанные.Номенклатура);
    
    // Заполняем реквизиты строки на основе результата запроса
    ТекущиеДанные.Цена = ДополнительныеДанные.Цена;
    ТекущиеДанные.ЕдиницаИзмерения = ДополнительныеДанные.Единица;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДанныеНаСервере(Знач ВыбраннаяНоменклатура)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЦеныНоменклатурыСрезПоследних.Цена КАК Цена,
        |   Номенклатура.ЕдиницаИзмерения КАК Единица
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
        |       ПО Номенклатура.Ссылка = ЦеныНоменклатурыСрезПоследних.Номенклатура
        |ГДЕ
        |   Номенклатура.Ссылка = &Номенклатура";
    
    Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Результат = Новый Структура("Цена, Единица", 0, Справочники.КлассификаторЕдиницИзмерения.ПустаяСсылка());
    Если Выборка.Следующий() Тогда
        ЗаполнитьЗначенияСвойств(Результат, Выборка);
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

Решение 2: Программное открытие формы и ОписаниеОповещения

Если вам необходимо использовать именно событие НачалоВыбора (например, для реализации сложного подбора), проанализируем ситуацию с программным открытием формы выбора. В этом случае мы устанавливаем СтандартнаяОбработка = Ложь и используем метод ОткрытьФорму.

Этот метод позволяет передать объект ОписаниеОповещения, который указывает платформе, какую процедуру нужно выполнить после закрытия формы выбора. В этой процедуре-обработчике серверные вызовы разрешены, так как контекст выбора уже завершен.

Рассмотрим пример реализации:


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

&НаКлиенте
Процедура ПослеЗавершенияВыбора(ВыбранноеЗначение, ДополнительныеПараметры) Экспорт
    
    Если ВыбранноеЗначение = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Здесь можно вызывать серверные процедуры с запросами
    // так как интерактивный выбор завершен
    ОбработатьВыборНаСервере(ВыбранноеЗначение);
    
КонецПроцедуры

Решение 3: Разрыв контекста через обработчик ожидания

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

Суть метода заключается в том, чтобы дать текущему потоку команд завершиться, а нужную нам логику выполнить через минимальную паузу (например, 0.1 секунды).

Проанализируем пример кода:


&НаКлиенте
Процедура ТЧТоварыНоменклатураПриИзменении(Элемент)
    
    // Вместо прямого вызова сервера подключаем обработчик
    // Параметр "Истина" означает однократное выполнение
    ПодключитьОбработчикОжидания("ВыполнитьЗапросПослеВыбора", 0.1, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьЗапросПослеВыбора()
    
    // Теперь мы находимся в "чистом" контексте
    ТекущиеДанные = Элементы.ТЧТовары.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        // Выполняем любые серверные действия
        Результат = ПолучитьДанныеИзЗапросаНаСервере(ТекущиеДанные.Номенклатура);
        // ... заполнение данных ...
    КонецЕсли;
    
КонецПроцедуры

Важные нюансы настройки общих модулей

Если вы решили вынести логику запроса в общий модуль (что является хорошей практикой), убедитесь в правильности его настроек. Если вы видите ошибку «Объект не обнаружен: Запрос», проверьте следующее:


#Если Сервер Тогда
    Запрос = Новый Запрос;
    // ... логика запроса ...
#КонецЕсли

Особенности события ОбработкаВыбора

Существует еще одно событие — ОбработкаВыбора. Оно срабатывает у поля ввода, когда форма выбора уже закрыта, значение получено, но еще не помещено в реквизит.

Важный момент: если в НачалоВыбора вы установили СтандартнаяОбработка = Ложь, то стандартное событие ОбработкаВыбора может не сработать автоматически, если вы не прописали логику возврата значения.

Рассмотрим, когда лучше использовать каждое событие:

  1. ПриИзменении: Лучший выбор для 90% задач. Значение уже в объекте, можно менять любые поля строки.
  2. ОбработкаВыбора: Полезно, если вы хотите подменить выбранное пользователем значение на другое до того, как оно отобразится в ячейке.
  3. НачалоВыбора: Используется только для установки фильтров (отборов) перед открытием окна выбора или для реализации собственного алгоритма подбора (например, через специальную обработку).

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

← На главную