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