Documentation / Guide développeur

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 :

NiveauRôlesDescription
Tenant (plateforme)SUPERADMIN, EMPLOYEE, SUPPLIERAccès global / employé / portail fournisseur
EtablissementOWNER, DAF, MANAGER, SERVER, POS, COOK, CLEANER, MAITRE_HOTELAccès spécifique à un établissement
Point de vente (optionnel)mêmes rôles EstablishmentRoleAffectation 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) ou SERVICE (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) :

  • SuperviseursSUPERADMIN, ou OWNER / DAF / MANAGER dans un établissement — voient tous les PdV de leur périmètre (aucune restriction).
  • Autres rôlesSERVER, 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)

ModuleActionAutorisé
Clés APICréer / modifier / supprimerOui
Canaux iCalConnecter / configurerOui
Configuration FedaPayConnecter / tester / déconnecterOui
FournisseursCRUD completOui
AbonnementVoir / renouveler via FedaPayOui
Remises (Discount Rules)Créer / modifier / activer (hébergements et commandes)Oui
ClientsVoir liste, fiche, télécharger carte de fidélité PDFOui
Points de venteCréer / modifier / désactiver / affecter le personnel (DAF inclus)Oui
Backfill factures channelPOST /api/reservations/admin/backfill-channel-invoicesOui

DAF (Directeur Administratif et Financier)

Le DAF est l’administrateur de l’établissement. Il valide les actions sensibles soumises par le Manager.

Permissions

