# Flux_Evenements_DataFlow.md
## Contrat d'Interface Inter-Services — Bus d'Événements Redis
### Digital Workspace — MONENTREPRISE
**Version : 1.0 — Mars 2025**
**Statut : DOCUMENT CONTRACTUEL — toute modification nécessite validation R&D**

---

> **Règle d'or** : Ce fichier est la source de vérité unique pour la communication
> entre microservices. Un service ne peut ni publier ni consommer un événement
> non listé ici. Aucune exception.

---

## 1. Principes Fondamentaux

### 1.1 Zéro Saisie Double
Toute donnée créée ou modifiée dans un service est automatiquement propagée
aux services abonnés via le bus Redis. **Aucune information ne doit être
ressaisie manuellement dans une autre interface.**

### 1.2 Découplage Fort
Un service publie un événement et **ne connaît pas ses consommateurs**.
Il ne fait aucun appel HTTP direct vers un autre service.
La communication est exclusivement asynchrone via Redis Streams.

### 1.3 Résilience
Si un consommateur tombe, les messages s'accumulent dans la Pending Entry List
(PEL) Redis et sont consommés à la reprise. **Un service défaillant ne bloque
jamais les autres.**

### 1.4 Idempotence Obligatoire
Chaque consommateur vérifie `t_processed_events` avant traitement.
Un événement déjà traité est silencieusement ignoré (pas d'erreur, pas de
double traitement).

---

## 2. Nomenclature des Canaux

### 2.1 Format Strict
```
[pays].[module].[entité].[action]
```

### 2.2 Valeurs Autorisées par Segment

| Segment  | Valeurs                                                                 |
|----------|-------------------------------------------------------------------------|
| `pays`   | `bj` · `tg` · `fr` · `mg` · `*` (broadcast multi-pays)                |
| `module` | `auth` · `hr` · `crm` · `project` · `doc` · `ticket` · `finance` · `it` · `comms` · `platform` · `report` |
| `entité` | Voir catalogues par module ci-dessous                                   |
| `action` | `created` · `updated` · `deleted` · `assigned` · `escalated` · `closed` · `approved` · `rejected` · `expired` · `onboarded` · `offboarded` · `generated` · `triggered` · `violated` · `paid` · `sent` · `toggled` · `purchased` · `detected` |

### 2.3 Canaux Redis Streams (groupes de consommateurs)
```
workspace.events.{pays}.{module}
```
Exemples :
- `workspace.events.bj.auth`
- `workspace.events.*.platform`
- `workspace.events.fr.finance`

---

## 3. Payload Standard — Format Obligatoire

```json
{
  "event":      "bj.hr.employee.onboarded",
  "event_id":   "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id":  "acme",
  "timestamp":  "2025-03-15T08:00:00Z",
  "data": {
    // Champs spécifiques à l'événement — voir catalogues ci-dessous
  },
  "meta": {
    "source_service":  "hr-service",
    "source_user":     "uuid-de-l-utilisateur-auteur",
    "ip":              "192.168.1.10",
    "correlation_id":  "uuid-v4-pour-traçabilité"
  }
}
```

### 3.1 Règles sur les champs

| Champ            | Type     | Obligatoire | Description                                      |
|------------------|----------|-------------|--------------------------------------------------|
| `event`          | string   | OUI         | Nom complet au format nomenclature               |
| `event_id`       | UUID v4  | OUI         | Identifiant unique — base de l'idempotence       |
| `tenant_id`      | string   | OUI         | Slug du tenant concerné                          |
| `timestamp`      | ISO 8601 | OUI         | Date/heure UTC de l'événement                    |
| `data`           | object   | OUI         | Données métier — voir schéma par événement       |
| `meta.source_service` | string | OUI    | Nom du service émetteur (`config('app.service_name')`) |
| `meta.source_user` | UUID   | NON         | User à l'origine de l'action (null si système)   |
| `meta.ip`        | string   | NON         | IP de la requête (0.0.0.0 si worker queue)       |
| `meta.correlation_id` | UUID v4 | NON    | Traçabilité bout-en-bout pour logs               |

---

## 4. Pattern d'Idempotence — Implémentation de Référence

