Aller au contenu

Story CP-013 : Canal B — Le CRM Crée un Tenant + Emails + Authentification

Module : control-plane Slice : Slice 13a from architecture (split from original CP-013) Brand context : [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority : 13 Depends on : CP-008 Estimated complexity : S


Objectif

Créer l'endpoint API interne permettant au module CRM (Sprint 3) de convertir des prospects en tenants. Il s'agit du parcours d'onboarding "Canal B" — où un commercial freelance inscrit directement un prospect depuis le CRM, en contournant le flux d'auto-inscription. Cette story établit le contrat d'interface, l'authentification service-à-service, la création de tenant depuis les données du prospect et les deux emails d'onboarding (création de mot de passe + lien de paiement). La gestion du désinscription, l'attribution des commissions et le provisionnement Canal B sont dans CP-030.


Périmètre Backend

Entités & Records

Événement :

public record ProspectConvertedToTenant(
    UUID prospectId,
    UUID tenantId,
    String companyName,
    String countryCode,
    String employeeCountRange,
    String adminEmail,
    String adminFullName,
    String adminPhone,
    List<String> selectedModuleCodes,
    UUID salespersonId,
    Instant timestamp
) {}

DTOs :

public record CreateTenantFromProspectRequest(
    @NotBlank String companyName,
    @NotBlank String countryCode,
    @NotBlank String employeeCountRange,
    @NotBlank @Email String adminEmail,
    @NotBlank String adminFullName,  // Split at first space → User.first_name + User.last_name
    @NotBlank String adminPhone,
    @Nullable String rccm,
    @NotEmpty List<String> selectedModuleCodes,
    @NotNull UUID salespersonId,
    @NotNull UUID prospectId
) {}

public record CreateTenantFromProspectResponse(
    UUID tenantId,
    String status  // "PENDING"
) {}

Couche Service

CrmConversionListener.java : - Écoute l'événement ProspectConvertedToTenant (si publié en interne) - OU traite la requête API directement : 1. Créer un Tenant avec status=PENDING, onboarding_channel=CRM, salesperson_id défini 2. Envoyer Email #1 : lien de création de mot de passe (expire dans 7 jours) — template ONBOARDING_WELCOME_B_PASSWORD 3. Envoyer Email #2 : lien de paiement avec résumé des modules — template ONBOARDING_WELCOME_B_PAYMENT 4. L'email d'activation inclut un lien de désinscription (la gestion du désinscription est dans CP-030) 5. Retourner tenantId

Endpoints API

Méthode Chemin Corps de requête Réponse Auth
POST /api/v1/internal/tenants/from-prospect CreateTenantFromProspectRequest CreateTenantFromProspectResponse JWT service-à-service

Règles de validation

  • Même validation que l'auto-inscription : email unique, téléphone unique, nom d'entreprise min 2
  • salespersonId : doit être un UUID valide (référençant une future entité utilisateur CRM)
  • prospectId : stocké sur le tenant pour traçabilité
  • JWT service-à-service : client Keycloak spécial avec le rôle CRM_SERVICE

Considérations multi-tenant

  • L'endpoint interne n'est PAS tenant-scopé (il crée un NOUVEAU tenant)
  • Le JWT service-à-service ne porte pas de tenant_id — il porte l'identité du service

Périmètre Frontend

Aucun pour cette story. La page de désinscription est dans CP-030.


Critères d'acceptation

AC-101: CRM creates tenant via internal API
Given the CRM module calls POST /api/v1/internal/tenants/from-prospect
When the request includes valid prospect data and salesperson_id
Then a tenant is created with status=PENDING and onboarding_channel=CRM
And salesperson_id is recorded on the tenant

AC-102: Channel B emails sent
Given a tenant is created via CRM
When the tenant creation succeeds
Then the tenant admin receives:
  - Email #1: password creation link (expires 7 days)
  - Email #2: payment link with module selection summary

AC-105: Service-to-service auth required
Given a regular tenant user JWT
When calling POST /api/v1/internal/tenants/from-prospect
Then HTTP 403 is returned (requires CRM_SERVICE role)

Règles OHADA & Réglementaires

  • L'email d'activation DOIT inclure un lien de désinscription (le destinataire n'a pas demandé cette inscription). Cela est conforme aux bonnes pratiques de protection des données (Loi 2013-450). Le lien de désinscription est inclus dans le template email ; le gestionnaire de désinscription est implémenté dans CP-030.
  • Commission du commercial : Suivie sur le paiement, pas sur l'inscription (PRD FR-CP-005). Le Control Plane stocke salesperson_id ; le calcul des commissions relève du module CRM.

Standards & Conventions

  • docs/standards/java-spring-guidelines.md — §Application Events, §Authentification service-à-service
  • docs/standards/api-guidelines.md — §Endpoints internes, §JWT service

Exigences de test

Tests unitaires

  • CrmConversionListener : création de tenant avec les champs corrects (status=PENDING, onboarding_channel=CRM, salesperson_id)
  • Dispatch email : les deux templates déclenchés avec les données correctes
  • Validation : email unique, téléphone unique, nom d'entreprise min 2

Tests d'intégration

  • POST /api/v1/internal/tenants/from-prospect — flux complet : requête → tenant créé → emails mis en file d'attente
  • Validation du JWT service-à-service (rôle CRM_SERVICE requis)
  • JWT utilisateur ordinaire → 403

Ce que le QA validera

  • Le contrat API correspond aux attentes du module CRM (Swagger)
  • Les emails contiennent les liens corrects (vérification manuelle via outil de test d'email)
  • Le tenant créé avec tous les champs correctement renseignés

Hors périmètre

  • Gestion du désinscription — CP-030
  • Suivi de l'attribution des commissions — CP-030
  • Pipeline de provisionnement pour le Canal B — CP-030
  • Module CRM lui-même — Sprint 3
  • Calcul des commissions commerciales — Module CRM
  • Porte d'approbation manager CRM — Explicitement NON inclus (FR-CP-005)
  • Canal C (création back-office admin) — V2
  • Rapport hebdomadaire d'inscriptions CRM — CP-012 (fonctionnalité de la console admin)

Définition de Done

  • [ ] Tous les endpoints backend implémentés et retournant les réponses correctes
  • [ ] Pas de pages frontend dans cette story
  • [ ] Les micro-textes français dans les templates email correspondent exactement à la spec design
  • [ ] Tous les critères d'acceptation passent manuellement
  • [ ] Tests unitaires écrits et passants
  • [ ] Tests d'intégration écrits et passants
  • [ ] Isolation multi-tenant vérifiée (données du nouveau tenant isolées)
  • [ ] Pas de types Java bruts — tous les génériques explicites
  • [ ] Les réponses API respectent le format d'enveloppe standard
  • [ ] Entrées d'audit créées pour chaque mutation de données