- docs/imports.md, docs/simple-mode.md: «Food Market» → «ALL MARKET». - Перегенерированы скриншоты кассы (вход/продажа/оплата/смена/возврат) с логотипом ALL MARKET в шапке и на экране входа. - Внутренние TZ/verify-доки оставлены как историческая фиксация. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
8.6 KiB
Markdown
155 lines
8.6 KiB
Markdown
# Simple / Advanced mode
|
||
|
||
Sprint 29, 2026-10-04.
|
||
|
||
ALL MARKET по умолчанию работает в «простом» режиме — виден только
|
||
минимальный набор функций розничного учёта, достаточный для
|
||
обычного магазина у дома. «Расширенный» режим включает дополнительные
|
||
модули (оптовые отгрузки, перемещения, лояльность и т.д.) и доступен
|
||
только после явного включения оператором платформы (SuperAdmin).
|
||
|
||
Переключатель в настройках магазина отсутствует намеренно —
|
||
администратор организации не может ни включить, ни выключить
|
||
расширенный режим себе сам.
|
||
|
||
## Простой режим — что видно
|
||
|
||
| Раздел | Что внутри |
|
||
|---|---|
|
||
| **Главная** | KPI «Выручка сегодня / неделя / месяц», средний чек, график продаж, «Последние продажи», «Низкий остаток». |
|
||
| **Товары** | Карточка товара без артикула (генерируется автоматически), описания, нескольких типов цен, страны происхождения, НДС-галок, «Услуги», «Маркируемый», максимального остатка. Остаются: Название, Штрихкоды, Группа, Единица измерения, Цена продажи (одна), Себестоимость (только чтение), Минимальный остаток, Фасовка (шт/вес/разливной), Фото. |
|
||
| **Группы товаров** | Полный доступ. |
|
||
| **Единицы измерения** | Полный доступ. |
|
||
| **Контрагенты** | Единый список. Поля: Название, Тип лица, БИН/ИИН, Адрес, Телефон, Заметки. |
|
||
| **Склад** | Приёмка, Оприходование, Списание, Инвентаризация, Возврат поставщику, Остатки. |
|
||
| **Продажи** | Продажи (POS), Возвраты покупателей. |
|
||
| **Отчёты** | Продажи, Прибыль, Остатки на дату. |
|
||
| **Настройки** | Магазин, Склады, Кассы (RetailPoints), Сотрудники, Роли, ОФД, Импорт товаров (CSV / 1С / МойСклад). |
|
||
| **Профиль** | Смена пароля, 2FA. |
|
||
|
||
Весь интерфейс по-русски (переключателя языка нет).
|
||
|
||
## Расширенный режим — что добавляется
|
||
|
||
- Оптовые отгрузки (`Demand`)
|
||
- Перемещения между точками (`Transfer`)
|
||
- Лояльность — программы и карты
|
||
- Промокоды / акции
|
||
- ABC-анализ
|
||
- Аудит организации (`/audit-log`)
|
||
- Несколько типов цен (не только retail)
|
||
- Bulk-операции в таблицах + сохранённые пресеты фильтров
|
||
- Telegram-бот владельца (ежедневная сводка)
|
||
- SSO (вход через Google / Microsoft)
|
||
- Выбор языка
|
||
- Виджеты «Топ товаров» и «Маржа» на дашборде
|
||
|
||
На карточках/документах появляются дополнительные поля из `Organization`
|
||
(«Описание», «Эталонная цена», «Валюта закупки», «НДС», «Услуга»,
|
||
«Маркируемый», «Макс. остаток»), которые управляются существующими
|
||
org-level `Show*` флагами.
|
||
|
||
## Как SuperAdmin включает / выключает
|
||
|
||
### Через API
|
||
|
||
```http
|
||
PUT /api/super-admin/organizations/{id}/advanced-mode
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
|
||
{ "enabled": true }
|
||
```
|
||
|
||
Ответ `200 OK`:
|
||
|
||
```json
|
||
{ "id": "...", "isAdvancedMode": true, "changed": true }
|
||
```
|
||
|
||
Повторный вызов с тем же состоянием → `changed: false` и запись в
|
||
аудит **не** делается (идемпотентность).
|
||
|
||
Переключение архивной организации → `409 Conflict`.
|
||
Эндпоинт разрешён только роли `SuperAdmin`; админ орги получает `403`.
|
||
|
||
### Через SuperAdmin UI
|
||
|
||
1. Открыть `/super-admin/organizations`.
|
||
2. В списке у каждой строки колонка **«Режим»** — бейдж «Простой»
|
||
(slate) или «Расширенный» (violet).
|
||
3. В экшн-колонке рядом с «Архивировать» — иконка-искра. Клик →
|
||
модалка подтверждения с описанием последствий.
|
||
4. Подтверждение → `PUT` на эндпоинт выше. После успеха список
|
||
автоматически перегружается.
|
||
|
||
### Запись в журнал
|
||
|
||
Каждое реальное переключение пишется в `super_admin_audit_log`:
|
||
|
||
```json
|
||
{
|
||
"actionType": "EnableAdvancedMode", // или DisableAdvancedMode
|
||
"organizationId": "...",
|
||
"description": "Включен расширенный режим «Org Name»",
|
||
"changesJson": { "before": false, "after": true },
|
||
"ipAddress": "...",
|
||
"superAdminUserId": "..."
|
||
}
|
||
```
|
||
|
||
Просмотр: `/super-admin/audit-log?organizationId=...` или UI
|
||
«SuperAdmin → Журнал».
|
||
|
||
## Что происходит с данными при выключении
|
||
|
||
**Ничего не удаляется.** Серверный фильтр
|
||
`[RequiresAdvancedMode]` возвращает `403 ProblemDetails` на скрытых
|
||
эндпоинтах; данные остаются в БД. При повторном включении всё снова
|
||
видно и доступно без миграций и восстановлений.
|
||
|
||
Пример `ProblemDetails` при 403:
|
||
|
||
```json
|
||
{
|
||
"type": "https://food-market.kz/errors/advanced-mode-required",
|
||
"title": "Функция доступна в расширенном режиме.",
|
||
"detail": "Этот раздел недоступен в простом режиме магазина. Обратитесь к администратору платформы, чтобы включить расширенный режим.",
|
||
"status": 403,
|
||
"instance": "/api/sales/demands",
|
||
"code": "advanced-mode-required"
|
||
}
|
||
```
|
||
|
||
Для UI это достаточный маркер, чтобы отличить этот 403 от обычного
|
||
«нет прав» и отрисовать страницу «Недоступно в простом режиме» вместо
|
||
общего сообщения.
|
||
|
||
## Как это устроено на бэкенде (кратко)
|
||
|
||
- `Organization.IsAdvancedMode` (bool, default `false`) — миграция
|
||
`20261004100000_Phase29a_IsAdvancedMode`.
|
||
- `[RequiresAdvancedMode]` — атрибут MVC
|
||
(`TypeFilterAttribute` → `AdvancedModeFilter : IAsyncAuthorizationFilter`),
|
||
читает флаг по `org_id`-claim через `AppDbContext`.
|
||
SuperAdmin bypass; аноним пропускается (отработает `[Authorize]`).
|
||
Кэш in-memory 30 с, инвалидация после переключения.
|
||
- Список скрытых контроллеров / действий —
|
||
см. `docs/simple-mode-progress.md` (шаг 3).
|
||
- `/api/me` расширен полем `advancedMode: boolean`.
|
||
|
||
Веб:
|
||
- `useMe()` отдаёт `advancedMode`.
|
||
- `AppLayout.buildNav(roles, advancedMode)` фильтрует меню.
|
||
- `AdvancedModeGuard` оборачивает скрытые маршруты и показывает
|
||
«Недоступно в простом режиме» вместо белого экрана / 403 в консоли.
|
||
- Карточка товара, контрагенты и приёмка в простом режиме прячут
|
||
advanced-поля; значения существующих записей сохраняются и при
|
||
переключении режима снова видны.
|
||
|
||
## Тесты
|
||
|
||
- Unit: `tests/food-market.UnitTests/AdvancedModeFilterTests.cs` (6/6).
|
||
- Integration: `tests/food-market.IntegrationTests/AdvancedModeTests.cs` (7/7).
|
||
- E2E: `tests/e2e/scenarios/stage-simple-mode.yml` (7/7 на стейдже).
|