food-market/docs/simple-mode.md
nns 828080c321 rebrand: пользовательские доки + скриншоты кассы с новым лого
- 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>
2026-10-08 17:35:15 +05:00

8.6 KiB
Raw Blame History

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

PUT /api/super-admin/organizations/{id}/advanced-mode
Authorization: Bearer <token>
Content-Type: application/json

{ "enabled": true }

Ответ 200 OK:

{ "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:

{
  "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:

{
  "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 на стейдже).