### 4.1 Vérification avant traitement (BaseConsumer)
```php
// Avant tout traitement dans handle()
$alreadyProcessed = ProcessedEvent::where('event_id', $eventId)
    ->where('service_name', config('app.service_name'))
    ->exists();

if ($alreadyProcessed) {
    return; // Silencieux — pas d'erreur
}

// Traitement métier...

// Enregistrement APRÈS succès
ProcessedEvent::create([
    'event_id'     => $eventId,
    'service_name' => config('app.service_name'),
]);
```

### 4.2 Compteur de tentatives (XPENDING — pas le payload)
```php
// Récupérer le delivery-count natif Redis (source de vérité)
$pending = Redis::xpending(
    $streamKey, $groupName, '-', '+', 1, $messageId
);
$deliveryCount = $pending[0][4] ?? 1;

if ($deliveryCount >= 3) {
    // Déplacer en DLQ et alerter Zabbix
    $this->sendToDlq($messageId, $payload);
    Redis::xack($streamKey, $groupName, $messageId);
    return;
}
```

---

## 5. Dead Letter Queue (DLQ)

### 5.1 Canal DLQ
```
workspace.dlq.{pays}.{module}
```

### 5.2 Format du message DLQ
```json
{
  "original_event_id": "uuid",
  "original_channel":  "workspace.events.bj.hr",
  "original_payload":  { /* payload complet */ },
  "failed_service":    "hr-service",
  "failure_reason":    "Description de l'erreur",
  "delivery_count":    3,
  "dlq_timestamp":     "2025-03-15T08:05:00Z"
}
```

### 5.3 Alerte Zabbix sur DLQ non vide
Tout message en DLQ déclenche une alerte via le canal Redis :
```
*.monitoring.service.alert
```

---

## 6. Catalogue des Événements — LOT 1

---

### E-001 · `*.auth.user.created`

**Canal Redis** : `workspace.events.*.auth`
**Émetteur** : `auth-service`
**Déclencheur** : Création d'un compte utilisateur (inscription, provisioning tenant, onboarding RH)

**Payload `data`** :
```json
{
  "user_id":    "uuid",
  "email":      "user@acme.workspace.bj",
  "full_name":  "Jean Dupont",
  "role":       "collaborateur",
  "department": "rd",
  "pays":       "bj",
  "is_active":  true
}
```

**Consommateurs et actions attendues** :

| Service         | Action                                                            |
|-----------------|-------------------------------------------------------------------|
| `hub-service`   | Ajouter l'utilisateur à l'index de recherche globale              |
| `comms-service` | Créer le profil de présence, rejoindre le channel `#général`      |
| `report-service`| Mettre à jour le compteur d'utilisateurs actifs dans les agrégats |

---

### E-002 · `*.auth.user.updated`

**Canal Redis** : `workspace.events.*.auth`
**Émetteur** : `auth-service`
**Déclencheur** : Modification du profil, changement de rôle, changement de département

**Payload `data`** :
```json
{
  "user_id":       "uuid",
  "updated_fields": ["role", "department"],
  "old_values":    { "role": "collaborateur", "department": "rd" },
  "new_values":    { "role": "manager",       "department": "operations" }
}
```

**Consommateurs et actions attendues** :

| Service         | Action                                                             |
|-----------------|--------------------------------------------------------------------|
| `hub-service`   | Mettre à jour l'index de recherche                                 |
| `comms-service` | Ajuster les permissions de channels selon le nouveau rôle         |
| `project-service`| Recharger les permissions sur les projets assignés               |

---

### E-003 · `*.auth.user.deactivated`

**Canal Redis** : `workspace.events.*.auth`
**Émetteur** : `auth-service`
**Déclencheur** : Désactivation manuelle, offboarding RH, révocation par admin

**Payload `data`** :
```json
{
  "user_id":          "uuid",
  "email":            "user@acme.workspace.bj",
  "deactivated_by":   "uuid-admin",
  "reason":           "offboarding",
  "deactivated_at":   "2025-03-15T08:00:00Z"
}
```

**Consommateurs et actions attendues** :

| Service          | Action                                                          |
|------------------|-----------------------------------------------------------------|
| `comms-service`  | Passer le statut en offline, retirer des channels privés       |
| `project-service`| Réassigner les tâches ouvertes au manager du département       |
| `report-service` | Décrémenter le compteur d'utilisateurs actifs                   |

---

### E-004 · `*.auth.user.mfa_enabled`

