RBAC : rôles et permissions
Architecture des rôles à deux niveaux, cloisonnement par point de vente, matrice de permissions complète et workflows automatiques.
Architecture des rôles
Le système utilise un RBAC à deux niveaux, plus un cloisonnement optionnel par point de vente :
| Niveau | Rôles | Description |
|---|---|---|
| Tenant (plateforme) | SUPERADMIN, EMPLOYEE, SUPPLIER | Accès global / employé / portail fournisseur |
| Etablissement | OWNER, DAF, MANAGER, SERVER, POS, COOK, CLEANER, MAITRE_HOTEL | Accès spécifique à un établissement |
| Point de vente (optionnel) | mêmes rôles EstablishmentRole | Affectation d’un membre à un PdV précis (PointOfSaleMember) |
Portail fournisseur (rôle SUPPLIER)
À la création d’un fournisseur avec email, un compte de profil SUPPLIER est auto-provisionné et lié (Supplier.userId). Ce compte est confiné à son portail (/api/supplier-portal, page /dashboard/supplier) et n’a accès qu’à ses propres données :
- ses articles (
ArticleSupplier) et leurs alertes de stock bas ; - son compte / relevé (grand-livre auxiliaire 401, solde) en lecture seule ;
- l’accusé de réception des demandes de réapprovisionnement (
StockAlert.supplierAckAt).
Un compte SUPPLIER n’a aucune appartenance d’établissement : le reste du dashboard lui est inaccessible (redirection automatique vers son portail). Il ne peut pas modifier le stock ni les écritures.
Type de tiers :
Supplier.type=GOODS(fournisseur de biens, avec articles) ouSERVICE(prestataire, sans articles) — les deux disposent d’un compte 401 + grand-livre.
Cloisonnement par point de vente (PdV)
Un établissement peut être découpé en plusieurs points de vente (Restaurant, Bar, Piscine…). L’appartenance à un PdV est portée par PointOfSaleMember (userId, pointOfSaleId, role) et chargée dans le token de session (req.user.pointOfSaleIds).
Règle de visibilité (posScopeFor, src/middlewares/rbac.middleware.ts) :
- Superviseurs —
SUPERADMIN, ouOWNER/DAF/MANAGERdans un établissement — voient tous les PdV de leur périmètre (aucune restriction). - Autres rôles —
SERVER,POS,COOK,MAITRE_HOTEL, etc. — ne voient que les articles, catégories, commandes et files cuisine des PdV auxquels ils sont affectés.
Ce cloisonnement est appliqué côté serveur (non contournable) sur /api/orders, /api/articles, /api/categories et /api/orders/kitchen/:estId. Conséquence : un employé non affecté à un PdV ne voit rien (il faut l’affecter via Points de vente → Personnel). Le sélecteur de PdV de la barre latérale est masqué pour le personnel non-superviseur (le périmètre est imposé par l’affectation).
La gestion des PdV (création, désactivation, affectation du personnel) est réservée à OWNER / DAF ; la création est plafonnée par l’abonnement (maxPointsOfSale, middleware checkPlanLimit('points_of_sale')).
Matrice des permissions par rôle
SuperAdmin
Accès complet à toutes les fonctionnalités, tous les établissements.
- Bypass de toutes les restrictions d’établissement
- Dashboard complet avec toutes les statistiques
- Gestion des utilisateurs et des établissements
- Gestion des abonnements : voir, renouveler, et activation manuelle (paiements en espèces)
Owner (Propriétaire)
Le propriétaire a les mêmes permissions que le DAF, plus la gestion des canaux de réservation, des clés API et de l’abonnement.
Permissions spécifiques (en plus du DAF)
| Module | Action | Autorisé |
|---|---|---|
| Clés API | Créer / modifier / supprimer | Oui |
| Canaux iCal | Connecter / configurer | Oui |
| Configuration FedaPay | Connecter / tester / déconnecter | Oui |
| Fournisseurs | CRUD complet | Oui |
| Abonnement | Voir / renouveler via FedaPay | Oui |
| Remises (Discount Rules) | Créer / modifier / activer (hébergements et commandes) | Oui |
| Clients | Voir liste, fiche, télécharger carte de fidélité PDF | Oui |
| Points de vente | Créer / modifier / désactiver / affecter le personnel (DAF inclus) | Oui |
| Backfill factures channel | POST /api/reservations/admin/backfill-channel-invoices | Oui |
DAF (Directeur Administratif et Financier)
Le DAF est l’administrateur de l’établissement. Il valide les actions sensibles soumises par le Manager.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Chambres | Créer / modifier / supprimer | Oui (direct) |
| Réservations | Créer / modifier tous les champs / annuler | Oui |
| Réservations | Check-in / Check-out | Oui |
| Articles | Créer / modifier / supprimer | Oui |
| Stock | Mouvements de stock (direct) | Oui |
| Commandes | Voir les commandes | Oui |
| Commandes | Changer statut cuisine (EN_COURS / PRET) | Non |
| Commandes | Marquer comme servie | Non |
| Commandes | Annuler une commande | Oui |
| Commandes | Basculer flag bon propriétaire (isVoucher) | Oui |
| Réservations | Modifier tous les champs (direct, sans approbation) | Oui |
| Dépenses | CRUD complet | Oui |
| Approbations | Voir toutes les demandes | Oui |
| Approbations | Approuver / rejeter | Oui |
| Cuisine | Voir le tableau cuisine | Oui (lecture seule) |
| Ménage | Pointage (clock-in/out) | Non |
| Clés API | Créer / modifier / supprimer | Oui |
| Abonnement | Voir / renouveler via FedaPay | Oui |
| Dashboard | Stats Manager + financières | Oui (7 graphiques) |
Dashboard DAF
Le dashboard DAF inclut les 4 graphiques Manager + 3 graphiques supplémentaires :
- Niveaux de stock (barres horizontales)
- Occupation des chambres (camembert)
- Commandes cuisine par jour (histogramme)
- Commandes par serveur (histogramme)
- Flux de paiements mensuels (histogramme)
- Mouvements de stock par type (camembert)
- Temps de traitement moyen (histogramme)
Manager
Le Manager gère l’établissement au quotidien. Certaines actions sensibles nécessitent la validation du DAF.
Permissions
| Module | Action | Autorisé | Approbation DAF |
|---|---|---|---|
| Chambres | Créer une chambre | Oui | Requise |
| Chambres | Modifier / supprimer | Oui | - |
| Réservations | Créer | Oui | - |
| Réservations | Modifier les dates, chambre, remise, invités | Oui | Requise (via approbation DAF) |
| Réservations | Check-in / Check-out | Oui | - |
| Articles | Créer (avec image et description) | Oui | - |
| Articles | Modifier / supprimer | Oui | - |
| Stock | Mouvements de stock | Oui | Requise |
| Commandes | Voir les commandes | Oui | - |
| Commandes | Changer statut cuisine (EN_COURS / PRET) | Non | - |
| Commandes | Marquer comme servie | Non | - |
| Commandes | Annuler une commande | Oui | - |
| Cuisine | Voir le tableau cuisine | Oui (lecture seule) | - |
| Ménage | Pointage (clock-in/out) | Non | - |
| Ménage | Assigner un ménage | Oui (aux CLEANERs) | - |
| Approbations | Voir ses propres demandes | Oui | - |
| Approbations | Approuver / rejeter | Non | - |
Dashboard Manager
4 graphiques :
- Niveaux de stock (barres horizontales)
- Occupation des chambres (camembert)
- Commandes cuisine par jour (histogramme)
- Commandes par serveur (histogramme)
Workflow d’approbation Manager
- Le Manager soumet une action (création chambre, mouvement stock, modification dates)
- Une demande d’approbation est créée (statut
PENDING) - Le Manager peut voir le statut de sa demande dans la page Approbations > Mes demandes
- Le DAF voit la demande dans sa page Approbations et peut approuver ou rejeter
- Si approuvée, l’action est exécutée automatiquement (chambre créée, stock mis à jour, etc.)
Serveur (Server)
Le serveur gère les commandes en salle.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Commandes | Créer une commande | Oui |
| Commandes | Marquer comme servie (SERVED) | Oui |
| Commandes | Changer statut cuisine (EN_COURS / PRET) | Non |
| Commandes | Annuler une commande | Non |
| Commandes | Basculer flag bon propriétaire (isVoucher) | Non |
| Commandes | Voir ses commandes (créées par lui OU attribuées par le POS) | Oui |
| Commandes | Saisir une commande avec date d’opération rétroactive (≤ 15 jours) | Oui |
| Commandes QR | Prendre en charge une commande non assignée (POST /orders/:id/claim) | Oui |
| Chambres | Créer / modifier | Non |
| Stock | Mouvements de stock | Non |
| Ménage | Pointage | Non |
Dashboard Serveur
- Statistiques de commandes globales
- Statistiques de commandes personnelles (mes commandes du jour — inclut les commandes saisies par le POS en son nom)
POS (Caissier)
Le POS saisit les commandes en caisse pour le compte des serveurs.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Point de vente | Accès à /dashboard/pos (web) et écran POS (mobile) | Oui |
| Commandes | Créer une commande | Oui |
| Commandes | Attribuer une commande à un serveur (sélecteur Serveur attribué) | Oui |
| Commandes | Saisir avec date d’opération rétroactive (≤ 15 jours) | Oui |
| Commandes | Saisir en mode hors ligne (file IndexedDB/Room DB) | Oui |
| Commandes | Basculer flag bon propriétaire (isVoucher) | Non |
| Paiements | Encaisser une commande (espèces, carte, mobile money) | Oui |
| Factures | Voir les factures générées | Oui |
| Commandes | Changer statut cuisine / annuler | Non |
| Chambres / Réservations / Stock | Accès | Non |
Attribution — Lorsque le POS coche un serveur dans « Serveur attribué », la commande est enregistrée avec
createdById = POS(audit de qui a tapé) etserverId = serveur choisi(pour le reporting). Le serveur verra la commande dans sa liste et dans ses stats ; les rapports l’attribuent au serveur, pas au POS.
Cuisinier (Cook)
Le cuisinier gère la préparation des commandes en cuisine.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Cuisine | Voir les commandes | Oui |
| Cuisine | Passer en EN_COURS (IN_PROGRESS) | Oui |
| Cuisine | Passer en PRET (READY) | Oui |
| Cuisine | Marquer comme servie | Non |
| Cuisine | Annuler | Non |
| Chambres | Accès | Non |
| Stock | Accès | Non |
| Ménage | Accès | Non |
Dashboard Cuisinier
- Statistiques cuisine uniquement (commandes en attente, en cours, prêtes)
Maître d’Hôtel (MAITRE_HOTEL)
Le maître d’hôtel supervise la salle et gère les commandes QR clients.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Commandes | Voir toutes les commandes de l’établissement | Oui |
| Commandes | Créer une commande | Oui |
| Commandes | Marquer comme servie (SERVED) | Oui |
| Commandes | Annuler une commande | Non |
| Commandes | Basculer flag bon propriétaire (isVoucher) | Non |
| Commandes QR | Prendre en charge une commande non assignée (POST /orders/:id/claim) | Oui |
| Tables | Voir le plan de salle | Oui |
| Tables | Assigner un serveur à une table (PATCH /restaurant-tables/:id/assign-server) | Oui |
| Tables | Configurer le mode d’attribution QR (PATCH /restaurant-tables/settings/qr-mode) | Oui |
| Tables | Voir les QR codes des tables | Oui |
| Date rétroactive | Saisir avec operationDate (≤ 15 jours) | Oui |
| Chambres / Réservations / Stock | Accès | Non |
Dashboard Maître d’Hôtel
- Statistiques de commandes globales
- Vue du plan de salle avec statut des tables
Ménage (Cleaner)
Le personnel de ménage gère le nettoyage des chambres.
Permissions
| Module | Action | Autorisé |
|---|---|---|
| Ménage | Pointage clock-in | Oui |
| Ménage | Pointage clock-out | Oui |
| Ménage | Voir les sessions de ménage | Oui |
| Chambres | Créer / modifier | Non |
| Commandes | Accès | Non |
| Stock | Accès | Non |
Dashboard Ménage
- Section ménage uniquement
- Résumé de l’état des chambres (disponibles, en nettoyage, occupées)
Workflows automatiques
Création de commande → Facture automatique
Lorsqu’une commande est créée, une facture est automatiquement générée :
- Numéro de facture :
FAC-YYYYMMDD-NNNN - Statut :
ISSUED - Montant : total de la commande
- La facture est liée à la commande
- Si
operationDateest fournie : la facture utiliseissueDate = operationDate(backdate), sinon la date courante
Attribution POS → Serveur
Lorsqu’une commande est créée depuis le module Point de vente (POS) avec un serverId :
createdById= ID du compte POS (audit : qui a saisi en caisse)serverId= ID du serveur attribué (revenue credit)- Filtre
forUserId=X: retourne les commandes oùcreatedById = XOUserverId = X— le serveur voit toutes les commandes qui le concernent - Agrégations de rapports :
attributed = server || createdBy— priorité au serveur attribué, fallback sur le créateur
Date d’opération (backdate)
Le paramètre operationDate permet d’enregistrer aujourd’hui une opération datée d’hier :
- Validation côté backend via
validateOperationDate(date, roleCtx) - Rôles opérationnels (SERVER, POS, MAITRE_HOTEL) : rejetés au-delà de 15 jours dans le passé
- Rôles superviseurs (OWNER, DAF, MANAGER, SUPERADMIN) : aucune limite
- Propagé sur
Invoice.issueDate,Payment.paidAt,Order.occurredAtselon le contexte
Création de réservation → Facture + QR code
Lorsqu’une réservation est créée (web, mobile ou WordPress) :
- Facture auto-générée :
FAC-YYYYMMDD-NNNN, statutISSUED - Le moyen de paiement est stocké sur la facture
- QR code de paiement disponible immédiatement
- Moyen de paiement : Espèces, Mobile Money, Flooz, Yas, FedaPay, Carte, Virement
- Si FedaPay : bouton + lien cliquable vers la gateway de paiement
- Reçu PDF téléchargeable (format ticket 80mm)
Réservation WordPress + FedaPay
Lorsqu’un client réserve depuis un site WordPress :
- Paiement FedaPay (Mobile Money, carte)
- Plugin WordPress envoie la réservation via
POST /api/external-bookings(instantané) - Réservation créée + facture auto-générée + paiement enregistré (montant partiel supporté : acompte 60%)
- Webhook FedaPay confirme le paiement (double sécurité)
- Notification vers WordPress via webhook de paiement (si configuré)
Checkout → Nettoyage automatique
Lorsqu’un check-out est effectué sur une réservation :
- La réservation passe en statut
CHECKED_OUT - La chambre passe automatiquement en statut
CLEANING - Un cleaner peut ensuite faire un clock-in sur cette chambre
- Au clock-out, la chambre repasse en statut
AVAILABLE
Workflows automatiques (suite)
Décrémentation automatique du stock à la vente
Lorsqu’un article a trackStock = true et qu’une commande est créée ou qu’un article est ajouté :
- Le stock est décrémenté atomiquement (transaction Serializable)
- Un mouvement
SALEest enregistré dansstock_movementsavec leorderId - Si
currentStock <= 0: la vente est bloquée avec une erreur 409 (web + Android) - En cas d’annulation de la commande : le stock est restauré (mouvement
RETURN)
Synchronisation channel manager → Facture automatique
Lorsqu’une réservation est importée via iCal ou /api/external-bookings :
- La réservation est créée via
reservationService.create()(pas d’insertion directe) - Une facture
FAC-YYYYMMDD-NNNNest générée automatiquement (statutPAID) - Un paiement est enregistré (
FEDAPAYpour les réservations en ligne,OTHERpour iCal) - Une fiche client est créée ou mise à jour si l’email est disponible
- Ces revenus sont inclus dans les rapports quotidiens et le tableau de bord
Flag bon propriétaire (isVoucher)
PATCH /api/orders/:id/voucher (OWNER, DAF, MANAGER) :
- Bascule
isVouchersur l’Order - Met à jour la note sur la facture associée
- Crée un
ApprovalRequestde typeVOUCHER_FLAGpour traçabilité DAF
Commande QR client → Attribution serveur
Un client scanne le QR code sur sa table et passe une commande sans compte Teranga :
- Le client ouvre
https://<domaine>/menu/<token>(page publique, sans authentification) - Il consulte la carte, ajoute des articles au panier, saisit son nom (optionnel) et valide
- Le backend reçoit
POST /api/public/orderset résout leserverIdselon le mode configuré :
| Mode | qrServerMode | Comportement |
|---|---|---|
| Désactivé | DISABLED | La page /menu/[token] affiche “Commande en ligne indisponible”. Aucun endpoint public n’accepte de commande. |
| Pré-assigné | PRE_ASSIGNED | Le serveur est celui enregistré sur RestaurantTable.currentServerId. Si aucun n’est défini, serverId = null. |
| Premier répondant | FIRST_RESPONDER | serverId = null ; notification envoyée à tous les SERVER et MAITRE_HOTEL — le premier qui clique “Prendre en charge” prend la commande. |
| Charge auto | AUTO_LOAD | Le serveur ayant le moins de commandes actives dans l’établissement est désigné automatiquement. |
| Manuel | MANUAL | serverId = null ; notification envoyée aux superviseurs (MANAGER, MAITRE_HOTEL) pour assignation manuelle. |
createdById= ID du compte OWNER de l’établissement (audit, requis par le modèle)- Une notification est envoyée aux rôles concernés selon le mode
- En mode
FIRST_RESPONDERouMANUAL: un SERVER ou MAITRE_HOTEL peut appelerPOST /api/orders/:id/claimpour prendre en charge la commande (idempotent — 409 si déjà assignée) - La commande apparaît dans le tableau cuisine et dans la liste des commandes des serveurs
Coexistence — les commandes QR et les commandes saisies par le staff (POS, SERVER, MAITRE_HOTEL) fonctionnent en parallèle dans tous les modes actifs. Le mode
DISABLEDn’affecte que les endpoints/api/public/*.
Configuration du mode QR
- Web : page Gestion des tables → bouton Mode attribution (rôle MAITRE_HOTEL, MANAGER, OWNER, DAF)
- Android : écran Commandes → icône QR dans la barre d’en-tête (rôle MAITRE_HOTEL+)
- Endpoint :
PATCH /api/restaurant-tables/settings/qr-mode{ mode: "PRE_ASSIGNED" | "FIRST_RESPONDER" | "AUTO_LOAD" | "MANUAL" | "DISABLED" }
Mode hors ligne — file de synchronisation
Le POS web utilise IndexedDB (Dexie) pour mettre en file d’attente les opérations hors ligne :
- Chaque opération est stockée avec un UUID idempotent avant envoi
- Le drain FIFO commence automatiquement à la reconnexion
- En cas d’erreur 4xx (client) : l’opération est marquée
FAILED(pas de retry infini) - En cas d’erreur 5xx (serveur) : backoff exponentiel avec max 5 tentatives
Module RH (hr)
Le module Ressources Humaines (backend/src/routes/resource.routes.ts, gates requireModuleWrite('hr')) est utilisable par tous les rôles tenant authentifiés, avec ces nuances :
| Action | Rôles autorisés |
|---|---|
| Voir la badgeuse + pointer | Tout employé authentifié (OWNER, DAF, MANAGER, MAITRE_HOTEL, SERVER, POS, COOK, CLEANER) |
| Créer/voir ses propres demandes de congé | Tout employé |
| Annuler sa propre demande PENDING | Employé concerné |
| Voir les fiches employé, planning, feuille de temps | OWNER, DAF, MANAGER |
| Créer/modifier/supprimer une fiche employé | OWNER, DAF |
| Approuver/refuser un congé · Annuler un congé APPROVED · Ajuster un solde | OWNER, DAF |
| Créer/modifier/supprimer un shift planning | OWNER, DAF, MANAGER |
| Générer une période de paie · Verrouiller/rouvrir/marquer payée | OWNER, DAF |
| Exporter le CSV SYSCOHADA · Télécharger un bulletin PDF | OWNER, DAF, MANAGER |
Désactivation du module pour un utilisateur : le SUPERADMIN peut ajouter hr à User.disabledModules pour révoquer les écritures (lecture conservée). Mécanisme générique partagé avec tous les autres modules.
Endpoints /me/* (/api/leaves/me, /api/time-entries/me/*, /api/shifts/me) : restreints à l’utilisateur authentifié, accessibles à tout employé sans condition de rôle d’établissement.
Onboarding (/api/onboarding)
Routes laissées toujours accessibles à un utilisateur authentifié, même s’il n’a pas terminé son onboarding :
/api/auth/*,/api/onboarding/*,/api/registration/*,/api/public/*,/api/health.
Pour toutes les autres routes, le middleware requireOnboardingCompleted (backend/src/middlewares/onboarding.middleware.ts) renvoie 403 avec un code :
ONBOARDING_REQUIRED— l’onboarding tenant (Phase A) n’est pas complet.PASSWORD_CHANGE_REQUIRED—User.mustChangePassword === true.TERMS_NOT_ACCEPTED—User.termsAcceptedAt IS NULL.RGPD_NOT_ACCEPTED—User.rgpdAcceptedAt IS NULL.
Le SUPERADMIN bypass intégralement ces vérifications.
L’intercepteur axios frontend (frontend/src/lib/api.ts) lit le code et redirige vers la page correspondante (/onboarding, /user-onboarding/password, /user-onboarding/terms, /user-onboarding/rgpd).
Types d’approbation
| Type | Déclencheur | Action à l’approbation |
|---|---|---|
ROOM_CREATION | Manager crée une chambre | Chambre créée à partir du payload |
STOCK_MOVEMENT | Manager crée un mouvement de stock | Mouvement exécuté, stock article mis à jour |
RESERVATION_MODIFICATION | Manager modifie une réservation | Modification appliquée, facture recalculée |
VOUCHER_FLAG | Owner/DAF/Manager bascule isVoucher sur une commande | Enregistrement pour audit (pas d’action supplémentaire) |