docs(stock): прогресс+mobile-api — центр уведомлений, агрегация/эскалация, каналы
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
09ee0562c2
commit
c69d40a366
|
|
@ -126,6 +126,18 @@ deprecated после обновления iOS (iOS обновит Mac-Claude).
|
||||||
`{ inventoryId, number, lines }`. Открыть через обычный экран инвентаризации.
|
`{ inventoryId, number, lines }`. Открыть через обычный экран инвентаризации.
|
||||||
- Отчёт остатков: `GET /api/inventory/stock?negativeOnly=true` — только минусы.
|
- Отчёт остатков: `GET /api/inventory/stock?negativeOnly=true` — только минусы.
|
||||||
|
|
||||||
|
### Центр уведомлений (персистентный)
|
||||||
|
Фоновый джоб кладёт агрегат «N товаров ушли в минус» на склад (дебаунс/тихие часы) и эскалацию
|
||||||
|
просроченных минусов в таблицу `notifications`. iOS читает тот же фид, что и веб-колокольчик.
|
||||||
|
- `GET /api/notifications[?unreadOnly=&take=50]` → `{ items:[{ id, type, severity, title, body,
|
||||||
|
link, dataJson, storeId, read, createdAt }], unread, total }`. `type` = `negative-stock` |
|
||||||
|
`negative-stock-overdue`; `dataJson` содержит `{ storeId, count, productIds[] }` для действия
|
||||||
|
«Пересчитать».
|
||||||
|
- `GET /api/notifications/count` → `{ unread }` — для бейджа.
|
||||||
|
- `POST /api/notifications/{id}/read` (204) — пометить одно прочитанным; `POST /api/notifications/read-all`
|
||||||
|
(204) — все.
|
||||||
|
- Создать инвентаризацию по предупреждениям — `POST /api/negative-stock/recount` `{ storeId }` (см. выше).
|
||||||
|
|
||||||
### Push-токены устройств (заложено; реальный APNs позже)
|
### Push-токены устройств (заложено; реальный APNs позже)
|
||||||
- `POST /api/me/device-tokens` body `{ platform: "ios"|"android"|"web", token }` — регистрация
|
- `POST /api/me/device-tokens` body `{ platform: "ios"|"android"|"web", token }` — регистрация
|
||||||
токена (идемпотентно по токену). `DELETE /api/me/device-tokens?token=…` — отзыв.
|
токена (идемпотентно по токену). `DELETE /api/me/device-tokens?token=…` — отзыв.
|
||||||
|
|
|
||||||
|
|
@ -32,43 +32,52 @@
|
||||||
Остаток ≥ 0 → закрывается автоматически. Миграция Phase39, частичный уникальный индекс.
|
Остаток ≥ 0 → закрывается автоматически. Миграция Phase39, частичный уникальный индекс.
|
||||||
2. [x] Подсказка причины: API списка отдаёт `hint` «возможно, не проведена приёмка П-… от <поставщик>»
|
2. [x] Подсказка причины: API списка отдаёт `hint` «возможно, не проведена приёмка П-… от <поставщик>»
|
||||||
(если по товару есть черновик приёмки).
|
(если по товару есть черновик приёмки).
|
||||||
3. [~] Без спама: счётчик/баннер — реалтайм-запрос (poll 60с), не спамит. Поля `NotifiedAt`/`EscalatedAt`
|
3. [x] Без спама: фоновый джоб `NegativeStockNotificationJob` (cron */5) агрегирует новые минусы в
|
||||||
в сущности заложены под фоновую агрегацию/рассылку. **Отложено:** фоновая job (email/telegram
|
ОДНО уведомление на склад, дебаунс по `NotifiedAt` + интервал орг. (по умолч. 15 мин), тихие
|
||||||
одним сообщением на склад, тихие часы, получатели по ролям/привязке, тумблеры в настройках).
|
часы (Asia/Almaty, настройка). Получатели — админы, владелец, кладовщики (право InventoryEdit).
|
||||||
|
Тумблеры/интервал/тихие часы/эскалация — в настройках организации (GET/PUT settings + UI).
|
||||||
4. [x] «Пересчитать эти товары»: `POST /api/negative-stock/recount` → черновик инвентаризации по складу
|
4. [x] «Пересчитать эти товары»: `POST /api/negative-stock/recount` → черновик инвентаризации по складу
|
||||||
ровно с минусовыми товарами (BookQty = текущий отрицательный остаток), открывается из баннера.
|
ровно с минусовыми товарами (BookQty = текущий отрицательный остаток), открывается из баннера,
|
||||||
5. [~] Каналы: **веб** — постоянный баннер + фильтр/пометка в остатках ✓; отчёт «Отрицательные» (фильтр
|
центра уведомлений и блока «Просроченные минусы».
|
||||||
`negativeOnly`) ✓. **Мобильное API** — `GET /api/negative-stock` + `/count` готовы (iOS читает) ✓.
|
5. [~] Каналы: **веб** — постоянный баннер + фильтр/пометка в остатках ✓; **центр уведомлений**
|
||||||
**APNs-заглушка заложена** ✓ — таблица `device_tokens` (Phase40) + `POST/DELETE /api/me/device-tokens`
|
(колокольчик, API-backed: `/api/notifications` список/счётчик/read/read-all, живой через SignalR
|
||||||
+ контракт `IPushSender`, в DI `StubPushSender` (лог); реальный APNs/FCM/VAPID подменит заглушку
|
`NotificationCreated`) ✓; отчёт «Отрицательные» ✓. **Telegram** — рассылка владельцу org ✓.
|
||||||
без правок вызова. Описано в `docs/mobile-api.md`. **Отложено:** центр уведомлений (realtime-событие
|
**Мобильное API** — `GET /api/notifications` + `/count` + `POST …/read` + recount ✓; `GET
|
||||||
NegativeStock), Telegram-рассылка, Web Push (VAPID + SW), реальная отправка push.
|
/api/negative-stock` + `/count` ✓. **APNs-заглушка** — `device_tokens` (Phase40) + `IPushSender`/
|
||||||
6. [~] Тесты: integration **2/2** (минус разрешён → алерт → recount → авто-закрытие; фильтр negativeOnly) ✓;
|
`StubPushSender` ✓. **Отложено:** Web Push (VAPID + SW push-обработчик) — браузерный канал.
|
||||||
full-chain шаг 9/13 переписаны (oversell/отмена приёмки → проведено + предупреждение) ✓.
|
6. [~] Тесты: unit тихих часов **3/3** ✓; integration **3/3** (минус→алерт→recount→авто-закрытие;
|
||||||
**Скрины на стенде** ✓ — `tests/e2e/reports/neg-banner-home-*.png` (баннер «22 товаров в минусе»),
|
фильтр negativeOnly; **джоб → уведомление админу + дебаунс + read-all**) ✓; full-chain 9/13 ✓.
|
||||||
`neg-stock-filter-*.png` (фильтр «Только минусы» + красные остатки). Стенд-smoke: `/count` 200,
|
Скрины стенда ✓ — баннер, фильтр, **центр уведомлений** (`neg-notif-center2-*.png`), **настройки**
|
||||||
`/api/negative-stock` 200, `device-tokens` DELETE 204 (Phase39+Phase40 применились).
|
(`neg-settings-*.png`). **Отложено:** автоматический e2e-браузер-тест колокольчика/«Пересчитать».
|
||||||
**Отложено:** unit на агрегацию/дебаунс (нет job), e2e в браузере на баннер.
|
|
||||||
7. [x] Снята блокировка продажи в минус: розница, касса (POS-синк), опт, списание, перемещение,
|
7. [x] Снята блокировка продажи в минус: розница, касса (POS-синк), опт, списание, перемещение,
|
||||||
возврат поставщику, отмена приёмки. Деньги (минус счёта) — не трогали (другое правило).
|
возврат поставщику, отмена приёмки. Деньги (минус счёта) — не трогали (другое правило).
|
||||||
8. [ ] **Отложено** (десктопная Avalonia-касса): предупреждение в строке + жёлтый индикатор в шапке +
|
8. [ ] **Отложено** (десктопная Avalonia-касса): предупреждение в строке + жёлтый индикатор в шапке +
|
||||||
офлайн по локальным остаткам. (Отдельная поверхность; iOS/касса — Mac-Claude.)
|
офлайн по локальным остаткам. Требует предварительного задела: POS-каталог (`PosProduct`/
|
||||||
|
`ProductSyncDto`) сейчас **не синхронизирует остатки** — нет поля количества; офлайн-предупреждение
|
||||||
|
невозможно без добавления остатков в sync-протокол. Онлайн-индикатор мог бы поллить
|
||||||
|
`/api/negative-stock/count`. Отдельная поверхность (SDK-пин, headless-UI).
|
||||||
9. [x] Админка: постоянный баннер «N товаров в минусе — нужна инвентаризация» (сворачивается, крестиком
|
9. [x] Админка: постоянный баннер «N товаров в минусе — нужна инвентаризация» (сворачивается, крестиком
|
||||||
не закрыть) + «Пересчитать эти товары»; в остатках — красная пометка + фильтр «Только минусы».
|
не закрыть) + «Пересчитать эти товары»; в остатках — красная пометка + фильтр «Только минусы».
|
||||||
10. [~] Мобильное API/iOS: `/api/negative-stock/count` + список готовы. Баннер в приложении — Mac-Claude.
|
10. [x] Мобильное API/iOS: счётчик `/api/notifications/count` + список `/api/notifications` + `/api/
|
||||||
11. [~] Эскалация: в API есть флаг `overdue` (минус висит дольше N часов, по умолч. 24). **Отложено:**
|
negative-stock` + recount. Баннер в самом приложении — Mac-Claude.
|
||||||
фоновое повторное уведомление владельцу/админу + блок «Просроченные минусы» на главной.
|
11. [x] Эскалация: джоб повторно уведомляет владельца/админов по минусам старше N часов (настройка, по
|
||||||
|
умолч. 24; `EscalatedAt`), тип `negative-stock-overdue`; на дашборде — блок «Просроченные минусы»
|
||||||
|
с «Пересчитать». Флаг `overdue` в API.
|
||||||
12. [ ] ОТЛОЖЕНО (поправка владельца 09.10) — предварительная себестоимость НЕ делаем.
|
12. [ ] ОТЛОЖЕНО (поправка владельца 09.10) — предварительная себестоимость НЕ делаем.
|
||||||
|
|
||||||
## Что сделано в этой сессии (ядро, оттестировано и выложено)
|
## Что сделано (ядро + уведомления, оттестировано и выложено)
|
||||||
Разрешение минуса + детекция/авто-закрытие предупреждений + API (список/счётчик/recount/фильтр) +
|
Пасс 1: разрешение минуса + детекция/авто-закрытие + API (список/счётчик/recount/фильтр) + веб-баннер/
|
||||||
веб-баннер и пометки в остатках + integration-тесты 2/2 + full-chain 9/13. Деплой на стенд.
|
пометки + APNs-задел. Пасс 2: персистентный центр уведомлений (`notifications`, Phase41) + REST +
|
||||||
|
фоновый джоб агрегации/эскалации (интервал/тихие часы/получатели по ролям) + Telegram + настройки орг.
|
||||||
|
+ блок «Просроченные минусы» + unit 3/3 + integration 3/3. Выложено на стенд, скрины сняты.
|
||||||
|
|
||||||
## Что отложено (заложен фундамент, нужен отдельный проход)
|
## Что отложено (нужен отдельный проход)
|
||||||
Фоновая рассылка (email/telegram, агрегация/тихие часы/получатели/настройки), эскалация-job и
|
- **Web Push (VAPID)** — браузерный push-канал (ключи + SW push/notificationclick + подписка).
|
||||||
«просроченные минусы», центр уведомлений (realtime-событие), Web Push (VAPID), **реальная**
|
- **Автоматический e2e-браузер-тест** колокольчика/«Пересчитать» (ручные скрины сняты).
|
||||||
отправка push (APNs/FCM — заглушка уже в DI), индикаторы в десктопной Avalonia-кассе (в т.ч. офлайн).
|
- **Avalonia-касса (п.8)** — индикатор/предупреждение; сначала нужен задел: остатки в POS-sync
|
||||||
**DONE не ставим — не все пункты закрыты.**
|
(у `PosProduct` нет количества). Отдельная десктоп-поверхность.
|
||||||
|
- **Реальная отправка push** (APNs/FCM) — нужен платный Apple Developer; заглушка в DI готова.
|
||||||
|
**DONE не ставим — остаются п.5 (Web Push), п.6 (e2e-браузер), п.8 (касса).**
|
||||||
|
|
||||||
## Что НЕ трогаем
|
## Что НЕ трогаем
|
||||||
Прод, shared-postgres, global.json, gateway nginx, sklad. Расчёт себестоимости (п.12 отложен).
|
Прод, shared-postgres, global.json, gateway nginx, sklad. Расчёт себестоимости (п.12 отложен).
|
||||||
|
|
@ -77,6 +86,10 @@
|
||||||
- 09.10 — старт: прогресс-док, сверка с МойСкладом, обследование кода.
|
- 09.10 — старт: прогресс-док, сверка с МойСкладом, обследование кода.
|
||||||
- 09.10 — ядро выложено на стенд (9ba1edb): разрешение минуса + детекция/авто-закрытие +
|
- 09.10 — ядро выложено на стенд (9ba1edb): разрешение минуса + детекция/авто-закрытие +
|
||||||
API (список/счётчик/recount/фильтр) + веб-баннер/пометки + integration 2/2.
|
API (список/счётчик/recount/фильтр) + веб-баннер/пометки + integration 2/2.
|
||||||
|
- 09.10 — пасс 2 (29a951a, 09ee056): центр уведомлений (notifications/Phase41) + REST +
|
||||||
|
джоб агрегации/эскалации + Telegram + настройки орг. + «Просроченные минусы» + unit 3/3 +
|
||||||
|
integration 3/3. Стенд: `/api/notifications` 200, settings-поля применились, джоб (cron */5)
|
||||||
|
создаёт агрегат «23 товара ушли в минус» админу — проверено скринами. Фикс: popover вправо.
|
||||||
- 09.10 — задел push (3547e82): `device_tokens` (Phase40) + `IPushSender`/`StubPushSender` +
|
- 09.10 — задел push (3547e82): `device_tokens` (Phase40) + `IPushSender`/`StubPushSender` +
|
||||||
`/api/me/device-tokens`, описано в `docs/mobile-api.md`. Выложено на стенд, Phase39+Phase40
|
`/api/me/device-tokens`, описано в `docs/mobile-api.md`. Выложено на стенд, Phase39+Phase40
|
||||||
применились (`/api/negative-stock/count` → 200). Скрины баннера + фильтра минусов сняты.
|
применились (`/api/negative-stock/count` → 200). Скрины баннера + фильтра минусов сняты.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue