# Уведомления — Администрирование

Для `notifications` администрирование распределено между несколькими доменами: `categories` (правила уведомлений по категориям), `users-and-groups` (персональные и дефолтные флаги), `mail` (почтовый канал и шаблоны), `mobile` (push-настройки), `system` (глобальные системные параметры).

Собственной "большой" формы администрирования у домена нет: результат формируется комбинацией настроек.

## Механизмы администрирования

### Системные формы администрирования

Ключевые формы автоадминистрирования, влияющие на уведомления:

| Alias формы | Название | Таблица БД | Где влияет на notifications |
|-------------|----------|------------|-----------------------------|
| `group-subcat-notificatons` | Уведомления при событиях в задаче | `dbo.GroupSubcatNotificatons` | Правила уведомлений/автодействий по группам в категории |
| `system-settings` | Общие настройки приложения | `dbo.Settings` | Глобальные флаги каналов и поведение отчётов/уведомлений |
| `email-settings` | Настройки почты | `dbo.EmailSettings` | Включение/выключение почтовой доставки |
| `mail-templates` | Шаблоны почтовых уведомлений | `dbo.MailTemplates` | Контент и формат писем-уведомлений |
| `push-settings` | Настройка PUSH | `dbo.PushSettings` | Сертификаты и параметры мобильного push. Пункт меню «МП» переведён на отдельную страницу `/administration/push-settings`; автоформа работает по прямому адресу |
| `service-mail-boxes` | Почта категории | `dbo.ServiceMailBoxes` | Почтовые ящики для категорийных уведомлений/обратной связи |

### API администрирования

Маршруты API для управления настройками уведомлений:

| Маршрут | Методы | Назначение |
|---------|--------|------------|
| `/api/admin/user/notifications` | GET, POST | Персональные флаги уведомлений пользователя |
| `/api/admin/users/default/notifications` | GET, POST | Уведомления по умолчанию для новых пользователей |
| `/api/admin/categories/{categoryId}/notifications/*` | GET, POST | Массовые/категорийные правила уведомлений |
| `/api/admin/subcategories/{subcatId}/notifications/{add,remove}` | POST | Подписать/отписать пользователя или группу на уведомления категории |
| `/api/admin/push/settings*` | GET, POST, PUT, DELETE | Канал мобильного push (сертификаты, приоритеты) |
| `/api/admin/mail-templates` | POST | Экспорт/импорт шаблонов уведомлений |

**API для проверки применённых настроек**

| Маршрут | Что проверяет |
|---------|---------------|
| `/api/subcategories/{subcatId:int}/notifications` | Фактические подписки и флаги по категории |
| `/api/admin/user/notifications/{userId:int}` | Применённые персональные флаги |

## Ключевые настройки

**1. Правила уведомлений по категории**

**Где настраивается:** категория → вкладка "Уведомления" (`cats_notifications`) / Admin API `categories/{categoryId}/notifications/*`

**Таблицы БД:**

- `dbo.GroupSubcatNotificatons`
- `dbo.UserSubcatNotificatons`
- `dbo.SubcatUserNotifications`

**Что контролируется:**

- автоподписка;
- автоназначение исполнителем/ответственным;
- уведомления о новой задаче, переносе срока, отклонении.

**Как применяется:** эти правила участвуют в расчёте адресатов и влияют на доставку уведомлений.

**Сохранение правила в окне редактирования.** Правило из списка открывается отдельным окном с кнопкой «Сохранить». Запись на сервер идёт до закрытия окна: при успешном ответе окно закрывается и список правил перечитывается, при ошибке выводится сообщение, а окно остаётся открытым с введёнными значениями — правка не теряется. Невалидная форма запись не отправляет и окно не закрывает. Закрытие крестиком, по Esc или кликом по фону правило не меняет и подтверждения не спрашивает.

**2. Персональные флаги и дефолты**

**Где настраивается:**

- профиль пользователя → уведомления (`users_notifications`);
- дефолты новых пользователей → `/api/admin/users/default/notifications`.

