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