Торговые точки — Карточка
Три режима одного экрана: просмотр, создание, редактирование.
Просмотр (/stores/:id)
API: GET /api/v1/stores/{id}
Что видит пользователь
Заголовок — название ТТ. Рядом — бейдж статуса (Черновик — серый, Опубликована — зелёный, Приостановлена — красный).
Кнопки в шапке (видимость по роли):
| Кнопка | Видимость | Что происходит |
|---|---|---|
| ”Редактировать” | Franchise — всегда; Franchisee — свои; Manager — своя | Переход в /stores/{id}/edit |
| ”Опубликовать” | Franchise + status=draft | POST /api/v1/stores/{id}/publish, toast “Торговая точка опубликована" |
| "Снять с публикации” | Franchise + status=published | POST /api/v1/stores/{id}/unpublish, toast “Торговая точка снята с публикации" |
| "Удалить” | Franchise | Модалка (см. логику удаления) |
Блоки данных
Основные данные:
| Поле | Значение |
|---|---|
| Название | name |
| Адрес | address |
| Широта | latitude |
| Долгота | longitude |
| Город | city или ”—“ |
| Телефон | phone или ”—“ |
email или ”—“ | |
| ЮЛ | legal_entity.name — кликабельная ссылка на /legal-entities/{legal_entity_id} |
График работы:
Таблица на 7 дней:
| День | Открытие | Закрытие | Выходной |
|---|---|---|---|
| Понедельник | schedule[0].open_time | schedule[0].close_time | ”Выходной” если is_closed=true |
| Вторник | schedule[1].open_time | schedule[1].close_time | |
| Среда | schedule[2].open_time | schedule[2].close_time | |
| Четверг | schedule[3].open_time | schedule[3].close_time | |
| Пятница | schedule[4].open_time | schedule[4].close_time | |
| Суббота | schedule[5].open_time | schedule[5].close_time | |
| Воскресенье | schedule[6].open_time | schedule[6].close_time |
Если is_closed=true — вместо времени отображается текст “Выходной” серым цветом.
Дополнительные секции на карточке просмотра
На странице /stores/:id в режиме просмотра под блоком графика работы отображаются три секции (каждая — отдельный компонент):
1. POS-терминалы (TerminalsSection) — файл pages/stores/TerminalsSection.tsx
- Список POS-терминалов привязанных к ТТ
- Колонки: название (label), заводской номер ФН (
fs_number), РН ККТ (rn_kkt), статус (active/inactive), последняя активность (last_seen_at) - Действия: добавить терминал, редактировать (label/rn_kkt/status), удалить
- Role-guard: Franchise — CRUD; Franchisee/Manager — только view
2. Столы зала (TablesSection) — файл pages/stores/TablesSection.tsx
- Canvas-визуализация столов с drag&drop (позиции
position_x/position_y) - Статусы: free / occupied / reserved (цветовая индикация)
- При выделении стола — редактор номера, метки, вместимости, управление бронью (occupy/release/reserve/cancel-reservation)
- Если стол занят — отображается номер текущего заказа (
current_order_idс ссылкой) - Role-guard: Franchise — CRUD; Manager/Cashier своей ТТ — управление статусами
3. Интеграции (IntegrationsSection) — файл pages/stores/IntegrationsSection.tsx
- Список подключений к агрегаторам (Я.Еда, Маркет Деливери)
- Колонки: провайдер, external_restaurant_id, статус подключения, последняя синхронизация меню
- Действия: синхронизировать меню, открыть лог операций, редактировать credentials, отключить
- Role-guard: Franchise — управление; остальные — read-only
4. Терминал PayKeeper (PaykeeperTerminalSection) — файл pages/stores/PaykeeperTerminalSection.tsx (BR 3.3)
- Видна только если у ЮЛ этой ТТ активна PK-интеграция
- В состоянии «не привязан»: empty state + кнопка «Привязать терминал» (при
integrations.manage) - В состоянии «привязан»: блок с полями
pk_terminal_id,pk_mpos_merchant_id,label, статус - Действия: редактировать (модалка с полями), отвязать (с подтверждением; блокируется если есть открытые инвойсы)
- Role-guard:
integrations.manage— CRUD;integrations.read(менеджер ТТ) — только просмотр - Детали — отдельная спека
Создание (/stores/new)
API: POST /api/v1/stores
Доступ: только Franchise
Что видит пользователь
Заголовок: “Новая торговая точка”. Форма с полями.
Форма
Основные данные:
| Поле | Тип ввода | Обязательно | Маска/Валидация |
|---|---|---|---|
| Название | Text input | Да | max 255 |
| Адрес | Text input | Да | max 500 |
| Широта | Number input | Да | -90..90, до 6 знаков после точки |
| Долгота | Number input | Да | -180..180, до 6 знаков после точки |
| Город | Text input | Нет | max 255 |
| Телефон | Text input | Нет | +7 (XXX) XXX-XX-XX |
| Text input | Нет | ||
| ЮЛ | Select | Да | Список активных ЮЛ (GET /api/v1/legal-entities?status=active) |
| Часовой пояс | Select | Нет | Default “Europe/Moscow”. Список часовых поясов РФ |
(Доработано в BUG-008)
Поле "ЮЛ" нельзя изменить после создания
При создании — обычный select. После создания — disabled с tooltip “ЮЛ нельзя изменить после создания”.
График работы:
(Доработано в BUG-008)
7 строк (Понедельник — Воскресенье), в каждой:
| Элемент | Тип | Описание |
|---|---|---|
| День недели | Label | Пн / Вт / Ср / Чт / Пт / Сб / Вс |
| Время открытия | Select (часы 00-23) + Select (минуты 00/15/30/45) | Формат 24h, disabled если “Выходной” |
| Время закрытия | Select (часы 00-23) + Select (минуты 00/15/30/45) | Формат 24h, disabled если “Выходной” |
| Выходной | Checkbox | При включении — очищает и блокирует поля времени |
Быстрая настройка:
- Checkbox “Одинаковый для всех дней” — при включении время и состояние “Выходной” из первой строки (Понедельник) копируется во все остальные дни
- При изменении значений в первой строке — синхронизация в реальном времени
- При отключении — каждый день становится независимым (значения сохраняются)
Валидация
- На клиенте: обязательные поля, формат координат, email, телефон
- С сервера: дубликат названия в рамках ЮЛ (
NAME_DUPLICATE), невалидное ЮЛ (LEGAL_ENTITY_NOT_FOUND) - Ошибки сервера показываются под соответствующим полем
Кнопки
- “Сохранить” — отправить форму
- “Отмена” — возврат на список (с подтверждением если есть несохранённые данные)
После успеха
- Redirect на
/stores/{new_id}(карточка просмотра) - Toast: “Торговая точка создана”
Редактирование (/stores/:id/edit)
API: PATCH /api/v1/stores/{id}
Доступ: Franchise — все поля (кроме ЮЛ); Franchisee — свои ТТ; Manager — своя ТТ
Что видит пользователь
Та же форма что при создании, но:
- Поле ЮЛ —
disabled, серый фон, tooltip “ЮЛ нельзя изменить после создания” - Предзаполнены текущие значения
Franchise — полный доступ
Все поля редактируемы, кроме ЮЛ.
Franchisee / Manager — ограничения
Редактируемые поля:
- Название
- Адрес
- Широта, Долгота
- Город
- Телефон
- График работы
Нельзя менять: ЮЛ (disabled для всех ролей).
Кнопки
- “Сохранить” — отправить PATCH
- “Отмена” — возврат на карточку просмотра
После успеха
- Redirect на
/stores/{id}(карточка просмотра) - Toast: “Изменения сохранены”
Состояния
| Состояние | Что показываем |
|---|---|
| Загрузка (просмотр/edit) | Skeleton-блоки вместо полей |
| ТТ не найдена (404) | “Торговая точка не найдена” + кнопка “Вернуться к списку” |
| Ошибка сохранения | Ошибки под полями (validation) или toast (серверная ошибка) |
| Несохранённые изменения | При уходе со страницы: “У вас есть несохранённые изменения. Покинуть страницу?” |
Переходы
| Откуда | Куда | Триггер |
|---|---|---|
| Карточка | Список | Кнопка “Назад” / breadcrumb |
| Карточка | Редактирование | Кнопка “Редактировать” |
| Создание | Карточка | После успешного сохранения |
| Создание | Список | Кнопка “Отмена” |
| Редактирование | Карточка | После сохранения или “Отмена” |
Прейскурант
(BR 1.10)
Поле “Прейскурант” в карточке ТТ (только Franchise).
- Тип: Select
- Опции: Все active прейскуранты + “По умолчанию (дефолтный)”
- Default: “По умолчанию” (price_list_id = null)
- API назначение:
PATCH /api/v1/stores/{id}/price-list→{ "price_list_id": "uuid" } - API снятие:
DELETE /api/v1/stores/{id}/price-list - Видимость: Только Franchise. Franchisee/Manager видят текущий прейскурант (read-only).