**Canal Redis** : `workspace.events.*.auth`
**Émetteur** : `auth-service`
**Déclencheur** : Activation réussie du MFA TOTP

**Payload `data`** :
```json
{
  "user_id": "uuid",
  "role":    "tenant_admin"
}
```

**Consommateurs et actions attendues** :

| Service       | Action                                            |
|---------------|---------------------------------------------------|
| `hub-service` | Envoyer une notification de confirmation à l'user |

---

### E-005 · `*.platform.tenant.created`

**Canal Redis** : `workspace.events.*.platform`
**Émetteur** : `platform-service`
**Déclencheur** : Création d'un nouveau tenant (par super_admin)

**Payload `data`** :
```json
{
  "tenant_id":     "acme",
  "tenant_slug":   "acme",
  "tenant_name":   "Acme Corp",
  "pays":          "bj",
  "plan":          "pro",
  "admin_email":   "admin@acme.com",
  "admin_name":    "Alice Martin",
  "modules_actifs": ["M1", "M2", "M3", "M10", "M11"],
  "domain":        "acme.workspace.bj"
}
```

**Consommateurs et actions attendues** :

| Service         | Action                                                              |
|-----------------|---------------------------------------------------------------------|
| `auth-service`  | Provisionner le schéma PostgreSQL `tenant_acme`, créer le compte `tenant_admin` → publie E-001 |
| `hub-service`   | Initialiser le dashboard par défaut du tenant                       |
| `report-service`| Initialiser les agrégats vides pour le nouveau tenant               |

---

### E-006 · `*.platform.tenant.updated`

**Canal Redis** : `workspace.events.*.platform`
**Émetteur** : `platform-service`
**Déclencheur** : Modification des paramètres tenant (plan, quotas, white-label)

**Payload `data`** :
```json
{
  "tenant_id":      "acme",
  "updated_fields": ["plan", "max_users"],
  "old_values":     { "plan": "starter", "max_users": 10 },
  "new_values":     { "plan": "pro",     "max_users": 50 }
}
```

**Consommateurs et actions attendues** :

| Service         | Action                                           |
|-----------------|--------------------------------------------------|
| `auth-service`  | Mettre à jour les limites d'inscription          |
| `report-service`| Mettre à jour les métriques SaaS du super_admin  |

---

### E-007 · `*.platform.module.toggled`

**Canal Redis** : `workspace.events.*.platform`
**Émetteur** : `platform-service`
**Déclencheur** : Activation ou désactivation d'un module pour un tenant

**Payload `data`** :
```json
{
  "tenant_id":   "acme",
  "module_code": "M7",
  "is_active":   true,
  "toggled_by":  "uuid-admin"
}
```

**Consommateurs et actions attendues** :

| Service         | Action                                                           |
|-----------------|------------------------------------------------------------------|
| `hub-service`   | Afficher / masquer le module dans la navigation                  |
| `report-service`| Activer / désactiver les sources de données du module dans M11  |

---

### E-008 · `*.report.execution.completed`

**Canal Redis** : `workspace.events.*.report`
**Émetteur** : `report-service`
**Déclencheur** : Génération asynchrone d'un rapport PDF ou Excel terminée

**Payload `data`** :
```json
{
  "execution_id":  "uuid",
  "template_id":   "uuid",
  "format":        "pdf",
  "requested_by":  "uuid-user",
  "download_url":  "https://acme.workspace.bj/api/reports/executions/uuid/download",
  "file_size_kb":  245,
  "duration_ms":   3200
}
```

**Consommateurs et actions attendues** :

| Service       | Action                                                    |
|---------------|-----------------------------------------------------------|
| `hub-service` | Envoyer une notification push à l'utilisateur demandeur  |

---

### E-009 · `*.report.schedule.triggered`

**Canal Redis** : `workspace.events.*.report`
**Émetteur** : `report-service` (cron worker)
**Déclencheur** : Envoi automatique d'un rapport planifié

**Payload `data`** :
```json
{
  "schedule_id":    "uuid",
  "template_id":    "uuid",
  "recipients":     ["manager@acme.com", "pdg@acme.com"],
  "execution_id":   "uuid",
  "triggered_at":   "2025-03-15T08:00:00Z"
}
```

**Consommateurs** : Aucun (événement de traçabilité uniquement → audit log)

---

## 7. Catalogue des Événements — LOT 2

---

### E-010 · `*.hr.employee.onboarded`

