POS — Прикрепление клиента к заказу

Источники

Об этом документе

POS-интерфейс (erp-pos) находится в bootstrap-стадии. Этот документ описывает только flow прикрепления клиента к заказу — другие POS-экраны (сам экран заказа, оплата, смена) ещё не специфицированы. Когда появятся — связать через ссылки.


Flow в одну картинку

┌─── Экран заказа ────────────────────────────┐
│ [🧑 Клиент]  ← кнопка если клиент не прикреплён
│ [🧑 Иван Петров · VIP · 🎂 скоро]  ← если прикреплён
│ ...                                          │
│ Позиции заказа                               │
└──────────────────────────────────────────────┘
        │ клик «Клиент»
        ▼
┌─── Модалка «Найти клиента» ─────────────────┐
│ Телефон: [+7 (___) ___-__-__]                │
│                                              │
│ [результаты поиска появляются по мере ввода] │
└──────────────────────────────────────────────┘
        │ телефон введён (10 цифр)
        ▼
    GET /pos/customers/search?phone=+7...
        │
    ┌───┴───┐
    │       │
  найден  не найден
    │       │
    ▼       ▼
┌─ найден ─────────────────────────────────────┐
│ Иван Петров                                  │
│ +7 (999) 123-45-67                           │
│ Chips: VIP · Клуб ценителей                  │
│ LTV: 34 500 ₽ · Последний визит 3 дня назад │
│ 🎂 ДР через 5 дней                          │
│ [Прикрепить к заказу]                        │
└──────────────────────────────────────────────┘

┌─ не найден — форма quick-create ────────────┐
│ Клиент не найден. Создать нового?            │
│                                              │
│ Телефон: +7 (999) 123-45-67 (уже введён)     │
│ Имя:     [____________________]              │
│ Email (опц): [____________________]          │
│ ДР (опц):    [__.__.____]                    │
│                                              │
│ [Создать и прикрепить]                       │
└──────────────────────────────────────────────┘
        │ после прикрепления
        ▼
┌─── Экран заказа ────────────────────────────┐
│ [🧑 Иван Петров · VIP]  ← клиент прикреплён │
│ ...                                          │
└──────────────────────────────────────────────┘

1. Кнопка «Клиент» на экране заказа

  • Располагается в верхней части экрана заказа (деталь расположения в рамках BR по POS)
  • Если клиент не прикреплён: иконка 🧑 + текст «Клиент»
  • Если клиент прикреплён: иконка 🧑 + имя клиента + chips групп + бейдж «🎂 скоро» (если ДР в ±14 дней)
  • Клик — открывает модалку «Найти клиента» (всегда, независимо от того прикреплён сейчас кто-то или нет)
  • Рядом — крестик «×» для открепления (виден только если прикреплён)

Видимость кнопки: permission customers.create_quick. Без permission кнопка не отображается.


2. Модалка «Найти клиента»

Поле ввода телефона

  • Маска: +7 (___) ___-__-__
  • Автозаполнение первой цифры +7 (или +7, если пользователь вводит 8XXX)
  • Нормализация в E.164 на уровне запроса: 89991234567, 79991234567, +7 (999) 123-45-67+79991234567

Поведение

  • Пока введено < 10 цифр номера — состояние default (никаких запросов)
  • Как только введено 10 цифр (полный номер) — запускается GET /pos/customers/search?phone=+7...
    • Debounce не требуется — запрос один раз после полного ввода
  • Пока запрос идёт — состояние searching (спиннер в области результатов)

Состояние found (клиент найден)

Показывается карточка-мини:

  • Имя (крупно)
  • Телефон
  • Chips групп (максимум 4 видимых)
  • LTV: {total_spent} ₽
  • Последний визит: N дней назад / «Впервые»
  • Бейдж «🎂 ДР через N дней» (если ДР в ±14 дней от сегодня)
  • Кнопка «Прикрепить к заказу» (большая, основной акцент) → PATCH /pos/orders/{id}/customer

Состояние not_found (клиент не найден)

Показывается сообщение + форма quick-create:

  • Заголовок: «Клиент не найден. Создать нового?»
  • Поля формы:
    • Телефон — readonly (уже введён выше), в виде серого chip «Телефон: +7 (999) …»
    • Имя — text input, обязательное, placeholder «Например: Иван»
    • Email — text input, опциональное
    • ДР — date picker, опциональное
  • Кнопка «Создать и прикрепить» → POST /pos/customers (с registration_source='pos', registered_by_employee_id из JWT кассира), затем PATCH /pos/orders/{id}/customer
    • Pre-flight валидация: если имя пустое — кнопка disabled

Состояние creating

  • Кнопка «Создать и прикрепить» в loading
  • Форма disabled

Состояние attached (успех)

  • Toast «Клиент прикреплён: Иван Петров»
  • Модалка закрывается автоматически
  • Экран заказа перерисовывается с клиентом в шапке

3. Открепление клиента

  • Иконка «×» рядом с именем клиента в шапке заказа
  • Клик → DELETE /pos/orders/{id}/customer
  • Без подтверждающей модалки (операция обратимая — можно тут же прикрепить снова)
  • Toast «Клиент откреплён»

Ошибки

API кодОтображение
VALIDATION_ERROR (невалидный phone)Inline под полем: «Неверный формат телефона»
CUSTOMER_PHONE_TAKEN (при quick-create)Редкий кейс: клиент создан параллельно в админке. Автоматически retry GET /customers/search, клиент найден → показываем ему. Без явной ошибки пользователю
CUSTOMER_NOT_FOUND (при PATCH attach)Toast «Клиент не найден. Обновите поиск»
ORDER_FINALIZEDToast «Заказ закрыт, клиента изменить нельзя» + модалка закрывается
FORBIDDENToast «Нет прав» (не должно происходить — кнопка скрыта без permission)
Сеть / 5xxToast «Не удалось. Проверьте соединение» + возможность повторить

Подсказки после quick-create

После успешного создания через quick-form — на короткое время (3 сек) тост / снек-бар:

«Клиент Иван Петров создан. Убедитесь, что собрано согласие на обработку ПД (уточните в админке).»

Это compliance-напоминание — отметку о согласии ФЗ-152 ставит потом админ в админке.


Ролевой доступ

РольДоступ
Кассир с customers.create_quickПолный flow — поиск, quick-create, прикрепление, открепление
Кассир без этого permissionКнопка «Клиент» скрыта на экране заказа. Заказ остаётся анонимным

Связь с сервером

Действие UIAPI endpoint
Поиск по телефонуGET /api/v1/pos/customers/search?phone=...
Quick-createPOST /api/v1/pos/customers (тело: phone, first_name, optional email/birthday)
ПрикрепитьPATCH /api/v1/pos/orders/{order_id}/customer body: { customer_id }
ОткрепитьDELETE /api/v1/pos/orders/{order_id}/customer

Все запросы идут через POS BFF (:3022), он проксирует в Customer Service (:3013) и Order Service (:3005). Authorization — JWT кассира.


Состояния экрана заказа (после прикрепления)

Когда клиент прикреплён — шапка экрана заказа показывает:

  • Имя клиента
  • Chips групп (max 3 видимых)
  • Иконка «🎂» если ДР в ±14 дней
  • Иконка «×» для открепления

Что кассир ПОКА НЕ видит (BR 3.2+):

  • Баланс баллов клиента
  • Применимые скидки
  • Купоны / промокоды

Это намеренное ограничение BR 3.1 — только привязка клиента, без финансовой логики.


Ссылки