Aller au contenu

Module PRD+FSD - Ingestion (ING)

Papillon Collection Solution - V1 (MVP)

Préfixe module : ING | Pays : CI (Côte d'Ivoire) | Date : 2026-04-30

Source legal audit : docs/legal/audit/memo-conformite-CI-v2-audit.md (audité le 30/04/2026 - disposition LEGAL REVIEW REQUIRED). Mémo source : docs/legal/memo-conformite-CI-v2.md (Section F1 pour ING).

Legal status discipline (memo v2) - toute obligation INCERTAIN - avocat requis bloque l'implémentation des stories qui en dépendent et impose une note [LEGAL-REVIEW] dans la spec/story. Toute obligation PRÉLIMINAIRE déclenche un avertissement + [LEGAL-REVIEW]. La conformité ne se déduit jamais d'un statut INCERTAIN.

⚠️ BLOQUANT V1 go-live pour ING - Autorisation préalable ARTCI pour le traitement de numéros de téléphone (Loi CI n°2013-450, art. 7 + Décision ARTCI n°2023-0877 du 25/05/2023, p. 3) = VÉRIFIÉ mais BLOQUANT. Toute story ING peut être conçue, codée et testée en pré-production, mais l'ingestion en production est désactivée tant que l'autorisation n'est pas accordée. Exigence d'architecture : feature flag feature.artci.phone-auth.granted (défaut false) bloquant tous les endpoints d'ingestion en prod ; déclaration ARTCI préalable du responsable de traitement (art. 6) à confirmer côté courtier/assureur. Minimisation, finalité, chiffrement transit + repos, journalisation = VÉRIFIÉ.


V0 Envelope Scope (ajout 2026-05-21)

Lire docs/prd/prd-v0.md § 4 (ligne ING) et § 8 avant toute story V0. Ce PRD reste le contrat V1. Le V0 coupe FROM cette spec ; chaque sous-version V0 est additive et empile vers la cible V1.

ING dans le V0 (per prd-v0.md § 4 + § 8) :

Sous-version Périmètre vs V1 PRD Story IDs
[V0.0.0] Pickup S3 via cron @02:00 Africa/Abidjan par tenant. Validation simplifiée : colonnes core uniquement (contrat / client / montant / échéance). Pas d'upload UI. Format de fichier figé Day 1 (colonnes futures présentes mais peuvent être vides). ING-001
[V0.0.1] Gestion d'erreur S3 : cron idempotent, échec pickup -> audit + retry manuel. Pas de feedback opérateur. ING-002
[V0.0.2] Réintroduction incrémentale des règles de validation V1 (champs optionnels). ING-003
[V0.0.3] Upload UI disponible (admin cabinet téléverse via web). L'atelier de correction complet V1 est layered ici. ING-004
[V1] Spec V1 complète telle qu'écrite ci-dessous (retry/erreur avancé, atelier de correction complet, set de validation complet). -

Hors V0 entièrement : import en masse d'agences via CSV ingestion. Les agences sont créées via l'UI TENANT à partir de v0.0.2 (cf. prd-v0.md § 4 ligne TENANT), pas via un fichier d'ingestion.

BLOQUANT production V0 (identique au V1) : autorisation ARTCI numéros de téléphone (mémo B5) reste gating production via feature.artci.phone-auth.granted=false. L'ingestion pré-production est autorisée dès v0.0.0 ; l'ingestion production reste désactivée tant que l'autorisation ARTCI n'est pas accordée. Le founder a [FOUNDER-RISK-ACCEPTED] le dépôt du dossier ARTCI en parallèle, mais le gate production reste actif.


Sommaire


Partie A - Exigences Produit

A.1 Résumé exécutif

Le module Ingestion (ING) est la porte d'entrée des données de la Papillon Collection Solution. Il permet aux opérateurs d'un cabinet de courtage de téléverser les fichiers clients et contrats standardisés qui alimentent l'ensemble des campagnes de renouvellement en aval.

Problème résolu : Les petits cabinets de courtage gèrent leur portefeuille dans Excel ou dans leur propre système de gestion, sans pont automatisé vers la plateforme de renouvellement. ING accepte leur export tel quel (CSV ou XLSX), le valide, remonte les anomalies, et permet à l'opérateur de corriger les blocages directement dans le navigateur - sans retourner dans Excel - avant de valider la base de données canonique de la plateforme.

L'atelier de correction pré-campagne intégré à ING constitue la première ligne de défense contre les problèmes de qualité de données : chaque contrat qui atteint le moteur de campagne du module OP est soit propre, soit délibérément accepté avec ses anomalies non-bloquantes documentées.

Périmètre ING [V1] : import de fichier → validation → staging → atelier de correction → commit dans les tables customer et contract. Le module OP consomme ensuite ces tables pour la sélection des cohortes de renouvellement.

Hors périmètre ING : sélection de campagne, envoi de notifications, portail client, paiement.


A.2 Personas utilisateurs

Persona Rôle dans ING Référence V1 PRD
Admin cabinet [OPERATOR] Importe des fichiers pour toutes les agences du tenant ; configure les codes agence ; télécharge le modèle §7, §12
Opérateur agence [OPERATOR] Importe des fichiers limités aux agences qui lui sont attribuées ; corrige les anomalies dans l'atelier §7, §12
Superviseur [OPERATOR] Lecture seule : historique des imports, compteurs d'anomalies ; aucune permission d'upload §7
Client assuré [CUSTOMER] Aucun - l'ingestion est un flux purement opérateur -
Système [BACKEND] Job de sweep quotidien (expiration staging) ; publication de l'événement ImportCommitted -

A.3 User Stories

ING-US-01 - Téléverser un fichier d'import [V1] [OPERATOR]

En tant qu' opérateur, je veux téléverser un fichier CSV ou XLSX de données clients/contrats, afin que la plateforme le valide et que je puisse démarrer l'atelier de correction.

Critères d'acceptation :

  • Étant donné un opérateur autorisé qui téléverse un fichier CSV valide (≤5 Mo, ≤5 000 lignes de données, UTF-8, séparateur , ou ;, ligne d'en-tête obligatoire correspondant au modèle canonique) Quand le serveur reçoit le fichier Alors (a) le hash SHA-256 du fichier est vérifié ; si identique à un import précédent du même tenant, le téléversement est rejeté avec le message « Ce fichier a déjà été importé le JJ/MM/AAAA à HH:MM par \<opérateur> » ; (b) sur hash nouveau, le serveur parse le fichier, valide toutes les lignes, et renvoie en ≤10 s soit un résultat STAGED_CLEAN soit un résultat STAGED_WITH_ANOMALIES.
  • Étant donné un fichier parsé avec succès Quand TOUTES les lignes passent les contrôles d'anomalies bloquantes Alors le système crée un import en staging avec statut STAGED_CLEAN et redirige l'opérateur vers l'écran de confirmation de commit.
  • Étant donné un fichier parsé Quand ≥1 ligne présente une anomalie bloquante Alors le système crée un import en staging avec statut STAGED_WITH_ANOMALIES ; l'opérateur est dirigé vers l'atelier de correction ; aucune ligne n'est committée dans les tables de production.
  • Étant donné un fichier dont le format est complètement invalide (extension incorrecte, XLSX corrompu, encodage illisible) Quand le serveur tente de le parser Alors l'import est rejeté avant staging avec une erreur française descriptive ; aucune entrée staging n'est créée ; un événement d'audit ING_PARSE_FAILED est écrit.

ING-US-02 - Corriger les anomalies bloquantes dans l'atelier [V1] [OPERATOR]

En tant qu' opérateur, je veux corriger les anomalies bloquantes ligne par ligne dans le navigateur, afin de valider l'import sans retourner dans Excel.

Critères d'acceptation :

  • Étant donné un import en staging avec anomalies bloquantes Quand l'opérateur ouvre l'atelier de correction Alors il voit une liste paginée des lignes anomales (jusqu'à 200 par page) avec : le type d'anomalie, la colonne concernée, et un champ de correction en ligne pour chaque champ corrigeable (telephone, date_naissance, montant_du, code_agence).
  • Étant donné un opérateur qui modifie un champ Quand il soumet la correction Alors le serveur re-valide le champ isolément ; en cas de succès, l'anomalie bloquante de la ligne est résolue et le champ affiche sa valeur normalisée (E.164 pour le téléphone, DD/MM/AAAA pour la date, entier pour le montant) ; en cas d'échec, une erreur française est affichée en ligne et la ligne reste BLOCKED.
  • Étant donné un opérateur ayant corrigé TOUTES les anomalies bloquantes de l'import staging Quand il clique sur « Valider l'import » Alors toutes les lignes stagées sont committées en une seule transaction (upsert customer + upsert contract) et un événement ImportCommitted est publié.
  • Étant donné un opérateur qui ne peut ou ne veut pas terminer la correction Quand il clique sur « Annuler cet import » Alors l'entrée staging et ses lignes sont purgées ; un événement d'audit ING_ABANDONED est écrit.

ING-US-03 - Valider et commettre un import propre [V1] [OPERATOR]

En tant qu' opérateur, je veux commettre un import validé en une action, afin que les contrats soient immédiatement disponibles pour la sélection de campagne dans le module OP.

Critères d'acceptation :

  • Étant donné un import staging avec statut STAGED_CLEAN Quand l'opérateur clique sur « Confirmer l'import » Alors le système committe toutes les lignes stagées ; publie l'événement ImportCommitted (import_id, tenant_id, compteurs lignes créées / mises à jour / ignorées) ; redirige l'opérateur vers l'historique des imports.
  • Étant donné un échec de la transaction de commit (erreur base de données) Quand l'opérateur réessaie Alors l'entrée staging est toujours présente et le commit est retentable sans re-téléversement du fichier.
  • Étant donné un import STAGED_CLEAN ou STAGED_WITH_ANOMALIES non commité après 7 jours Quand le job de sweep quotidien s'exécute Alors l'entrée staging est purgée ; un événement d'audit ING_STAGING_EXPIRED est écrit ; la console OP affiche un badge d'alerte (« Import en attente expiré - [nom du fichier] du [date] ») jusqu'à ce que l'opérateur le ferme.

ING-US-04 - Consulter l'historique des imports [V1] [OPERATOR]

En tant qu' admin ou superviseur, je veux consulter l'historique des imports de mon tenant, afin d' auditer ce qui a été téléversé, par qui, et ce qui a été commité.

Critères d'acceptation :

  • Étant donné un admin Quand il ouvre l'historique des imports Alors il voit tous les imports du tenant (toutes agences), triés par date décroissante, avec : nom du fichier, pilule de statut (STAGED_CLEAN / STAGED_WITH_ANOMALIES / COMMITTED / ABANDONED / EXPIRED / PARSE_FAILED), nom de l'opérateur, horodatage de l'upload, compteurs de lignes.
  • Étant donné un opérateur agence Quand il ouvre l'historique Alors il ne voit que les imports scopés à ses agences attribuées.
  • Étant donné tout utilisateur autorisé Quand il clique sur un import Alors il peut consulter le rapport d'anomalies et (pour les imports COMMITTED) télécharger le fichier source d'origine depuis MinIO pendant 12 mois à compter de la date d'upload.

ING-US-05 - Télécharger le modèle de fichier [V1] [OPERATOR]

En tant que nouvel opérateur, je veux télécharger le modèle d'import canonique, afin de connaître exactement les colonnes que mon export doit contenir.

Critères d'acceptation :

  • Étant donné tout opérateur authentifié Quand il clique sur « Télécharger le modèle » dans l'interface d'import Alors un fichier XLSX pré-rempli est téléchargé avec les colonnes canoniques en français, une ligne de données exemple, et des commentaires in-cell expliquant chaque colonne. Nom du fichier : modele_import_papillon.xlsx.

A.4 Exigences non-fonctionnelles

NFR Cible Note
Latence parse + validation ≤10 s pour 5 000 lignes Synchrone ; pas de job queue nécessaire
Taille max fichier téléversé ≤5 Mo Appliqué par la couche HTTP avant parsing
Lignes max par fichier ≤5 000 lignes de données Rejeté avec TROP_DE_LIGNES au-delà
Durabilité staging 7 jours, survit au redémarrage serveur Tables PostgreSQL - pas Redis
Rétention fichiers source 12 mois dans MinIO puis purge automatique Provisoirement validé
Isolation tenant 100 % - aucune fuite inter-tenant tenant_id sur chaque requête
Écriture audit log Synchrone dans la transaction d'import Ne doit pas être perdu en cas de rollback
Charge opérateur (côté opérateur) Desktop-first, online-first Aucune contrainte low-bandwidth spécifique à ING
Rate-limit upload Max 10 tentatives par opérateur par heure Compteur Redis par user_id

A.5 Périmètre MVP - V1 vs V2/V3

Fonctionnalité Statut
Import CSV (UTF-8, ;/,) + XLSX V1
Modèle de colonnes rigide (en-têtes français, nom et prenoms séparés) V1
Déduplication par hash de fichier V1
Commit tout-ou-rien V1
Table de staging + atelier de correction V1
Correction en ligne : telephone, date_naissance, montant_du, code_agence V1
Classification des anomalies (bloquant / non-bloquant / surveillance) V1
Normalisation téléphone E.164 (+225, CI) V1
XOF codé en dur - pas de colonne devise V1
Rétention 12 mois fichiers source (MinIO) V1
Expiration auto staging (7 j) + badge quotidien V1
Admin = toutes agences ; Opérateur = agences attribuées V1
Événement domaine ImportCommitted → OP V1
Téléchargement du modèle XLSX V1
Imports déclenchés via API / connecteurs ERP / schedules V2
Mapping de colonnes par tenant V2
Format XLS (legacy Excel 97-2003) V2
Upload batch multi-fichiers V2
Colonne devise / multi-devises V2
Validation référentiel branche V2
TTL staging configurable par tenant V2
Notification email/SMS à l'opérateur sur expiration staging V2
Suggestions de correction assistées par IA V3

A.6 Questions ouvertes

  1. date_echeance dans le passé >30 jours = BLOQUANT ou NON-BLOQUANT ? Certains cabinets pourraient vouloir importer des fichiers historiques à des fins de réconciliation. Recommandation : verrouiller à BLOQUANT pour le pilote V1 ; prévoir un flag de configuration tenant (Autoriser les échéances passées) en V2.
  2. Validation référentiel branche : si le moteur de campagne OP filtre par branche, des valeurs texte libres rendront le filtrage non fiable. Recommandation : V1 accepte toute chaîne non vide ; V2 introduit une table de référence par tenant.
  3. Canal de la notification quotidienne "import en attente" : badge console OP uniquement (V1) ou aussi email/SMS à l'opérateur (V2) ?
  4. Lignes non-corrigeables dans le workshop (identifiant_client absent, doublon intra-fichier, date_echeance dépassée) : faut-il afficher un bouton « Supprimer cette ligne » pour permettre à l'opérateur de retirer les lignes irrécupérables sans re-téléverser le fichier ? → À arbitrer avant développement de l'atelier.

A.7 Provisoirement validé

Élément Hypothèse de travail Résolution requise
Rétention fichiers source : 12 mois Fenêtre d'audit CIMA + principe de minimisation ARTCI convergent vers 12 mois Avocat : confirmer sous loi n°2013-450 art. X + CIMA Rég. 01-24
Base légale de l'ingestion Sous-traitance des données du courtier en vertu du DPA plateforme-courtier Confirmer avec avocat ; obligations de déclaration ARTCI pour l'éditeur de la plateforme (lacune audit juridique #3)
PII au repos - colonnes plain text + chiffrement disque Acceptable pour le pilote V1 ; clients courtiers/assureurs doivent signer DPA reconnaissant le modèle shared-schema Réévaluer avant >10 tenants ou si un tenant BYO-DB demande un chiffrement au niveau champ
Base légale CIMA Rég. 01-24 - positionnement prestataire technique Asserté dans memo juridique ; ancré sur source secondaire (blog) Validation avocat obligatoire avant gel architecture (lacune audit #1)

Partie B - Spécifications Fonctionnelles

B.1 Règles fonctionnelles

ING-R-001 - Format d'import accepté [V1] [BACKEND]

SI le fichier téléversé a l'extension .csv ALORS parser en CSV (UTF-8 ; séparateur auto-détecté , ou ; ; première ligne = en-tête obligatoire). SI le fichier a l'extension .xlsx ALORS parser la première feuille uniquement (XLSX) ; les cellules Excel de type Date sont auto-converties en chaîne DD/MM/AAAA ; première ligne = en-tête obligatoire. SI le fichier a toute autre extension (.xls, .xlsm, .ods, .txt, etc.) ALORS rejeter immédiatement avec le code FORMAT_NON_SUPPORTE ; aucune entrée staging créée. Validation complémentaire : vérifier les magic bytes du fichier indépendamment de l'extension pour détecter les uploads malveillants (ex. fichier .csv avec magic bytes OOXML).

ING-R-002 - Limites de taille et de volume [V1] [BACKEND]

SI le fichier téléversé dépasse 5 Mo (mesuré après upload, avant parsing) ALORS rejeter avec FICHIER_TROP_GRAND avant tout parsing ; aucune entrée staging. SI le fichier parsé contient plus de 5 000 lignes de données (hors en-tête) ALORS rejeter avec TROP_DE_LIGNES ; aucune entrée staging.

ING-R-003 - Déduplication par hash de fichier [V1] [BACKEND]

Avant le parsing, calculer le SHA-256 du fichier brut téléversé. SI le SHA-256 existe déjà dans ingestion_import pour le même tenant_id avec un statut ≠ PARSE_FAILED ALORS rejeter avec FICHIER_DEJA_IMPORTE en incluant dans la réponse : date de l'import précédent, heure, nom de l'opérateur. Aucune entrée staging créée. Note : la déduplication par hash est par tenant_id - deux tenants différents peuvent importer le même fichier sans conflit.

ING-R-004 - Modèle de colonnes rigide [V1] [BACKEND]

SI la ligne d'en-tête du fichier ne contient pas toutes les colonnes obligatoires (correspondance exacte, insensible à la casse, caractères accentués en NFC canonique) ALORS rejeter avec EN_TETES_MANQUANTS en listant les colonnes manquantes. Aucune entrée staging.

Colonnes obligatoires : identifiant_client, nom, prenoms, telephone, date_naissance, identifiant_contrat, branche, date_echeance, montant_du, code_agence.

Colonnes optionnelles (ignorées si absentes, parsées si présentes) : email, numero_police, immatriculation, statut_source, informations_complementaires.

Toute colonne extra non présente dans la liste canonique : ignorée silencieusement ; journalisée en niveau INFO.

ING-R-005 - Normalisation du numéro de téléphone [V1] [BACKEND]

Pour chaque champ telephone d'une ligne : 1. Supprimer tous les espaces, tirets, points, parenthèses. 2. Si la chaîne commence par 00225 → remplacer par +225. 3. Si la chaîne est une séquence de 10 chiffres commençant par 0 (ex. 0712345678) → préfixer +225. 4. Si la chaîne commence par 225 sans + → préfixer +. 5. Le résultat doit correspondre au masque /^\+225[0-9]{10}$/ (format E.164 CI, 13 caractères).

SI le résultat ne correspond pas ALORS anomalie TELEPHONE_INVALIDEBLOQUANT. SI le champ est vide ALORS anomalie TELEPHONE_ABSENTBLOQUANT. Valeur stockée : toujours E.164 (+225XXXXXXXXXX).

ING-R-006 - Validation de la date de naissance [V1] [BACKEND]

Champ date_naissance : SI c'est une cellule Excel de type Date → convertir en chaîne DD/MM/AAAA. SI c'est une chaîne → valider le format DD/MM/AAAA (strict : 2 chiffres jour, 2 chiffres mois, 4 chiffres année). SI la date résultante est dans le futur OU antérieure au 01/01/1900 ALORS anomalie DATE_NAISSANCE_INVALIDEBLOQUANT. SI le champ est vide ALORS anomalie DATE_NAISSANCE_ABSENTEBLOQUANT. Tout autre format de chaîne (AAAA-MM-JJ, MM/JJ/AAAA, etc.) → anomalie DATE_NAISSANCE_INVALIDEBLOQUANT.

ING-R-007 - Validation de la date d'échéance [V1] [BACKEND]

Même validation de format que ING-R-006. SI le champ est vide ALORS anomalie DATE_ECHEANCE_ABSENTEBLOQUANT. SI la date parsée est antérieure de plus de 30 jours à la date d'import ALORS anomalie ECHEANCE_DEPASSEEBLOQUANT. (Provisoirement validé - pourra devenir un flag de configuration tenant en V2.)

ING-R-008 - Validation et normalisation du montant [V1] [BACKEND]

Champ montant_du : Formats d'entrée acceptés : entier (15000), avec séparateur espace (15 000), avec séparateur point (15.000). Supprimer tous les caractères non-numériques, parser en entier. SI le résultat est ≤ 0 ALORS anomalie MONTANT_INVALIDEBLOQUANT. SI le champ est vide ALORS anomalie MONTANT_ABSENTBLOQUANT. Stocké toujours en entier (unités XOF, 0 décimales). Devise XOF codée en dur pour V1 (CI uniquement) - pas de colonne devise dans le modèle V1.

ING-R-009 - Résolution du code agence [V1] [BACKEND]

Champ code_agence : SI vide ALORS anomalie CODE_AGENCE_ABSENTBLOQUANT. SI non vide ALORS rechercher la valeur code_agence (insensible à la casse) dans la table agency pour ce tenant_id. SI aucune correspondance ALORS anomalie CODE_AGENCE_INCONNU (valeur : le code inconnu) → BLOQUANT. SI le rôle de l'opérateur importateur est OPERATOR (pas ADMIN) et que l'agency_id résolu n'est pas dans ses agences autorisées ALORS anomalie CODE_AGENCE_NON_AUTORISEBLOQUANT (empêche un opérateur d'ingérer des lignes hors de son périmètre).

ING-R-010 - Identifiants et champs structurels obligatoires [V1] [BACKEND]

identifiant_client vide → anomalie IDENTIFIANT_CLIENT_ABSENTBLOQUANT. identifiant_contrat vide → anomalie IDENTIFIANT_CONTRAT_ABSENTBLOQUANT. branche vide → anomalie BRANCHE_ABSENTEBLOQUANT. nom vide → anomalie NOM_ABSENTBLOQUANT. prenoms vide → anomalie PRENOMS_ABSENTBLOQUANT.

ING-R-011 - Doublons intra-fichier [V1] [BACKEND]

SI le même tuple (identifiant_client, identifiant_contrat) apparaît plus d'une fois dans le fichier courant ALORS anomalie DOUBLON_INTRA_FICHIER sur la 2e occurrence et suivantes → BLOQUANT. Seule la première occurrence est stagée ; les doublons sont listés dans le rapport d'anomalies. Ce champ n'est pas corrigeable dans l'atelier (le fichier source doit être corrigé).

ING-R-012 - Clé de déduplication inter-fichiers [V1] [BACKEND]

La clé de déduplication d'un contrat entre plusieurs imports est (tenant_id, identifiant_client, identifiant_contrat). La date_echeance et le montant_du sont des données du contrat, pas des critères d'identité. Un import ultérieur avec la même clé mais un montant_du ou une date_echeance différents déclenche une mise à jour silencieuse + audit (voir ING-R-013) et une anomalie de surveillance (voir ING-R-014).

ING-R-013 - Champs optionnels [V1] [BACKEND]

email absent → anomalie EMAIL_ABSENTNON-BLOQUANT. numero_police absent → anomalie NUMERO_POLICE_ABSENTNON-BLOQUANT. Les anomalies non-bloquantes sont stockées dans la liste d'anomalies de la ligne stagée mais ne bloquent pas le commit. Elles sont visibles dans l'atelier à titre informatif.

ING-R-014 - Comportement tout-ou-rien au commit [V1] [BACKEND]

SI une ligne stagée a le statut BLOCKED au moment du commit ALORS le commit est rejeté dans sa totalité ; aucune ligne n'est promue dans les tables de production ; une erreur française est retournée indiquant le nombre d'anomalies bloquantes restantes.

SI toutes les lignes stagées sont PENDING_CLEAN ou CORRECTED (aucune ligne BLOCKED) ALORS la totalité est committée en une seule transaction DB : 1. Upsert customer par (tenant_id, source_customer_id) (ING-R-015). 2. Upsert contract par (tenant_id, source_customer_id, source_contract_id) mettant à jour date_echeance, montant_du, branche, statut_source (ING-R-016). 3. Insertion d'un enregistrement ingestion_import_row par ligne (traçabilité). 4. Insertion de l'entrée audit ING_COMMITTED. 5. Publication de l'événement domaine ImportCommitted. 6. Suppression de toutes les lignes staging de cet import. 7. Mise à jour ingestion_import.statusCOMMITTED.

Si la transaction échoue (erreur DB), l'entrée staging est préservée et le commit est retentable.

ING-R-015 - Upsert client (Customer) [V1] [BACKEND]

SI (tenant_id, source_customer_id) existe dans customer ALORS : - Mettre à jour nom et prenoms silencieusement ; émettre l'entrée d'audit ING_CUSTOMER_UPDATED avec le diff. - SI la date_naissance du fichier diffère de la valeur stockée ALORS créer une anomalie DATE_NAISSANCE_MODIFIEESURVEILLANCE (la ligne reste commitable, la divergence est signalée dans la vue anomalies).

SI (tenant_id, source_customer_id) n'existe pas ALORS insérer une nouvelle ligne customer ; émettre l'entrée d'audit ING_CUSTOMER_CREATED.

ING-R-016 - Upsert contrat (Contract) [V1] [BACKEND]

SI (tenant_id, source_customer_id, source_contract_id) existe dans contract ALORS : - Mettre à jour date_echeance, montant_du, branche, agency_id, statut_source, informations_complementaires, numero_police, immatriculation silencieusement. - SI date_echeance ou montant_du ont changé ALORS créer une anomalie DONNEES_CONTRAT_MODIFIEESSURVEILLANCE avec les valeurs avant/après ; émettre l'entrée d'audit ING_CONTRACT_UPDATED avec le diff.

SI (tenant_id, source_customer_id, source_contract_id) n'existe pas ALORS insérer une nouvelle ligne contract ; émettre l'entrée d'audit ING_CONTRACT_CREATED.

ING-R-017 - Permissions d'import [V1] [BACKEND]

Rôle ADMIN : peut importer des fichiers contenant des lignes de n'importe quelle agence du tenant. Rôle OPERATOR : peut importer des fichiers contenant des lignes uniquement des agences qui lui sont attribuées. Les lignes avec un code_agence hors périmètre → anomalie CODE_AGENCE_NON_AUTORISEBLOQUANT. Rôle READ : ne peut pas téléverser de fichier ; l'endpoint d'upload retourne HTTP 403.

ING-R-018 - Expiration du staging [V1] [BACKEND]

Un job système s'exécute quotidiennement à 02h00 heure locale : POUR CHAQUE ingestion_import avec statut IN (STAGED_CLEAN, STAGED_WITH_ANOMALIES) ET created_at < MAINTENANT() - INTERVALLE '7 jours' : 1. Supprimer les lignes staging associées. 2. Mettre à jour ingestion_import.statusEXPIRED. 3. Émettre l'entrée d'audit ING_STAGING_EXPIRED. 4. Afficher un badge dans la console OP (« Import en attente expiré - [nom fichier] du [date] ») jusqu'à fermeture par l'opérateur.

ING-R-019 - Signalement quotidien des imports en attente [V1] [OPERATOR]

SI un ingestion_import d'un tenant a le statut STAGED_WITH_ANOMALIES et created_at entre 1 jour et 6 jours (non expiré) ALORS un badge d'alerte est affiché dans la console OP (« Import en attente de correction depuis N jour(s) - [nom fichier] »). V1 : badge console uniquement. V2 : notification email/SMS opérateur.


B.2 Machines à états

B.2.1 - Cycle de vie d'un Import (ingestion_import)

stateDiagram-v2
    [*] --> UPLOADING : Opérateur téléverse le fichier

    UPLOADING --> PARSE_FAILED : Format / taille / hash invalide
    UPLOADING --> STAGED_CLEAN : Parse OK, 0 anomalie bloquante
    UPLOADING --> STAGED_WITH_ANOMALIES : Parse OK, ≥1 anomalie bloquante

    STAGED_CLEAN --> COMMITTED : Opérateur confirme le commit
    STAGED_CLEAN --> ABANDONED : Opérateur annule
    STAGED_CLEAN --> EXPIRED : Job sweep 7 jours

    STAGED_WITH_ANOMALIES --> STAGED_CLEAN : Toutes anomalies bloquantes corrigées
    STAGED_WITH_ANOMALIES --> ABANDONED : Opérateur annule
    STAGED_WITH_ANOMALIES --> EXPIRED : Job sweep 7 jours

    COMMITTED --> [*]
    ABANDONED --> [*]
    EXPIRED --> [*]
    PARSE_FAILED --> [*]

B.2.2 - Cycle de vie d'une Ligne stagée (ingestion_staged_row)

stateDiagram-v2
    [*] --> BLOCKED : Parse : ≥1 anomalie bloquante
    [*] --> PENDING_CLEAN : Parse : 0 anomalie bloquante (non-bloquantes ou aucune)

    BLOCKED --> CORRECTED : Opérateur corrige tous les champs bloquants ; re-validation réussie
    BLOCKED --> BLOCKED : Opérateur corrige ; re-validation échoue (anomalie persistante ou nouvelle)

    PENDING_CLEAN --> COMMITTED_ROW : Import parent commité
    CORRECTED --> COMMITTED_ROW : Import parent commité

    BLOCKED --> PURGED : Import parent abandonné ou expiré
    PENDING_CLEAN --> PURGED : Import parent abandonné ou expiré
    CORRECTED --> PURGED : Import parent abandonné ou expiré

    COMMITTED_ROW --> [*]
    PURGED --> [*]

B.3 Table de décision - Classification des anomalies

Champ / condition Code anomalie Classe Corrigeable dans l'atelier
identifiant_client vide IDENTIFIANT_CLIENT_ABSENT BLOQUANT Non - re-téléverser
identifiant_contrat vide IDENTIFIANT_CONTRAT_ABSENT BLOQUANT Non - re-téléverser
nom vide NOM_ABSENT BLOQUANT Non - re-téléverser
prenoms vide PRENOMS_ABSENT BLOQUANT Non - re-téléverser
telephone vide TELEPHONE_ABSENT BLOQUANT Oui
telephone invalide après normalisation TELEPHONE_INVALIDE BLOQUANT Oui
date_naissance absente DATE_NAISSANCE_ABSENTE BLOQUANT Oui
date_naissance format invalide ou date impossible DATE_NAISSANCE_INVALIDE BLOQUANT Oui
montant_du absent MONTANT_ABSENT BLOQUANT Oui
montant_du ≤ 0 MONTANT_INVALIDE BLOQUANT Oui
date_echeance absente DATE_ECHEANCE_ABSENTE BLOQUANT Non - re-téléverser
date_echeance > 30 jours dans le passé ECHEANCE_DEPASSEE BLOQUANT Non - re-téléverser
branche vide BRANCHE_ABSENTE BLOQUANT Non - re-téléverser
code_agence vide CODE_AGENCE_ABSENT BLOQUANT Oui
code_agence inconnu dans la table agence du tenant CODE_AGENCE_INCONNU BLOQUANT Oui
code_agence hors portée de l'opérateur (rôle OPERATOR) CODE_AGENCE_NON_AUTORISE BLOQUANT Non - escalade admin
Doublon intra-fichier (même identifiant_client + identifiant_contrat) DOUBLON_INTRA_FICHIER BLOQUANT Non - re-téléverser
email absent EMAIL_ABSENT NON-BLOQUANT -
numero_police absent NUMERO_POLICE_ABSENT NON-BLOQUANT -
date_naissance du fichier ≠ valeur stockée pour ce client DATE_NAISSANCE_MODIFIEE SURVEILLANCE - (commit auto + alerte anomalie)
montant_du ou date_echeance du fichier ≠ valeur stockée pour ce contrat DONNEES_CONTRAT_MODIFIEES SURVEILLANCE - (commit auto + alerte anomalie)

Règles de classification : - BLOQUANT : empêche le commit. L'import reste STAGED_WITH_ANOMALIES tant qu'au moins une ligne est BLOCKED. - NON-BLOQUANT : ligne commitable avec avertissement. Visible dans l'atelier et dans le rapport d'import. - SURVEILLANCE : ligne commitable ; anomalie promue dans la vue anomalies de la console OP pour revue humaine.


B.4 Spécification du modèle de colonnes canonique

Fichier modèle V1 : modele_import_papillon.xlsx (téléchargeable depuis l'interface)

# Nom de colonne Obligatoire Type Format attendu Anomalie si absent/invalide
1 identifiant_client Chaîne Non vide, max 100 car. IDENTIFIANT_CLIENT_ABSENT → BLOQUANT
2 nom Chaîne Non vide, max 100 car. NOM_ABSENT → BLOQUANT
3 prenoms Chaîne Non vide, max 150 car. PRENOMS_ABSENT → BLOQUANT
4 telephone Chaîne Normalisé en E.164 +225XXXXXXXXXX TELEPHONE_ABSENT/INVALIDE → BLOQUANT
5 date_naissance Date DD/MM/AAAA (cellule Date Excel acceptée) DATE_NAISSANCE_ABSENTE/INVALIDE → BLOQUANT
6 identifiant_contrat Chaîne Non vide, max 100 car. IDENTIFIANT_CONTRAT_ABSENT → BLOQUANT
7 branche Chaîne Non vide, max 100 car. (texte libre V1) BRANCHE_ABSENTE → BLOQUANT
8 date_echeance Date DD/MM/AAAA (cellule Date Excel acceptée) DATE_ECHEANCE_ABSENTE / ECHEANCE_DEPASSEE → BLOQUANT
9 montant_du Entier Entier positif XOF ; séparateurs espace ou point acceptés MONTANT_ABSENT/INVALIDE → BLOQUANT
10 code_agence Chaîne Doit exister dans agency du tenant (insensible à la casse) CODE_AGENCE_ABSENT/INCONNU/NON_AUTORISE → BLOQUANT
11 email Chaîne Format email ou vide EMAIL_ABSENT → NON-BLOQUANT
12 numero_police Chaîne Max 100 car. NUMERO_POLICE_ABSENT → NON-BLOQUANT
13 immatriculation Chaîne Max 50 car. -
14 statut_source Chaîne Max 50 car. (statut dans le système source) -
15 informations_complementaires Chaîne Max 500 car. (texte libre pour templates notification) -

Devise : XOF codé en dur pour V1 (CI uniquement). Aucune colonne devise dans le modèle V1. La colonne devise + profil pays sera introduite en V2.


B.5 Modèle de données

B.5.1 Entités propres au module ING

ingestion_import - Entête d'un import

Colonne Type Contrainte
id UUID PK
tenant_id UUID FK → tenant, NOT NULL
uploaded_by UUID FK → user, NOT NULL
filename VARCHAR(255) NOT NULL
file_hash_sha256 CHAR(64) NOT NULL ; UNIQUE par tenant_id
file_mime VARCHAR(100) text/csv ou OOXML
file_size_bytes INTEGER NOT NULL
file_minio_path TEXT Convention : /<tenant_id>/ingestion/<import_id>/<filename>
status ENUM UPLOADING, PARSE_FAILED, STAGED_CLEAN, STAGED_WITH_ANOMALIES, COMMITTED, ABANDONED, EXPIRED
row_count_total INTEGER Défini après parsing
row_count_blocked INTEGER
row_count_non_blocking INTEGER
row_count_monitoring INTEGER
row_count_clean INTEGER
committed_created INTEGER Défini au commit
committed_updated INTEGER Défini au commit
committed_skipped INTEGER Défini au commit
parse_error_detail TEXT (JSON) Pour PARSE_FAILED uniquement
created_at TIMESTAMPTZ NOT NULL
expires_at TIMESTAMPTZ created_at + 7 jours ; défini à l'insertion
committed_at TIMESTAMPTZ NULL jusqu'au commit

ingestion_staged_row - Ligne en attente de validation dans l'atelier

Colonne Type Contrainte
id UUID PK
import_id UUID FK → ingestion_import
tenant_id UUID NOT NULL (dénormalisé pour isolation)
row_number INTEGER Numéro de ligne 1-based dans le fichier source
status ENUM BLOCKED, PENDING_CLEAN, CORRECTED, COMMITTED_ROW, PURGED
source_customer_id VARCHAR(100) Tel qu'importé
nom VARCHAR(100)
prenoms VARCHAR(150)
telephone_raw VARCHAR(50) Tel qu'importé
telephone_normalized VARCHAR(15) E.164 après normalisation
date_naissance DATE Parsée ou NULL
source_contract_id VARCHAR(100)
branche VARCHAR(100)
date_echeance DATE
montant_du INTEGER XOF entier
agency_id UUID FK → agency ; NULL si non résolu
code_agence_raw VARCHAR(50) Tel qu'importé
email VARCHAR(255)
numero_police VARCHAR(100)
immatriculation VARCHAR(50)
statut_source VARCHAR(50)
informations_complementaires VARCHAR(500)
anomalies JSONB Tableau de {code, champ, valeur, classe, corrigeable}
corrected_fields JSONB Champs corrigés dans l'atelier avec valeurs avant/après
last_corrected_at TIMESTAMPTZ

ingestion_import_row - Trace immuable après commit (un enregistrement par ligne committée)

Colonne Type Contrainte
id UUID PK
import_id UUID FK → ingestion_import
tenant_id UUID NOT NULL
customer_id UUID FK → customer
contract_id UUID FK → contract
action ENUM CREATED, UPDATED, SKIPPED
anomalies_at_commit JSONB Anomalies non-bloquantes + surveillance restantes au commit
committed_at TIMESTAMPTZ

B.5.2 Entités partagées (upsertées par ING, lues par tous les modules)

Per V1 PRD §27 - ING écrit mais ne détient pas la gouvernance de schéma de ces entités.

customer - Champs minimaux écrits par ING : tenant_id, source_customer_id (clé de dédup), nom, prenoms, telephone (E.164), date_naissance, email (optionnel), created_at, updated_at.

contract - Champs minimaux écrits par ING : tenant_id, agency_id, customer_id, source_contract_id (clé de dédup avec tenant_id + customer_id), branche, date_echeance, montant_du (entier XOF), numero_police, immatriculation, statut_source, informations_complementaires, import_id (dernier import ayant touché ce contrat), created_at, updated_at.


B.6 Implications multi-tenant

Préoccupation Règle V1
Isolation des lignes Chaque requête sur ingestion_import, ingestion_staged_row, ingestion_import_row DOIT inclure WHERE tenant_id = :tenantId issu du contexte tenant de la requête.
Unicité du hash fichier file_hash_sha256 unique par tenant_id, pas globalement. Deux tenants différents peuvent importer le même fichier sans conflit.
Résolution du code agence Toujours résolue dans la table agency du tenant. Pas de namespace agence global.
Shared DB / shared schema (défaut) Toutes les tables ING coexistent ; isolation par tenant_id à la couche application.
BYO-DB Tables ING dans le PostgreSQL propre au tenant. Routing résolu à la requête. Schéma identique.
Convention chemin MinIO /<tenant_id>/ingestion/<import_id>/<nom_fichier_original>
Pas de UNIQUE global sur noms file_hash_sha256 unique par tenant, pas globalement. Aucune contrainte globale sur des champs d'identité qui supposerait un tenant unique.

B.7 Dépendances inter-modules

ING consomme :

Dépendance Module source Raison
Table agency (lecture) TENANT Résoudre code_agenceagency_id ; vérifier la portée de l'opérateur
Table user / token Keycloak (lecture) AUTH Identifier l'opérateur importateur ; vérifier rôle et agences attribuées
Résolveur de contexte tenant AUTH Peupler tenant_id sur chaque requête
Client MinIO Infrastructure Stocker et récupérer les fichiers source
Client Redis Infrastructure Rate-limit upload (10 tentatives/opérateur/heure)

ING produit (publié vers) :

Dépendance Module cible Mécanisme
Événement domaine ImportCommitted OP Spring Modulith @ApplicationEvent ; payload : {importId, tenantId, agencyIds[], rowCountCreated, rowCountUpdated, rowCountSkipped}
Lignes customer (upsert) OP, CP, PAY, AUDIT Écriture directe DB dans la table partagée customer
Lignes contract (upsert) OP, CP, PAY, AUDIT Écriture directe DB dans la table partagée contract
Entrées audit log AUDIT Via l'API du module audit-log

API ING exposée (consommée par le frontend operator-console) :

Endpoint Méthode Description
/api/ingestion/imports POST (multipart) Téléverser + parser un fichier ; retourne le résumé de l'import stagé
/api/ingestion/imports GET Historique des imports paginé, scopé tenant
/api/ingestion/imports/{importId} GET Détails de l'import + compteurs + résumé anomalies
/api/ingestion/imports/{importId}/rows GET Lignes stagées paginées ; filtrables par statut
/api/ingestion/imports/{importId}/rows/{rowId} PATCH Corriger un champ dans l'atelier (re-validation immédiate)
/api/ingestion/imports/{importId}/commit POST Commettre l'import stagé
/api/ingestion/imports/{importId}/abandon POST Abandonner et purger le staging
/api/ingestion/template GET Télécharger le modèle XLSX canonique
/api/ingestion/imports/{importId}/source-file GET URL signée MinIO du fichier source (fenêtre 12 mois ; URL signée ≤15 min)

B.8 Événements d'audit

(Per V1 PRD §18 - chaque événement doit comporter au minimum : identifiant d'événement, horodatage, acteur initiateur, entité concernée, résultat.)

Code événement Déclencheur Champs obligatoires
ING_UPLOAD_RECEIVED Fichier reçu par le serveur (avant parsing) tenant_id, user_id, filename, file_size_bytes, file_hash
ING_PARSE_FAILED Fichier rejeté avant staging tenant_id, user_id, filename, reason_code, detail
ING_STAGED Entrée staging créée après parsing tenant_id, user_id, import_id, row_count_total, row_count_blocked, row_count_non_blocking
ING_ROW_CORRECTED Opérateur corrige un champ dans l'atelier tenant_id, user_id, import_id, staged_row_id, field_name, old_value, new_value
ING_COMMITTED Import commité avec succès tenant_id, user_id, import_id, rows_created, rows_updated, rows_skipped
ING_ABANDONED Opérateur annule le staging tenant_id, user_id, import_id
ING_STAGING_EXPIRED Job sweep 7 jours purge le staging tenant_id, import_id (acteur = système)
ING_CUSTOMER_CREATED Nouveau client upsert au commit tenant_id, import_id, customer_id, source_customer_id
ING_CUSTOMER_UPDATED Client existant mis à jour au commit tenant_id, import_id, customer_id, diff
ING_DOB_DRIFT_DETECTED Date de naissance fichier ≠ valeur stockée (SURVEILLANCE) tenant_id, import_id, customer_id, old_year (AAAA uniquement), new_year (AAAA uniquement)
ING_CONTRACT_CREATED Nouveau contrat upsert au commit tenant_id, import_id, contract_id, source_contract_id
ING_CONTRACT_UPDATED Contrat existant mis à jour au commit tenant_id, import_id, contract_id, diff (champs modifiés + valeurs avant/après)

Note sur la minimisation PII dans l'audit : seule l'année (AAAA) de la date de naissance est journalisée dans l'audit - jamais la date complète. Principe de minimisation, loi n°2013-450.


Partie C - Contraintes et frontières

C.1 Conformité réglementaire

CIMA Rég. 01-24

  • La plateforme agit en tant que prestataire technique pour le compte du courtier/assureur qui est le responsable de traitement. ING traite leurs données clients et contrats en vertu d'un accord de sous-traitance B2B (DPA plateforme-courtier). (Provisoirement validé - lacune audit juridique #1 : position prestataire technique à valider par avocat.)
  • Le flux d'import génère une piste de preuves (audit log + fichier original dans MinIO) suffisante pour reconstruire la lignée de données pour tout cycle de renouvellement (V1 PRD §16.5).
  • Le module ING ne distribue ni ne gère de contrats d'assurance lui-même - il transforme et achemine uniquement des données pour l'intermédiaire agréé.

ARTCI / Loi n°2013-450

  • Base légale : ING traite la DCP (données à caractère personnel) des clients - nom, téléphone, date de naissance - en qualité de sous-traitant pour le courtier (responsable de traitement). Le DPA plateforme-courtier doit couvrir l'ingestion comme activité de traitement. (Provisoirement validé - lacune audit #3 : formailités ARTCI pour l'éditeur à clarifier.)
  • Minimisation : seuls les 10 champs obligatoires + optionnels définis dans le modèle canonique sont stockés. Le champ informations_complementaires est limité à 500 caractères.
  • Rétention :
  • Fichiers source : purgés de MinIO après 12 mois. (Provisoirement validé.)
  • Lignes staging : purgées au commit, à l'abandon ou à l'expiration (7 jours).
  • ingestion_import_row (trace de commit) : conservée 12 mois. (Provisoirement validé.)
  • Date de naissance dans l'audit : seule l'année est retenue ; la date complète n'est jamais écrite dans l'audit trail.

Règlement CIMA cybersécurité (010-24 ou équivalent en vigueur)

  • Chiffrement en transit (TLS 1.2+) sur tous les endpoints de téléversement/téléchargement.
  • Fichiers MinIO stockés avec chiffrement côté serveur (SSE).
  • (Lacune audit juridique #4 : numéro d'article précis et période transitoire à confirmer par avocat. Recommandation : prévoir un backlog item ING-SEC-001 une fois les obligations de base ancrées.)

C.2 Localisation

  • Toute copie UI en français.
  • Messages d'erreur : chaque CODE_ANOMALIE est mappé à une phrase française lisible par un humain. Exemples :
  • TELEPHONE_INVALIDE → « Le numéro de téléphone « {valeur} » n'est pas un numéro ivoirien valide. Format attendu : +225 suivi de 10 chiffres. »
  • CODE_AGENCE_INCONNU → « Le code agence « {valeur} » n'existe pas dans votre configuration. Vérifiez les codes agence dans les paramètres de votre cabinet. »
  • FICHIER_DEJA_IMPORTE → « Ce fichier a déjà été importé le {date} à {heure} par {prénom nom}. »
  • ECHEANCE_DEPASSEE → « La date d'échéance {date} est dépassée depuis plus de 30 jours. »
  • Montants affichés : 15 000 XOF (espace comme séparateur de milliers, 0 décimales).
  • Dates affichées : JJ/MM/AAAA.
  • Téléphone stocké en E.164 (+225XXXXXXXXXX) ; affiché avec espaces pour la lisibilité (+225 07 12 34 56 78) dans l'atelier de correction.
  • Fichier modèle téléchargé sous le nom modele_import_papillon.xlsx.
  • Pilule de statut import :
Statut interne Libellé français
STAGED_CLEAN En attente de validation
STAGED_WITH_ANOMALIES En attente - anomalies à corriger
COMMITTED Importé
ABANDONED Annulé
EXPIRED Expiré
PARSE_FAILED Échec de lecture

C.3 Sécurité

  • Isolation tenant : chaque endpoint ING extrait le tenant_id du token Keycloak de l'opérateur authentifié - jamais depuis une entrée utilisateur.
  • Contrôle de rôle : téléversement et commit nécessitent le rôle ADMIN ou OPERATOR (ING-R-017). Rôle READ → HTTP 403.
  • Validation du type de fichier : l'endpoint multipart valide le Content-Type + les magic bytes du fichier (indépendamment de l'extension). Rejeter un .csv avec magic bytes OOXML, etc.
  • Accès MinIO : fichiers source accessibles uniquement via des URL signées côté serveur (pas d'URL publiques directes) ; durée de vie de l'URL signée ≤ 15 minutes (distinct de la période de rétention de 12 mois).
  • Visibilité PII : l'atelier de correction masque la date de naissance après affichage (affiche ••/••/AAAA en liste ; révèle la valeur complète uniquement dans le champ d'édition actif).
  • Pas d'exposition de données client dans les messages d'erreur de hash : le message FICHIER_DEJA_IMPORTE révèle uniquement la date, l'heure et le nom de l'opérateur importateur - jamais de données contrat.
  • Rate-limiting sur l'endpoint upload : max 10 tentatives de téléversement par opérateur par heure (compteur Redis par user_id).
  • Idempotence : la déduplication par hash (ING-R-003) rend le re-téléversement du même fichier idempotent.

C.4 Connectivité

Le module ING est utilisé exclusivement du côté opérateur (desktop-first, online-first per V1 PRD §30.3). Aucune contrainte low-bandwidth ou reprenabilité spécifique à ING.

  • Le téléversement est un POST multipart HTTP unique ; la limite de 5 Mo garantit la complétion en quelques secondes même sur une connexion de bureau lente.
  • Si la requête d'upload expire côté client, l'opérateur re-téléverse ; la vérification du hash fichier prévient un double import.
  • Les imports stagés survivent aux redémarrages serveur (stockés en PostgreSQL) ; l'opérateur peut reprendre l'atelier de correction après une déconnexion.
  • L'atelier de correction ne fait pas d'UI optimiste (pas de file client, pas d'IndexedDB) : chaque correction de champ est un aller-retour serveur. Acceptable côté opérateur (en ligne).

C.5 Hors périmètre V1

Élément Cible
Imports déclenchés via API / connecteurs ERP / schedulers V2
Mapping de colonnes flexible par tenant V2
Format XLS (legacy Excel 97-2003) V2
Upload batch multi-fichiers V2
Colonne devise / multi-devises V2
Validation référentiel branche (liste de valeurs) V2
TTL staging configurable par tenant V2
Notification email/SMS à l'opérateur sur expiration staging V2
Suppression de ligne individuelle dans l'atelier sans re-téléversement V2 (question ouverte A.6 #4)
Atelier de correction en mode hors-ligne Hors périmètre
Import pour des filiales étrangères (tenants séparés) V2 - flux d'onboarding tenant distinct
Suggestions de correction assistées par IA V3

Document produit par le personnage ANALYST (Claude Code) - session du 2026-04-30. À soumettre à l'approbation du Founder/PO avant de passer à la session ARCHITECT.