Адресное сообщение другому сотруднику в 1С 8.3: три рабочих способа с кодом

· 9 мин

Содержание

Вывести сообщение определенному пользователю в 1С 8.3 через Сообщить() нельзя: метод пишет только в окно сообщений того сеанса, где выполняется код. Чтобы текст увидел конкретный сотрудник, нужен канал доставки — встроенная система взаимодействия (обсуждения) или свой регистр сведений , который клиент получателя опрашивает по таймеру.

Типичная ситуация: кладовщик проводит приходную накладную, а менеджер должен сразу узнать, что товар пришёл. Программист пишет Сообщить("Товар поступил") в обработке проведения — и сообщение видит сам кладовщик. Ниже код и порядок действий для обоих способов, плюс как показать текст получателю и записать факт отправки.

Почему Сообщить() не доходит до коллеги

В клиент-серверной и файловой базе каждый пользователь работает в своём сеансе. У сеанса свой клиент, свои открытые формы и своя очередь сообщений. Прямого вызова «выполни этот код в чужом сеансе» в платформе нет.

Что происходит с сообщением, выведенным в коде:

  • 🖥️ На клиенте — Сообщить() сразу показывает текст в окне сообщений текущей формы.
  • 🗄️ На сервере — сообщение копится в буфере сеанса и передаётся клиенту, когда серверный вызов вернул управление. Получает его всё тот же пользователь, чей вызов работал.
  • ⚙️ В фоновом задании — сообщения остаются у задания. Их забирает тот, кто запустил задание, методом ПолучитьСообщенияПользователю() объекта ФоновоеЗадание.
  • 📣 Оповестить() — рассылает событие открытым формам, но только внутри своего клиентского приложения. В формы другого пользователя оно не попадёт.

Вывод простой: «адресное» сообщение — это всегда запись в общее хранилище, из которого клиент получателя её достаёт. Различаются способы только тем, кто это хранилище обслуживает: платформа или ваш код.

СпособЧто нужноКогда подходитОграничения
Система взаимодействияРегистрация базы на сервере взаимодействия, платформа 8.3.9 и новееНужна переписка, история, уведомления «как в мессенджере»Нужен доступ к серверу взаимодействия: облачному или своему
Регистр сведений + опросИзменение конфигурации или расширениеЗакрытый контур без интернета, служебные уведомленияЗадержка равна периоду опроса, нагрузка на сервер
Блокировка сеансов с сообщениемПрава администратораПредупредить всех перед обновлениемСообщение всем, не одному человеку

Объект СообщениеПользователю: кому его можно адресовать

У объекта СообщениеПользователю есть свойство, которое часто принимают за «адрес получателя», — ИдентификаторНазначения. На деле это уникальный идентификатор формы, а не пользователя. С его помощью сообщение из серверного кода попадает в нужную форму, если у клиента открыто несколько окон.

// В модуле формы, на клиенте
ИдентификаторФормы = УникальныйИдентификатор;
ПроверитьНаСервере(ИдентификаторФормы);

// В серверной процедуре
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен контрагент";
Сообщение.ИдентификаторНазначения = ИдентификаторФормы;
Сообщение.Поле = "Объект.Контрагент";
Сообщение.Сообщить();

Свойства Поле, ПутьКДанным и КлючДанных привязывают текст к реквизиту: при щелчке по сообщению курсор переходит к нужному полю. Это удобно для проверок заполнения, но адресация остаётся в пределах одного сеанса.

Если код работает в фоновом задании, а результат должен увидеть пользователь, который задание запустил, сохраните ИдентификаторНазначения формы до запуска. Потом заберите сообщения через ФоновоеЗадание.ПолучитьСообщенияПользователю(Истина) и выведите их в той же форме.

Как отправить сообщение конкретному пользователю через систему взаимодействия

Система взаимодействия — встроенный механизм обсуждений, который появился в платформе 8.3.9. Сообщения хранятся на сервере взаимодействия, а у получателя они появляются в панели обсуждений и во всплывающем уведомлении. Если сотрудник не в сети, он увидит сообщение при следующем входе.

Подготовка базы

  1. Убедитесь, что номер версии платформы не ниже 8.3.9, а у сервера 1С или компьютера с файловой базой есть доступ к серверу взаимодействия.
  2. В типовых конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) откройте Администрирование → Общие настройки и найдите группу «Обсуждения».
  3. Нажмите регистрацию базы, укажите адрес электронной почты администратора и введите код, который придёт в письме. По умолчанию используется облачный сервер 1С:Диалог; в закрытом контуре разворачивают свой сервер взаимодействия.
  4. После регистрации у пользователей появляется кнопка обсуждений в панели интерфейса.

Проверить регистрацию из кода можно методом СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована().

Код отправки

Код выполняется на сервере, например в общем модуле с флажками «Сервер» и «Вызов сервера». Получателя ищем по имени для входа — не по полному имени, которое видно в списке пользователей.

