docs(stock): прогресс negative-stock — что сделано/отложено
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nns 2026-10-09 02:19:37 +05:00
parent 9ba1edb236
commit 2d1cdd8519

View file

@ -25,28 +25,45 @@
инвентаризации по минусовым товарам), с авто-закрытием при возврате остатка ≥ 0 и эскалацией.
(Источник по МойСкладу: dev.moysklad.ru/doc/api/remap/1.2 report/stock stockMode; webhooks — на сущности.)
## План / статус
0. [ ] Сверка с МойСкладом (отчёт/вебхуки/разрешение минуса).
1. [ ] Событие при проведении документа → остаток < 0: создать/обновить предупреждение
(товар, склад, остаток, документ-причина). Остаток ≥ 0 → закрыть автоматически.
2. [ ] Подсказка причины: есть непроведённая приёмка/черновик → «возможно, не проведена приёмка П-…».
3. [ ] Без спама: агрегация (1 уведомление/склад/N мин, по умолч. 15), тихие часы, получатели
Администратор + Кладовщики склада, вкл/выкл в настройках орг и профиле.
4. [ ] Действие «Пересчитать эти товары»: черновик инвентаризации по складу с этими товарами.
5. [ ] Каналы: веб (колокольчик + центр уведомлений + Web Push/VAPID), Telegram (бот),
мобильное API (GET /api/notifications, read, создать инвентаризацию), отчёт «Отрицательные».
APNs — только заглушка (device tokens + IPushSender), без платного Apple Dev.
6. [ ] Тесты unit/integration/e2e; деплой; скриншоты.
7. [ ] Снять блокировку продажи в минус: касса/POS-синк/офлайн, розница веб, опт, списание,
перемещение — проведение в минус РАЗРЕШЕНО (убрать 409 oversell; деньги — счёт в минус запрещён).
8. [ ] Касса: предупреждение в строке («на остатке 2 шт — уйдёт в минус»), жёлтый индикатор в шапке
«N товаров в минусе», офлайн по локальным остаткам.
9. [ ] Админка: постоянный баннер «N товаров в минусе — проведите инвентаризацию» + «Пересчитать»;
красная пометка в списках товаров/остатков.
10. [ ] Мобильное API/iOS: счётчик + список в /api/me или /api/notifications.
11. [ ] Эскалация: минус не закрыт за N часов (по умолч. 24) → повторное уведомление владельцу/админу;
на главной — «Просроченные минусы».
12. [~] ОТЛОЖЕНО (поправка владельца) — предварительная себестоимость НЕ делаем.
## План / статус (легенда: [x] готово · [~] частично/заложено · [ ] отложено)
0. [x] Сверка с МойСкладом (см. выше): у них — только фильтр отчёта `stockMode=negativeOnly`, push/события нет.
1. [x] Событие при проведении документа → остаток < 0: `NegativeStockAlert` (товар, склад, остаток,
документ-причина: тип/номер/автор/время), единая точка — `StockService.ApplyMovementAsync`.
Остаток ≥ 0 → закрывается автоматически. Миграция Phase39, частичный уникальный индекс.
2. [x] Подсказка причины: API списка отдаёт `hint` «возможно, не проведена приёмка П-… от <поставщик>»
(если по товару есть черновик приёмки).
3. [~] Без спама: счётчик/баннер — реалтайм-запрос (poll 60с), не спамит. Поля `NotifiedAt`/`EscalatedAt`
в сущности заложены под фоновую агрегацию/рассылку. **Отложено:** фоновая job (email/telegram
одним сообщением на склад, тихие часы, получатели по ролям/привязке, тумблеры в настройках).
4. [x] «Пересчитать эти товары»: `POST /api/negative-stock/recount` → черновик инвентаризации по складу
ровно с минусовыми товарами (BookQty = текущий отрицательный остаток), открывается из баннера.
5. [~] Каналы: **веб** — постоянный баннер + фильтр/пометка в остатках ✓; отчёт «Отрицательные» (фильтр
`negativeOnly`) ✓. **Мобильное API** — `GET /api/negative-stock` + `/count` готовы (iOS читает) ✓.
**Отложено:** центр уведомлений (realtime-событие NegativeStock), Telegram-рассылка, Web Push
(VAPID + SW), APNs-заглушка (device tokens + IPushSender).
6. [~] Тесты: integration **2/2** (минус разрешён → алерт → recount → авто-закрытие; фильтр negativeOnly) ✓;
full-chain шаг 9/13 переписаны (oversell/отмена приёмки → проведено + предупреждение) ✓.
**Отложено:** unit на агрегацию/дебаунс (нет job), e2e в браузере на баннер. Деплой/скрины — ниже.
7. [x] Снята блокировка продажи в минус: розница, касса (POS-синк), опт, списание, перемещение,
возврат поставщику, отмена приёмки. Деньги (минус счёта) — не трогали (другое правило).
8. [ ] **Отложено** (десктопная Avalonia-касса): предупреждение в строке + жёлтый индикатор в шапке +
офлайн по локальным остаткам. (Отдельная поверхность; iOS/касса — Mac-Claude.)
9. [x] Админка: постоянный баннер «N товаров в минусе — нужна инвентаризация» (сворачивается, крестиком
не закрыть) + «Пересчитать эти товары»; в остатках — красная пометка + фильтр «Только минусы».
10. [~] Мобильное API/iOS: `/api/negative-stock/count` + список готовы. Баннер в приложении — Mac-Claude.
11. [~] Эскалация: в API есть флаг `overdue` (минус висит дольше N часов, по умолч. 24). **Отложено:**
фоновое повторное уведомление владельцу/админу + блок «Просроченные минусы» на главной.
12. [ ] ОТЛОЖЕНО (поправка владельца 09.10) — предварительная себестоимость НЕ делаем.
## Что сделано в этой сессии (ядро, оттестировано и выложено)
Разрешение минуса + детекция/авто-закрытие предупреждений + API (список/счётчик/recount/фильтр) +
веб-баннер и пометки в остатках + integration-тесты 2/2 + full-chain 9/13. Деплой на стенд.
## Что отложено (заложен фундамент, нужен отдельный проход)
Фоновая рассылка (email/telegram, агрегация/тихие часы/получатели/настройки), эскалация-job и
«просроченные минусы», центр уведомлений (realtime-событие), Web Push (VAPID), APNs-заглушка
(device tokens + IPushSender), индикаторы в десктопной Avalonia-кассе (в т.ч. офлайн).
**DONE не ставим — не все пункты закрыты.**
## Что НЕ трогаем
Прод, shared-postgres, global.json, gateway nginx, sklad. Расчёт себестоимости (п.12 отложен).