Initialisation du dépôt : specs (socle plateforme, modèle de données, site vitrine) et steering
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# Design Requirements — Socle Plateforme SchotzimmO (aspects techniques)
|
||||
|
||||
## Introduction
|
||||
|
||||
Ce document rassemble les exigences **techniques** dérivées du besoin fonctionnel métier décrit dans `requirements.md`. Il est organisé **en miroir** : chaque section reprend le même numéro et le même titre que `requirements.md`, suivi de « — aspects techniques ». Seules les exigences comportant effectivement des critères techniques y figurent.
|
||||
|
||||
Ces exigences sont arbitrées conjointement par le PO business et le PO IT. Les choix d'architecture, modèles de données et contrats d'API détaillés sont, eux, décrits dans `design.md`.
|
||||
|
||||
> Les mots-clés EARS (WHEN / IF / WHILE / WHERE / THE ... SHALL) sont conservés. Les termes techniques, identifiants, codes HTTP, en-têtes et noms d'API restent en anglais ou sous leur forme littérale.
|
||||
|
||||
---
|
||||
|
||||
## Glossary (technique)
|
||||
|
||||
- **Base_Socle** : La base MariaDB centrale et unique portant l'authentification, le référentiel des Reseaux et leurs informations de connexion, les abonnements, le catalogue des Modules/Packs et les KPI d'usage par Reseau. Aucune donnée métier produite par les Mandataires.
|
||||
- **Base_Reseau** : La base MariaDB propre à un Reseau, portant toutes ses données métier (profil mandataire, parrainage, données des Modules). Une Base_Reseau par Reseau.
|
||||
- **Reference_Logique** : Un identifiant (`AccountId`) généré par la Base_Socle et réutilisé dans une Base_Reseau sans contrainte FK SQL ni jointure inter-bases ; la cohérence est assurée applicativement.
|
||||
- **NetworkConnectionResolver** : Le composant qui, à partir du claim `network` du JWT, résout la clé logique (`db_connection_key`) du Reseau vers la chaîne de connexion de sa Base_Reseau fournie par configuration.
|
||||
- **JWT** : *JSON Web Token* signé émis à la connexion, portant l'identité du Mandataire (`sub` = AccountId), son Reseau (`network`), son rôle (`role` ∈ { `mandataire`, `admin` }) et une échéance (`exp`).
|
||||
- **Claim** : Une assertion portée par le JWT (identité, Reseau, rôle), utilisée côté Backend pour le contrôle d'accès.
|
||||
- **Access_Token** : Le JWT d'accès transmis par le Frontend dans l'en-tête `Authorization: Bearer <token>` à chaque appel protégé.
|
||||
- **Hash_Mot_De_Passe** : L'empreinte du Mot_De_Passe calculée avec un algorithme de hachage adapté aux mots de passe (ex. PBKDF2/BCrypt/Argon2), avec sel ; le Mot_De_Passe n'est jamais stocké en clair ni journalisé.
|
||||
- **SMTP** : Le service d'envoi d'e-mails (invitations, réinitialisation). Configurable ; un `Smtp:Host` vide active un repli de journalisation du lien plutôt qu'un envoi.
|
||||
- **Config_BDD** : Les chaînes de connexion MariaDB/MySQL (Base_Socle et chaque Base_Reseau), fournies par configuration (variables d'environnement), pointant soit vers des instances conteneurisées, soit vers un serveur externe. La Base_Socle ne stocke qu'une clé logique par Reseau, jamais la chaîne elle-même.
|
||||
- **Migration** : Une migration de schéma gérée par l'ORM (EF Core), appliquée de façon idempotente au démarrage ou via commande.
|
||||
- **DTO** : *Data Transfer Object*, objet de transport exposé par l'API, distinct des entités persistées.
|
||||
- **Code_HTTP** : Le statut HTTP retourné par l'API (ex. 200, 201, 400, 401, 403, 404, 409, 422, 500).
|
||||
- **Portee_Hierarchique** : Le périmètre de données qu'un appel peut retourner compte tenu de la Visibilite_Descendante (soi-même, Descendance agrégée, Filleuls_Directs en détail).
|
||||
|
||||
---
|
||||
|
||||
## Requirements (aspects techniques)
|
||||
|
||||
### Requirement 1: Inscription d'un Mandataire — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer l'inscription via `POST /api/auth/register` acceptant un DTO { email, password, firstName, lastName, networkId, networkMemberId, legalStatus?, siren?, referralToken? }.
|
||||
2. WHEN l'inscription réussit, THE Backend SHALL répondre `201 Created` et ne jamais retourner le Hash_Mot_De_Passe ni aucune donnée de sécurité.
|
||||
3. IF l'Adresse_Email est déjà utilisée, THEN THE Backend SHALL répondre `409 Conflict` avec un message générique ne révélant aucune donnée du Compte existant.
|
||||
4. IF le DTO est invalide (champ obligatoire manquant, email mal formé, password < 8 caractères, siren au format incorrect), THEN THE Backend SHALL répondre `422 Unprocessable Entity` avec le détail des champs en erreur.
|
||||
5. THE Backend SHALL stocker le Mot_De_Passe uniquement sous forme de Hash_Mot_De_Passe salé ; en aucun cas en clair.
|
||||
6. THE Backend SHALL valider l'Adresse_Email par une expression de forme standard et normaliser sa casse (minuscule) avant unicité et Rapprochement_Email.
|
||||
7. WHEN un `referralToken` valide et non expiré est fourni, THE Backend SHALL en extraire `networkId` et l'identité du Parrain et ignorer tout `networkId` divergent fourni par le client.
|
||||
8. IF le `referralToken` est fourni mais invalide ou expiré, THEN THE Backend SHALL répondre `400 Bad Request` et signaler que le lien d'invitation n'est plus valide.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 2: Authentification et session — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `POST /api/auth/login` { email, password, rememberMe? } et retourner, en cas de succès, un Access_Token (JWT) et son échéance.
|
||||
2. THE Backend SHALL signer le JWT avec une clé secrète fournie par configuration (`JWT:SecretKey`), échouer au démarrage si la clé est absente, et appliquer une durée de validité configurable (`JWT:ExpirationMinutes`, défaut 720).
|
||||
3. THE JWT SHALL porter les claims `sub` (AccountId), `network` (NetworkId), `role` et `exp`.
|
||||
4. WHEN les identifiants sont invalides, THE Backend SHALL répondre `401 Unauthorized` avec un message générique, sans distinguer email inconnu et mot de passe erroné, et SHALL appliquer une temporisation ou limitation de débit contre le forçage.
|
||||
5. THE Backend SHALL protéger toutes les routes métier par une authentification Bearer et répondre `401 Unauthorized` en l'absence d'un Access_Token valide.
|
||||
6. THE Frontend SHALL transmettre l'Access_Token dans l'en-tête `Authorization: Bearer <token>` et effacer le token local à la déconnexion.
|
||||
7. THE Frontend SHALL stocker l'Identifiant_Mémorisé dans le stockage local du navigateur et ne jamais y stocker le Mot_De_Passe ni l'Access_Token au-delà de la politique de session définie.
|
||||
8. THE Backend SHALL exposer `POST /api/auth/forgot-password` { email } répondant toujours `200 OK` quel que soit l'existence de l'Adresse_Email (anti-énumération), et générer le Jeton_Reinitialisation uniquement si le Compte existe.
|
||||
9. THE Backend SHALL générer un Jeton_Reinitialisation cryptographiquement aléatoire, à usage unique, de durée de vie configurable (`Auth:ResetTokenLifetimeMinutes`, défaut 60), stocké sous forme hachée.
|
||||
10. THE Backend SHALL exposer `POST /api/auth/reset-password` { token, newPassword } répondant `200 OK` en cas de succès, `400 Bad Request` si le token est invalide, expiré ou déjà consommé, et `422` si le nouveau mot de passe est trop court.
|
||||
11. THE Backend SHALL construire les liens d'e-mail à partir d'une base d'URL configurable (`Auth:FrontendBaseUrl`).
|
||||
|
||||
---
|
||||
|
||||
### Requirement 3: Réseaux — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister dans la Base_Socle un référentiel `Networks` (code, libellé, `db_connection_key`, `theme_settings`, `is_active`, `is_provisioned`) et exposer `GET /api/networks` (Reseaux actifs et provisionnés) accessible sans authentification pour alimenter l'inscription.
|
||||
2. THE Backend SHALL stocker toutes les données métier d'un Reseau dans sa Base_Reseau dédiée ; les tables de Base_Reseau ne portent pas de colonne `NetworkId` (le cloisonnement est physique).
|
||||
3. THE NetworkConnectionResolver SHALL déterminer la Base_Reseau de chaque requête métier à partir du claim `network` du JWT, indépendamment de tout paramètre fourni par le client.
|
||||
4. IF le claim `network` ne correspond à aucun Reseau actif et provisionné, THEN THE Backend SHALL répondre `403 Forbidden` et n'ouvrir aucune connexion.
|
||||
5. WHEN l'Administrateur réalise l'Onboarding_Reseau, THE Backend SHALL créer/initialiser la Base_Reseau (schéma + données de référence) via `POST /api/admin/networks`, enregistrer `db_connection_key` et `theme_settings`, puis positionner `is_provisioned = 1`.
|
||||
6. IF la Base_Reseau d'un Reseau est indisponible ou non provisionnée, THEN THE Backend SHALL répondre `503 Service Unavailable` sans exposer de détail d'infrastructure.
|
||||
7. THE Backend SHALL fournir au Frontend la Personnalisation_Reseau (`theme_settings`) du Reseau courant pour l'application du thème et des libellés.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 4: Parrainage et hiérarchie — aspects techniques
|
||||
|
||||
1. THE Backend SHALL modéliser l'Arbre_Parrainage dans la Base_Reseau par une relation d'auto-référence `mandataire_profiles.sponsor_account_id` (vraie FK intra-base), et un état d'Affiliation (`supposee`, `validee`).
|
||||
2. THE Backend SHALL modéliser le Membre_Declare dans la Base_Reseau comme une entité porteuse d'Adresse_Email normalisée, lien hiérarchique et statut, sans credentials ni données métier (le cloisonnement Reseau étant physique, pas de `NetworkId`).
|
||||
3. THE Backend SHALL exposer `POST /api/referrals/declare` (déclarer un Parrain ou des Filleuls) et `POST /api/referrals/{id}/invite` (envoyer l'Invitation par SMTP).
|
||||
4. WHEN une Invitation est envoyée, THE Backend SHALL générer un Lien_Parrainage signé (token portant `networkId` et identité du Parrain), de durée de vie configurable, et l'inclure dans l'e-mail via `Auth:FrontendBaseUrl`.
|
||||
5. WHEN `Smtp:Host` est vide, THE Backend SHALL journaliser le Lien_Parrainage et le lien de réinitialisation au lieu de les envoyer, pour les environnements sans SMTP.
|
||||
6. THE Backend SHALL effectuer le Rapprochement_Email sur l'Adresse_Email normalisée (casse ignorée) lors de toute inscription, et convertir le Membre_Declare (Base_Reseau) en profil mandataire lié au Compte créé (Base_Socle), en coordonnant les deux bases avec compensation en cas d'échec partiel.
|
||||
7. IF une déclaration introduit un cycle (le descendant deviendrait ancêtre), THEN THE Backend SHALL répondre `409 Conflict` sans persister.
|
||||
8. THE Backend SHALL exposer `POST /api/referrals/confirmations` et `POST /api/referrals/confirmations/{id}/approve|reject` pour la Confirmation_Affiliation entre Comptes déjà inscrits, et répondre `403 Forbidden` si l'appelant n'est pas la partie concernée.
|
||||
9. THE Backend SHALL garantir, par contrainte applicative, au plus un `sponsor_account_id` validé par mandataire à un instant donné.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 5: Visibilité hiérarchique descendante — aspects techniques
|
||||
|
||||
1. THE Backend SHALL dériver la Portee_Hierarchique d'un appel à partir du claim `sub`, en calculant la Descendance via la relation `sponsor_account_id` au sein de la Base_Reseau de l'appelant.
|
||||
2. WHEN une Vue_Agregee est demandée, THE Backend SHALL calculer les agrégats sur l'ensemble de la Descendance par requête serveur, sans retourner d'identifiants individuels dans la réponse agrégée.
|
||||
3. WHEN une Vue_Detail_Direct est demandée, THE Backend SHALL restreindre le jeu de résultats aux mandataires dont le `sponsor_account_id` est directement l'appelant (N+1).
|
||||
4. IF un appel vise un AccountId hors de la Portee_Hierarchique de l'appelant, THEN THE Backend SHALL répondre `403 Forbidden` sans révéler l'existence de la ressource (ne pas distinguer de `404` pour les ressources hors périmètre lorsque la divulgation serait sensible).
|
||||
5. THE Backend SHALL exposer les capacités de partage hiérarchique via un contrat commun que chaque Module implémente (fourniture d'agrégats et, le cas échéant, de détail N+1), sans que le socle ne présume des données de chaque Module.
|
||||
|
||||
> **Note de calcul (indicateurs envisagés, non figés)** : les premiers agrégats pressentis sont le nombre de mandats en entrée, le nombre de mandats en sortie, le chiffre d'affaires et le nombre de Filleuls par niveau. Leur définition précise relève des specs des Modules fournisseurs.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 6: Annuaire des abonnés — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `GET /api/directory` retournant les Comptes et Membres_Declares du `network` du claim, paginé, avec un indicateur d'état (inscrit / déclaré).
|
||||
2. THE Backend SHALL exposer `GET /api/directory/tree` retournant l'Arbre_Parrainage de la Descendance de l'appelant.
|
||||
3. THE Backend SHALL n'exposer dans ces endpoints que des champs d'annuaire (nom, prénom, Identifiant_Reseau, état, lien hiérarchique) et aucune donnée métier de Module.
|
||||
4. THE Backend SHALL supporter une recherche serveur par nom, prénom ou Identifiant_Reseau et limiter la taille de page par défaut (ex. 50) avec pagination.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 7: Abonnements et Packs — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister les Packs et les Abonnements, un Abonnement portant AccountId, PackId, dates d'effet et statut (`actif`, `expire`).
|
||||
2. THE Backend SHALL exposer `GET /api/subscription` (abonnement courant et Modules résultants) et `POST /api/subscription` (changement de Pack).
|
||||
3. WHEN un changement de Pack est appliqué, THE Backend SHALL recalculer côté serveur la liste des Modules_Accessibles par inclusion des Packs inférieurs.
|
||||
4. THE Backend SHALL déterminer les Modules_Consultation_Seule en comparant les Modules non inclus dans le Pack courant avec la présence de données persistées du Mandataire pour ces Modules.
|
||||
5. IF une action d'écriture est tentée sur un Module hors Abonnement, THEN THE Backend SHALL répondre `403 Forbidden` avec un code applicatif indiquant « abonnement requis ».
|
||||
6. WHERE un paiement en ligne sera introduit ultérieurement, THE Backend SHALL isoler la logique de facturation d'abonnement derrière une abstraction (le MVP gère l'activation manuelle par l'Administrateur ou l'auto-attribution Starter), sans dépendance de paiement dans le socle initial.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 8: Catalogue et activation des Modules — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister un Catalogue_Modules { ModuleKey, libellé, PackId requis, actif } et l'exposer via `GET /api/modules`.
|
||||
2. THE Backend SHALL initialiser le Catalogue avec : `carnet-adresses`, `kms`, `facturation` → Pack Starter ; `crm`, `agenda` → Pack Pro.
|
||||
3. THE Backend SHALL protéger les routes de chaque Module par un contrôle d'autorisation vérifiant, à partir des claims et de l'Abonnement, le droit d'accès (plein accès ou consultation seule), et répondre `403 Forbidden` à défaut.
|
||||
4. THE Backend SHALL distinguer, dans la réponse `GET /api/modules`, l'état de chaque Module pour l'appelant (`accessible`, `consultation`, `verrouille`) afin que le Frontend adapte la navigation.
|
||||
5. THE Frontend SHALL masquer ou désactiver les actions d'écriture d'un Module en état `consultation` et proposer un parcours de montée en abonnement pour un Module `verrouille`.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 9: Administration de la Plateforme — aspects techniques
|
||||
|
||||
1. THE Backend SHALL matérialiser le rôle Administrateur par le claim `role=admin` et protéger les routes `/api/admin/**` par une autorisation dédiée répondant `403 Forbidden` à tout appelant non admin.
|
||||
2. THE Backend SHALL exposer `GET /api/admin/accounts` (tous Reseaux, via la Base_Socle, avec Reseau, Abonnement, statut), `PUT /api/admin/accounts/{id}/subscription`, et `PUT /api/admin/modules` (gestion Module->Pack).
|
||||
3. THE Backend SHALL exposer `POST /api/admin/networks` et `PUT /api/admin/networks/{id}` pour l'Onboarding_Reseau et le paramétrage (Personnalisation_Reseau).
|
||||
4. THE Backend SHALL exposer `GET /api/admin/stats` retournant les KPI d'usage **remontés par chaque Reseau** (table `network_usage_kpis` de la Base_Socle), sans requête ni jointure traversant les Base_Reseau et sans donnée métier individuelle.
|
||||
5. THE Backend SHALL exposer des endpoints d'administration de l'Arbre_Parrainage (Base_Reseau concernée) permettant de rattacher, détacher ou réaffecter un mandataire, en refusant (`409 Conflict`) toute opération créant un cycle.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 10: Tableau de bord — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `GET /api/dashboard` agrégeant les contributions des seuls Modules_Accessibles de l'appelant via un contrat de fourniture commun.
|
||||
2. THE Backend SHALL borner le périmètre des données du Tableau_De_Bord à l'appelant et à sa Portee_Hierarchique.
|
||||
3. THE Frontend SHALL afficher un état vide explicite par section lorsque le Module correspondant ne fournit aucun élément.
|
||||
4. THE Backend SHALL concevoir l'agrégation du Tableau_De_Bord de façon résiliente : la défaillance de la contribution d'un Module ne doit pas empêcher le rendu des autres sections.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 11: Cadre applicatif et navigation — aspects techniques
|
||||
|
||||
1. THE Frontend SHALL être une application Angular (TypeScript) à routage protégé par un garde d'authentification redirigeant vers la connexion en l'absence de Session valide.
|
||||
2. THE Frontend SHALL construire dynamiquement la navigation des Modules à partir de `GET /api/modules` (états `accessible` / `consultation` / `verrouille`).
|
||||
3. THE Frontend SHALL présenter un composant d'identité (avatar par initiales) donnant accès au Profil, au paramétrage, à l'Abonnement et à la déconnexion.
|
||||
4. THE Frontend SHALL centraliser la présentation des erreurs d'API par mappage des Codes_HTTP vers des messages utilisateur en français (401 → reconnexion, 403 → accès/abonnement requis, 422 → erreurs de champ, 5xx → indisponibilité).
|
||||
5. THE Backend SHALL exposer `GET /api/profile` et `PUT /api/profile` permettant de modifier l'identité et les coordonnées, en interdisant la modification du `NetworkId`.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 12: Persistance et isolation des données par Réseau — aspects techniques
|
||||
|
||||
1. THE Backend SHALL utiliser MariaDB/MySQL avec une Base_Socle et une Base_Reseau par Reseau ; chaque Config_BDD est fournie par variable d'environnement, pointant vers une instance conteneurisée **ou** un serveur externe, sans changement de code.
|
||||
2. THE Backend SHALL gérer deux jeux de Migrations EF Core : le schéma socle appliqué à la Base_Socle, et le schéma réseau appliqué de façon idempotente à **chaque** Base_Reseau.
|
||||
3. THE Backend SHALL utiliser deux DbContext (`SocleDbContext`, `ReseauDbContext`), le `ReseauDbContext` recevant sa chaîne de connexion de la requête via le NetworkConnectionResolver.
|
||||
4. THE Backend SHALL garantir qu'aucune requête ni jointure ne traverse deux Base_Reseau, et qu'aucune donnée d'un Reseau n'est atteignable depuis le contexte d'un autre Reseau ; les requêtes métier sont en outre bornées à la Portee_Hierarchique de l'appelant.
|
||||
5. WHEN une opération écrit plusieurs entités au sein d'une même base, THE Backend SHALL l'exécuter dans une transaction unique avec rollback complet en cas d'échec ; WHEN une opération touche à la fois la Base_Socle et une Base_Reseau (ex. création de Compte + profil), THE Backend SHALL la séquencer avec compensation applicative (pas de transaction distribuée).
|
||||
6. THE Backend SHALL ne jamais journaliser de secret (Mot_De_Passe, token, chaîne de connexion) et masquer ces valeurs dans les logs et messages d'erreur ; la Base_Socle ne stocke qu'une clé logique de connexion par Reseau.
|
||||
7. THE Backend SHALL exposer `GET /health` pour la sonde de disponibilité du déploiement (healthcheck conteneur).
|
||||
8. THE deployment SHALL s'orchestrer via Docker Compose (service Backend ASP.NET Core, service Frontend statique), les bases MariaDB (Base_Socle et Base_Reseau) étant soit des services du Compose, soit un serveur externe référencé par la Config_BDD.
|
||||
@@ -0,0 +1,426 @@
|
||||
# Design Document
|
||||
|
||||
**Spec : Socle Plateforme SchotzimmO**
|
||||
|
||||
> Document de conception technique rédigé en français ; les titres de sections suivent le gabarit standard. Le besoin fonctionnel métier est décrit dans `requirements.md` ; les exigences techniques dérivées dans `design-requirements.md`. La présente conception met en œuvre ces exigences.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**SchotzimmO** (« l'ami de l'immobilier » ; *Schotzi* signifie « ami » en alsacien, *immO* pour l'immobilier) est une plateforme web **SaaS multi-utilisateur, multi-réseaux et multi-module** destinée aux mandataires immobiliers. Ce document décrit la conception technique du **socle** : les capacités transverses communes à tous les Modules (authentification, comptes, Réseaux, parrainage/hiérarchie, visibilité descendante, annuaire, abonnements/Packs, activation des Modules, administration, tableau de bord, cadre applicatif, isolation des données).
|
||||
|
||||
Le socle fournit l'ossature sur laquelle se branchent les Modules métier (Carnet d'adresses, KMS, CRM, Agenda, Facturation…), chacun décrit dans sa propre spec. Il définit les contrats transverses que ces Modules implémentent (contribution au Tableau_De_Bord, partage hiérarchique, contrôle d'accès par Abonnement).
|
||||
|
||||
### Décision structurante : architecture à deux niveaux de base
|
||||
|
||||
L'isolation des réseaux est **physique**, par une architecture à deux niveaux de base de données :
|
||||
|
||||
- **Base Socle** (centrale, unique) : authentification, référentiel des Réseaux et leurs informations de connexion, abonnements, catalogue des Modules/Packs, administration, KPI d'usage remontés par réseau. Elle ne contient **aucune donnée métier** produite par les mandataires.
|
||||
- **Base Réseau** (une par Réseau) : toutes les données métier cloisonnées — profil mandataire, parrainage/hiérarchie, et données des Modules.
|
||||
|
||||
À la connexion, le socle authentifie l'utilisateur sur la Base Socle, lit son Réseau, en déduit la **chaîne de connexion de sa Base Réseau**, et toute l'activité métier de la session s'exécute sur cette Base Réseau. L'identité (`account_id`) est générée côté socle et **réutilisée** comme clé du profil côté réseau (identité partagée).
|
||||
|
||||
### Périmètre fonctionnel (socle)
|
||||
|
||||
| # | Capacité | Description |
|
||||
|---|----------|-------------|
|
||||
| 1 | Inscription | Création de Compte avec Reseau, Identifiant_Reseau, Statut_Juridique, SIREN optionnel ; via Lien_Parrainage ou directe ; Starter par défaut |
|
||||
| 2 | Authentification & session | Login e-mail/mot de passe (Base Socle), JWT, « se souvenir de moi », reset par e-mail |
|
||||
| 3 | Réseaux | Référentiel multi-réseaux, base par réseau, onboarding réseau par l'Administrateur, personnalisation (thème/paramétrage) |
|
||||
| 4 | Parrainage & hiérarchie | Déclaration parrain/filleuls, invitations e-mail, états supposé/validé, rapprochement par e-mail, confirmation bilatérale, anti-cycle (Base Réseau) |
|
||||
| 5 | Visibilité descendante | Agrégats sur toute la Descendance, détail limité aux Filleuls_Directs (N+1) |
|
||||
| 6 | Annuaire | Liste et arbre des membres d'un Reseau (inscrits + déclarés), recherche |
|
||||
| 7 | Abonnements & Packs | Starter/Pro/Premium inclusifs, souscription, rétrogradation = conservation + consultation seule |
|
||||
| 8 | Catalogue & activation Modules | Référentiel Module->Pack, états accessible/consultation/verrouillé, contrôle d'accès par appel |
|
||||
| 9 | Administration | Rôle admin, gestion comptes/abonnements/catalogue, onboarding réseau, correction d'arbre, KPI d'usage par réseau |
|
||||
| 10 | Tableau de bord | Agrégation résiliente des contributions des Modules accessibles |
|
||||
| 11 | Cadre applicatif | Navigation dynamique, avatar/profil, mapping d'erreurs, garde d'auth, personnalisation par réseau |
|
||||
| 12 | Isolation des données | Isolation **physique** par Base Réseau + authentification centrale ; références inter-bases logiques |
|
||||
|
||||
### Contraintes techniques
|
||||
|
||||
- **Multi-utilisateur authentifié** : chaque Mandataire possède un Compte (Base Socle) ; session par **JWT signé HS256** (durée configurable, défaut 720 min / 12 h) ; tous les endpoints de données sont protégés par authentification Bearer.
|
||||
- **Isolation physique par réseau** : une **Base Réseau par Réseau** ; la Base Socle ne porte aucune donnée métier. Les références `account_id` entre Base Socle et Base Réseau sont **logiques** (mêmes valeurs d'identifiant), jamais des FK SQL ni des jointures inter-bases.
|
||||
- **Résolution de connexion par réseau** : la Base Socle stocke, par Réseau, une **clé logique** (`db_connection_key`) qui résout la chaîne de connexion de la Base Réseau via la **configuration** (jamais la chaîne en base). Un service de résolution fournit le `DbContext` réseau de la requête selon le claim `network` du JWT.
|
||||
- **Frontend** : **Angular (TypeScript)**, SPA, routage protégé par un garde d'authentification, navigation construite dynamiquement depuis le catalogue des Modules, thème/libellés personnalisables par Réseau.
|
||||
- **Backend** : **ASP.NET Core Web API (C#)**, architecture en couches (Controllers -> Services -> Repositories/DbContext) avec **deux DbContext** : `SocleDbContext` et `ReseauDbContext`.
|
||||
- **Base de données** : **MariaDB / MySQL**, via le provider EF Core **Pomelo.EntityFrameworkCore.MySql**. Chaînes de connexion fournies par variable d'environnement : la Base Socle et chaque Base Réseau peuvent être **conteneurisées** ou **externes** (cas de l'environnement de PROD de l'exploitant). Migrations EF Core appliquées de façon idempotente : schéma socle sur la Base Socle, schéma réseau sur **chaque** Base Réseau.
|
||||
- **E-mails** : service **SMTP** configurable (invitations, réinitialisation). Si `Smtp:Host` est vide, repli de **journalisation** du lien.
|
||||
- **Hébergement** : **Docker Compose** — un conteneur `frontend` (Angular servi par nginx) publié sur un port hôte, un conteneur `backend` (ASP.NET Core) non exposé directement, et des bases MariaDB **conteneurisées** (services avec volumes) ou **externes**. Terminaison **HTTPS par un reverse-proxy externe** (ex. Caddy).
|
||||
- **Deux réseaux au lancement** : `iad` (production) et `demo` (démonstration en ligne + tests de développement).
|
||||
- **Politique de visibilité** : `403 Forbidden` pour tout accès hors Portee_Hierarchique ; `404` pour les ressources réellement inexistantes dans le périmètre autorisé.
|
||||
|
||||
### Charte graphique
|
||||
|
||||
Reprise de la palette SchotzimmO : variables CSS globales — Orange `#F28C28`, Bleu `#0074B8` (primaire), Anthracite `#343A40`, Gris clair `#E7E9EA`. Le **bleu est conservé comme clin d'oeil au réseau IAD** (réseau partenaire au lancement). La charte de base est **personnalisable par Réseau** (thème, libellés) via le paramétrage porté par la Base Socle. Convention d'interaction : les boutons d'action passent à l'orange plein au survol ; les boutons « Annuler » restent neutres, les suppressions rouges, les états actifs en bleu plein. Les assets (logo, favicon) seront fournis ultérieurement.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Architecture globale
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Navigateur (SPA)"
|
||||
FE[Angular App<br/>TypeScript]
|
||||
end
|
||||
|
||||
subgraph "Backend (ASP.NET Core)"
|
||||
API[API REST<br/>Controllers]
|
||||
AUTHZ[Autorisation<br/>Auth + Abonnement + Hierarchie]
|
||||
RES[NetworkConnectionResolver<br/>resout la base reseau]
|
||||
SVC[Services socle + modules]
|
||||
MOD[Contrats Modules<br/>IModuleAccess<br/>IDashboardContributor<br/>IHierarchySharing]
|
||||
MAIL[EmailService<br/>SMTP / repli log]
|
||||
SOCLE[(SocleDbContext)]
|
||||
RESEAU[(ReseauDbContext)]
|
||||
end
|
||||
|
||||
subgraph "Stockage"
|
||||
DBS[(Base Socle<br/>MariaDB)]
|
||||
DBR1[(Base Reseau IAD)]
|
||||
DBR2[(Base Reseau Demo)]
|
||||
end
|
||||
|
||||
SMTP[Serveur SMTP]
|
||||
|
||||
FE -- "HTTP/JSON + Bearer JWT" --> API
|
||||
API --> AUTHZ
|
||||
AUTHZ --> RES
|
||||
AUTHZ --> SVC
|
||||
SVC --> MOD
|
||||
SVC --> MAIL
|
||||
SVC --> SOCLE
|
||||
SVC --> RESEAU
|
||||
SOCLE --> DBS
|
||||
RES -. "chaine de connexion selon claim network" .-> RESEAU
|
||||
RESEAU --> DBR1
|
||||
RESEAU --> DBR2
|
||||
MAIL -- "SMTP" --> SMTP
|
||||
```
|
||||
|
||||
### Flux de connexion et résolution de base réseau
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as Utilisateur
|
||||
participant FE as Angular Frontend
|
||||
participant API as AuthController
|
||||
participant SOCLE as SocleDbContext (Base Socle)
|
||||
participant RES as NetworkConnectionResolver
|
||||
participant RESEAU as ReseauDbContext (Base Reseau)
|
||||
|
||||
U->>FE: Saisit e-mail + mot de passe
|
||||
FE->>API: POST /api/auth/login
|
||||
API->>SOCLE: Verifie identifiants + lit network_id
|
||||
SOCLE-->>API: Compte valide (account_id, network, role)
|
||||
API-->>FE: JWT { sub, network, role }
|
||||
U->>FE: Requete metier (Bearer JWT)
|
||||
FE->>API: GET /api/... (module)
|
||||
API->>RES: Resout la connexion pour claim network
|
||||
RES-->>RESEAU: Chaine de connexion de la Base Reseau
|
||||
API->>RESEAU: Execute la requete metier (account_id)
|
||||
RESEAU-->>FE: Donnees du reseau
|
||||
```
|
||||
|
||||
### Flux de contrôle d'accès à un Module
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant FE as Frontend
|
||||
participant API as Module Controller
|
||||
participant AUTHZ as Autorisation
|
||||
participant SUB as SubscriptionService (Base Socle)
|
||||
participant SVC as Service du Module (Base Reseau)
|
||||
|
||||
FE->>API: Requete Module + Bearer JWT
|
||||
API->>AUTHZ: Verifie JWT (sub, network, role)
|
||||
AUTHZ->>SUB: Droit sur ce Module ?
|
||||
SUB-->>AUTHZ: accessible | consultation | verrouille
|
||||
alt verrouille
|
||||
AUTHZ-->>FE: 403 (abonnement requis)
|
||||
else consultation + ecriture
|
||||
AUTHZ-->>FE: 403 (lecture seule)
|
||||
else autorise
|
||||
AUTHZ->>SVC: Execute sur la Base Reseau (account_id)
|
||||
SVC-->>FE: 200 / 201
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
Le modèle est réparti sur **deux bases**. Le **détail physique** (types MariaDB, index, contraintes, seed) est tenu dans `.kiro/specs/_data-model/database.md`.
|
||||
|
||||
### Base Socle (centrale)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
NETWORK ||--o{ ACCOUNT : "network_id"
|
||||
NETWORK ||--o{ NETWORK_USAGE_KPI : "network_id"
|
||||
ACCOUNT ||--|| SUBSCRIPTION : "account_id"
|
||||
PACK ||--o{ SUBSCRIPTION : "pack_id"
|
||||
PACK ||--o{ MODULE_CATALOG : "required_pack_id"
|
||||
ACCOUNT ||--o{ PASSWORD_RESET : "account_id"
|
||||
|
||||
NETWORK {
|
||||
int Id PK
|
||||
string Code UK
|
||||
string Name UK
|
||||
string DbConnectionKey UK
|
||||
text ThemeSettings "nullable"
|
||||
bool IsActive
|
||||
bool IsProvisioned
|
||||
}
|
||||
ACCOUNT {
|
||||
int Id PK
|
||||
int NetworkId FK
|
||||
string Email UK
|
||||
string PasswordHash
|
||||
string Role "mandataire|admin"
|
||||
bool IsActive
|
||||
datetime CreatedAt
|
||||
}
|
||||
PACK {
|
||||
int Id PK
|
||||
string PackKey UK
|
||||
int Level UK
|
||||
bool IsFree
|
||||
}
|
||||
MODULE_CATALOG {
|
||||
int Id PK
|
||||
string ModuleKey UK
|
||||
int RequiredPackId FK
|
||||
bool IsActive
|
||||
}
|
||||
SUBSCRIPTION {
|
||||
int Id PK
|
||||
int AccountId FK
|
||||
int PackId FK
|
||||
datetime StartsAt
|
||||
datetime EndsAt "nullable"
|
||||
string Status "actif|expire"
|
||||
}
|
||||
PASSWORD_RESET {
|
||||
int Id PK
|
||||
int AccountId FK
|
||||
string TokenHash
|
||||
datetime ExpiresAt
|
||||
bool Used
|
||||
}
|
||||
NETWORK_USAGE_KPI {
|
||||
int Id PK
|
||||
int NetworkId FK
|
||||
string MetricKey
|
||||
decimal MetricValue
|
||||
string Period "nullable"
|
||||
}
|
||||
```
|
||||
|
||||
### Base Réseau (une par réseau)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
MANDATAIRE_PROFILE ||--o| MANDATAIRE_PROFILE : "sponsor_account_id"
|
||||
MANDATAIRE_PROFILE ||--o{ DECLARED_MEMBER : "declared_by_account_id"
|
||||
MANDATAIRE_PROFILE ||--o{ AFFILIATION_REQUEST : "requester / target"
|
||||
|
||||
MANDATAIRE_PROFILE {
|
||||
int AccountId PK "= accounts.id du socle (ref logique)"
|
||||
string FirstName
|
||||
string LastName
|
||||
string NetworkMemberId UK
|
||||
string LegalStatus "nullable"
|
||||
string Siren "nullable"
|
||||
int SponsorAccountId FK "nullable"
|
||||
datetime CreatedAt
|
||||
}
|
||||
DECLARED_MEMBER {
|
||||
int Id PK
|
||||
string Email
|
||||
string FirstName "nullable"
|
||||
string LastName "nullable"
|
||||
int DeclaredByAccountId FK
|
||||
string Relation "parrain|filleul"
|
||||
string Status "declare|invite"
|
||||
}
|
||||
AFFILIATION_REQUEST {
|
||||
int Id PK
|
||||
int RequesterAccountId FK
|
||||
int TargetAccountId FK
|
||||
string ProposedRelation "parrain|filleul"
|
||||
string Status "en_attente|approuvee|refusee"
|
||||
}
|
||||
```
|
||||
|
||||
### Notes de modélisation
|
||||
|
||||
- **Identité partagée (Option B)** : `ACCOUNT` (Base Socle) porte le strict minimum d'authentification. `MANDATAIRE_PROFILE` (Base Réseau) porte le profil métier et le parrainage, sa clé primaire `AccountId` **réutilisant** `accounts.id`. La cohérence des deux morceaux (création, désactivation) est assurée applicativement.
|
||||
- **Parrainage dans la Base Réseau** : l'auto-référence `sponsor_account_id` est une **vraie FK** au sein de la base réseau -> intégrité du parrainage garantie par la base. Le cloisonnement réseau est **physique** (base propre au réseau), d'où l'absence de colonne `network_id` dans les tables réseau.
|
||||
- **Références inter-bases** : `account_id` circule entre les deux bases comme **référence logique** (`FK*`), jamais via une FK SQL ou une jointure traversant deux bases.
|
||||
- **Abonnement** : un seul `SUBSCRIPTION` actif par Compte (Base Socle). Packs **inclusifs par `Level`**. **Consultation seule** déduite : Module hors Pack courant **mais** données présentes dans la Base Réseau.
|
||||
- **Réseau** : `networks.db_connection_key` = clé logique de connexion (secret en configuration) ; `is_provisioned` indique que la Base Réseau est initialisée (schéma + seed) avant toute inscription.
|
||||
- **KPI d'usage** : `network_usage_kpis` reçoit des agrégats **non nominatifs** remontés par chaque réseau ; aucune consolidation ne traverse les bases réseau.
|
||||
|
||||
---
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### Endpoints d'API (socle)
|
||||
|
||||
| Domaine | Méthode & route | Rôle |
|
||||
|---|---|---|
|
||||
| Réseaux | `GET /api/networks` | Liste des Reseaux ouverts à l'inscription (public) |
|
||||
| Inscription | `POST /api/auth/register` | Création de Compte (directe ou via referralToken) |
|
||||
| Auth | `POST /api/auth/login` | Connexion (Base Socle), retourne le JWT |
|
||||
| Auth | `POST /api/auth/forgot-password` | Déclenche la réinitialisation (réponse constante) |
|
||||
| Auth | `POST /api/auth/reset-password` | Applique le nouveau mot de passe via jeton |
|
||||
| Auth | `POST /api/auth/change-password` | Changement de mot de passe (authentifié) |
|
||||
| Profil | `GET /api/profile`, `PUT /api/profile` | Consulter/modifier le Profil (Base Réseau ; Reseau non modifiable) |
|
||||
| Parrainage | `GET /api/referrals/validate` | Valide un Lien_Parrainage et retourne le pré-remplissage |
|
||||
| Parrainage | `POST /api/referrals/declare` | Déclarer parrain / filleuls |
|
||||
| Parrainage | `POST /api/referrals/{id}/invite` | Envoyer une Invitation par e-mail |
|
||||
| Parrainage | `POST /api/referrals/confirmations` | Proposer une Confirmation_Affiliation |
|
||||
| Parrainage | `POST /api/referrals/confirmations/{id}/approve\|reject` | Approuver / refuser |
|
||||
| Annuaire | `GET /api/directory` | Membres du Reseau (inscrits + déclarés), paginé, recherche |
|
||||
| Annuaire | `GET /api/directory/tree` | Arbre de la Descendance de l'appelant |
|
||||
| Abonnement | `GET /api/subscription`, `POST /api/subscription` | Abonnement courant / changement de Pack |
|
||||
| Modules | `GET /api/modules` | Catalogue + état par Module (accessible/consultation/verrouille) |
|
||||
| Tableau de bord | `GET /api/dashboard` | Agrégation des contributions des Modules accessibles |
|
||||
| Admin | `GET /api/admin/accounts`, `PUT /api/admin/accounts/{id}/subscription` | Gestion des comptes/abonnements |
|
||||
| Admin | `POST /api/admin/networks`, `PUT /api/admin/networks/{id}` | Onboarding et paramétrage d'un Reseau |
|
||||
| Admin | `PUT /api/admin/modules` | Rattachement Module -> Pack |
|
||||
| Admin | `GET /api/admin/stats` | KPI d'usage remontés par réseau (non nominatifs) |
|
||||
| Admin | `POST /api/admin/referrals/reassign` | Correction de l'arbre de parrainage |
|
||||
| Santé | `GET /health` | Sonde de disponibilité (healthcheck conteneur) |
|
||||
|
||||
### Composants backend structurants
|
||||
|
||||
- **SocleDbContext** — accès à la Base Socle (networks, accounts, packs, module_catalog, subscriptions, password_resets, network_usage_kpis).
|
||||
- **ReseauDbContext** — accès à une Base Réseau (mandataire_profiles, declared_members, affiliation_requests, + tables des Modules). Sa chaîne de connexion est fournie à la requête.
|
||||
- **NetworkConnectionResolver** — à partir du claim `network` du JWT, résout la `db_connection_key` du Réseau vers la chaîne de connexion (configuration) et fournit le `ReseauDbContext` de la requête.
|
||||
- **AuthService / AccountService** — authentification sur la Base Socle, création de Compte (socle) + profil (réseau) dans une opération coordonnée.
|
||||
- **ReferralService** — déclarations, invitations, rapprochement par e-mail, confirmations (Base Réseau).
|
||||
- **SubscriptionService** — abonnement et calcul des Modules accessibles / consultation seule.
|
||||
- **NetworkProvisioningService** — onboarding d'un Réseau (création/initialisation de la Base Réseau : schéma + seed), réservé à l'Administrateur.
|
||||
|
||||
### Contrats transverses pour les Modules
|
||||
|
||||
Approche « plugins applicatifs » par injection de dépendances (composition à la compilation) :
|
||||
|
||||
- **IModuleAccess** — déclare le `ModuleKey` et le `RequiredPack` d'un Module ; permet au socle de calculer l'état (`accessible` / `consultation` / `verrouille`) et de garder les routes.
|
||||
- **IDashboardContributor** — fournit les éléments d'un Module pour le Tableau_De_Bord, appelé de façon **résiliente**.
|
||||
- **IHierarchySharing** — fournit, pour un périmètre hiérarchique, les **agrégats** (toute la Descendance) et le **détail N+1** (Filleuls_Directs) que le Module accepte de partager.
|
||||
|
||||
Ajouter un Module = enregistrer son implémentation (injection de dépendances), son entrée de Catalogue_Modules, et ses tables dans le schéma de la Base Réseau.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Mapping des Codes_HTTP** (centralisé côté Frontend, messages en français) : `401` (session expirée -> reconnexion), `403` (accès refusé / abonnement requis / lecture seule), `404` (ressource inexistante dans le périmètre autorisé), `409` (conflit : e-mail déjà utilisé, cycle de parrainage), `422` (erreurs de validation), `5xx` (indisponibilité).
|
||||
- **Résolution de base réseau** : si la Base Réseau d'un Réseau est indisponible ou non provisionnée, le Backend répond `503` (service indisponible) sans exposer de détail d'infrastructure ; la connexion n'est jamais tentée pour un Réseau `is_provisioned = 0`.
|
||||
- **Validation d'entrée** : DTO validés côté Backend ; erreurs de champ en `422`, sans divulgation sensible.
|
||||
- **Anti-énumération** : `forgot-password` répond toujours `200` ; `register` renvoie un message générique si l'e-mail est déjà pris.
|
||||
- **Isolation en cas d'erreur d'accès** : accès hors Portee_Hierarchique -> `403` ; `404` possible pour les ressources dont l'existence serait sensible.
|
||||
- **Cohérence inter-bases** : la création d'un Compte (socle) et de son profil (réseau) est coordonnée ; en cas d'échec de l'une des étapes, l'opération est annulée pour ne pas laisser un Compte sans profil (ou l'inverse).
|
||||
- **Transactions** : toute écriture multi-entités **au sein d'une base** est atomique (rollback complet en cas d'échec). Les opérations traversant les deux bases sont séquencées avec compensation applicative (pas de transaction distribuée).
|
||||
- **Secrets** : aucun secret (mot de passe, token, chaîne de connexion) n'est journalisé ni renvoyé dans un message d'erreur.
|
||||
- **Résilience du Tableau de bord** : l'échec de la contribution d'un Module est isolé et n'empêche pas le rendu des autres sections.
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- **Tests unitaires (Backend)** : services socle (authentification, création compte+profil coordonnée, calcul des Modules accessibles, portée hiérarchique), résolution de connexion réseau (mock de configuration), avec mocks (SMTP, horloge).
|
||||
- **Tests d'intégration (Backend)** : endpoints d'API sur une Base Socle + une Base Réseau de test (conteneurs éphémères), vérifiant les Codes_HTTP, l'isolation par base réseau et la cohérence inter-bases.
|
||||
- **Tests de propriétés / invariants** : non-existence de cycle dans l'Arbre_Parrainage, au plus un abonnement actif par compte, visibilité strictement descendante, aucune jointure/accès traversant deux bases réseau.
|
||||
- **Tests Frontend** : composants Angular clés (formulaires d'inscription/connexion, navigation dynamique, personnalisation par réseau, mapping d'erreurs).
|
||||
- **Tests de sécurité ciblés** : routes protégées refusant l'accès sans JWT valide, routes admin refusant un rôle `mandataire`, impossibilité d'accéder à une Base Réseau autre que celle du claim `network`.
|
||||
- **Non-régression** : suite exécutée au build ; aucune livraison sans build vert.
|
||||
|
||||
---
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
Propriétés que le système doit garantir en permanence (invariants vérifiables) :
|
||||
|
||||
### Property 1: Isolation physique par réseau
|
||||
**Validates: Requirements 3.3, 3.4, 12.2, 12.5**
|
||||
|
||||
Toute donnée métier d'un Réseau réside dans sa Base Réseau ; aucune requête ne lit les données d'un autre Réseau, et aucune jointure ne traverse deux bases.
|
||||
|
||||
### Property 2: Authentification centrale cohérente
|
||||
**Validates: Requirements 1.3, 2.2, 12.1**
|
||||
|
||||
Un Compte existe dans la Base Socle avec un e-mail unique sur toute la plateforme ; tout Compte actif possède un profil correspondant dans sa Base Réseau (`account_id` partagé), et réciproquement.
|
||||
|
||||
### Property 3: Acyclicité du parrainage
|
||||
**Validates: Requirements 4.5, 4.6**
|
||||
|
||||
L'Arbre_Parrainage ne contient aucun cycle ; un mandataire a au plus un `sponsor_account_id` validé.
|
||||
|
||||
### Property 4: Visibilité descendante stricte
|
||||
**Validates: Requirements 5.1, 5.2, 5.3, 5.4**
|
||||
|
||||
Un mandataire n'accède jamais aux données d'un ancêtre ou d'une autre branche ; le détail est limité au niveau N+1, l'agrégat couvre toute la Descendance.
|
||||
|
||||
### Property 5: Unicité de l'abonnement actif
|
||||
**Validates: Requirements 7.7**
|
||||
|
||||
Au plus un abonnement au statut `actif` par Compte à un instant donné (Base Socle).
|
||||
|
||||
### Property 6: Inclusivité des Packs
|
||||
**Validates: Requirements 7.2, 8.1**
|
||||
|
||||
Un Compte accède à tout Module dont `RequiredPack.Level <= Level` du Pack courant.
|
||||
|
||||
### Property 7: Conservation à la rétrogradation
|
||||
**Validates: Requirements 7.5, 7.6, 7.8**
|
||||
|
||||
Aucune donnée n'est supprimée lors d'une Retrogradation ; les Modules hors Pack mais pourvus de données (Base Réseau) restent accessibles en consultation seule.
|
||||
|
||||
### Property 8: Confidentialité des secrets
|
||||
**Validates: Requirements 2.7, 2.8**
|
||||
|
||||
Les mots de passe, jetons et chaînes de connexion ne sont jamais stockés en clair ni journalisés ; la Base Socle ne stocke qu'une clé logique de connexion.
|
||||
|
||||
### Property 9: Atomicité et cohérence inter-bases
|
||||
**Validates: Requirements 12.4**
|
||||
|
||||
Toute écriture multi-entités au sein d'une base est atomique ; les opérations traversant les deux bases sont compensées pour ne jamais laisser un Compte sans profil (ou l'inverse).
|
||||
|
||||
### Property 10: Réseau provisionné avant inscription
|
||||
**Validates: Requirements 3.1, 3.2**
|
||||
|
||||
Aucune inscription n'est possible sur un Réseau non actif ou non provisionné (`is_active = 0` ou `is_provisioned = 0`).
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **Deux niveaux de base (Base Socle + Base Réseau par réseau)** (retenu) vs **base unique mutualisée avec cloisonnement applicatif** (rejeté) : l'isolation physique par réseau garantit le cloisonnement par construction (pas de fuite inter-réseaux par bug), facilite la personnalisation par réseau et une éventuelle exigence contractuelle de séparation. Coût assumé : migrations à appliquer sur N bases réseau, provisioning d'une base à chaque nouveau réseau, et références inter-bases logiques (pas de FK SQL). Alternative mutualisée écartée car le projet valorise l'isolation forte et la personnalisation par réseau.
|
||||
- **Identité partagée, Option B** (retenu) vs **compte entièrement au socle** (rejeté) : l'authentification minimale reste centrale (login unique), mais le profil et le parrainage vivent dans la Base Réseau pour que les FK métier (parrainage, propriété des données) restent **intra-base** et garanties par MariaDB. Le compte entièrement au socle aurait dispersé les FK métier entre deux bases, sans intégrité référentielle.
|
||||
- **Clé logique de connexion + secret en configuration** (retenu) vs **chaîne de connexion stockée en base** (rejeté) : conforme à la règle « aucun secret en base/log » ; la Base Socle ne contient qu'une clé, le secret restant dans la configuration d'environnement.
|
||||
- **Pas de consolidation inter-réseaux ; KPI remontés par réseau** (retenu) : l'administration consomme des agrégats non nominatifs poussés par chaque réseau dans `network_usage_kpis`, sans requête traversant les bases réseau. Évite toute jointure inter-bases et respecte le cloisonnement.
|
||||
- **MariaDB/MySQL via Pomelo** (retenu) vs **SQLite** (rejeté) : multi-utilisateur concurrent + serveur MariaDB déjà présent en PROD. Connexions en configuration (conteneurisées ou externes) sans changement de code.
|
||||
- **JWT HS256** (retenu) : simple à exploiter ; clé en configuration, démarrage refusé si absente. Passage à RS256 possible plus tard.
|
||||
- **Contrats de Modules (plugins applicatifs)** (retenu) vs **socle monolithique** (rejeté) : permet d'empiler les Modules sans modifier le socle.
|
||||
- **Packs inclusifs par niveau** (retenu) vs **composition module par module** (rejeté au MVP) : trois Packs ordonnés simples à comprendre et facturer ; rattachement Module->Pack paramétrable.
|
||||
- **Rétrogradation = conservation + consultation seule** (retenu) vs **suppression/archivage** (rejeté) : aucune perte de donnée ; consultation seule déduite de la présence de données.
|
||||
- **Onboarding réseau par l'Administrateur** (retenu) : un Réseau est créé, provisionné et paramétré (thème) avant toute inscription de mandataire ; détail à affiner après l'expérience IAD.
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
- **docker-compose** : services `backend` (ASP.NET Core, non exposé), `frontend` (nginx servant la SPA Angular, publié sur un port hôte), et **optionnellement** des services `db` MariaDB (Base Socle et bases réseau) avec volumes, pour les environnements sans serveur externe.
|
||||
- **Bases externes** : en PROD, les services `db` sont omis ; les chaînes de connexion (socle + réseaux) pointent vers le serveur MariaDB existant de l'exploitant.
|
||||
- **Provisioning d'un réseau** : création de la Base Réseau (schéma + seed), enregistrement du Réseau dans la Base Socle (`db_connection_key`, `theme_settings`), passage `is_provisioned = 1`. Au lancement : réseaux `iad` et `demo`.
|
||||
- **Migrations** : schéma socle appliqué à la Base Socle ; schéma réseau appliqué à **chaque** Base Réseau, de façon idempotente.
|
||||
- **Secrets par environnement** : clé JWT, chaînes de connexion (socle + réseaux) et paramètres SMTP via variables d'environnement (`.env`), jamais dans l'image. Démarrage refusé si la clé JWT est absente.
|
||||
- **Reverse-proxy HTTPS externe** (Caddy) relaie vers le frontend ; le backend ne gère pas la terminaison TLS.
|
||||
- **Healthcheck** : `GET /health` sur le backend.
|
||||
@@ -0,0 +1,285 @@
|
||||
# Requirements Document
|
||||
|
||||
**Spec : Socle Plateforme SchotzimmO**
|
||||
|
||||
## Introduction
|
||||
|
||||
**SchotzimmO** (« l'ami de l'immobilier » ; *Schotzi* signifie « ami » en alsacien, *immO* pour l'immobilier) est une **plateforme SaaS multi-utilisateur, multi-réseaux et multi-module** destinée aux **mandataires immobiliers**, en complément de la plateforme mère de leur réseau (ex. IAD). Elle les aide à gérer en propre leur activité quotidienne : relation prospects, suivi des opportunités et des actions, agenda, carnet d'adresses, suivi kilométrique, facturation vers le siège, notes de frais, tableau de bord et reporting.
|
||||
|
||||
La plateforme est **multi-réseaux** : chaque mandataire appartient à un **Réseau** choisi à l'inscription (IAD et un réseau de démonstration au lancement, d'autres réseaux ultérieurement). Les données de chaque Réseau sont **physiquement isolées** dans une base de données propre à ce Réseau ; les arbres de parrainage sont donc **cloisonnés par Réseau**. Un Réseau est préalablement initialisé par l'Administrateur (Onboarding_Reseau) avant toute inscription.
|
||||
|
||||
L'accès aux fonctionnalités est organisé en **Modules** regroupés dans des **Packs d'abonnement** (Starter gratuit, Pro, Premium). Chaque Mandataire souscrit un abonnement **individuel** qui détermine les Modules auxquels il accède.
|
||||
|
||||
Ce document, dit **Socle Plateforme**, décrit les exigences **transversales** communes à tous les Modules : comptes et inscription, authentification, Réseaux, parrainage et hiérarchie, visibilité hiérarchique, annuaire, abonnements et Packs, activation des Modules, administration, tableau de bord, cadre applicatif et isolation des données par Réseau. Les exigences propres à chaque Module (Carnet d'adresses, KMS, CRM, Agenda, Facturation…) sont décrites dans leurs specs dédiées.
|
||||
|
||||
> **Note** : Ce document exprime le besoin **fonctionnel métier** (attendu fonctionnel, mode opératoire, règles de gestion) fourni par le Product Owner business. Les exigences **techniques** dérivées (isolation par deux niveaux de base MariaDB conteneurisée ou externe, résolution de connexion par Réseau, JWT, codes HTTP, sécurité, e-mails, rendu) sont rassemblées dans le document `design-requirements.md`, à préciser conjointement par le PO business et le PO IT. Chaque exigence renvoie, lorsque pertinent, à son pendant technique dans `design-requirements.md`.
|
||||
|
||||
---
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Plateforme** : L'ensemble du système SchotzimmO (socle + Modules).
|
||||
- **Frontend** : L'interface utilisateur de la Plateforme.
|
||||
- **Backend** : Le serveur d'API de la Plateforme.
|
||||
- **API** : L'interface de programmation exposée par le Backend pour communiquer avec le Frontend.
|
||||
- **Mandataire** : Un utilisateur de la Plateforme, mandataire immobilier d'un Réseau, disposant d'un Compte.
|
||||
- **Compte** : Le compte individuel d'un Mandataire, identifié par une Adresse_Email (login) et protégé par un Mot_De_Passe. Un Compte appartient à exactement un Réseau. L'authentification est centralisée ; les données métier du Mandataire résident dans la base de son Réseau.
|
||||
- **Adresse_Email** : L'adresse e-mail du Mandataire, utilisée comme identifiant de connexion et destinataire des e-mails (invitation, réinitialisation de mot de passe).
|
||||
- **Mot_De_Passe** : Le secret d'au moins 8 caractères associé au Compte, jamais conservé en clair.
|
||||
- **Session** : L'état authentifié d'un Mandataire, établi à la connexion et vérifié à chaque accès protégé.
|
||||
- **Jeton_Reinitialisation** : Un jeton à usage unique et à durée de vie limitée, envoyé par e-mail, permettant de définir un nouveau Mot_De_Passe sans connaître l'ancien.
|
||||
- **Identifiant_Mémorisé** : L'Adresse_Email de connexion conservée par le navigateur lorsque le Mandataire a demandé « Se souvenir de moi », afin de pré-remplir le champ Adresse_Email lors des visites ultérieures sur ce même navigateur. Le Mot_De_Passe n'en fait jamais partie.
|
||||
- **Profil** : Les paramètres personnels du Mandataire : identité (nom, prénom), Identifiant_Reseau, Statut_Juridique, numéro SIREN/RSAC optionnel, coordonnées, et données utiles aux Modules (ex. Adresse_Domicile pour KMS).
|
||||
- **Reseau** : L'organisation mère à laquelle appartient un Mandataire (ex. IAD). Choisi à l'inscription. Chaque Reseau dispose d'une **base de données propre** qui héberge toutes ses données métier : les relations de parrainage et la visibilité hiérarchique sont ainsi strictement cloisonnées par Reseau, aucun lien ni aucune donnée ne traversant deux Reseaux.
|
||||
- **Onboarding_Reseau** : Le parcours d'initialisation d'un Reseau mené par l'Administrateur avant toute inscription de Mandataire : création et initialisation de la base du Reseau, puis paramétrage (Personnalisation_Reseau). Un Reseau n'est ouvert à l'inscription qu'une fois initialisé.
|
||||
- **Personnalisation_Reseau** : Le paramétrage propre à un Reseau (thème, libellés et réglages d'interface), appliqué par-dessus le socle commun.
|
||||
- **Identifiant_Reseau** : L'identifiant du Mandataire propre à son Reseau (fourni par le Reseau). Obligatoire à l'inscription.
|
||||
- **Statut_Juridique** : La forme d'exercice du Mandataire (ex. auto-entrepreneur, EIRL). Détermine notamment la présence d'un numéro SIREN/RSAC.
|
||||
- **Numero_Siren** : Le numéro SIREN/RSAC du Mandataire, **optionnel** (présent lorsque le Statut_Juridique l'implique, ex. EIRL ; absent en auto-entrepreneur).
|
||||
- **Parrain** : Le Mandataire qui a recruté/affilié un autre Mandataire (son Filleul) au sein d'un même Reseau. Un Filleul a au plus un Parrain.
|
||||
- **Filleul** : Le Mandataire affilié à un Parrain au sein d'un même Reseau. Un Parrain peut avoir plusieurs Filleuls.
|
||||
- **Filleul_Direct** : Un Filleul rattaché directement à un Parrain (niveau immédiatement inférieur, N+1).
|
||||
- **Descendance** : L'ensemble des Mandataires situés sous un Parrain dans l'arbre de parrainage, tous niveaux confondus (Filleuls_Directs, puis leurs propres Filleuls, etc.).
|
||||
- **Arbre_Parrainage** : La structure hiérarchique des relations Parrain/Filleul au sein d'un Reseau donné.
|
||||
- **Membre_Declare** : Un Mandataire déclaré dans l'Arbre_Parrainage (comme Parrain ou Filleul d'un Mandataire inscrit) mais **pas encore inscrit** : il figure dans l'annuaire comme lien « supposé », sans Compte actif et sans aucune donnée métier.
|
||||
- **Invitation** : Une proposition d'inscription envoyée par e-mail par un Mandataire à son Parrain supposé ou à l'un de ses Filleuls supposés, porteuse d'un Lien_Parrainage pré-renseignant le Reseau et le lien hiérarchique.
|
||||
- **Lien_Parrainage** : Un lien (ou code) d'inscription transmis par Invitation ou partagé par un Parrain, pré-renseignant à l'inscription le Reseau et l'identité du Parrain, afin d'établir l'affiliation.
|
||||
- **Affiliation** : Le lien Parrain/Filleul. Elle est **validée** lorsque le Filleul s'inscrit via un Lien_Parrainage ou confirme son rattachement ; elle est **supposée** tant qu'elle n'est pas validée par le Filleul.
|
||||
- **Confirmation_Affiliation** : L'acte par lequel deux Mandataires déjà inscrits établissent ou valident entre eux un lien Parrain/Filleul resté supposé (cas de « perte de lien » lorsque le rapprochement automatique par e-mail n'a pas opéré). Le lien devient validé lorsque la partie concernée (Parrain ou Filleul) l'approuve.
|
||||
- **Rapprochement_Email** : Le mécanisme par lequel un Membre_Declare (identifié par une Adresse_Email lors de sa déclaration) est automatiquement relié au Compte s'inscrivant avec cette même Adresse_Email, afin d'éviter les doublons et d'établir l'Affiliation.
|
||||
- **Annuaire** : Le répertoire des Mandataires d'un Reseau (inscrits et Membres_Declares), permettant de visualiser l'organisation et de retrouver un contact.
|
||||
- **Visibilite_Descendante** : Le principe selon lequel un Parrain peut consulter des informations de sa Descendance, jamais l'inverse (aucune visibilité ascendante ni latérale par défaut).
|
||||
- **Vue_Agregee** : Une consultation d'indicateurs **agrégés** (totaux, KPI consolidés) portant sur toute la Descendance d'un Parrain, sans détail nominatif.
|
||||
- **Vue_Detail_Direct** : Une consultation de données **détaillées** limitée aux Filleuls_Directs (niveau N+1) d'un Parrain, dans les limites définies par chaque Module.
|
||||
- **Module** : Une unité fonctionnelle de la Plateforme (ex. Carnet_Adresses, KMS, CRM, Agenda, Facturation). Chaque Module est rattaché à un Pack.
|
||||
- **Catalogue_Modules** : La liste des Modules disponibles sur la Plateforme, chacun associé au Pack qui le débloque.
|
||||
- **Pack** : Une formule d'abonnement regroupant un ensemble de Modules. Trois Packs existent : **Starter** (gratuit), **Pro**, **Premium**. Un Pack inclut tous les Modules des Packs de niveau inférieur.
|
||||
- **Abonnement** : La souscription individuelle d'un Mandataire à un Pack, déterminant les Modules auxquels il a accès. Un Mandataire a exactement un Abonnement actif à un instant donné.
|
||||
- **Retrogradation** : Le passage d'un Abonnement vers un Pack de niveau inférieur (ou l'expiration d'un Pack payant vers Starter). Les données des Modules devenus inaccessibles sont **conservées** et restent accessibles **en consultation a minima**.
|
||||
- **Module_Accessible** : Un Module auquel le Mandataire a accès en pleine fonctionnalité au titre de son Abonnement courant.
|
||||
- **Module_Consultation_Seule** : Un Module non inclus dans l'Abonnement courant mais pour lequel le Mandataire possède des données antérieures, accessible en lecture seule à la suite d'une Retrogradation.
|
||||
- **Administrateur** : Le gestionnaire de la Plateforme (exploitant SchotzimmO), disposant de droits d'administration transverses (comptes, abonnements, Catalogue_Modules, Onboarding_Reseau, indicateurs d'usage par Réseau). Rôle distinct de celui de Mandataire.
|
||||
- **Tableau_De_Bord** : L'écran d'accueil présenté après connexion, regroupant les actions du jour et des indicateurs clés (KPI), alimenté par le socle et enrichi par les Modules_Accessibles.
|
||||
- **KPI** : Un indicateur clé de performance affiché sur le Tableau_De_Bord ou dans un reporting.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: Inscription d'un Mandataire
|
||||
|
||||
**User Story:** En tant que mandataire immobilier, je veux créer un compte sur SchotzimmO en indiquant mon réseau et mon identité, afin d'accéder aux outils de gestion de mon activité.
|
||||
|
||||
> Exigences techniques associées (validation serveur, unicité, hachage du mot de passe, codes HTTP) : `design-requirements.md`, Requirement 1.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Frontend SHALL proposer un parcours d'inscription demandant au minimum : Adresse_Email, Mot_De_Passe, nom, prénom, Reseau et Identifiant_Reseau.
|
||||
2. THE Frontend SHALL permettre de renseigner, à l'inscription, un Statut_Juridique et un Numero_Siren optionnel.
|
||||
3. WHEN le Mandataire soumet le formulaire d'inscription avec tous les champs obligatoires valides, THE Backend SHALL créer un Compte rattaché au Reseau choisi et retourner une confirmation.
|
||||
4. IF l'Adresse_Email soumise correspond déjà à un Compte existant, THEN THE Backend SHALL refuser la création et signaler que l'adresse est déjà utilisée, sans révéler d'autre information sur le Compte existant.
|
||||
5. IF le Mot_De_Passe soumis comporte moins de 8 caractères, THEN THE Backend SHALL refuser la création et signaler que le mot de passe est trop court.
|
||||
6. IF un champ obligatoire (Adresse_Email, Mot_De_Passe, nom, prénom, Reseau, Identifiant_Reseau) est absent ou vide, THEN THE Frontend SHALL afficher un message d'erreur identifiant chaque champ manquant sans soumettre la requête au Backend.
|
||||
7. WHEN l'inscription est effectuée via un Lien_Parrainage valide, THE Frontend SHALL pré-renseigner le Reseau et l'identité du Parrain et empêcher la modification du Reseau pré-renseigné.
|
||||
8. WHEN un Compte est créé via un Lien_Parrainage valide, THE Backend SHALL établir l'Affiliation entre le nouveau Mandataire et le Parrain désigné à l'état validé.
|
||||
9. WHEN un Compte est créé, THE Backend SHALL lui attribuer par défaut un Abonnement au Pack Starter.
|
||||
10. IF le Numero_Siren est fourni mais ne respecte pas un format de numéro valide, THEN THE Backend SHALL refuser la création et signaler que le numéro est invalide.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 2: Authentification et session
|
||||
|
||||
**User Story:** En tant que mandataire, je veux me connecter de façon sécurisée et rester authentifié, afin d'accéder à mes données en toute confidentialité.
|
||||
|
||||
> Exigences techniques associées (JWT, durée de session, en-têtes, codes HTTP) : `design-requirements.md`, Requirement 2.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Frontend SHALL proposer un écran de connexion demandant l'Adresse_Email et le Mot_De_Passe.
|
||||
2. WHEN le Mandataire soumet des identifiants valides, THE Backend SHALL établir une Session et retourner au Frontend de quoi accéder aux ressources protégées.
|
||||
3. IF les identifiants soumis sont invalides, THEN THE Backend SHALL refuser la connexion et retourner un message générique n'indiquant pas lequel de l'Adresse_Email ou du Mot_De_Passe est erroné.
|
||||
4. WHILE une Session est active, THE Backend SHALL n'autoriser l'accès aux ressources d'un Mandataire qu'à ce Mandataire authentifié.
|
||||
5. WHEN le Mandataire demande à se déconnecter, THE Frontend SHALL mettre fin à la Session côté client et rediriger vers l'écran de connexion.
|
||||
6. WHEN le Mandataire coche « Se souvenir de moi » lors d'une connexion réussie, THE Frontend SHALL conserver l'Identifiant_Mémorisé pour pré-remplir l'Adresse_Email lors des visites ultérieures sur ce navigateur, sans jamais conserver le Mot_De_Passe.
|
||||
7. WHEN le Mandataire demande une réinitialisation de mot de passe pour une Adresse_Email, THE Backend SHALL générer un Jeton_Reinitialisation à usage unique et à durée de vie limitée et l'envoyer par e-mail, en répondant de façon identique que l'adresse existe ou non.
|
||||
8. WHEN le Mandataire soumet un nouveau Mot_De_Passe avec un Jeton_Reinitialisation valide et non expiré, THE Backend SHALL mettre à jour le Mot_De_Passe et invalider le jeton.
|
||||
9. IF un Jeton_Reinitialisation est expiré, déjà utilisé ou inconnu, THEN THE Backend SHALL refuser la réinitialisation et signaler que le lien n'est plus valide.
|
||||
10. WHILE aucune Session valide n'est présente, THE Frontend SHALL rediriger toute tentative d'accès à une ressource protégée vers l'écran de connexion.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 3: Réseaux
|
||||
|
||||
**User Story:** En tant qu'exploitant, je veux que chaque mandataire soit rattaché à un réseau, afin de cloisonner les hiérarchies et de préparer l'ouverture à d'autres réseaux que IAD.
|
||||
|
||||
> Exigences techniques associées (référentiel des réseaux, cloisonnement en base) : `design-requirements.md`, Requirement 3.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL maintenir un référentiel de Reseaux ; seuls les Reseaux actifs et initialisés (Onboarding_Reseau effectué) SHALL être proposés à l'inscription.
|
||||
2. WHEN un Compte est créé, THE Backend SHALL le rattacher à exactement un Reseau et interdire la modification ultérieure de ce rattachement par le Mandataire.
|
||||
3. THE Backend SHALL garantir qu'aucune relation d'Affiliation ne relie deux Mandataires appartenant à des Reseaux différents.
|
||||
4. WHEN la Plateforme présente l'Annuaire, l'Arbre_Parrainage ou une Vue_Agregee à un Mandataire, THE Backend SHALL restreindre les données au seul Reseau du Mandataire.
|
||||
5. IF aucun Reseau n'est sélectionné à l'inscription (hors Lien_Parrainage), THEN THE Frontend SHALL empêcher la soumission et signaler que le Reseau est obligatoire.
|
||||
6. WHEN l'Administrateur réalise l'Onboarding_Reseau d'un nouveau Reseau, THE Backend SHALL initialiser l'espace de données propre à ce Reseau et enregistrer sa Personnalisation_Reseau avant toute inscription.
|
||||
7. IF un Mandataire tente de s'inscrire sur un Reseau non actif ou non initialisé, THEN THE Backend SHALL refuser l'inscription et signaler que le Reseau n'est pas disponible.
|
||||
8. WHERE un Reseau définit une Personnalisation_Reseau (thème, libellés), THE Frontend SHALL appliquer ce paramétrage par-dessus la présentation commune du socle.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 4: Parrainage et hiérarchie
|
||||
|
||||
**User Story:** En tant que mandataire, je veux déclarer mon parrain et mes filleuls et les inviter à rejoindre la plateforme, afin de reconstituer mon équipe au sein de mon réseau.
|
||||
|
||||
> Exigences techniques associées (e-mails d'invitation, génération et validité des Liens_Parrainage, états en base) : `design-requirements.md`, Requirement 4.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Frontend SHALL permettre à un Mandataire de déclarer son Parrain (au plus un) et ses Filleuls (plusieurs) au sein de son Reseau, en fournissant pour chaque personne déclarée au minimum son Adresse_Email, servant de clé de Rapprochement_Email.
|
||||
2. WHEN un Mandataire déclare un Parrain ou un Filleul non encore inscrit, THE Backend SHALL enregistrer un Membre_Declare correspondant dans l'Arbre_Parrainage du Reseau, sans Compte ni donnée métier associée.
|
||||
3. WHEN un Mandataire déclenche une Invitation vers un Parrain ou un Filleul déclaré, THE Backend SHALL envoyer un e-mail contenant un Lien_Parrainage pré-renseignant le Reseau et le lien hiérarchique.
|
||||
4. WHEN un destinataire s'inscrit via un Lien_Parrainage, THE Backend SHALL convertir le Membre_Declare correspondant en Compte et établir l'Affiliation à l'état validé.
|
||||
5. THE Backend SHALL garantir qu'un Filleul a au plus un Parrain à un instant donné.
|
||||
6. IF une déclaration de hiérarchie créerait un cycle dans l'Arbre_Parrainage (un Mandataire devenant ancêtre et descendant d'un autre), THEN THE Backend SHALL refuser la déclaration et signaler l'incohérence.
|
||||
7. WHILE une Affiliation reste supposée (Filleul non inscrit ou non confirmée), THE Backend SHALL n'exposer aucune donnée métier du Membre_Declare concerné.
|
||||
8. WHEN un Membre_Declare existe pour une Adresse_Email ensuite utilisée pour une inscription (via Rapprochement_Email), THE Backend SHALL rattacher l'inscription au Membre_Declare existant plutôt que d'en créer un doublon.
|
||||
9. WHEN un Mandataire déjà inscrit déclare comme Parrain ou Filleul un autre Mandataire déjà inscrit (cas de perte de lien, sans Rapprochement_Email préalable), THE Backend SHALL créer une Affiliation à l'état supposé en attente de Confirmation_Affiliation par la partie concernée.
|
||||
10. WHEN la partie concernée approuve une Confirmation_Affiliation, THE Backend SHALL faire passer l'Affiliation à l'état validé et recomposer l'Arbre_Parrainage en conséquence.
|
||||
11. IF la partie concernée refuse une Confirmation_Affiliation, THEN THE Backend SHALL ne pas établir le lien et conserver l'état antérieur de l'Arbre_Parrainage.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 5: Visibilité hiérarchique descendante
|
||||
|
||||
**User Story:** En tant que parrain, je veux consulter des informations sur ma descendance (agrégées sur tous les niveaux, détaillées pour mes filleuls directs), afin de suivre et d'accompagner mon équipe, sans que mes filleuls ne voient mes données.
|
||||
|
||||
> Exigences techniques associées (filtrage des requêtes par périmètre hiérarchique, portée par Module) : `design-requirements.md`, Requirement 5.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL appliquer le principe de Visibilite_Descendante : un Parrain peut consulter des informations de sa Descendance, et aucun Mandataire ne peut consulter les informations d'un de ses ancêtres ou d'un Mandataire d'une autre branche.
|
||||
2. WHEN un Parrain consulte une Vue_Agregee, THE Backend SHALL calculer les indicateurs agrégés sur l'ensemble de sa Descendance (tous niveaux) au sein de son Reseau, sans exposer de détail nominatif.
|
||||
3. WHEN un Parrain consulte une Vue_Detail_Direct, THE Backend SHALL limiter le détail aux seuls Filleuls_Directs (niveau N+1), dans les limites de portée définies par chaque Module concerné.
|
||||
4. IF un Mandataire tente d'accéder à des données hors de son périmètre de Visibilite_Descendante, THEN THE Backend SHALL refuser l'accès.
|
||||
5. WHERE un Module ne définit pas de données partageables en consultation hiérarchique, THE Backend SHALL n'exposer aucune donnée de ce Module dans les Vues_Agregees ou Vues_Detail_Direct.
|
||||
|
||||
> **Note d'évolution** : un paramètre « nombre de niveaux » permettant à un Parrain de limiter la portée de certaines Vues_Agregees est envisagé ultérieurement et n'entre pas dans le périmètre initial.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 6: Annuaire des abonnés
|
||||
|
||||
**User Story:** En tant que mandataire, je veux consulter un annuaire des membres de mon réseau, afin de visualiser l'organisation et de retrouver des contacts.
|
||||
|
||||
> Exigences techniques associées (champs exposés, recherche, pagination) : `design-requirements.md`, Requirement 6.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN un Mandataire consulte l'Annuaire, THE Backend SHALL retourner les Mandataires inscrits et les Membres_Declares de son Reseau uniquement.
|
||||
2. THE Frontend SHALL distinguer visuellement, dans l'Annuaire, les Mandataires inscrits des Membres_Declares non encore inscrits.
|
||||
3. THE Frontend SHALL permettre de rechercher un membre de l'Annuaire par nom, prénom ou Identifiant_Reseau.
|
||||
4. THE Backend SHALL n'exposer dans l'Annuaire que des informations d'identification et de contact de niveau annuaire, à l'exclusion de toute donnée métier des Modules.
|
||||
5. WHEN un Mandataire visualise son équipe, THE Frontend SHALL présenter l'Arbre_Parrainage de sa Descendance au sein de son Reseau.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 7: Abonnements et Packs
|
||||
|
||||
**User Story:** En tant que mandataire, je veux souscrire une formule d'abonnement, afin de débloquer les modules dont j'ai besoin ; en tant qu'exploitant, je veux financer l'hébergement et la maintenance via des formules payantes.
|
||||
|
||||
> Exigences techniques associées (modèle de données abonnement, paiement éventuel, codes HTTP) : `design-requirements.md`, Requirement 7.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL proposer exactement trois Packs : Starter (gratuit), Pro et Premium.
|
||||
2. THE Backend SHALL garantir qu'un Pack inclut l'ensemble des Modules des Packs de niveau inférieur (Pro inclut Starter ; Premium inclut Pro et Starter).
|
||||
3. THE Backend SHALL attribuer par défaut le Pack Starter à tout Compte sans Abonnement payant actif.
|
||||
4. WHEN un Mandataire souscrit ou change de Pack, THE Backend SHALL mettre à jour son Abonnement et recalculer la liste de ses Modules_Accessibles.
|
||||
5. WHEN une Retrogradation survient (changement vers un Pack inférieur ou expiration d'un Pack payant), THE Backend SHALL conserver l'intégralité des données des Modules devenus inaccessibles.
|
||||
6. WHILE un Module n'est plus inclus dans l'Abonnement courant mais contient des données du Mandataire, THE Backend SHALL exposer ce Module en Module_Consultation_Seule (lecture seule), sans permettre de nouvelle saisie ni modification.
|
||||
7. THE Backend SHALL garantir qu'un Mandataire a au plus un Abonnement actif à un instant donné.
|
||||
8. IF un Mandataire tente une action d'écriture sur un Module_Consultation_Seule, THEN THE Backend SHALL refuser l'action et signaler que le Module nécessite un abonnement de niveau supérieur.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 8: Catalogue et activation des Modules
|
||||
|
||||
**User Story:** En tant que mandataire, je veux n'accéder qu'aux modules compris dans mon abonnement, et voir clairement ceux que je pourrais débloquer, afin de comprendre l'offre.
|
||||
|
||||
> Exigences techniques associées (contrôle d'accès par Module, référentiel Module→Pack) : `design-requirements.md`, Requirement 8.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL maintenir un Catalogue_Modules associant chaque Module au Pack qui le débloque.
|
||||
2. THE Backend SHALL rattacher, au lancement, les Modules suivants aux Packs : Carnet_Adresses, KMS et Facturation au Pack Starter ; CRM et Agenda au Pack Pro.
|
||||
3. WHEN le Frontend présente la navigation, THE Frontend SHALL afficher les Modules_Accessibles comme actifs, les Modules_Consultation_Seule comme consultables, et les Modules non souscrits comme proposables à l'abonnement.
|
||||
4. WHEN un Mandataire tente d'accéder à un Module non inclus dans son Abonnement et sans données existantes, THE Backend SHALL refuser l'accès fonctionnel et le Frontend SHALL proposer la montée en abonnement correspondante.
|
||||
5. THE Backend SHALL vérifier, à chaque appel d'API d'un Module, que le Mandataire dispose du droit d'accès correspondant à son Abonnement (plein accès ou consultation seule).
|
||||
|
||||
---
|
||||
|
||||
### Requirement 9: Administration de la Plateforme
|
||||
|
||||
**User Story:** En tant qu'exploitant SchotzimmO, je veux administrer les comptes, les abonnements et le catalogue des modules, afin de gérer la plateforme et d'en suivre l'activité globale.
|
||||
|
||||
> Exigences techniques associées (rôle Administrateur, périmètre des droits, statistiques) : `design-requirements.md`, Requirement 9.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL distinguer le rôle Administrateur du rôle Mandataire et réserver les fonctions d'administration au seul Administrateur.
|
||||
2. WHEN l'Administrateur consulte la liste des Comptes, THE Backend SHALL retourner les Comptes de tous les Reseaux avec leur Reseau, leur Abonnement et leur statut.
|
||||
3. THE Backend SHALL permettre à l'Administrateur de modifier l'Abonnement d'un Compte et de gérer le rattachement des Modules aux Packs dans le Catalogue_Modules.
|
||||
4. WHEN l'Administrateur consulte les statistiques globales, THE Backend SHALL retourner des KPI consolidés (ex. nombre de Comptes par Reseau, répartition par Pack) sans exposer de donnée métier individuelle au-delà du nécessaire.
|
||||
5. IF un Mandataire non Administrateur tente d'accéder à une fonction d'administration, THEN THE Backend SHALL refuser l'accès.
|
||||
6. WHEN un différend ou une incohérence de hiérarchie lui est signalé, THE Backend SHALL permettre à l'Administrateur de corriger l'Arbre_Parrainage d'un Reseau (rattacher, détacher ou réaffecter un Mandataire à un Parrain), dans le respect du cloisonnement par Reseau et de l'interdiction des cycles.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 10: Tableau de bord
|
||||
|
||||
**User Story:** En tant que mandataire, je veux retrouver à l'ouverture mes actions du jour et des indicateurs clés, afin de démarrer ma journée efficacement.
|
||||
|
||||
> Exigences techniques associées (agrégation inter-modules, rafraîchissement) : `design-requirements.md`, Requirement 10.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN un Mandataire se connecte, THE Frontend SHALL présenter le Tableau_De_Bord comme écran d'accueil.
|
||||
2. THE Tableau_De_Bord SHALL regrouper les actions du jour et des KPI issus des seuls Modules_Accessibles du Mandataire.
|
||||
3. WHERE un Module_Accessible fournit des éléments pour le Tableau_De_Bord, THE Frontend SHALL afficher la section correspondante ; WHERE aucun Module ne fournit d'élément, THE Frontend SHALL afficher un état vide explicite.
|
||||
4. WHEN le Mandataire sélectionne une action ou un indicateur du Tableau_De_Bord, THE Frontend SHALL le rediriger vers le Module concerné.
|
||||
5. THE Backend SHALL ne fournir au Tableau_De_Bord que des données appartenant au Mandataire ou relevant de sa Visibilite_Descendante.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 11: Cadre applicatif et navigation
|
||||
|
||||
**User Story:** En tant que mandataire, je veux une interface cohérente avec une navigation claire entre les modules et l'accès à mon profil, afin d'utiliser la plateforme facilement.
|
||||
|
||||
> Exigences techniques associées (structure de navigation, avatar, pages de paramétrage) : `design-requirements.md`, Requirement 11.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Frontend SHALL présenter une navigation donnant accès au Tableau_De_Bord, à l'Annuaire et aux Modules selon l'Abonnement du Mandataire.
|
||||
2. THE Frontend SHALL afficher un contrôle d'identité (avatar) donnant accès au Profil, au paramétrage, à l'Abonnement et à la déconnexion.
|
||||
3. WHEN le Mandataire accède à son Profil, THE Frontend SHALL permettre de consulter et modifier son identité, ses coordonnées et les données transverses, à l'exception du Reseau de rattachement.
|
||||
4. THE Frontend SHALL présenter de façon cohérente, sur tous les Modules, l'en-tête, la navigation et les messages d'erreur ou de confirmation.
|
||||
5. WHEN un Module n'est pas accessible, THE Frontend SHALL le signaler dans la navigation et proposer, le cas échéant, l'accès en consultation seule ou la montée en abonnement.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 12: Persistance et isolation des données par Réseau
|
||||
|
||||
**User Story:** En tant que mandataire, je veux que mes données soient strictement isolées de celles des autres mandataires, afin de garantir la confidentialité de mon activité.
|
||||
|
||||
> Exigences techniques associées (cloisonnement par tenant en base MariaDB, index, intégrité) : `design-requirements.md`, Requirement 12.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Backend SHALL persister les données métier de chaque Mandataire de manière durable, dans la base de données propre à son Reseau, rattachées à son Compte.
|
||||
2. THE Backend SHALL garantir qu'une requête de données d'un Mandataire ne retourne que des données de son propre Reseau lui appartenant ou relevant de sa Visibilite_Descendante, aucune donnée d'un autre Reseau ne pouvant être atteinte.
|
||||
3. IF une requête vise des données n'appartenant pas au Mandataire et hors de sa Visibilite_Descendante, THEN THE Backend SHALL refuser l'accès sans divulguer l'existence de ces données.
|
||||
4. WHEN une opération d'écriture échoue partiellement, THE Backend SHALL annuler l'intégralité de l'opération et conserver l'état précédent sans modification partielle.
|
||||
5. THE Backend SHALL garantir l'isolation des Arbres_Parrainage et des données entre Reseaux distincts.
|
||||
6. WHEN un Compte quitte le Reseau (désinscription ou départ), THE Backend SHALL rattacher par défaut sa branche (ses Filleuls_Directs) au Parrain du Compte partant (grand-parrain), en préservant la cohérence de l'Arbre_Parrainage et l'absence de cycle.
|
||||
7. WHERE le rattachement par défaut d'une branche ne correspond pas à la réalité du Reseau, THE Backend SHALL permettre aux Mandataires concernés de réorganiser la branche via le mécanisme de déclaration et de Confirmation_Affiliation (auto-gestion), et à l'Administrateur de corriger l'Arbre_Parrainage.
|
||||
8. IF le Compte partant n'a pas de Parrain (racine de branche), THEN THE Backend SHALL conserver ses Filleuls_Directs comme nouvelles racines de branche de leur Reseau, sans rattachement ascendant.
|
||||
Reference in New Issue
Block a user