Процедура ОтправитьЛичноеСообщение(ИмяПолучателя, ТекстСообщения) Экспорт

    Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована() Тогда
        ВызватьИсключение "База не зарегистрирована в системе взаимодействия";
    КонецЕсли;

    УстановитьПривилегированныйРежим(Истина);
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПолучателя);
    УстановитьПривилегированныйРежим(Ложь);

    Если ПользовательИБ = Неопределено Тогда
        ВызватьИсключение СтрШаблон("Пользователь «%1» не найден", ИмяПолучателя);
    КонецЕсли;

    ИДПолучателя = СистемаВзаимодействия.ПолучитьИдентификаторПользователя(
        ПользовательИБ.УникальныйИдентификатор);

    Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
    Обсуждение.Заголовок = "Уведомление";
    Обсуждение.Групповое = Ложь;
    Обсуждение.Участники.Добавить(СистемаВзаимодействия.ТекущийПользователь());
    Обсуждение.Участники.Добавить(ИДПолучателя);
    Обсуждение.Записать();

    Сообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
    Сообщение.Текст = ТекстСообщения;
    Сообщение.Записать();

КонецПроцедуры

Вызов из обработки проведения или из команды:

ОтправитьЛичноеСообщение("Петров", "Поступил товар по накладной № 154");

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

Свой канал доставки на регистре сведений

Когда интернета нет, а свой сервер взаимодействия разворачивать не хотят, канал делают на обычном регистре сведений. Отправитель пишет запись, клиент получателя раз в N секунд спрашивает сервер «есть ли что-то для меня».

Структура регистра

Создайте в расширении независимый непериодический регистр сведений СообщенияПользователям:

РеквизитВидТип
ПолучательИзмерениеСтрока, 36 (идентификатор пользователя ИБ)
ИдентификаторСообщенияИзмерениеСтрока, 36
ОтправительРесурсСтрока, 100
ТекстРесурсСтрока неограниченной длины
ДатаОтправкиРесурсДата и время
ПрочитаноРесурсБулево

Идентификатор вместо имени нужен потому, что имя для входа администратор может поменять, а УникальныйИдентификатор пользователя остаётся прежним.

Отправка и получение на сервере

Общий модуль СообщенияПользователямВызовСервера с флажками «Сервер» и «Вызов сервера»:

Процедура Отправить(ИмяПолучателя, ТекстСообщения) Экспорт

    УстановитьПривилегированныйРежим(Истина);

    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПолучателя);
    Если ПользовательИБ = Неопределено Тогда
        ВызватьИсключение СтрШаблон("Пользователь «%1» не найден", ИмяПолучателя);
    КонецЕсли;

    Запись = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
    Запись.Получатель = Строка(ПользовательИБ.УникальныйИдентификатор);
    Запись.ИдентификаторСообщения = Строка(Новый УникальныйИдентификатор);
    Запись.Отправитель = ИмяПользователя();
    Запись.Текст = ТекстСообщения;
    Запись.ДатаОтправки = ТекущаяДатаСеанса();
    Запись.Прочитано = Ложь;
    Запись.Записать();

КонецПроцедуры

Функция ПолучитьНепрочитанные() Экспорт

    УстановитьПривилегированныйРежим(Истина);

    Результат = Новый Массив;
    Получатель = Строка(ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор);

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

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл

        Результат.Добавить(Новый Структура("Отправитель, Текст",
            Выборка.Отправитель, Выборка.Текст));

        Запись = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
        Запись.Получатель = Получатель;
        Запись.ИдентификаторСообщения = Выборка.ИдентификаторСообщения;
        Запись.Прочитать();
        Если Запись.Выбран() Тогда
            Запись.Прочитано = Истина;
            Запись.Записать();
        КонецЕсли;

    КонецЦикла;

    Возврат Результат;

КонецФункции

Привилегированный режим нужен, чтобы рядовому пользователю не выдавать права на чтение списка пользователей ИБ и на запись в регистр.

Опрос на клиенте

Обработчик ожидания подключают при запуске клиента. В расширении это процедура модуля управляемого приложения с аннотацией &После("ПриНачалеРаботыСистемы"):

&После("ПриНачалеРаботыСистемы")
Процедура Расш_ПриНачалеРаботыСистемы()
    ПодключитьОбработчикОжидания("Расш_ПроверитьВходящие", 60);
КонецПроцедуры

Процедура Расш_ПроверитьВходящие() Экспорт
    Входящие = СообщенияПользователямВызовСервера.ПолучитьНепрочитанные();
    Для Каждого Сообщение Из Входящие Цикл
        ПоказатьОповещениеПользователя("Сообщение от " + Сообщение.Отправитель, ,
            Сообщение.Текст, БиблиотекаКартинок.Информация);
    КонецЦикла;
КонецПроцедуры

Период опроса подбирайте по числу пользователей. При 100 активных сеансах и периоде 30 секунд сервер получает в среднем 3–4 запроса в секунду только на проверку почты. Для служебных уведомлений обычно хватает 60–120 секунд.

Очистка прочитанного

