Files
han-app/busines_tasks/notification-requirements.md
T

494 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Бизнес-постановка: Уведомления (Notification Center)
**Статус:** бизнес-постановка согласована (v3: D1–D13 закрыты) → вход в проектирование arch / OpenAPI / модули
**Продукт:** HAN Chat (клиентское приложение + `api-backend`)
**Источники:** макет Figma (**не канон** — только визуализация элементов UI, где применимо; при расхождении приоритет у этого ТЗ), черновик требований, архитектура `HAN_chat_specification`
**Связанный backlog:** «Моделирование уведомлений»; смежно — кнопка «Позвонить оператору», кнопка «Войти»; **отдельно** — индикатор непрочитанных сообщений чата (не в scope этого ТЗ)
---
## 1. Цель
Дать клиенту единый канал сервисных и маркетинговых коммуникаций вне чата: срочные напоминания, статусы услуг, документы, оплаты, акции, а также подсказки в гостевой зоне (авторизация, установка PWA).
Чат остаётся каналом диалога с оператором; уведомления — канал «компания → клиент» с действиями (переход, оплата, загрузка/скачивание документов, переход в чат по акции). Непрочитанные ответы оператора в чате **не** моделируются как уведомления (см. §3.2).
Гостевые `ads`/`promo` — не персонализированная реклама на клиента; отдельное маркетинговое согласие не требуется. Функционал описывается в Пользовательском соглашении.
---
## 2. Два контура показа
| Контур | Кто видит | Источник данных | Frontend |
|---|---|---|---|
| **G. Гостевые уведомления** | Только гость | Таблица гостевых уведомлений в App DB → **public API** | Единственный источник в гостевом режиме |
| **P. Персональные уведомления** | Только авторизованный | Таблица персональных (`user_id` NOT NULL) → **JWT API** | Единственный источник в ЛК |
Правила:
1. До авторизации: только контур **G** (ответ public API as-is).
2. После авторизации: только контур **P**. Гостевые **не** показываются и **не** переносятся в персональные.
3. Смешивать G и P в одной таблице / через `user_id IS NULL`**запрещено**.
4. В v1 **нет** клиентских условий показа (ОС / PWA / прочие фильтры): фронт показывает список из API в рамках лимита и сортировки. Локальная генерация карточек на устройстве **запрещена**.
5. У фронта всегда ровно один источник: public API **или** JWT API.
Дубли тематики `ads`/`promo` в G и P допустимы; после входа виден только P.
---
## 3. Границы релиза
### 3.1. В scope
- Контуры G и P (§2); персональный жизненный цикл: `lifecycle_status` / `visibility` / `is_read` / `close_reason` + `record_status` (arch-05).
- UI: карусель на главной, Центр уведомлений, деталка (где применимо).
- Каталог типов; публичный id — **UUID v7** (генерация в приложении).
- Клиентские действия (ЛК): крестик, CTA, деталка/оплата, done/got it, документы (`chat.attachments.*`).
- Internal API: **Create**, **Cancel** (без upsert / без update content) — §7.4.
- Константы `notification.*` в `app_settings`; вложения — reuse `chat.attachments.*`.
- Кнопки **Чат** + **Оператор** (`tel:``operator.call.phone`).
- Готовность модели к Web Push (UUID, события); реализация push — out of scope.
### 3.2. Вне scope
- Тип `message` / непрочитанный чат (отдельная фича).
- Push / Web Push / deep link из push.
- Админ-UI для гостевых уведомлений (v1: insert/SQL).
- Жёсткая привязка «тип ↔ upstream-сервис».
- Профиль «Документы» / архив оплат — не заменяются уведомлениями.
- Запись в `sync_queue` из application-кода: только **триггер БД** + новый `task_type`; куда в Bitrix — ТЗ `bitrix-sync`.
- SMS/email поверх ЛК.
- История чатов как UI-раздел — deprecated; backend API диалогов этим ТЗ не удаляется.
- **Архив `lifecycle_status = N` в UI v1 — нет.**
- Поле `opened` / impression — **не моделируем** (CTA → `is_read`).
---
## 4. Изменения UI
| Место | Было | Стало |
|---|---|---|
| Под полем чата | (см. текущий UI) | **Чат** + **Оператор** (`operator.call.phone`) |
| Верхнее меню | История чата | Центр уведомлений (колокольчик; бейдж только в ЛК) |
| Главная | — | Карусель (лимит/сортировка §6.1) |
| Центр | — | ЛК — список; гость — auth-gate + «Авторизоваться» |
| Деталка | — | Для типов с деталкой; гостевые `ads`/`promo`**без** деталки |
Визуал типов — по Figma, где применимо. Figma не канон поведения.
---
## 5. Бизнес-модель
### 5.1. Слои контента
1. **Краткий** — баннер и строка списка: `header`, `text`, цена, CTA.
2. **Детальный**`details.*`, документы, кнопки. Только если у типа есть деталка.
### 5.2. Справочник типов (**принято**)
| Слой | Где | Содержимое |
|---|---|---|
| Структурный | App DB `notification_types` | `code`, `priority`, `countable`, признак «есть деталка», `record_status` |
| Визуал | Frontend (тема/константы), сверка с Figma | Цвет, иконка, оформление |
| Тексты по умолчанию | `text_resources` | `label`, дефолтный CTA, empty states |
В экземпляре **не** дублируются label/цвет/icon/countable/priority/action — только `notification_type` (FK/code).
### 5.3. Каталог типов
| type | label | priority | countable | Action по CTA | Контур |
|---|---|---|---|---|---|
| `authorize` | Гостевой режим | 2 | нет | Сценарий авторизации | G |
| `install_app` | Приложение | 2 | нет | Адаптивная установка PWA (§5.7) | G |
| `urgent` | Срочно | **1** | да | Детальный просмотр | P |
| `reminder` | Напоминание | 2 | да | Детальный просмотр | P |
| `news` | Новость | 2 | да | Детальный просмотр | P |
| `ads` | Предложение | 2 | да* | G: чат после auth; P: чат + закрытие (§7.2) | G и/или P |
| `promo` | Акция | 2 | да* | G: чат после auth; P: чат + закрытие (§7.2) | G и/или P |
| `docs_required` | Требуются документы | 2 | да | Деталка (+ загрузка) | P |
| `docs_ready` | Документы готовы | 2 | да | Деталка (+ скачивание) | P |
| `status_changed` | Статус | 2 | да | Детальный просмотр | P |
| `payment_pending` | Оплата | 2 | да | Переход по `payment_url` | P |
\* Countable/read применяются только в контуре **P**. В **G** read-state нет.
Типы `install_Android` / `install_IOS-HarmonyOS` / `message`**отсутствуют**.
`priority`: меньше = важнее. Seed: `urgent = 1`, остальные = `2`.
CTA по умолчанию:
- `authorize` — «Войти →»
- `install_app` — «Установить →» (на UI инструкции текст может быть «Как установить →»)
- `urgent` / `reminder` / `news` / `status_changed` — «Подробнее →»
- `ads` / `promo` — «Узнать подробнее →»
- `docs_required` — «Загрузить документы →»
- `docs_ready` — «Скачать →»
- `payment_pending` — «Оплатить →»
**Инвариант:** CTA у `docs_required` / `docs_ready` открывает **карточку**; upload/download — на карточке.
**Инвариант:** жёсткой привязки «сервис X → тип Y» нет.
### 5.4. Поля экземпляра (контур P)
| Поле | Обяз. | Описание |
|---|---|---|
| `id` | да | UUID v7 (api-backend при Create) |
| `user_id` | да | `UserIdentity.id` NOT NULL |
| `notification_type` | да | Код из справочника |
| `notification_datetime` | да | UTC |
| `header` | да | Заголовок |
| `text` | нет | Подзаголовок |
Опционально:
| Поле | Описание |
|---|---|
| `date_expired` | → `lifecycle_status=N`, `close_reason=expired` |
| `price` | Текущая цена (₽), опционально. Может быть без `old_price` |
| `old_price` | Старая цена (₽), опционально. Имеет смысл только вместе с `price` (зачёркнутая «было») |
| `payment_url` | Обязателен для `payment_pending` |
| `details` / `details.*` | Деталка (если нужна) |
| `details.deadline` | Срок на карточке |
| `send_documents` | UI загрузки клиентом |
| `documents[]` | Вложения компании в бакете **`han-chat-attachments`** (id объектов working S3) |
| `chat_message_text` | Текст в чат при CTA (`ads`/`promo` P — **обязателен**) |
| `button_done` / `button_gotit` / `button_gotit_text` | Кнопки деталки |
| `external_id` + `source` | Идемпотентность Create (§8) |
### 5.5. Состояние экземпляра (контур P)
| Атрибут | Значения | Семантика |
|---|---|---|
| `record_status` | `A` / `D` | Soft-delete строки (arch-05) |
| `lifecycle_status` | `A` / `N` | `N` — нет в UI; в БД для аудита |
| `visibility` | `V` / `I` | `I` — нет на главной, есть в Центре (пока `lifecycle_status=A`) |
| `is_read` | да / нет | Для countable; бейдж = `A` ∧ countable ∧ ¬is_read |
| `close_reason` | см. ниже | При `A``N` |
| `close_reason` | Когда |
|---|---|
| `user_done` | «Выполнено»; успех upload `docs_required`; Cancel после успешной оплаты |
| `expired` | `date_expired` |
| `cancelled` | Cancel (отзыв системой) |
**`is_read``visibility`:** прочтение → бейдж; видимость → главная. Правила — §7.1.
Контур G: per-user lifecycle / visibility / is_read **нет**.
### 5.6. Константы и мнемоники
`notification.*` в `app_settings`. Вложения — ключи чата:
- `chat.attachments.allowed_extensions`
- `chat.attachments.allowed_mime_types`
- `chat.attachments.disallowed_extensions`
- `chat.attachments.max_size_mb`
- `chat.attachments.presigned_upload_ttl_seconds`
Оператор: **`operator.call.phone`**.
Seed `notification.*`:
| Ключ | Смысл | Default |
|---|---|---|
| `notification.gotit.default_ttl_days` | Got it без `date_expired` → now+N дней | `3` |
| `notification.docs_ready.default_ttl_days` | После скачивания документов, если `date_expired` пуст → now+N дней | `3` |
| `notification.home.max_items` | Лимит карусели | `15` |
| `notification.center.max_items` | Лимит Центра (ЛК) | `15` |
| `notification.carousel.autoplay_enabled` | `Y`/`N` | `N` |
| `notification.carousel.autoplay_interval_ms` | Интервал | `5000` |
Мнемоники UI (`text_resources`) — **принято**:
- `notification.center.empty.title` / `.text` — пустой Центр (ЛК)
- `notification.guest_center.title` / `.text` / `.cta` — гостевой Центр
- `notification.detail.not_found.title` / `.text` — 404 деталки («На главную»)
API при чужом/`N`/отсутствии → **404**; UI — нейтрально «Уведомление недоступно».
### 5.7. Гостевые уведомления (контур G)
- Одна таблица гостевых уведомлений; `record_status` + актуальность **`A`/`N`** (без per-user state).
- Public API отдаёт все активные (`A`); фронт **не фильтрует** по ОС/PWA в v1.
- Наполнение v1 — insert/SQL; public API — read-only.
- Типы в G: `authorize`, `install_app`, `ads`, `promo` (и при необходимости другие broadcast-типы позже).
- Крестика нет. Деталки у `ads`/`promo` в G **нет**.
**`install_app` — один тип, адаптивный CTA (принято, технически возможно):**
1. Пользователь нажал CTA.
2. Если среда поддерживает установку PWA (`beforeinstallprompt` / эквивалент) — запускаем установку.
3. Иначе — показываем инструкцию установки (локальный экран/модалка на фронте; отдельный тип в API не нужен).
Fallback (если адаптивный CTA окажется нереализуем на конкретной платформе сборки): две записи в G (`install` для Android-потока и для iOS/Harmony) — только как запасной план реализации, не целевая модель данных.
**CTA прочих G:**
- `authorize` → сценарий авторизации.
- `ads` / `promo` → как «популярные вопросы»: старт auth; после успеха в чат уходит `chat_message_text` кампании от лица клиента. Кампания для других гостей остаётся `A`.
### 5.8. Персональные (контур P) — кратко
Создаются Internal **Create**; полная модель §5.4–5.5. Поведение CTA — §7.
---
## 6. Поведение UI
### 6.1. Главная (карусель)
**ЛК (P):**
- Выборка: `record_status=A`, `lifecycle_status=A`, `visibility=V`.
- Лимит `notification.home.max_items` (15).
- Сортировка: `priority` ↑, затем `notification_datetime` ↓.
- Крестик → `visibility=I` (синхрон на все устройства).
- Карусель: свайп; автопрокрутка по settings.
- CTA → action типа + эффекты §7.1.
**Гость (G):**
- Список = ответ public API; лимит 15; сортировка: `priority` ↑, затем datetime ↓ (поля сортировки — в таблице G / типе).
- Крестика нет. Один источник — public API.
### 6.2. Центр
**Гость:** колокольчик без бейджа; экран auth-gate + «Авторизоваться» (мнемоники §5.6). Списка нет.
**ЛК:**
- `record_status=A`, `lifecycle_status=A` (в т.ч. `visibility=I`), лимит 15, без архива `N`.
- Точка на непрочитанных countable.
- Сортировка: `priority` ↑ → непрочитанные выше → datetime ↓.
- Бейдж = непрочитанные countable (`lifecycle_status=A`); sync через бэкенд.
- Empty state — мнемоники §5.6.
### 6.3. Деталка (контур P; инструкция `install_app` — локальный UI)
- SPA: `/notification/{uuid}`.
- Чужой / `N` / deleted → **404** (не 403).
- Upload: quarantine → Message Safety → working (`han-chat-attachments`) → **триггер БД**`sync_queue`.
- Company `documents[]`: тот же бакет **`han-chat-attachments`**; preview/download + audit как в чате.
- `button_done``lifecycle_status=N`, `close_reason=user_done`.
- `button_gotit``visibility=I`; при пустом `date_expired` — now + `notification.gotit.default_ttl_days`; `is_read=true` (got it = действие пользователя, см. §7.1).
- Обе кнопки допустимы одновременно; исход по нажатой. Обе false → только back.
### 6.4. Завершение по типам (P)
| Тип | → `lifecycle_status=N` |
|---|---|
| `payment_pending` | Cancel после callback оплаты, `close_reason=user_done` |
| `docs_required` | Успешный upload → `user_done` |
| Другой тип с `send_documents` | Upload сам не закрывает |
| `ads` / `promo` | CTA → `N` (§7.2) |
| `status_changed` | «Выполнено»; с главной уходит уже по CTA (`visibility=I`) |
| `docs_ready` | После скачивания — `I` + TTL/`date_expired` (§7.1); в `N` уходит по expire (или Cancel/done) |
| Прочие | done / expired / cancelled |
---
## 7. Матрицы поведения (контур P)
### 7.1. `is_read` и `visibility` — раздельно
**Правило прочтения (единое):**
> Любое **CTA** → `is_read = true` (идемпотентно).
CTA = первичное действие карточки: кнопка CTA на баннере, тап по строке Центра ведущий к action типа, «Оплатить», переход в деталку по CTA, отправка в чат по ads/promo.
Не CTA: системный back, крестик (крестик не ставит `is_read`).
**Матрица `visibility` / `lifecycle` (избирательно):**
| Тип | Событие | `is_read` | `visibility` | `lifecycle_status` |
|---|---|---|---|---|
| любой | Крестик на главной | — | `I` | — |
| любой с `button_gotit` | Got it | `true` | `I` (+ TTL если нужно) | — |
| любой с `button_done` | «Выполнено» | `true` | — | `N` / `user_done` |
| `urgent` | CTA → деталка | `true` | — | — |
| `reminder` | CTA → деталка | `true` | — | — |
| `status_changed` | CTA → деталка | `true` | `I` | — |
| `news` | CTA → деталка | `true` | `I` | — |
| `ads` / `promo` | CTA (чат) | `true` | `I` | `N` (+ сообщение в чат) |
| `docs_required` | CTA → деталка | `true` | — | — |
| `docs_required` | Успешный upload | `true` | — | `N` / `user_done` |
| `docs_ready` | CTA → деталка | `true` | — | — |
| `docs_ready` | Скачивание документов | `true` | `I` | — (+ TTL, см. ниже) |
| `payment_pending` | CTA «Оплатить» | `true` | `I` | — (далее Cancel→`N` при оплате) |
Пояснения:
- Прочерк «—» = поле этим событием не меняется.
- Для `ads`/`promo`: одновременно `is_read`, `visibility=I` и `lifecycle_status=N`. В UI запись исчезает из-за `N`; `I` фиксирует намерение «убрать с главной» на случай гонок/аудита.
- Открытие деталки из Центра по тапу строки = CTA → всегда `is_read=true`; `visibility` меняется только если тип/событие есть в матрице выше.
- `status_changed`: CTA → деталка сразу уводит карточку с главной (`visibility=I`); в Центре остаётся, пока `lifecycle_status=A`.
- `docs_ready`, скачивание документов: `is_read=true`, `visibility=I`; если `date_expired` пуст — выставить `date_expired = now + notification.docs_ready.default_ttl_days` (default 3). Если `date_expired` уже задан продюсером — **не перезаписывать**. Далее сработает общий expired-job → `lifecycle_status=N`, `close_reason=expired`.
### 7.2. `ads` / `promo` (P)
1. CTA отправляет в чат `chat_message_text` (обязательное поле).
2. `is_read=true`, `visibility=I`, `lifecycle_status=N`.
3. Деталки у персональных ads/promo на MVP **нет** (симметрично G).
### 7.3. Валидация Internal Create (**принято**)
| Тип | Обязательно | Не ожидается | Примечания |
|---|---|---|---|
| `urgent`, `reminder`, `news`, `status_changed` | `header`; `details` если нужна деталка | `payment_url` | `button_*` по необходимости |
| `ads`, `promo` | `header`; **`chat_message_text`** | `payment_url` | `price` и `old_price` — разные опц. поля; `price` без `old_price` допустим; `old_price` без `price` — нет; деталки нет |
| `docs_required` | `header`; `send_documents=true` | `payment_url` | |
| `docs_ready` | `header`; `documents[]` ≥1 | `payment_url` | |
| `payment_pending` | `header`; `payment_url` | — | `price` **не** обязателен; `details` **разрешены** |
| любой | `user_id`, `notification_type`, `source`, `external_id` | неизвестный тип / user | |
Ошибка валидации → `400` (OpenAPI).
Типы контура G через Internal Create **не** создаются (только insert/public table).
### 7.4. Контракт продюсера
| Операция | Семантика |
|---|---|
| **Create** | Создать новое уведомление. Ключ `source`+`external_id`: если уже есть активная (`lifecycle_status=A`) запись с той же парой → **409 Conflict** (контент не обновляется). |
| **Cancel** | `lifecycle_status=N` + `close_reason` (`cancelled` \| `user_done`). |
| **Смена контента** | Upsert / update content **нет**. Сценарий: **Cancel** старого → **Create** нового (новый `external_id` или та же пара после `N` — допустим Create, т.к. активной записи с ключом больше нет; политика ключа после `N` — в OpenAPI: разрешить Create с тем же ключом только если нет активной). |
Кто закрывает:
| Сценарий | Кто | reason |
|---|---|---|
| Оплата успешна | Внешний → Cancel | `user_done` |
| Upload `docs_required` | api-backend | `user_done` |
| CTA ads/promo | api-backend по действию клиента | (закрытие как `user_done` или отдельный reason — по OpenAPI; default `user_done`) |
| «Выполнено» | Клиент | `user_done` |
| Отзыв | Внешний → Cancel | `cancelled` |
| TTL | Scheduler | `expired` |
---
## 8. Идентификация
- Id P и G — **UUID v7** (генерация приложением / seed-скриптом, не клиентом).
- SPA: `/notification/{uuid}` (только P; гостевая инструкция install — локальный роут без обязательного id в API).
- Deep link — с push (later).
- Идемпотентность Create — §7.4 (без upsert).
---
## 9. API
### 9.1. Клиентские (JWT) — контур P
Список / get one / hide / mark-read·done·gotit / upload / counter — по §6–§7.
Владелец = `user_id` из JWT. Чужое/`N` → 404.
### 9.2. Public — контур G
`GET` списка активных гостевых уведомлений (без JWT).
### 9.3. Internal
- Path: **`/internal/notifications/v1/...`**
- Service token — как у прочих internal (arch-02).
- Callers: сервисы приватной сети облака (в т.ч. другие ВМ). Интернет — нельзя.
- Операции: **Create**, **Cancel** только.
### 9.4. Realtime (**подписка**)
Желательно в релизе, иначе polling.
После connect на `WS /api/v1/realtime` клиент выполняет явный **`subscribe` на уведомления** (например `subscribe: { notifications: true }`), отдельно от `dialog_ids`.
События: `notification.created` / `notification.updated` / `notification.closed`.
Reconnect → reconciliation GET списком. Гостю WS для G не требуется.
### 9.5. Готовность к Web Push
Стабильный UUID v7; события жизненного цикла; `header`/`text` для текста push; path `/notification/{uuid}`. Реализация push — вне scope.
---
## 10. Смежные сервисы
| Компонент | Изменение |
|---|---|
| **Frontend** | Один источник (G или P); карусель; Центр; адаптивный `install_app`; Чат/Оператор |
| **api-backend** | Модель G+P; public/JWT/internal; scheduler expired; WS subscribe notifications; upload |
| **App DB** | Две таблицы; types+priority; триггер → `sync_queue`; seed |
| **nginx** | Internal не в интернет; rate limit; SPA |
| **Redis/WS** | События notification.* по подписке |
| **message-safety** | Как chat attachments |
| **bitrix-sync** | Новый task_type |
| **S3** | Бакет вложений уведомлений (клиент и компания): **`han-chat-attachments`** |
| **settings** | `notification.*`, `chat.attachments.*`, `operator.call.phone`, мнемоники |
Порядок: (1) модель + API + UI → (2) Internal Create/Cancel + seed G → (3) документы + триггер → (4) WS subscribe.
---
## 11. Критерии приёмки
1. Колокольчик; ЛК — бейдж непрочитанных, sync `is_read`/`visibility` между устройствами; гость — без бейджа, Центр = «Авторизоваться».
2. ЛК главная: лимит 15, sort priority→datetime, только `A`+`V`; крестик → `I`; `N` не в UI.
3. Гость: только public API, без клиентских фильтров ОС/PWA; один тип `install_app` с адаптивным CTA.
4. Типы каталога отличимы; `message` нет; после логина только P.
5. Деталка/кнопки по флагам; docs_* CTA → карточка; ads/promo P: чат + `is_read`+`I`+`N`.
6. Internal только Create/Cancel; дубликат активного `source`+`external_id` → 409; смена контента = Cancel+Create.
7. Expired → `N`, UI исчезает.
8. Upload → quarantine → working → триггер → `sync_queue`; company docs в `han-chat-attachments`.
9. Оператор — только `operator.call.phone`.
10. Константы из `app_settings`; UUID v7; 404 на чужое/`N`.
11. Валидация Create по §7.3.
12. WS: события после `subscribe` на notifications.
13. Push не обязателен; модель push-ready.
14. Архива `N` нет; поля `opened` нет.
---
## 12. Решения D1–D13 (закрыты)
| # | Решение |
|---|---|
| **D1** | Два контура: **G** (всё через public API) и **P** (JWT). Без локальных карточек и без условий показа в v1. `install_app` — один тип, адаптивный CTA (установка / инструкция). Fallback — два баннера в данных, не целевая модель. |
| **D2** | Схема справочника §5.2 принята. |
| **D3** | Мнемоники empty/404 и поведение 404 приняты (§5.6). |
| **D4** | ads/promo CTA: `is_read` + `visibility=I` + `lifecycle_status=N` + чат. |
| **D5** | Любое CTA → `is_read=true`. `visibility` — только по матрице §7.1. |
| **D6** | Валидация §7.3: `chat_message_text` обязателен для ads/promo; `price` у payment необязателен; `details` у payment разрешены. |
| **D7** | `/internal/notifications/v1/...` + service token. |
| **D8** | WS: явная подписка на notifications. |
| **D9** | Бакет company/client файлов уведомлений: `han-chat-attachments`. |
| **D10** | `opened` / impression удалены; достаточно `is_read` от CTA. |
| **D11** | Upsert нет; механика **Cancel + Create**; конфликт активного ключа → 409. |
| **D12** | IDOR: единый 404. |
| **D13** | Одновременные done+gotit допустимы без доп. правил продюсеру. |
---
## 13. История решений (Q)
| # | Решение |
|---|---|
| **Q1** | Непрочитанный чат / `message` — вне ТЗ. |
| **Q2** | `N` в БД, не в UI; архива v1 нет. |
| **Q3** | `payment_pending`: CTA «Оплатить» → `is_read`+`I`; деталка не обязательна. |
| **Q4** | Гостевые — отдельная таблица; insert v1. |
| **Q5** | Документы через S3; клиент quarantine→working. |
| **Q6** | `sync_queue` только триггером БД. |
| **Q7** | Лимит 15; sort priority→datetime. |
| **Q8** | Подсказки установки/входа — в контуре G через API (не локальный FE). |
| **Q9** | UUID v7; `/notification/{uuid}`. |
| **Q10** | `message` исключён. |
| **Q11** | Владелец = `user_id`. |
| **Q12** | `record_status``lifecycle_status`. |
| **Q13** | `operator.call.phone`. |
| **Q14** | Got it = `I` (+TTL), не обязательно `N`. |
| **Q15** | Два контура G/P (вместо A/B/C). |
| **Q16** | Гостевой Центр = auth-gate. |
| **Q17** | Reuse `chat.attachments.*`. |
| **Q18** | Поле `opened` снято (D10). |
| **Q19** | Модель push-ready; push вне scope. |
---