Адресное сообщение другому сотруднику в 1С 8.3: три рабочих способа с кодом
Содержание
Вывести сообщение определенному пользователю в 1С 8.3 через Сообщить() нельзя: метод пишет только в окно сообщений того сеанса, где выполняется код. Чтобы текст увидел конкретный сотрудник, нужен канал доставки — встроенная система взаимодействия (обсуждения) или свой регистр сведений
, который клиент получателя опрашивает по таймеру.
Типичная ситуация: кладовщик проводит приходную накладную, а менеджер должен сразу узнать, что товар пришёл. Программист пишет Сообщить("Товар поступил") в обработке проведения — и сообщение видит сам кладовщик. Ниже код и порядок действий для обоих способов, плюс как показать текст получателю и записать факт отправки.
Почему Сообщить() не доходит до коллеги
В клиент-серверной и файловой базе каждый пользователь работает в своём сеансе. У сеанса свой клиент, свои открытые формы и своя очередь сообщений. Прямого вызова «выполни этот код в чужом сеансе» в платформе нет.
Что происходит с сообщением, выведенным в коде:
- 🖥️ На клиенте —
Сообщить()сразу показывает текст в окне сообщений текущей формы. - 🗄️ На сервере — сообщение копится в буфере сеанса и передаётся клиенту, когда серверный вызов вернул управление. Получает его всё тот же пользователь, чей вызов работал.
- ⚙️ В фоновом задании — сообщения остаются у задания. Их забирает тот, кто запустил задание, методом
ПолучитьСообщенияПользователю()объектаФоновоеЗадание. - 📣
Оповестить()— рассылает событие открытым формам, но только внутри своего клиентского приложения. В формы другого пользователя оно не попадёт.
Вывод простой: «адресное» сообщение — это всегда запись в общее хранилище, из которого клиент получателя её достаёт. Различаются способы только тем, кто это хранилище обслуживает: платформа или ваш код.
| Способ | Что нужно | Когда подходит | Ограничения |
|---|---|---|---|
| Система взаимодействия | Регистрация базы на сервере взаимодействия, платформа 8.3.9 и новее | Нужна переписка, история, уведомления «как в мессенджере» | Нужен доступ к серверу взаимодействия: облачному или своему |
| Регистр сведений + опрос | Изменение конфигурации или расширение | Закрытый контур без интернета, служебные уведомления | Задержка равна периоду опроса, нагрузка на сервер |
| Блокировка сеансов с сообщением | Права администратора | Предупредить всех перед обновлением | Сообщение всем, не одному человеку |
Объект СообщениеПользователю: кому его можно адресовать
У объекта СообщениеПользователю есть свойство, которое часто принимают за «адрес получателя», — ИдентификаторНазначения. На деле это уникальный идентификатор формы, а не пользователя. С его помощью сообщение из серверного кода попадает в нужную форму, если у клиента открыто несколько окон.
// В модуле формы, на клиенте
ИдентификаторФормы = УникальныйИдентификатор;
ПроверитьНаСервере(ИдентификаторФормы);
// В серверной процедуре
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен контрагент";
Сообщение.ИдентификаторНазначения = ИдентификаторФормы;
Сообщение.Поле = "Объект.Контрагент";
Сообщение.Сообщить();
Свойства Поле, ПутьКДанным и КлючДанных привязывают текст к реквизиту: при щелчке по сообщению курсор переходит к нужному полю. Это удобно для проверок заполнения, но адресация остаётся в пределах одного сеанса.
Если код работает в фоновом задании, а результат должен увидеть пользователь, который задание запустил, сохраните ИдентификаторНазначения формы до запуска. Потом заберите сообщения через ФоновоеЗадание.ПолучитьСообщенияПользователю(Истина) и выведите их в той же форме.
Как отправить сообщение конкретному пользователю через систему взаимодействия
Система взаимодействия — встроенный механизм обсуждений, который появился в платформе 8.3.9. Сообщения хранятся на сервере взаимодействия, а у получателя они появляются в панели обсуждений и во всплывающем уведомлении. Если сотрудник не в сети, он увидит сообщение при следующем входе.
Подготовка базы
- Убедитесь, что номер версии платформы не ниже 8.3.9, а у сервера 1С или компьютера с файловой базой есть доступ к серверу взаимодействия.
- В типовых конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) откройте
Администрирование → Общие настройкии найдите группу «Обсуждения». - Нажмите регистрацию базы, укажите адрес электронной почты администратора и введите код, который придёт в письме. По умолчанию используется облачный сервер 1С:Диалог; в закрытом контуре разворачивают свой сервер взаимодействия.
- После регистрации у пользователей появляется кнопка обсуждений в панели интерфейса.
Проверить регистрацию из кода можно методом СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована().
Код отправки
Код выполняется на сервере, например в общем модуле с флажками «Сервер» и «Вызов сервера». Получателя ищем по имени для входа — не по полному имени, которое видно в списке пользователей.
Процедура ОтправитьЛичноеСообщение(ИмяПолучателя, ТекстСообщения) Экспорт
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована() Тогда
ВызватьИсключение "База не зарегистрирована в системе взаимодействия";
КонецЕсли;
УстановитьПривилегированныйРежим(Истина);
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПолучателя);
УстановитьПривилегированныйРежим(Ложь);
Если ПользовательИБ = Неопределено Тогда
ВызватьИсключение СтрШаблон("Пользователь «%1» не найден", ИмяПолучателя);
КонецЕсли;
ИДПолучателя = СистемаВзаимодействия.ПолучитьИдентификаторПользователя(
ПользовательИБ.УникальныйИдентификатор);
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Заголовок = "Уведомление";
Обсуждение.Групповое = Ложь;
Обсуждение.Участники.Добавить(СистемаВзаимодействия.ТекущийПользователь());
Обсуждение.Участники.Добавить(ИДПолучателя);
Обсуждение.Записать();
Сообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
Сообщение.Текст = ТекстСообщения;
Сообщение.Записать();
КонецПроцедуры
Вызов из обработки проведения или из команды:
ОтправитьЛичноеСообщение("Петров", "Поступил товар по накладной № 154");
Пример создаёт новое обсуждение на каждое сообщение. Для частых уведомлений это засоряет панель. Сохраните идентификатор созданного обсуждения (например, в регистре сведений по паре «отправитель — получатель») и пишите новые сообщения в него.
Свой канал доставки на регистре сведений
Когда интернета нет, а свой сервер взаимодействия разворачивать не хотят, канал делают на обычном регистре сведений. Отправитель пишет запись, клиент получателя раз в N секунд спрашивает сервер «есть ли что-то для меня».
Структура регистра
Создайте в расширении независимый непериодический регистр сведений СообщенияПользователям:
| Реквизит | Вид | Тип |
|---|---|---|
| Получатель | Измерение | Строка, 36 (идентификатор пользователя ИБ) |
| ИдентификаторСообщения | Измерение | Строка, 36 |
| Отправитель | Ресурс | Строка, 100 |
| Текст | Ресурс | Строка неограниченной длины |
| ДатаОтправки | Ресурс | Дата и время |
| Прочитано | Ресурс | Булево |
Идентификатор вместо имени нужен потому, что имя для входа администратор может поменять, а УникальныйИдентификатор пользователя остаётся прежним.
Отправка и получение на сервере
Общий модуль СообщенияПользователямВызовСервера с флажками «Сервер» и «Вызов сервера»:
Процедура Отправить(ИмяПолучателя, ТекстСообщения) Экспорт
УстановитьПривилегированныйРежим(Истина);
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПолучателя);
Если ПользовательИБ = Неопределено Тогда
ВызватьИсключение СтрШаблон("Пользователь «%1» не найден", ИмяПолучателя);
КонецЕсли;
Запись = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
Запись.Получатель = Строка(ПользовательИБ.УникальныйИдентификатор);
Запись.ИдентификаторСообщения = Строка(Новый УникальныйИдентификатор);
Запись.Отправитель = ИмяПользователя();
Запись.Текст = ТекстСообщения;
Запись.ДатаОтправки = ТекущаяДатаСеанса();
Запись.Прочитано = Ложь;
Запись.Записать();
КонецПроцедуры
Функция ПолучитьНепрочитанные() Экспорт
УстановитьПривилегированныйРежим(Истина);
Результат = Новый Массив;
Получатель = Строка(ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| С.ИдентификаторСообщения КАК ИдентификаторСообщения,
| С.Отправитель КАК Отправитель,
| С.Текст КАК Текст
|ИЗ
| РегистрСведений.СообщенияПользователям КАК С
|ГДЕ
| С.Получатель = &Получатель
| И НЕ С.Прочитано
|
|УПОРЯДОЧИТЬ ПО
| С.ДатаОтправки";
Запрос.УстановитьПараметр("Получатель", Получатель);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Результат.Добавить(Новый Структура("Отправитель, Текст",
Выборка.Отправитель, Выборка.Текст));
Запись = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
Запись.Получатель = Получатель;
Запись.ИдентификаторСообщения = Выборка.ИдентификаторСообщения;
Запись.Прочитать();
Если Запись.Выбран() Тогда
Запись.Прочитано = Истина;
Запись.Записать();
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Привилегированный режим нужен, чтобы рядовому пользователю не выдавать права на чтение списка пользователей ИБ и на запись в регистр.
Опрос на клиенте
Обработчик ожидания подключают при запуске клиента. В расширении это процедура модуля управляемого приложения с аннотацией &После("ПриНачалеРаботыСистемы"):
&После("ПриНачалеРаботыСистемы")
Процедура Расш_ПриНачалеРаботыСистемы()
ПодключитьОбработчикОжидания("Расш_ПроверитьВходящие", 60);
КонецПроцедуры
Процедура Расш_ПроверитьВходящие() Экспорт
Входящие = СообщенияПользователямВызовСервера.ПолучитьНепрочитанные();
Для Каждого Сообщение Из Входящие Цикл
ПоказатьОповещениеПользователя("Сообщение от " + Сообщение.Отправитель, ,
Сообщение.Текст, БиблиотекаКартинок.Информация);
КонецЦикла;
КонецПроцедуры
Период опроса подбирайте по числу пользователей. При 100 активных сеансах и периоде 30 секунд сервер получает в среднем 3–4 запроса в секунду только на проверку почты. Для служебных уведомлений обычно хватает 60–120 секунд.
Очистка прочитанного
Регистр растёт с каждым уведомлением. Добавьте в расширение регламентное задание, которое раз в сутки удаляет прочитанные записи старше 30 дней. Отберите их запросом по полям Прочитано и ДатаОтправки, а удаляйте через менеджер записи: заполните оба измерения и вызовите Удалить(). Непрочитанные не трогайте — получатель мог быть в отпуске.
Как показать сообщение получателю: оповещения и диалоги
Доставить текст — полдела. Важно выбрать, как он появится на экране.
- 🔔
ПоказатьОповещениеПользователя()— всплывающее окно в углу экрана. Не мешает работе, но через несколько секунд исчезает. Подходит для «товар поступил», «отчёт сформирован». - ⚠️
ПоказатьПредупреждение()— окно с кнопкой ОК, которое нельзя пропустить. Для важного: «закройте программу до 18:00, будет обновление». - ❓
ПоказатьВопрос()— окно с выбором ответа. Позволяет получить подтверждение и отправить его обратно автору.
В конфигурациях с режимом использования модальности «Не использовать» старые Предупреждение() и Вопрос() вызывают ошибку. Асинхронные методы работают через ОписаниеОповещения: код после вызова не ждёт ответа, ответ приходит в отдельную процедуру.
Процедура ПоказатьСПодтверждением(Сообщение)
Оповещение = Новый ОписаниеОповещения("ПослеОтвета", ЭтотОбъект, Сообщение);
ПоказатьВопрос(Оповещение, Сообщение.Текст + Символы.ПС + "Принято в работу?",
РежимДиалогаВопрос.ДаНет, 0, , "Сообщение от " + Сообщение.Отправитель);
КонецПроцедуры
Процедура ПослеОтвета(Ответ, Сообщение) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
СообщенияПользователямВызовСервера.Отправить(Сообщение.Отправитель,
"Сообщение «" + Лев(Сообщение.Текст, 50) + "» принято в работу");
КонецЕсли;
КонецПроцедуры
Четвёртый параметр ПоказатьВопрос — таймаут в секундах; ноль означает «ждать без ограничения». Ответное сообщение уходит по имени отправителя, поэтому в регистре храните именно имя для входа, которое возвращает ИмяПользователя().
Ссылка на документ в уведомлении
Второй параметр ПоказатьОповещениеПользователя() — навигационная ссылка. Если передать её, щелчок по всплывающему окну откроет документ. Ссылку получают на сервере функцией ПолучитьНавигационнуюСсылку(Документ) и сохраняют в отдельный ресурс регистра рядом с текстом. Менеджеру не придётся искать накладную № 154 в журнале: она откроется одним щелчком.
Журнал регистрации: след об отправке
Журнал регистрации сообщения не доставляет: его читают администраторы, а не адресат. Зато в нём удобно хранить факт отправки, чтобы при разборе «мне ничего не приходило» было на что опереться.
Добавьте запись в процедуру Отправить после Запись.Записать():
ЗаписьЖурналаРегистрации("СообщенияПользователям.Отправка",
УровеньЖурналаРегистрации.Информация, , ,
СтрШаблон("Получатель: %1. Текст: %2", ИмяПолучателя, Лев(ТекстСообщения, 200)));
Посмотреть записи можно в Администрирование → Обслуживание → Журнал регистрации: установите отбор по событию СообщенияПользователям.Отправка. Программно журнал выгружают методом ВыгрузитьЖурналРегистрации() с отбором по событию и периоду.
Не пишите в журнал полный текст сообщений, если в них бывают персональные данные или суммы. Журнал читают все, у кого есть право администрирования, и хранится он долго.
Ошибки при отправке и как их ловить
Отправка — серверная операция, которая может упасть посередине: пользователя переименовали, база потеряла регистрацию, сервер взаимодействия недоступен. Если вызов стоит в обработке проведения, исключение прервёт проведение документа. Поэтому уведомление оборачивают в Попытка:
Попытка
ОтправитьЛичноеСообщение("Петров", "Поступил товар по накладной № 154");
Исключение
ЗаписьЖурналаРегистрации("СообщенияПользователям.Ошибка",
УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
Так документ проведётся, а сбой уведомления останется в журнале.
Частые вопросы
Можно ли отправить сообщение всем пользователям сразу?
ПользователиИнформационнойБазы.ПолучитьПользователей().