**Canal Redis** : `workspace.events.*.hr`
**Émetteur** : `hr-service`
**Déclencheur** : Dossier employé validé et onboarding activé par le RH

**Payload `data`** :
```json
{
  "employee_id":   "uuid",
  "user_id":       "uuid",
  "full_name":     "Marie Koffi",
  "email":         "marie.koffi@acme.workspace.bj",
  "role":          "collaborateur",
  "department":    "rd",
  "manager_id":    "uuid-manager",
  "pays":          "bj",
  "start_date":    "2025-04-01",
  "contract_type": "CDI"
}
```

**Consommateurs et actions attendues** :

| Service          | Action                                                                    |
|------------------|---------------------------------------------------------------------------|
| `auth-service`   | Créer le compte utilisateur → publie E-001                                |
| `comms-service`  | Créer profil présence, rejoindre channels du département                  |
| `doc-service`    | Créer le dossier personnel `/tenants/{id}/employees/{employee_id}/`       |
| `project-service`| Ajouter l'employé aux projets en cours de son département                |
| `report-service` | Mettre à jour les agrégats RH (headcount, répartition par pays)           |

> ⚠️ **Critère de performance** : ces 5 actions doivent être complètes en < 3 secondes.

---

### E-011 · `*.hr.employee.offboarded`

**Canal Redis** : `workspace.events.*.hr`
**Émetteur** : `hr-service`
**Déclencheur** : Validation de l'offboarding par le RH (départ, licenciement, fin de contrat)

**Payload `data`** :
```json
{
  "employee_id":     "uuid",
  "user_id":         "uuid",
  "full_name":       "Marie Koffi",
  "email":           "marie.koffi@acme.workspace.bj",
  "department":      "rd",
  "manager_id":      "uuid-manager",
  "offboarding_date":"2025-04-30",
  "transfer_to":     "uuid-responsable-reprise"
}
```

**Consommateurs et actions attendues** :

| Service          | Action                                                              |
|------------------|---------------------------------------------------------------------|
| `auth-service`   | Désactiver le compte → publie E-003                                 |
| `comms-service`  | Retirer des channels, archiver les conversations                    |
| `project-service`| Réassigner les tâches vers `transfer_to`                           |
| `doc-service`    | Archiver le dossier personnel, transférer propriété des documents  |
| `report-service` | Mettre à jour les agrégats RH                                       |

---

### E-012 · `*.hr.leave.approved`

**Canal Redis** : `workspace.events.*.hr`
**Émetteur** : `hr-service`
**Déclencheur** : Approbation d'une demande de congé par le manager N+1

**Payload `data`** :
```json
{
  "leave_id":     "uuid",
  "employee_id":  "uuid",
  "user_id":      "uuid",
  "leave_type":   "conge_annuel",
  "start_date":   "2025-04-10",
  "end_date":     "2025-04-17",
  "days_count":   5,
  "approved_by":  "uuid-manager"
}
```

**Consommateurs et actions attendues** :