Регистр растёт с каждым уведомлением. Добавьте в расширение регламентное задание, которое раз в сутки удаляет прочитанные записи старше 30 дней. Отберите их запросом по полям Прочитано и ДатаОтправки, а удаляйте через менеджер записи: заполните оба измерения и вызовите Удалить(). Непрочитанные не трогайте — получатель мог быть в отпуске.

Как показать сообщение получателю: оповещения и диалоги

Доставить текст — полдела. Важно выбрать, как он появится на экране.

  • 🔔 ПоказатьОповещениеПользователя() — всплывающее окно в углу экрана. Не мешает работе, но через несколько секунд исчезает. Подходит для «товар поступил», «отчёт сформирован».
  • ⚠️ ПоказатьПредупреждение() — окно с кнопкой ОК, которое нельзя пропустить. Для важного: «закройте программу до 18:00, будет обновление».
  • ❓ ПоказатьВопрос() — окно с выбором ответа. Позволяет получить подтверждение и отправить его обратно автору.

В конфигурациях с режимом использования модальности «Не использовать» старые Предупреждение() и Вопрос() вызывают ошибку. Асинхронные методы работают через ОписаниеОповещения: код после вызова не ждёт ответа, ответ приходит в отдельную процедуру.

Процедура ПоказатьСПодтверждением(Сообщение)
    Оповещение = Новый ОписаниеОповещения("ПослеОтвета", ЭтотОбъект, Сообщение);
    ПоказатьВопрос(Оповещение, Сообщение.Текст + Символы.ПС + "Принято в работу?",
        РежимДиалогаВопрос.ДаНет, 0, , "Сообщение от " + Сообщение.Отправитель);
КонецПроцедуры

Процедура ПослеОтвета(Ответ, Сообщение) Экспорт
    Если Ответ = КодВозвратаДиалога.Да Тогда
        СообщенияПользователямВызовСервера.Отправить(Сообщение.Отправитель,
            "Сообщение «" + Лев(Сообщение.Текст, 50) + "» принято в работу");
    КонецЕсли;
КонецПроцедуры

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

Ссылка на документ в уведомлении

Второй параметр ПоказатьОповещениеПользователя() — навигационная ссылка. Если передать её, щелчок по всплывающему окну откроет документ. Ссылку получают на сервере функцией ПолучитьНавигационнуюСсылку(Документ) и сохраняют в отдельный ресурс регистра рядом с текстом. Менеджеру не придётся искать накладную № 154 в журнале: она откроется одним щелчком.

Журнал регистрации: след об отправке

Журнал регистрации сообщения не доставляет: его читают администраторы, а не адресат. Зато в нём удобно хранить факт отправки, чтобы при разборе «мне ничего не приходило» было на что опереться.

Добавьте запись в процедуру Отправить после Запись.Записать():

ЗаписьЖурналаРегистрации("СообщенияПользователям.Отправка",
    УровеньЖурналаРегистрации.Информация, , ,
    СтрШаблон("Получатель: %1. Текст: %2", ИмяПолучателя, Лев(ТекстСообщения, 200)));

Посмотреть записи можно в Администрирование → Обслуживание → Журнал регистрации: установите отбор по событию СообщенияПользователям.Отправка. Программно журнал выгружают методом ВыгрузитьЖурналРегистрации() с отбором по событию и периоду.

Не пишите в журнал полный текст сообщений, если в них бывают персональные данные или суммы. Журнал читают все, у кого есть право администрирования, и хранится он долго.

Ошибки при отправке и как их ловить

Отправка — серверная операция, которая может упасть посередине: пользователя переименовали, база потеряла регистрацию, сервер взаимодействия недоступен. Если вызов стоит в обработке проведения, исключение прервёт проведение документа. Поэтому уведомление оборачивают в Попытка:

Попытка
    ОтправитьЛичноеСообщение("Петров", "Поступил товар по накладной № 154");
Исключение
    ЗаписьЖурналаРегистрации("СообщенияПользователям.Ошибка",
        УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;

Так документ проведётся, а сбой уведомления останется в журнале.

Диагностика: выберите симптом

Частые вопросы

Можно ли отправить сообщение всем пользователям сразу?
Да. В системе взаимодействия создайте групповое обсуждение со всеми участниками. В варианте с регистром запишите по строке на каждого пользователя из ПользователиИнформационнойБазы.ПолучитьПользователей().
Дойдёт ли сообщение, если получатель не в программе?
В обоих способах сообщение хранится до прочтения. Получатель увидит его при следующем входе: в панели обсуждений или при первом срабатывании обработчика ожидания.
Работает ли адресная отправка в веб-клиенте?
Да. Обработчики ожидания, оповещения и система взаимодействия поддерживаются и в тонком, и в веб-клиенте.
Нужно ли снимать конфигурацию с поддержки?
Нет. Регистр, общий модуль и обработчик запуска размещают в расширении конфигурации, типовая остаётся на поддержке и обновляется как обычно.
Как предупредить всех перед обновлением, не программируя?
Воспользуйтесь блокировкой работы пользователей в разделе администрирования: при установке блокировки задают текст сообщения, и его увидят все, кто работает в базе.