# 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