| Service          | Action                                                         |
|------------------|----------------------------------------------------------------|
| `project-service`| Marquer l'utilisateur indisponible sur la période dans Gantt   |
| `comms-service`  | Mettre à jour le statut de présence (absent avec dates)       |
| `report-service` | Mettre à jour les agrégats de congés (soldes, taux d'absence)  |

---

### E-013 · `*.hr.leave.rejected`

**Canal Redis** : `workspace.events.*.hr`
**Émetteur** : `hr-service`
**Déclencheur** : Refus d'une demande de congé

**Payload `data`** :
```json
{
  "leave_id":    "uuid",
  "employee_id": "uuid",
  "user_id":     "uuid",
  "reason":      "Période de forte activité sur le projet X"
}
```

**Consommateurs** :

| Service       | Action                                                  |
|---------------|---------------------------------------------------------|
| `hub-service` | Envoyer notification à l'employé avec le motif de refus |

---

### E-014 · `*.hr.payslip.generated`

**Canal Redis** : `workspace.events.*.hr`
**Émetteur** : `hr-service`
**Déclencheur** : Génération mensuelle des bulletins de salaire (job cron)

**Payload `data`** :
```json
{
  "payslip_id":    "uuid",
  "employee_id":   "uuid",
  "user_id":       "uuid",
  "period_month":  3,
  "period_year":   2025,
  "salary_gross":  450000,
  "net_payable":   389500,
  "currency":      "XOF",
  "pays":          "bj",
  "pdf_path":      "/tenants/acme/payslips/uuid.pdf"
}
```

**Consommateurs et actions attendues** :

| Service           | Action                                                      |
|-------------------|-------------------------------------------------------------|
| `finance-service` | Créer l'écriture comptable de charges de personnel (Lot 3)  |
| `hub-service`     | Notifier l'employé que son bulletin est disponible          |
| `report-service`  | Mettre à jour les agrégats de masse salariale               |

---

### E-015 · `*.crm.contact.created`

**Canal Redis** : `workspace.events.*.crm`
**Émetteur** : `crm-service`
**Déclencheur** : Création d'un nouveau contact ou compte client

**Payload `data`** :
```json
{
  "contact_id":  "uuid",
  "full_name":   "Paul Mensah",
  "email":       "paul@client.bj",
  "company_id":  "uuid",
  "company_name":"Client Corp",
  "assigned_to": "uuid-commercial",
  "pays":        "bj"
}
```

**Consommateurs** :

| Service         | Action                                            |
|-----------------|---------------------------------------------------|
| `hub-service`   | Indexer dans la recherche globale                 |
| `report-service`| Mettre à jour le compteur de contacts CRM         |

---

### E-016 · `*.crm.deal.created`

**Canal Redis** : `workspace.events.*.crm`
**Émetteur** : `crm-service`

**Payload `data`** :
```json
{
  "deal_id":            "uuid",
  "title":              "Déploiement ERP Acme",
  "contact_id":         "uuid",
  "company_id":         "uuid",
  "stage":              "qualification",
  "amount":             2500000,
  "currency":           "XOF",
  "assigned_to":        "uuid-commercial",
  "expected_close_date":"2025-06-30"
}
```

**Consommateurs** :

| Service         | Action                                         |
|-----------------|------------------------------------------------|
| `report-service`| Mettre à jour le pipeline CA prévisionnel      |

---

### E-017 · `*.crm.deal.closed_won`

**Canal Redis** : `workspace.events.*.crm`
**Émetteur** : `crm-service`
**Déclencheur** : Deal marqué "Gagné" par le commercial

**Payload `data`** :
```json
{
  "deal_id":    "uuid",
  "title":      "Déploiement ERP Acme",
  "contact_id": "uuid",
  "company_id": "uuid",
  "amount":     2500000,
  "currency":   "XOF",
  "won_at":     "2025-03-15T08:00:00Z",
  "assigned_to":"uuid-commercial"
}
```

**Consommateurs et actions attendues** :

| Service           | Action                                                           |
|-------------------|------------------------------------------------------------------|
| `project-service` | Créer automatiquement un projet depuis le deal gagné             |
| `finance-service` | Initialiser le dossier de facturation client (Lot 3)             |
| `report-service`  | Mettre à jour le CA réalisé, le taux de conversion               |

---

### E-018 · `*.crm.deal.closed_lost`

**Canal Redis** : `workspace.events.*.crm`
**Émetteur** : `crm-service`

**Payload `data`** :
```json
{
  "deal_id":       "uuid",
  "lost_reason":   "Prix trop élevé",
  "lost_to":       "Concurrent X",
  "assigned_to":   "uuid-commercial"
}
```

**Consommateurs** :

| Service         | Action                              |
|-----------------|-------------------------------------|
| `report-service`| Mettre à jour le taux de conversion  |

---

### E-019 · `*.crm.quote.sent`

**Canal Redis** : `workspace.events.*.crm`
**Émetteur** : `crm-service`

**Payload `data`** :
```json
{
  "quote_id":    "uuid",
  "deal_id":     "uuid",
  "contact_id":  "uuid",
  "total_ttc":   1850000,
  "currency":    "XOF",
  "sent_to":     "paul@client.bj",
  "expires_at":  "2025-04-15"
}
```

**Consommateurs** :

| Service         | Action                                  |
|-----------------|-----------------------------------------|
| `report-service`| Comptabiliser les devis envoyés         |

---

### E-020 · `*.ticket.ticket.created`

**Canal Redis** : `workspace.events.*.ticket`
**Émetteur** : `ticket-service`
**Déclencheur** : Création d'un ticket par email, portail, API ou messagerie

**Payload `data`** :
```json
{
  "ticket_id":    "uuid",
  "title":        "Impossible de me connecter",
  "priority":     "haute",
  "category":     "acces",
  "contact_id":   "uuid",
  "assigned_to":  "uuid-support",
  "sla_config_id":"uuid",
  "channel":      "portail",
  "created_at":   "2025-03-15T08:00:00Z"
}
```

**Consommateurs** :

| Service         | Action                                                          |
|-----------------|-----------------------------------------------------------------|
| `hub-service`   | Notifier l'agent assigné + le manager support                   |
| `report-service`| Mettre à jour les compteurs de tickets par priorité/catégorie   |

---

### E-021 · `*.ticket.ticket.assigned`

**Canal Redis** : `workspace.events.*.ticket`
**Émetteur** : `ticket-service`

**Payload `data`** :
```json
{
  "ticket_id":    "uuid",
  "assigned_to":  "uuid-agent",
  "assigned_by":  "uuid-manager",
  "previous_agent": null
}
```

**Consommateurs** :

| Service       | Action                               |
|---------------|--------------------------------------|
| `hub-service` | Notifier le nouvel agent assigné     |

---

### E-022 · `*.ticket.ticket.escalated`

**Canal Redis** : `workspace.events.*.ticket`
**Émetteur** : `ticket-service` (worker SLA ou action manuelle)

**Payload `data`** :
```json
{
  "ticket_id":      "uuid",
  "escalated_to":   "uuid-manager",
  "escalation_level": 2,
  "reason":         "sla_violation",
  "sla_type":       "resolution",
  "overdue_hours":  2
}
```

**Consommateurs** :

| Service         | Action                                                      |
|-----------------|-------------------------------------------------------------|
| `hub-service`   | Alerte critique visible sur le dashboard du manager         |
| `report-service`| Incrémenter le compteur d'escalades                         |

---

### E-023 · `*.ticket.ticket.closed`

**Canal Redis** : `workspace.events.*.ticket`
**Émetteur** : `ticket-service`

**Payload `data`** :
```json
{
  "ticket_id":          "uuid",
  "closed_by":          "uuid-agent",
  "resolution_summary": "Réinitialisation du mot de passe effectuée",
  "satisfaction_score": 5,
  "first_response_at":  "2025-03-15T08:12:00Z",
  "resolved_at":        "2025-03-15T09:30:00Z"
}
```

**Consommateurs** :

| Service         | Action                                                     |
|-----------------|------------------------------------------------------------|
| `report-service`| Mettre à jour MTTR, taux SLA, satisfaction moyenne         |
| `doc-service`   | Si solution documentable → suggestion de création article  |

---

### E-024 · `*.ticket.sla.violated`

**Canal Redis** : `workspace.events.*.ticket`
**Émetteur** : `ticket-service` (worker de surveillance SLA)

**Payload `data`** :
```json
{
  "ticket_id":    "uuid",
  "sla_type":     "first_response",
  "deadline_at":  "2025-03-15T09:00:00Z",
  "violated_at":  "2025-03-15T09:15:00Z",
  "overdue_min":  15,
  "assigned_to":  "uuid-agent"
}
```

**Consommateurs** :

| Service         | Action                                              |
|-----------------|-----------------------------------------------------|
| `hub-service`   | Alerte rouge sur le tableau de bord support         |
| `report-service`| Incrémenter le compteur de violations SLA           |

---

### E-025 · `*.doc.document.created`

**Canal Redis** : `workspace.events.*.doc`
**Émetteur** : `doc-service`

**Payload `data`** :
```json
{
  "document_id":  "uuid",
  "name":         "Contrat_Client_Acme_v1.pdf",
  "type":         "contract",
  "folder_path":  "/tenants/acme/contracts/",
  "created_by":   "uuid-user",
  "department":   "commercial",
  "size_bytes":   245000
}
```

**Consommateurs** :

| Service         | Action                                       |
|-----------------|----------------------------------------------|
| `hub-service`   | Indexer dans la recherche globale             |
| `report-service`| Mettre à jour le compteur de documents       |

---

### E-026 · `*.doc.document.shared`

**Canal Redis** : `workspace.events.*.doc`
**Émetteur** : `doc-service`

**Payload `data`** :
```json
{
  "document_id":  "uuid",
  "shared_with":  "uuid-user-ou-group",
  "share_type":   "internal",
  "expires_at":   null,
  "shared_by":    "uuid-user"
}
```

**Consommateurs** :

| Service       | Action                                                     |
|---------------|------------------------------------------------------------|
| `hub-service` | Notifier le destinataire du partage                        |

---

## 8. Catalogue des Événements — LOT 3

---

### E-027 · `*.finance.invoice.created`

**Canal Redis** : `workspace.events.*.finance`
**Émetteur** : `finance-service`
**Déclencheur** : Création manuelle ou automatique (depuis deal CRM E-017)

**Payload `data`** :
```json
{
  "invoice_id":  "uuid",
  "reference":   "FACT-2025-0042",
  "contact_id":  "uuid",
  "deal_id":     "uuid",
  "total_ht":    2000000,
  "tva_rate":    18,
  "total_ttc":   2360000,
  "currency":    "XOF",
  "due_date":    "2025-04-15",
  "pays":        "bj"
}
```

**Consommateurs** :

| Service         | Action                                             |
|-----------------|----------------------------------------------------|
| `hub-service`   | Notifier le responsable facturation                |
| `report-service`| Mettre à jour le CA facturé et la trésorerie prévisionnelle |

---

### E-028 · `*.finance.invoice.paid`

**Canal Redis** : `workspace.events.*.finance`
**Émetteur** : `finance-service`
**Déclencheur** : Rapprochement bancaire confirmé ou saisie manuelle

**Payload `data`** :
```json
{
  "invoice_id":   "uuid",
  "reference":    "FACT-2025-0042",
  "paid_amount":  2360000,
  "paid_at":      "2025-04-10T10:00:00Z",
  "payment_mode": "virement",
  "bank_ref":     "VIR-20250410-001"
}
```

**Consommateurs** :

| Service         | Action                                              |
|-----------------|-----------------------------------------------------|
| `crm-service`   | Mettre à jour le statut de paiement du client       |
| `report-service`| Mettre à jour la trésorerie réalisée                |

---

### E-029 · `*.finance.po.approved`

**Canal Redis** : `workspace.events.*.finance`
**Émetteur** : `finance-service`

**Payload `data`** :
```json
{
  "po_id":       "uuid",
  "reference":   "BC-2025-0015",
  "supplier":    "Fournisseur X",
  "total_ht":    500000,
  "currency":    "XOF",
  "approved_by": "uuid-manager",
  "approved_at": "2025-03-15T08:00:00Z"
}
```

**Consommateurs** :

| Service         | Action                                       |
|-----------------|----------------------------------------------|
| `report-service`| Mettre à jour les engagements d'achats       |

---

### E-030 · `*.it.asset.created`

**Canal Redis** : `workspace.events.*.it`
**Émetteur** : `it-service`

**Payload `data`** :
```json
{
  "asset_id":      "uuid",
  "name":          "MacBook Pro 14\" M3",
  "type":          "hardware",
  "serial_number": "C02X1234567",
  "assigned_to":   "uuid-user",
  "department":    "rd",
  "purchase_date": "2025-03-01",
  "warranty_end":  "2028-03-01"
}
```

**Consommateurs** :

| Service         | Action                                         |
|-----------------|------------------------------------------------|
| `report-service`| Mettre à jour l'inventaire IT agrégé           |

---

### E-031 · `*.it.incident.detected`

**Canal Redis** : `workspace.events.*.it`
**Émetteur** : `it-service` (consommateur webhook Zabbix)
**Déclencheur** : Alerte Zabbix reçue (service DOWN, latence critique, DLQ non vide)

**Payload `data`** :
```json
{
  "incident_id":     "uuid",
  "title":           "auth-service DOWN",
  "severity":        "critique",
  "affected_service":"auth-service",
  "detected_at":     "2025-03-15T08:00:00Z",
  "zabbix_alert_id": "12345",
  "auto_ticket":     true
}
```

**Consommateurs** :

| Service          | Action                                                         |
|------------------|----------------------------------------------------------------|
| `ticket-service` | Créer automatiquement un ticket IT avec priorité "critique"    |
| `hub-service`    | Afficher alerte rouge sur tous les dashboards admin du tenant  |
| `report-service` | Enregistrer l'incident dans les agrégats IT                    |

---

### E-032 · `*.it.license.expiring`

**Canal Redis** : `workspace.events.*.it`
**Émetteur** : `it-service` (cron job 30 jours avant expiration)

**Payload `data`** :
```json
{
  "license_id":     "uuid",
  "software_name":  "Adobe Creative Cloud",
  "expiry_date":    "2025-04-15",
  "days_remaining": 30,
  "seats_total":    5,
  "annual_cost":    350000,
  "currency":       "XOF"
}
```

**Consommateurs** :

| Service       | Action                                                          |
|---------------|-----------------------------------------------------------------|
| `hub-service` | Notifier le DSI avec alerte d'action requise                    |

---

### E-033 · `*.platform.module.purchased`

**Canal Redis** : `workspace.events.*.platform`
**Émetteur** : `platform-service` (après confirmation paiement Stripe/CinetPay)
**Déclencheur** : Achat d'un module depuis la Marketplace

**Payload `data`** :
```json
{
  "tenant_id":    "acme",
  "module_code":  "M7",
  "plan":         "pro",
  "amount":       25000,
  "currency":     "XOF",
  "payment_ref":  "STRIPE-pi_xxxx",
  "purchased_at": "2025-03-15T08:00:00Z"
}
```

**Consommateurs** :

| Service           | Action                                               |
|-------------------|------------------------------------------------------|
| `finance-service` | Créer la facture SaaS et l'écriture comptable        |
| `report-service`  | Mettre à jour les métriques MRR/ARR du super_admin   |

---

## 9. Matrice de Dépendances Inter-Services

```
PUBLIE ↓  / CONSOMME →   auth  hub  comms  project  doc  crm  ticket  finance  it   report
auth                       -    E001  E001   E002     -    -    -       -        -    E001
platform                  E005   -    -      -        -    -    -       -        -    E006
hr          E010/E011     E014  E012  E010   E012     E010  -   -      E014      -    E010
crm                        -   E015   -     E017      -    -    -      E017      -    E016
ticket                     -   E020   -      -        E023  -   -       -        -    E020
doc                        -   E025   -      -        -    -    E023    -        -    E025
finance                    -   E027   -      -        -   E028  -       -        -    E027
it                         -   E031   -      -        -    -   E031     -        -    E030
report                     -   E008   -      -        -    -    -       -        -    -
```

---

## 10. Règles de Gestion des Erreurs

### 10.1 Stratégie par type d'erreur

| Type d'erreur                        | Comportement                                                   |
|--------------------------------------|----------------------------------------------------------------|
| Erreur métier attendue               | ACK + log dans t_audit_logs, ne pas retenter                   |
| Erreur technique (DB down, timeout)  | NACK → retry automatique (PEL Redis)                          |
| Erreur après 3 tentatives            | DLQ + alerte Zabbix via `*.monitoring.service.alert`           |
| Event déjà traité (idempotence)      | ACK silencieux, ne rien faire                                   |
| Tenant non trouvé                    | ACK + log d'anomalie (le tenant a peut-être été supprimé)      |
| Payload malformé                     | ACK + DLQ avec reason="invalid_payload", ne jamais retenter    |

### 10.2 Format de l'alerte monitoring

```
Canal : *.monitoring.service.alert
```
```json
{
  "alert_type":    "dlq_message",
  "service":       "hr-service",
  "tenant_id":     "acme",
  "original_event":"bj.hr.employee.onboarded",
  "error":         "Database connection timeout",
  "timestamp":     "2025-03-15T08:05:00Z"
}
```

---

## 11. Checklist de Validation pour chaque Consommateur

Avant toute mise en production d'un consommateur, vérifier :

- [ ] L'événement est listé dans ce document
- [ ] L'idempotence est implémentée via `t_processed_events`
- [ ] Le contexte tenant est initialisé (`tenancy()->initialize()`) avant toute requête BDD
- [ ] `EventContext::fromEvent()` est passé à `EventPublisherService` si une cascade est publiée
- [ ] Les erreurs métier attendues sont ACK sans retry
- [ ] Les erreurs techniques laissent le message en PEL (NACK)
- [ ] Un test Pest couvre le happy path ET le cas idempotence
- [ ] Le service répond correctement à `/health` avant et après le traitement

---

*Document établi par MONENTREPRISE R&D — Version 1.0 — Mars 2025*
*Toute modification de ce contrat nécessite une validation formelle de l'équipe R&D*
*et la mise à jour synchronisée de tous les consommateurs concernés.*
