Оповещения форм 1С — механизм, которым одна открытая форма сообщает остальным, что данные изменились: записан документ, пересчитана сводка, клиент передан другому менеджеру. Отправляет оповещение метод Оповестить(ИмяСобытия, Параметр, Источник), принимает — событие формы ОбработкаОповещения с теми же тремя параметрами. Рядом живёт ОповеститьОбИзменении: он не передаёт формам данных, а обновляет динамические списки и сбрасывает кэши клиента. Ниже — как это устроено в 1С 8.3, код отправителя и получателя с фильтрами, перечитывание открытой карточки и рассылка в другие сеансы. Имена типовых объектов и событий сверены по выгрузке УТ 11.4.
Запись_ПриходныйКассовыйОрдер, Запись_ПоступлениеБезналичныхДенежныхСредств и другие события документов оплаты.ScanData с источником-строкой "ПодключаемоеОборудование".В 1С 8.3 оповещение отправляет Оповестить — метод глобального контекста, который есть только на клиенте: тонком, веб, мобильном и толстом. По синтаксис-помощнику метод посылает оповещение всем созданным формам и процедурам, подключённым через ПодключитьОбработчикОповещения. В каждой форме, где назначен обработчик ОбработкаОповещения, он вызывается с параметрами отправителя:
| Параметр | Тип | Что передают |
|---|---|---|
| ИмяСобытия | Строка | ключ, по которому получатель понимает, что случилось |
| Параметр | Произвольный | данные: ссылку, массив ссылок, структуру, параметры записи |
| Источник | Произвольный | отправителя: форму, ссылку на записанный объект или строку-метку |
Что важно знать до написания кода:
ОбработкаОповещения сама не вызывается. В конфигураторе выделите корень формы, в палитре свойств найдите раздел «События» → «ОбработкаОповещения» и нажмите кнопку с лупой: появится заготовка с директивой &НаКлиенте.Запись_... сама. Это соглашение типовых конфигураций: вызов Оповестить написан в ПослеЗаписи форм объектов, и формат у форм разный.ПодключитьОбработчикОповещения. Так событие ловят, даже когда нужная форма закрыта.Как шлют оповещения типовые формы УТ 11.4 — от этого зависит, где получатель ищет ссылку:
| Событие | Параметр | Источник |
|---|---|---|
| Запись_ЗаказКлиента | ПараметрыЗаписи с добавленной Сделкой | Объект.Ссылка |
| Запись_РеализацияТоваровУслуг | ПараметрыЗаписи | Объект.Ссылка |
| Запись_Партнеры | Объект.Ссылка | не передаётся |
| ScanData | данные со сканера | "ПодключаемоеОборудование" |
Отправителя любого события найдёт глобальный поиск конфигуратора (Ctrl+Shift+F) по имени события в кавычках.
Пример — своя обработка «Передача клиентов менеджеру». Пользователь отмечает партнёров и выбирает нового основного менеджера; реквизит ОсновнойМенеджер у справочника Партнеры в УТ 11.4 есть. Сервер переписывает карточки, клиент сообщает об этом двумя способами: динамическим спискам — через ОповеститьОбИзменении, открытым формам — своим событием. Отправлять оповещения программно можно из любой клиентской процедуры: команды, обработчика события, обработчика ожидания.
// Обработка «Передача клиентов менеджеру», модуль формы.
// Реквизиты формы: Клиенты (ТаблицаЗначений: Пометка, Партнер),
// НовыйМенеджер (СправочникСсылка.Пользователи). Команда «Передать».
&НаКлиенте
Процедура Передать(Команда)
Если Не ЗначениеЗаполнено(НовыйМенеджер) Тогда
ПоказатьПредупреждение(, "Выберите менеджера, которому передаются клиенты.");
Возврат;
КонецЕсли;
Изменены = ПередатьНаСервере();
Если Изменены.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// 1. Динамические списки партнеров: по каждой ссылке или сразу по типу.
// Порог условный: для большой пачки хватит одного вызова на тип.
Если Изменены.Количество() > 20 Тогда
ОповеститьОбИзменении(Тип("СправочникСсылка.Партнеры"));
Иначе
Для Каждого Партнер Из Изменены Цикл
ОповеститьОбИзменении(Партнер);
КонецЦикла;
КонецЕсли;
// 2. Открытые формы сеанса: свое событие с данными для фильтра.
ДанныеСобытия = Новый Структура("Партнеры, НовыйМенеджер", Изменены, НовыйМенеджер);
Оповестить("ИзмененОсновнойМенеджер", ДанныеСобытия, ЭтотОбъект);
ПоказатьОповещениеПользователя("Клиенты переданы", ,
СтрШаблон("Изменено карточек: %1", Изменены.Количество()));
КонецПроцедуры
&НаСервере
Функция ПередатьНаСервере()
Изменены = Новый Массив;
Для Каждого СтрокаКлиента Из Клиенты Цикл
Если Не СтрокаКлиента.Пометка Или Не ЗначениеЗаполнено(СтрокаКлиента.Партнер) Тогда
Продолжить;
КонецЕсли;
ПартнерОбъект = СтрокаКлиента.Партнер.ПолучитьОбъект();
Если ПартнерОбъект = Неопределено
Или ПартнерОбъект.ОсновнойМенеджер = НовыйМенеджер Тогда
Продолжить; // битая ссылка или менеджер уже назначен
КонецЕсли;
Попытка
// Карточку редактируют в другом окне - блокировка не встанет, пропускаем
ПартнерОбъект.Заблокировать();
ПартнерОбъект.ОсновнойМенеджер = НовыйМенеджер;
ПартнерОбъект.Записать();
Изменены.Добавить(СтрокаКлиента.Партнер);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("%1: %2", СтрокаКлиента.Партнер,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Сообщение.Сообщить();
КонецПопытки;
КонецЦикла;
Возврат Изменены;
КонецФункции
Получатель — своя обработка «Клиенты менеджера»: партнёры с открытыми заказами и датой последнего заказа. Таблица собрана запросом с группировкой, это реквизит типа ТаблицаЗначений, а не динамический список, поэтому ОповеститьОбИзменении её не обновит. Обработка оповещения здесь — цепочка фильтров: имя события, тип и содержимое параметра, источник. Запрос удобно проверить в консоли запросов.
// Обработка «Клиенты менеджера», модуль формы.
// Реквизиты формы: Менеджер (СправочникСсылка.Пользователи),
// Клиенты (ТаблицаЗначений: Партнер, Заказов, ПоследнийЗаказ).
// ОбработкаОповещения и МенеджерПриИзменении назначены в палитре свойств.
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ИзмененОсновнойМенеджер" Тогда
// Свое событие: структура с массивом партнеров и новым менеджером.
// Обновляемся, если клиенты пришли к нам или ушли из нашей таблицы.
Партнеры = Неопределено;
НовыйМенеджер = Неопределено;
Если ТипЗнч(Параметр) = Тип("Структура")
И Параметр.Свойство("Партнеры", Партнеры)
И Параметр.Свойство("НовыйМенеджер", НовыйМенеджер)
И (НовыйМенеджер = Менеджер Или ЕстьВТаблице(Партнеры)) Тогда
ЗапланироватьОбновление();
// Источник-строка: изменение пришло из другого сеанса (раздел ниже)
Если Источник = "УведомленияКлиента" Тогда
ПоказатьОповещениеПользователя("Список клиентов изменился", ,
"Основного менеджера сменили в другом сеансе");
КонецЕсли;
КонецЕсли;
ИначеЕсли ИмяСобытия = "Запись_ЗаказКлиента" Тогда
// Типовая форма заказа: параметры записи в Параметре, ссылка в Источнике.
// Чей это клиент, без сервера не узнать - обновляемся всегда.
ЗапланироватьОбновление();
ИначеЕсли ИмяСобытия = "Запись_Партнеры" Тогда
// Типовая форма партнера: ссылка в Параметре, Источник не передается.
// Менеджера могли сменить в самой карточке, фильтр по таблице не годится.
ЗапланироватьОбновление();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ЕстьВТаблице(Партнеры)
Если ТипЗнч(Партнеры) <> Тип("Массив") Тогда
Возврат Ложь;
КонецЕсли;
// НайтиСтроки на клиенте обращается к серверу, поэтому перебираем строки сами
Для Каждого СтрокаКлиента Из Клиенты Цикл
Если Партнеры.Найти(СтрокаКлиента.Партнер) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
&НаКлиенте
Процедура ЗапланироватьОбновление()
// Пачка оповещений подряд даст одно обращение к серверу, а не десять
ОтключитьОбработчикОжидания("ОбновитьКлиентов");
ПодключитьОбработчикОжидания("ОбновитьКлиентов", 0.3, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьКлиентов()
ОбновитьКлиентовНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура МенеджерПриИзменении(Элемент)
ОбновитьКлиентовНаСервере();
КонецПроцедуры
&НаСервере
Процедура ОбновитьКлиентовНаСервере()
Если Не ЗначениеЗаполнено(Менеджер) Тогда
Клиенты.Очистить();
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиента.Партнер КАК Партнер,
| КОЛИЧЕСТВО(ЗаказКлиента.Ссылка) КАК Заказов,
| МАКСИМУМ(ЗаказКлиента.Дата) КАК ПоследнийЗаказ
|ИЗ
| Документ.ЗаказКлиента КАК ЗаказКлиента
|ГДЕ
| ЗаказКлиента.Проведен
| И ЗаказКлиента.Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Закрыт)
| И ЗаказКлиента.Партнер.ОсновнойМенеджер = &Менеджер
|
|СГРУППИРОВАТЬ ПО
| ЗаказКлиента.Партнер
|
|УПОРЯДОЧИТЬ ПО
| ПоследнийЗаказ УБЫВ";
Запрос.УстановитьПараметр("Менеджер", Менеджер);
ЗначениеВРеквизитФормы(Запрос.Выполнить().Выгрузить(), "Клиенты");
КонецПроцедуры
Открытая форма объекта хранит данные у себя, и ни один из двух вызовов её не обновит. Пользователь увидит старого менеджера, а записать карточку платформа не даст: версия в базе уже новее. В ресурсах платформы 8.3.27 для этого есть сообщение «Данные были изменены или удалены другим пользователем». Пусть карточка перечитывается сама — но только без несохранённых правок. В типовой УТ это делают расширением: заимствуйте форму элемента партнёра и в палитре свойств назначьте событию ОбработкаОповещения обработчик с типом вызова «После».
// Расширение (префикс Расш1_), заимствованная форма Справочник.Партнеры.ФормаЭлемента.
// В палитре свойств формы событию ОбработкаОповещения назначен обработчик
// с типом вызова «После»: он выполнится после типового.
&НаКлиенте
Процедура Расш1_ОбработкаОповещенияПосле(ИмяСобытия, Параметр, Источник)
Партнеры = Неопределено;
Если ИмяСобытия <> "ИзмененОсновнойМенеджер"
Или ТипЗнч(Параметр) <> Тип("Структура")
Или Не Параметр.Свойство("Партнеры", Партнеры)
Или ТипЗнч(Партнеры) <> Тип("Массив") Тогда
Возврат;
КонецЕсли;
// Наша ли это карточка? У нового, еще не записанного партнера ссылка пустая.
Если Не ЗначениеЗаполнено(Объект.Ссылка) Или Партнеры.Найти(Объект.Ссылка) = Неопределено Тогда
Возврат;
КонецЕсли;
Если Не Модифицированность Тогда
ЭтотОбъект.Прочитать();
ПоказатьОповещениеПользователя("Карточка перечитана", , "Основной менеджер изменен в другом окне");
Возврат;
КонецЕсли;
// Есть несохраненные правки: молча перечитывать нельзя
ПослеОтвета = Новый ОписаниеОповещения("Расш1_ПеречитатьЗавершение", ЭтотОбъект);
ПоказатьВопрос(ПослеОтвета, "Основного менеджера этого партнера изменили в другом окне. "
+ "Перечитать карточку? Несохраненные правки пропадут.", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура Расш1_ПеречитатьЗавершение(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
ЭтотОбъект.Прочитать();
КонецЕсли;
КонецПроцедуры
Оповестить("ИзмененОсновнойМенеджер", ДанныеСобытия, ЭтотОбъект) — своё имя события, не похожее на типовые Запись_...; в параметре структура с массивом ссылок, чтобы получатель решил «моё — не моё» без сервера; источник — форма-отправитель.ОповеститьОбИзменении(Партнер) и ОповеститьОбИзменении(Тип(...)): по справке вариант с типом очищает всю информацию, связанную с типом, а вариант со ссылкой часть кэшей чистит только по этой ссылке. Порог 20 условный.ТипЗнч(Параметр) = Тип("Структура") И Параметр.Свойство(...) — одно имя события могут слать разные формы с разным параметром. Логические выражения 1С вычисляет по сокращённой схеме, поэтому Свойство не вызовется у ссылки или Неопределено. Переменную для второго параметра Свойство заводят заранее присваиванием — так же делает код БСП.Источник = "УведомленияКлиента" — фильтр по источнику: строку-метку ставит код из раздела про другие сеансы, по ней форма понимает, что изменение пришло извне.ЕстьВТаблице перебирает строки циклом. По синтаксис-помощнику НайтиСтроки у ДанныеФормыКоллекция на клиенте выполняет обращение к серверу, а цикл по уже загруженным строкам — нет.ОтключитьОбработчикОжидания перед ПодключитьОбработчикОжидания(..., 0.3, Истина) склеивает пачку оповещений в одно обновление. Интервал меньше секунды справка разрешает только с третьим параметром Истина.Модифицированность проверяется до Прочитать(): перечитывание выбрасывает правки пользователя, поэтому при изменённой форме сначала вопрос через ПоказатьВопрос.ОповеститьОбИзменении не несёт имени события и параметра и в ОбработкаОповещения не приходит. У метода два варианта: со ссылкой или ключом записи регистра — когда изменился один объект, и с типом — когда изменилось много. Он обновляет динамические списки, у которых основная таблица этого типа, и очищает клиентские кэши: представлений ссылок, данных через точку, быстрого выбора. Списки без основной таблицы и реквизиты-таблицы значений он не трогает.
В конфигурациях на БСП оба вызова собраны в ОбщегоНазначенияКлиент.ОповеститьОбИзмененииОбъекта и ОповеститьОбИзмененииОбъектов (есть в УТ 11.4 и КА 2.5). Они вызывают ОповеститьОбИзменении и Оповестить("Запись_" + имя объекта); для одной ссылки она уходит в источник, для массива из нескольких источник не передаётся.
Остальные «оповещения» 1С 8 — другие механизмы с похожими именами:
| Метод | Кому | Как принять |
|---|---|---|
| Оповестить | все созданные формы сеанса | ОбработкаОповещения |
| ОповеститьОбИзменении | динамические списки и кэши клиента | обработчика нет, список перечитается сам |
| ОповеститьОВыборе | владелец формы выбора или подбора | ОбработкаВыбора |
| ОповеститьОЗаписиНового | владелец подчинённой формы | ОбработкаЗаписиНового |
| ОписаниеОповещения | одна процедура обратного вызова | экспортная процедура из описания |
| ПоказатьОповещениеПользователя | пользователь: всплывающее окно | — |
Вернуть результат из формы, которую открыли сами, проще через ОписаниеОповещения — примеры в статье как открыть форму программно. Широковещательное Оповестить нужно, когда отправитель не знает получателей.
С платформы 8.3.26 сервер может отправить уведомление клиентским сеансам: УведомленияКлиента.ОтправитьУведомление(Ключ, Данные, Адресаты). Без адресатов оно уходит всем сеансам, с массивом номеров сеансов — только им. На клиенте его принимают через УведомленияКлиента.ПодключитьОбработчик(Ключ, ОписаниеОповещения). Удобно сразу превратить уведомление в обычное Оповестить: формы-получатели менять не придётся.
// Обработка «Передача клиентов менеджеру»: вызов в конце ПередатьНаСервере(),
// когда массив Изменены не пустой. Нужна платформа 8.3.26 или новее.
&НаСервереБезКонтекста
Процедура УведомитьДругиеСеансы(Знач Партнеры, Знач НовыйМенеджер)
Данные = Новый Структура;
Данные.Вставить("Партнеры", Партнеры);
Данные.Вставить("НовыйМенеджер", НовыйМенеджер);
Данные.Вставить("Сеанс", НомерСеансаИнформационнойБазы());
// Адресаты не указаны: уведомление получат все сеансы, включая текущий
УведомленияКлиента.ОтправитьУведомление("ИзмененОсновнойМенеджер", Данные);
КонецПроцедуры
// Обработка «Клиенты менеджера», дополнение к модулю формы.
// Реквизит формы НомерСеанса (Число). ПриСозданииНаСервере, ПриОткрытии
// и ПриЗакрытии назначены в палитре свойств формы.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// На тонком клиенте номер сеанса можно получить только через сервер
НомерСеанса = НомерСеансаИнформационнойБазы();
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
УведомленияКлиента.ПодключитьОбработчик("ИзмененОсновнойМенеджер",
Новый ОписаниеОповещения("ПриПолученииУведомления", ЭтотОбъект));
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
УведомленияКлиента.ОтключитьОбработчик("ИзмененОсновнойМенеджер");
КонецПроцедуры
&НаКлиенте
Процедура ПриПолученииУведомления(Данные, ДополнительныеПараметры) Экспорт
// Свой сеанс формы уже оповестил обычным Оповестить
Сеанс = Неопределено;
Если ТипЗнч(Данные) <> Тип("Структура")
Или Не Данные.Свойство("Сеанс", Сеанс)
Или Сеанс = НомерСеанса Тогда
Возврат;
КонецЕсли;
// Дальше - обычное оповещение своего сеанса: обработчики форм не меняются
Оповестить("ИзмененОсновнойМенеджер", Данные, "УведомленияКлиента");
КонецПроцедуры
ОтключитьОбработчик по справке работает по ключу. Если форм-подписчиков в сеансе может быть несколько, держите одну подписку на сеанс.
ОбработкаОповещения написана, но не назначена событию в палитре свойств; в расширении у заимствованной формы не назначен обработчик с типом вызова. Ошибки при этом нет.&НаСервере: метод только клиентский. Верните данные с сервера и оповестите на клиенте.Параметр равен Неопределено: Оповестить вызвали без параметра. Проверяйте ТипЗнч и Свойство.Запись_ЗаказКлиента несёт ссылку в источнике, Запись_Партнеры — в параметре. Условие Параметр = Объект.Ссылка для заказа молча не выполнится.ОповеститьОбИзменении. У динамического списка нет основной таблицы, а такие списки уведомление не трогает. Обновите его в ОбработкаОповещения через Элементы.Список.Обновить() — подробнее в статье про динамический список.Да: оповещение уходит всем созданным формам, а отправитель — одна из них. Если форма и шлёт, и слушает одно событие, первой строкой обработчика отсеките своё: Если Источник = ЭтотОбъект Тогда Возврат; КонецЕсли;. Это работает, если в источник передаётся форма.
Нет, метод есть только на клиенте. Серверная процедура возвращает ссылки или флаг, а клиентская часть вызывает Оповестить. Для фонового задания то же делают в клиентском обработчике его завершения. Другим сеансам на 8.3.26+ сообщают через УведомленияКлиента, а в документации БСП 3.2.1 описан метод ОтправитьСерверноеОповещение.
Нет. По справке динамические списки обновляются при выполнении стандартных команд записи форм. Вызов нужен, когда данные записал код: команда, обработка, загрузка, фоновое задание.
Да, механизм старше управляемых форм: по справке Оповестить доступен с версии 8.0, а у обычной формы в толстом клиенте есть событие ОбработкаОповещения с теми же параметрами. Для этой пары оповещения в управляемых формах 1С 8.3 отличаются директивой: там обработчик помечен &НаКлиенте, в обычной форме директиву не пишут. Назначают обработчик так же, в свойствах формы.
Если формы типовой конфигурации должны реагировать на изменения из обменов, фоновых заданий или других сеансов, а доработку нужно сделать расширением без снятия с поддержки, проще поручить её исполнителю: услуги по доработке 1С. Опишите, какие формы и когда должны обновляться, на странице разместить задание — оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.