ModuleActionAutorisé
ChambresCréer / modifier / supprimerOui (direct)
RéservationsCréer / modifier tous les champs / annulerOui
RéservationsCheck-in / Check-outOui
ArticlesCréer / modifier / supprimerOui
StockMouvements de stock (direct)Oui
CommandesVoir les commandesOui
CommandesChanger statut cuisine (EN_COURS / PRET)Non
CommandesMarquer comme servieNon
CommandesAnnuler une commandeOui
CommandesBasculer flag bon propriétaire (isVoucher)Oui
RéservationsModifier tous les champs (direct, sans approbation)Oui
DépensesCRUD completOui
ApprobationsVoir toutes les demandesOui
ApprobationsApprouver / rejeterOui
CuisineVoir le tableau cuisineOui (lecture seule)
MénagePointage (clock-in/out)Non
Clés APICréer / modifier / supprimerOui
AbonnementVoir / renouveler via FedaPayOui
DashboardStats Manager + financièresOui (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

ModuleActionAutoriséApprobation DAF
ChambresCréer une chambreOuiRequise
ChambresModifier / supprimerOui-
RéservationsCréerOui-
RéservationsModifier les dates, chambre, remise, invitésOuiRequise (via approbation DAF)
RéservationsCheck-in / Check-outOui-
ArticlesCréer (avec image et description)Oui-
ArticlesModifier / supprimerOui-
StockMouvements de stockOuiRequise
CommandesVoir les commandesOui-
CommandesChanger statut cuisine (EN_COURS / PRET)Non-
CommandesMarquer comme servieNon-
CommandesAnnuler une commandeOui-
CuisineVoir le tableau cuisineOui (lecture seule)-
MénagePointage (clock-in/out)Non-
MénageAssigner un ménageOui (aux CLEANERs)-
ApprobationsVoir ses propres demandesOui-
ApprobationsApprouver / rejeterNon-

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

  1. Le Manager soumet une action (création chambre, mouvement stock, modification dates)
  2. Une demande d’approbation est créée (statut PENDING)
  3. Le Manager peut voir le statut de sa demande dans la page Approbations > Mes demandes
  4. Le DAF voit la demande dans sa page Approbations et peut approuver ou rejeter
  5. 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

ModuleActionAutorisé
CommandesCréer une commandeOui
CommandesMarquer comme servie (SERVED)Oui
CommandesChanger statut cuisine (EN_COURS / PRET)Non
CommandesAnnuler une commandeNon
CommandesBasculer flag bon propriétaire (isVoucher)Non
CommandesVoir ses commandes (créées par lui OU attribuées par le POS)Oui
CommandesSaisir une commande avec date d’opération rétroactive (≤ 15 jours)Oui
Commandes QRPrendre en charge une commande non assignée (POST /orders/:id/claim)Oui
ChambresCréer / modifierNon
StockMouvements de stockNon
MénagePointageNon

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

ModuleActionAutorisé
Point de venteAccès à /dashboard/pos (web) et écran POS (mobile)Oui
CommandesCréer une commandeOui
CommandesAttribuer une commande à un serveur (sélecteur Serveur attribué)Oui
CommandesSaisir avec date d’opération rétroactive (≤ 15 jours)Oui
CommandesSaisir en mode hors ligne (file IndexedDB/Room DB)Oui
CommandesBasculer flag bon propriétaire (isVoucher)Non
PaiementsEncaisser une commande (espèces, carte, mobile money)Oui
FacturesVoir les factures généréesOui
CommandesChanger statut cuisine / annulerNon
Chambres / Réservations / StockAccèsNon

Attribution — Lorsque le POS coche un serveur dans « Serveur attribué », la commande est enregistrée avec createdById = POS (audit de qui a tapé) et serverId = 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

ModuleActionAutorisé
CuisineVoir les commandesOui
CuisinePasser en EN_COURS (IN_PROGRESS)Oui
CuisinePasser en PRET (READY)Oui
CuisineMarquer comme servieNon
CuisineAnnulerNon
ChambresAccèsNon
StockAccèsNon
MénageAccèsNon

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

ModuleActionAutorisé
CommandesVoir toutes les commandes de l’établissementOui
CommandesCréer une commandeOui
CommandesMarquer comme servie (SERVED)Oui
CommandesAnnuler une commandeNon
CommandesBasculer flag bon propriétaire (isVoucher)Non
Commandes QRPrendre en charge une commande non assignée (POST /orders/:id/claim)Oui
TablesVoir le plan de salleOui
TablesAssigner un serveur à une table (PATCH /restaurant-tables/:id/assign-server)Oui
TablesConfigurer le mode d’attribution QR (PATCH /restaurant-tables/settings/qr-mode)Oui
TablesVoir les QR codes des tablesOui
Date rétroactiveSaisir avec operationDate (≤ 15 jours)Oui
Chambres / Réservations / StockAccèsNon

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

ModuleActionAutorisé
MénagePointage clock-inOui
MénagePointage clock-outOui
MénageVoir les sessions de ménageOui
ChambresCréer / modifierNon
CommandesAccèsNon
StockAccèsNon

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 operationDate est fournie : la facture utilise issueDate = 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 = X OU serverId = 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.occurredAt selon 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, statut ISSUED
  • 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 :

  1. Paiement FedaPay (Mobile Money, carte)
  2. Plugin WordPress envoie la réservation via POST /api/external-bookings (instantané)
  3. Réservation créée + facture auto-générée + paiement enregistré (montant partiel supporté : acompte 60%)
  4. Webhook FedaPay confirme le paiement (double sécurité)
  5. Notification vers WordPress via webhook de paiement (si configuré)

Checkout → Nettoyage automatique

Lorsqu’un check-out est effectué sur une réservation :

  1. La réservation passe en statut CHECKED_OUT
  2. La chambre passe automatiquement en statut CLEANING
  3. Un cleaner peut ensuite faire un clock-in sur cette chambre
  4. 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 SALE est enregistré dans stock_movements avec le orderId
  • 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 :

  1. La réservation est créée via reservationService.create() (pas d’insertion directe)
  2. Une facture FAC-YYYYMMDD-NNNN est générée automatiquement (statut PAID)
  3. Un paiement est enregistré (FEDAPAY pour les réservations en ligne, OTHER pour iCal)
  4. Une fiche client est créée ou mise à jour si l’email est disponible
  5. 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 isVoucher sur l’Order
  • Met à jour la note sur la facture associée
  • Crée un ApprovalRequest de type VOUCHER_FLAG pour traçabilité DAF

Commande QR client → Attribution serveur

Un client scanne le QR code sur sa table et passe une commande sans compte Teranga :

  1. Le client ouvre https://<domaine>/menu/<token> (page publique, sans authentification)
  2. Il consulte la carte, ajoute des articles au panier, saisit son nom (optionnel) et valide
  3. Le backend reçoit POST /api/public/orders et résout le serverId selon le mode configuré :
ModeqrServerModeComportement
DésactivéDISABLEDLa page /menu/[token] affiche “Commande en ligne indisponible”. Aucun endpoint public n’accepte de commande.
Pré-assignéPRE_ASSIGNEDLe serveur est celui enregistré sur RestaurantTable.currentServerId. Si aucun n’est défini, serverId = null.
Premier répondantFIRST_RESPONDERserverId = null ; notification envoyée à tous les SERVER et MAITRE_HOTEL — le premier qui clique “Prendre en charge” prend la commande.
Charge autoAUTO_LOADLe serveur ayant le moins de commandes actives dans l’établissement est désigné automatiquement.
ManuelMANUALserverId = null ; notification envoyée aux superviseurs (MANAGER, MAITRE_HOTEL) pour assignation manuelle.
  1. createdById = ID du compte OWNER de l’établissement (audit, requis par le modèle)
  2. Une notification est envoyée aux rôles concernés selon le mode
  3. En mode FIRST_RESPONDER ou MANUAL : un SERVER ou MAITRE_HOTEL peut appeler POST /api/orders/:id/claim pour prendre en charge la commande (idempotent — 409 si déjà assignée)
  4. 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 DISABLED n’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 :

ActionRôles autorisés
Voir la badgeuse + pointerTout 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 PENDINGEmployé concerné
Voir les fiches employé, planning, feuille de tempsOWNER, DAF, MANAGER
Créer/modifier/supprimer une fiche employéOWNER, DAF
Approuver/refuser un congé · Annuler un congé APPROVED · Ajuster un soldeOWNER, DAF
Créer/modifier/supprimer un shift planningOWNER, DAF, MANAGER
Générer une période de paie · Verrouiller/rouvrir/marquer payéeOWNER, DAF
Exporter le CSV SYSCOHADA · Télécharger un bulletin PDFOWNER, 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_REQUIREDUser.mustChangePassword === true.
  • TERMS_NOT_ACCEPTEDUser.termsAcceptedAt IS NULL.
  • RGPD_NOT_ACCEPTEDUser.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

TypeDéclencheurAction à l’approbation
ROOM_CREATIONManager crée une chambreChambre créée à partir du payload
STOCK_MOVEMENTManager crée un mouvement de stockMouvement exécuté, stock article mis à jour
RESERVATION_MODIFICATIONManager modifie une réservationModification appliquée, facture recalculée
VOUCHER_FLAGOwner/DAF/Manager bascule isVoucher sur une commandeEnregistrement pour audit (pas d’action supplémentaire)