**Таблицы БД:**

- `dbo.UserNotifications`
- `dbo.UserLentaCommentTypes`
- профильные поля `dbo.Users` (legacy-флаги)

**Что контролируется:** попадание события "в конверт", в ленту, desktop/mobile push и почтовые дубли.

![Форма «Редактирование уведомлений»: типы событий и колонки «Непрочитанные», «Видно в ленте», «Уведомлять»](https://help.1forma.ru/help-images/notifications/user_notification-1.png)

**3. Почтовый канал уведомлений и шаблоны**

**Где настраивается:**

- автоадминка `email-settings`;
- автоадминка `mail-templates`;
- почтовые настройки категории (`subcategory-mail-messages-settings`).

**Таблицы БД:**

- `dbo.EmailSettings`
- `dbo.MailTemplates`
- `dbo.ServiceMailBoxes`

**Что контролируется:** включение почтового канала, формат письма, маршрутизация через сервисные ящики.

**Внешние и внутренние ссылки в письмах-уведомлениях:**

- В шаблонах почтовых уведомлений можно формировать две ссылки на задачу: для внутреннего (локального) и внешнего доступа.
- Для настройки двух различных URL используются параметры системы: "Путь к приложению" (ApplicationPath) для локальных пользователей и "Путь к приложению IP" (ApplicationIPPath) для внешних адресов.
- Когда в шаблоне включен режим `fullmode="true"`, первая ссылка формируется через `GetTaskUrl()` (использует ApplicationPath), вторая — через `GetExternalTaskUrl()` (использует ApplicationIPPath с fallback на ApplicationPath, если внешний путь не задан).
- Если параметр ApplicationIPPath пуст, поведение остаётся прежним: обе ссылки будут указывать на ApplicationPath.

**4. Mobile push**

**Где настраивается:** `/api/admin/push/settings*`, страницы `mobile_push` и `mob_push`.

**Таблицы БД:**

- `dbo.PushSettings`
- `dbo.PushDeviceTokens`

**Что контролируется:** сертификаты push, приоритеты Android, техническая возможность доставки на устройство.

**5. Просроченные уведомления**

**Где настраивается:** категория (`cats_notifications`) + системные/почтовые настройки.

**Зависимые джобы:**

- `OverdueCommentsNotificationJob`
- `OverdueNotificationJob`

**Что контролируется:** генерация комментариев и почтовых отчётов по просроченным задачам.

## Типичные ошибки настройки

Частые проблемы настройки уведомлений и способы их диагностики:

| Симптом | Причина | Где проверить | SQL-диагностика |
|---------|---------|---------------|-----------------|
| Пользователь не получает уведомления нужного типа | Отключены флаги в персональных настройках | Профиль пользователя → уведомления | `select UserId, CommentTypeId, Envelope, Push from dbo.UserNotifications where UserId = @UserId order by CommentTypeId;` |
| По категории не срабатывает автоподписка/автоназначение | Неполные правила в user/group-таблицах | Вкладка "Уведомления" категории | `select * from dbo.UserSubcatNotificatons where SubcatId = @SubcatId; select * from dbo.GroupSubcatNotificatons where SubcatId = @SubcatId;` |
| Письма-уведомления не приходят | Почтовый канал выключен или неверный шаблон | `email-settings`, `mail-templates` | `select IsEmailEnabled, IsEmailJobSendEnabled from dbo.EmailSettings; select top (20) * from dbo.MailTemplates;` |
| Push не доходит до устройства | Нет валидного токена или проблема сертификата | `mobile_push`, `/api/admin/push/settings` | `select top (50) UserID, DeviceToken, Status, DateAdded from dbo.PushDeviceTokens where UserID = @UserId order by DateAdded desc;` |
| Отчёт о просрочке не рассылается | Категория исключена из отчёта/неверные статусы | Вкладка "Уведомления" категории | `select SubcatId, ExcludeFromOverdueReport from dbo.Subcategories where SubcatId = @SubcatId;` |


## Пользовательские индикаторы и тикеры

Базовая диагностика цепочки расчёта, кэшей и API вынесена в [runbook по тикерам и счётчикам](https://help.1forma.ru/domains/notifications/runbook-tickers-counters.md). UI-форма списка тикеров описана в [администрировании списков](https://help.1forma.ru/domains/grids/admin.md). В этом разделе зафиксированы правила настройки, не дублирующие runbook.

### Стандартные и пользовательские индикаторы

Правила настройки для двух типов индикаторов:

| Тип | Правило настройки |
|---|---|
| Стандартные индикаторы | Порядок зафиксирован в коде фронтенда и через настройки не меняется. Системные индикаторы не удаляют: если нужно скрыть индикатор, используют ограничение по группам. |
| Пользовательские индикаторы | Порядок задаётся строками в списке настроек в обратном порядке: верхняя строка в админке — ближайший к профилю пользователя индикатор в панели навигации. |
| Веб vs мобильное приложение | Веб-индикаторы отображаются только в веб-интерфейсе. Для мобильного приложения используется плитка рабочего стола, см. ниже. |

> ⚠️ Если поле **«Ограничить доступ к индикатору группами»** пустое, индикатор не отображается никому. Заполненное поле означает видимость только для указанных групп.

### Источник данных, клик и числовой счётчик

Инварианты, которые нужно соблюдать при настройке источника данных индикатора:

| Настройка | Инвариант |
|---|---|
| `ClickAction` / действие по клику | Поддерживаются варианты: открыть страницу из поля «Ссылка», открыть список задач, открыть список пользователей, открыть список произвольных элементов. |
| Табличная функция SQL | Принимает `@UserID int` и возвращает таблицу. Для списка задач обязательна колонка `TaskID`, для списка пользователей — `UserID`. |
| Произвольный источник | Если одновременно заполнены «Табличная функция SQL» и «Произвольный источник», используется произвольный источник, SQL-функция игнорируется. |
| Числовой счётчик | Пузырь со значением отображается только для индикаторов, у которых настроена табличная SQL-функция или отдельная функция счётчика. Индикатор-ссылка без функции показывает только иконку и действие по клику. |
| Произвольные элементы | Для действия «Открыть список произвольных элементов» поле «Табличная функция SQL» нужно очистить, иначе индикатор в пользовательском режиме может стать некликабельным. |

> ⚠️ Если табличная функция не проверяет право просмотра косвенно через заказчика, исполнителя или подписку, добавляйте явную проверку через `UserTaskPermissions`.
>
> ⚠️ Псевдонимы вычисляемых колонок в возвращаемой таблице не должны совпадать с именами существующих колонок БД.

![Числовой счётчик индикатора: пузырь со значением рядом с названием](https://help.1forma.ru/help-images/categories/tickers_tasks.png)

### Функции-счётчики

Если заполнено поле `SQLSPCountName`, при обновлении используется функция счётчика; пересчёт выполняется процедурой `UserMenuItemTickers_refresh`. Поддерживаемые соглашения имён:

| Тип | Платформа | Сигнатура / результат |
|---|---|---|
| `_COUNT_IF` | MSSQL, PostgreSQL | Inline/table function, возвращает одну строку с `cnt` |
| `_COUNT_TF` | MSSQL | Процедурная table-valued function с `@ret table(cnt int)` |
| `_COUNT_FN` | MSSQL, PostgreSQL | Скалярная функция, возвращает `int` |

Минимальные шаблоны:

```sql
-- MSSQL inline counter
create or alter function dbo.fn_example_ticker_COUNT_IF(@UserID int)
returns table
as
return (
    select cnt = count(*)
    from dbo.Tasks t
    join dbo.UserTaskPermissions utp on utp.TaskID = t.TaskID and utp.UserID = @UserID
    where t.IsClosed = 0
);
```

```sql
-- MSSQL scalar counter
create or alter function dbo.fn_example_ticker_COUNT_FN(@UserID int)
returns int
as
begin
    return (
        select count(*)
        from dbo.Tasks t
        join dbo.UserTaskPermissions utp on utp.TaskID = t.TaskID and utp.UserID = @UserID
        where t.IsClosed = 0
    );
end;
```

```sql
-- PostgreSQL table counter
create or replace function dbo.fn_example_ticker_COUNT_IF(p_UserID int)
returns table(cnt int)
stable
as $$
    select count(*)::int
    from dbo.Tasks t
    join dbo.UserTaskPermissions utp on utp.TaskID = t.TaskID and utp.UserID = p_UserID
    where t.IsClosed = 0;
$$ language sql;
```

### Частота обновления, оформление и мобильный аналог тикера

Параметры обновления и оформления индикатора:

| Параметр | Правило |
|---|---|
| Онлайн-пользователь | Частота обновления = «Время неактивности (мин)» / 5. |
| Офлайн-пользователь | Используется «Интервал обновления в оффлайне (мин)». |
| Набор иконок | Иконки берутся из набора `/spa/icons`; имя пишется строчными буквами через подчёркивание, например `calendar_today`. |
| Цвет индикатора | Типовые схемы: белый на синем или белый на красном; CSS иконки имеет приоритет, если задан. |

**Мобильный аналог тикера**

В мобильном приложении веб-индикаторы не отображаются. Аналог настраивается как плитка рабочего стола:

1. Создать источник данных задач.
2. Проверить наличие шаблона `taskSourceDashboardItem`.
3. В контейнере `Dashboard` добавить элемент «Синдикат».
4. Задать `Id = TaskSource{N}`, где `{N}` — ID источника данных.
5. При необходимости скрывать пустую плитку через `hideOnZeroCount = 1`.

> ⚠️ Для произвольных `TaskSource{N}`-плиток счётчик может не рассчитываться. Параметры `hideCounter`, `hideOnZeroCount`, `badgeItemColor` и поле «Количество задач» влияют только на отображение уже полученного счётчика, но не включают расчёт счётчика для произвольного TaskSource. Для обязательного числового бейджа используйте системный синдикат, где счётчик поддержан платформой.

### Создание индикатора на площадке без записей `Ticker` (с 2.268.393)

Раньше на площадках, где в таблице `Ticker` не было ни одной записи, форма создания пользовательского индикатора в админке падала с NRE: `TickersAdminService` при построении списка ожидал хотя бы одну существующую запись и не обрабатывал пустой результат. Теперь `TickersAdminService` корректно инициализирует индикатор на пустой площадке — создание доступно с первой записи, отдельного «сидового» тикера заводить не нужно. Признак затронутой площадки: `select count(*) from dbo.Ticker` = 0 и 500-я ошибка при открытии карточки нового индикатора в админке.

## Mobile push: администрирование канала

Push для мобильного приложения относится к домену уведомлений. Пользовательская логика push описана в [пользовательской документации](https://help.1forma.ru/domains/notifications/business.md#push-уведомления).

**Страница «Настройка PUSH» (с 2.268).** Ключи мобильных приложений настраиваются на отдельной странице администрирования `/administration/push-settings` (раздел «МП»): здесь список записей и модальная форма создания и правки. В таблице — колонки «Ключ» (ссылка на скачивание сохранённого файла; при пустом значении ссылки нет), TeamID, KeyID, BundleID и «Приложение»; отдельной колонки с кодом приложения нет, а имена приложений показываются как имена справочника — без перевода.

- **Создание обычной настройки.** Выбирается приложение (его код показывается рядом только для чтения), заполняются TeamID, KeyID и BundleID — для обычной записи все три обязательны, ввод одних пробелов считается пустым — и прикладывается файл ключа: без него запись не сохраняется.
- **Правка обычной настройки.** Те же три поля остаются обязательными; текущий файл можно скачать, а новый — выбрать. Если новый файл не выбран, ключ не меняется; выбранный заменяет прежний.
- **Строка провайдера Huawei.** Приложение зафиксировано, поля TeamID/KeyID/BundleID не показываются, а значение ключа — секрет, а не скачиваемый сертификат: в форму он не читается и скачать его нельзя. Чтобы сменить секрет, вводится новое значение; пустое поле оставляет прежнее. Удалить эту строку нельзя, и в списке приложений при создании её нет.
- **Чего на странице нет.** Пароль сертификата и VOIP-поля здесь не редактируются, приоритеты Android остаются на прежней форме. Автоформа `push-settings` сохранена для прямого доступа, а пункт меню ведёт на новую страницу, если так задан адрес формы в дереве администрирования.

Для интеграции и диагностики служат маршруты `/api/admin/push/settings`: список записей, создание и обновление записи (при отсутствии нового файла ключ не заменяется), удаление обычной записи и отдельные операции скачивания и загрузки файла ключа. Права проверяются сервером при каждом вызове.

| Настройка | Правило |
|---|---|
| Сертификаты iOS / APNs | Для приложения загружается файл сертификата и пароль. Если используется несколько самостоятельных мобильных приложений 1F Mobile, для каждого нужен отдельный сертификат. |
| `VOIP Сертификат`, `VOIP Пароль` | Поля устарели и не используются; их можно заполнять произвольными значениями, если форма требует значение. |
| Режим дебаг | Флаг `IsDebug` записи сертификата. В форме «Настройка PUSH» автоадминки поле скрыто, и новая запись сохраняется с выключенным режимом (`IsDebug = 0`). |
| Приоритеты Android | Устаревшая настройка. В новом режиме все Android-push имеют повышенный приоритет. Android может начать блокировать push с повышенным приоритетом, если пользователь систематически их игнорирует. |
| Секрет провайдера Huawei HMS (`ClientSecret`) | Вводится в форме настроек push в поле write-only: после сохранения значение на форме не показывается. Ротация выполняется через это же поле; смена секрета подхватывается без перезапуска сервера. |

## Быстрые ответы и умные подсказки

### Быстрые ответы

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

| Поле / действие | Правило |
|---|---|
| Текст ответа | Содержимое, которое подставляется пользователю при выборе ответа. |
| Порядок | Меньшее значение выводится выше; ответы с одинаковым порядком сортируются по алфавиту. |
| Удаление | Доступно из формы ответа или из контекстного меню строки. |


### Умные подсказки `CannedResponses` и событие «Во время открытия задачи»

Функция доступна начиная с версии `2.265 Цефей`; текущий источник фиксирует ограничение «только для чатов». Подсказки показываются над полем ввода: если подписи всех вариантов не длиннее 25 символов — чипами, иначе списком. Клик по варианту вставляет его текст в поле ввода по режиму вставки варианта (формат вариантов — ниже), автоматической отправки нет. В чате после клика подсказки сразу закрываются — и над полем ввода, и в ленте, кроме случая, когда вариант с режимом `insert` не изменил поле; вариант-каталог (раздел с вложенными вариантами) в ленте подсказки не закрывает, а открывает следующий уровень. Кнопка «Свернуть ответы» / «Развернуть ответы» есть только у списка, у чипов её нет.

AI-подсказки запускаются смарт-событиями, к которым администратор привязывает смарт-действие **«Отправить сигнал»** с типом `CannedResponses`. Доступны три события — по мере роста контекстности:

**1. «Во время открытия задачи» (WhenOpenTask, EventID 64)**

Срабатывает при открытии чата. AI получает `(userId, taskId)` и генерирует подсказки «вслепую» — без привязки к конкретному сообщению.

Пример настройки:

| Элемент | Значение |
|---|---|
| Смарт-событие | «Во время открытия задачи» |
| Смарт-действие | «Отправить сигнал» |
| Тип сигнала | `CannedResponses` |
| Текст сигнала | JSON с `responses` и `TaskId` |

```json
{
  "responses": [
    "Здравствуйте! Чем могу помочь?",
    "Опишите, пожалуйста, вашу задачу",
    "Готово. Ваш запрос выполнен."
  ],
  "TaskId": 123456
}
```

Подсказки отображаются пользователям, получившим сигнал, если у них в этот момент открыта задача из поля `TaskId`. При смене контекста беседы старые варианты сбрасываются.

Вариант в `responses` задаётся строкой или объектом. Строка выводится на подсказке целиком, клик по ней заменяет текст поля ввода. Объект задаёт подпись и режим вставки:

| Поле | Назначение |
|---|---|
| `id` | Числовой идентификатор варианта. |
| `text` | Текст, который вставляется в поле ввода. |
| `alias` | Подпись на подсказке; полный `text` виден при наведении. Если `alias` не задан, на подсказке выводится `text`. |
| `mode` | Режим вставки: `fullreplace` — заменить текст поля; `addtoend` — дописать в конец текста поля через пробел; `insert` — вставить после первого совпадения с `findtext`. Если `mode` не задан или содержит другое значение, действует `fullreplace`. |
| `findtext` | Регулярное выражение для режима `insert`, не длиннее 200 символов. Текст вставляется сразу после первого совпадения, при необходимости с пробелами по краям. Если `findtext` не задан, длиннее 200 символов, содержит ошибку или совпадения нет, поле ввода не меняется, подсказки остаются открытыми и событие черновика не отправляется. |

#### 2. «Пользователь начал отвечать на комментарий» (WhenStartReply, EventID 196)

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

| Параметр | Идентификатор | Тип |
|---|---|---|
| Задача | `@eventParam0` | Задача-чат |
| Комментарий | `@eventParam1` | Комментарий, на который отвечают |
| Тред | `@eventParam2` | ID треда (если ответ в ветке) |
| Ответить всем | `@eventParam3` | bool |
| Вопрос ко мне | `@eventParam4` | bool (интерфейс вычисляет: комментарий помечен «требует ответа», написан не текущим пользователем, и пользователь в адресатах) |

#### 3. «Пользователь печатает черновик комментария» (WhenWritingComment, EventID 201)

Срабатывает при вводе текста в поле ответа (интерфейс отправляет событие с задержкой), а также сразу после клика по подсказке — с итоговым текстом поля. AI получает текст черновика и может скорректировать подсказки под уже набранное. Пустая строка в `DraftText` означает «черновик отменён». Параметры события:

| Параметр | Идентификатор | Тип |
|---|---|---|
| Задача | `@eventParam0` | Задача-чат |
| Черновик текста | `@eventParam1` | string (plain text; пусто = отмена) |
| Комментарий | `@eventParam2` | Комментарий (если ответ начат с «Ответить») |
| Тред | `@eventParam3` | ID треда |
| Ответить всем | `@eventParam4` | bool |
| Вопрос ко мне | `@eventParam5` | bool |

#### Глобальная настройка (опционально)

Все три события поддерживают флаг `canBeGlobal` — их можно привязать к пакету действий в разделе **Общие SMART** без указания категории. В этом случае подключение AI-сервиса настраивается один раз на всю систему, без дублирования по категориям. Если настроены и категорийные, и глобальные пакеты — сначала отрабатывают категорийные.

## Реакции на комментарии: административная настройка

Здесь зафиксированы настройки каталога реакций (групп и локализации), которые задаются на стороне базы данных.

| Таблица / поле | Назначение |
|---|---|
| `EmojiReactions.IsEnabledForComments` | `1` включает реакцию в списке доступных для комментариев. |
| `EmojiReactions.OrderInGroup` | Порядок реакции внутри группы в интерфейсе выбора. |
| `EmojiReactions.SubGroupId` | ID группы реакции. |
| `EmojiReactionsGroup` | Справочник групп реакций. |
| `EmojiReactionsSubGroup` | Связь реакций с группами. |
| `LocalizedBusinessObjects`, `LocalizedBusinessObjectValues` | Локализованные название и описание реакции. |

Пример локализации названия:

```sql
declare @rowid int;

insert into dbo.LocalizedBusinessObjects ([Guid]) values (newid());
set @rowid = scope_identity();

update dbo.EmojiReactions
set LocalizedNameId = @rowid,
    Name = N'Название реакции'
where Id = 1;

insert into dbo.LocalizedBusinessObjectValues (LocalizationId, LanguageId, [Value], [Guid])
values (@rowid, 1, N'Название реакции', newid()),
       (@rowid, 2, N'Reaction name', newid());
```

Пример локализации описания:

```sql
declare @rowid int;

insert into dbo.LocalizedBusinessObjects ([Guid]) values (newid());
set @rowid = scope_identity();

update dbo.EmojiReactions
set LocalizedDescriptionId = @rowid,
    Description = N'Описание реакции'
where Id = 1;

insert into dbo.LocalizedBusinessObjectValues (LocalizationId, LanguageId, [Value], [Guid])
values (@rowid, 1, N'Описание реакции', newid()),
       (@rowid, 2, N'Reaction description', newid());
```

## Дефолты новых пользователей: остаточные настройки

Большая часть пользовательских notification-флагов описана в [business.md#пользовательские-настройки](https://help.1forma.ru/domains/notifications/business.md#пользовательские-настройки). Для администрирования дефолтов важно отделять их от уже созданных пользователей: настройки SystemRobot применяются к новым учётным записям, а массовое применение к существующим пользователям затрагивает всех пользователей.


> **Хранение (актуализировано).** Дефолтные настройки уведомлений новых пользователей вынесены из таблицы `UserNotifications` (где хранились под `UserId = SystemRobotId`) в отдельные таблицы `Default*` (`DefaultNotifications`, `DefaultUserNotificationSettings`, `DefaultUserMobileAppsSettings` и др.). Для администратора это незаметно — настройки задаются там же; изменилось только место хранения в БД.

| Блок | Правило |
|---|---|
| Лицензия 1 | Значения: «нет», «обычная», «конкурентная». Если лицензии нет или лимит исчерпан, пользователь/администратор получает сообщение. |
| «Сотрудник компании» | Отключённая настройка делает пользователя внешним: видимость пользователей ограничивается компанией, группы других пользователей не видны, отправка группе как адресату недоступна без права администратора категории. |
| Отправка по Enter | «Отправлять комментарий по Enter вместо Ctrl+Enter» влияет только на поле отправки сообщений, не на системные и дополнительные поля. |
| Автовопрос | «Не помечать коммент как вопрос автоматически» отключает автоматическую постановку признака вопроса по символу `?`. |

**CustomSettings — прочие ключи уведомлений**

| Ключ | Тип / Default | Назначение |
|------|---------------|------------|
| `MaxSMSLength` | int | Максимум символов в SMS, отправляемом из 1Формы. Символы сверх лимита **обрезаются**. Подбирать под тарифные ограничения SMS-провайдера |

**Семантика отсутствующей строки.** Новая учётная запись и её настройки уведомлений записываются одной операцией: вставка пользователя и создание строк настроек идут в одном скоупе транзакции, поэтому сбой между ними не оставляет активного пользователя без строк. Отсутствие строки в настройках уведомлений читается как «выключено» — push по такому типу не уходит, а конверт уходит только если включён на уровне категории. Поэтому строка создаётся только для типов, где в настройках по умолчанию включён конверт или push; типы, у которых оба флага сняты, строки не получают. Настройки мобильного приложения устроены иначе: отсутствие строки там — откат на настройки по умолчанию, а не выключение.

**Штатный сброс для выравнивания данных не подходит.** Он перезаписывает настройки ленты и мобильного приложения и снимает пользовательские изменения. Выравнивание накопленных расхождений выполняется разовой миграцией и только дописывает недостающие строки, не трогая уже существующие.
