Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
134 lines
5.1 KiB
Markdown
134 lines
5.1 KiB
Markdown
# Réservations CSV & Portail Carrier
|
|
|
|
---
|
|
|
|
## Vue d'ensemble
|
|
|
|
Le système de réservation CSV permet aux admins de créer des réservations en important des fichiers CSV et d'envoyer automatiquement un **lien magique** aux transporteurs pour qu'ils acceptent ou rejettent via un portail dédié.
|
|
|
|
---
|
|
|
|
## Workflow complet
|
|
|
|
```
|
|
1. Admin upload CSV → création csv_bookings
|
|
2. Admin assigne un carrier à la réservation
|
|
3. Système envoie email avec magic link (expiry 1h)
|
|
4. Carrier clique → authentification automatique (token URL)
|
|
5. Carrier voit les détails → Accept ou Reject
|
|
6. Activité logguée dans carrier_activities
|
|
7. Admin voit le statut mis à jour en dashboard
|
|
```
|
|
|
|
---
|
|
|
|
## API — Réservations CSV (Admin)
|
|
|
|
| Méthode | Route | Description |
|
|
|---------|-------|-------------|
|
|
| GET | /api/v1/csv-bookings | Liste des réservations CSV |
|
|
| POST | /api/v1/csv-bookings | Créer une réservation CSV |
|
|
| GET | /api/v1/csv-bookings/:id | Détail d'une réservation |
|
|
| PATCH | /api/v1/csv-bookings/:id | Mettre à jour |
|
|
| DELETE | /api/v1/csv-bookings/:id | Supprimer |
|
|
| POST | /api/v1/csv-bookings/:id/assign-carrier | Assigner un carrier |
|
|
| POST | /api/v1/csv-bookings/:id/send-magic-link | Envoyer le lien magique |
|
|
|
|
---
|
|
|
|
## API — Portail Carrier
|
|
|
|
| Méthode | Route | Description |
|
|
|---------|-------|-------------|
|
|
| GET | /api/v1/carrier/auth | Auth via magic link token |
|
|
| GET | /api/v1/carrier/booking | Voir la réservation assignée |
|
|
| POST | /api/v1/carrier/booking/accept | Accepter |
|
|
| POST | /api/v1/carrier/booking/reject | Rejeter |
|
|
| POST | /api/v1/carrier/booking/documents | Uploader des documents |
|
|
|
|
### Authentification portail carrier
|
|
|
|
Le lien magique contient un token unique :
|
|
```
|
|
https://app.xpeditis.com/carrier/auth?token=xxxxxxxx
|
|
```
|
|
|
|
Le token est stocké dans `csv_bookings.carrier_magic_link_token` (hashé).
|
|
Expiry : 1 heure. Si expiré, l'admin doit renvoyer un nouveau lien.
|
|
|
|
---
|
|
|
|
## Statuts des réservations CSV
|
|
|
|
Valeurs réelles de l'énumération `CsvBookingStatus` (`domain/entities/csv-booking.entity.ts`) :
|
|
|
|
| Statut | Description |
|
|
|--------|-------------|
|
|
| PENDING_PAYMENT | Réservation créée, commission non payée |
|
|
| PENDING_BANK_TRANSFER | Virement déclaré, en attente de validation par l'administration |
|
|
| PENDING | Commission payée, en attente de réponse du transporteur |
|
|
| ACCEPTED | Le transporteur a accepté |
|
|
| REJECTED | Le transporteur a refusé |
|
|
| CANCELLED | Annulée par l'utilisateur |
|
|
|
|
---
|
|
|
|
## Suppression d'une réservation impayée
|
|
|
|
`DELETE /api/v1/csv-bookings/:id` supprime définitivement une réservation **dont la commission n'a pas été payée**, c'est-à-dire au seul statut `PENDING_PAYMENT`. Seul le propriétaire peut le faire ; une réservation appartenant à quelqu'un d'autre répond `404`, sans se distinguer d'une réservation inexistante.
|
|
|
|
Une fois la commission payée, la réservation est partie chez le transporteur et porte une trace comptable : elle ne peut plus qu'être **annulée** (`PATCH :id/cancel`), jamais effacée. L'API répond alors `400`.
|
|
|
|
`PENDING_BANK_TRANSFER` est volontairement exclu : le virement déclaré peut être en cours d'acheminement, et supprimer la réservation priverait l'administration de ce qu'elle doit rapprocher à sa réception. Étendre la règle à ce statut est une décision comptable, pas technique — il suffit d'ajouter le statut à `DELETABLE_STATUSES` dans l'entité.
|
|
|
|
Les documents déjà téléversés restent dans le stockage objet, conformément à la politique appliquée à la suppression d'un document isolé (conservation pour l'audit). Le quota de réservations n'est pas affecté : il ne compte que les expéditions payées.
|
|
|
|
Côté interface, l'action « Supprimer » n'apparaît dans le menu d'une ligne que pour les réservations impayées, à côté de « Modifier » et « Payer ». Elle demande une confirmation qui nomme la réservation concernée.
|
|
|
|
---
|
|
|
|
## Pages frontend
|
|
|
|
| Route | Description |
|
|
|-------|-------------|
|
|
| /dashboard/bookings | Liste des réservations (voir, modifier, payer, supprimer) |
|
|
| /dashboard/csv-bookings | Liste admin des réservations CSV |
|
|
| /carrier/auth | Page d'auth carrier (via magic link) |
|
|
| /carrier/booking | Dashboard carrier (accept/reject) |
|
|
| /carrier/documents | Upload documents carrier |
|
|
|
|
---
|
|
|
|
## Import CSV (upload admin)
|
|
|
|
Le fichier CSV peut être uploadé via `POST /api/v1/admin/csv-rates/upload`.
|
|
|
|
Format des colonnes requis : voir [../csv-system/CSV_RATE_SYSTEM.md](../csv-system/CSV_RATE_SYSTEM.md).
|
|
|
|
---
|
|
|
|
## Profils carrier
|
|
|
|
Les carriers qui utilisent le portail ont un `CarrierProfile` lié à leur `Organization`.
|
|
Chaque action (accept, reject, document upload) est tracée dans `carrier_activities`.
|
|
|
|
```sql
|
|
SELECT ca.action, ca.created_at, cb.booking_number
|
|
FROM carrier_activities ca
|
|
JOIN csv_bookings cb ON cb.id = ca.csv_booking_id
|
|
WHERE ca.carrier_profile_id = 'xxx'
|
|
ORDER BY ca.created_at DESC;
|
|
```
|
|
|
|
---
|
|
|
|
## Email magic link
|
|
|
|
Template MJML dans `apps/backend/src/infrastructure/email/templates/`.
|
|
|
|
Variables disponibles :
|
|
- `bookingNumber` — numéro de réservation
|
|
- `magicLink` — URL avec token
|
|
- `expiresIn` — durée de validité ("1 heure")
|
|
- `carrierName` — nom du carrier
|