Files
geo/app/docs/GESTION-API-SECTORS.md
pierre 1018b86537 feat: Gestion des secteurs et migration v3.0.4+304
- Ajout système complet de gestion des secteurs avec contours géographiques
- Import des contours départementaux depuis GeoJSON
- API REST pour la gestion des secteurs (/api/sectors)
- Service de géolocalisation pour déterminer les secteurs
- Migration base de données avec tables x_departements_contours et sectors_adresses
- Interface Flutter pour visualisation et gestion des secteurs
- Ajout thème sombre dans l'application
- Corrections diverses et optimisations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 11:01:45 +02:00

16 KiB

GESTION-SECTORS.md

Vue d'ensemble

Ce document décrit le système de gestion des secteurs dans l'API Geosector, incluant la connexion aux bases de données d'adresses externes, la validation des limites départementales, et le processus complet de création de secteurs avec génération automatique des passages.

Évolutions récentes

Gestion des sessions

  • La session stocke maintenant entity_id depuis fk_entite lors du login
  • Méthode Session::getEntityId() disponible pour récupérer l'ID de l'entité
  • Utilisation cohérente de l'entity_id dans toutes les opérations

Gestion des passages orphelins

  • Les passages avec fk_sector = 0 sont automatiquement intégrés au nouveau secteur
  • Évite les doublons pour les passages ayant déjà une fk_adresse
  • Mise à jour atomique dans la transaction de création du secteur

Architecture multi-bases

Bases de données principales

  1. Base principale (geosector_app)

    • Contient toutes les tables de l'application
    • Tables concernées : ope_sectors, sectors_adresses, ope_pass, ope_users_sectors, x_departements_contours
  2. Base adresses (dans conteneurs Incus séparés)

    • DVA : dva-maria (13.23.33.46) - base adresses
    • RCA : rca-maria (13.23.33.36) - base adresses
    • PRA : pra-maria (13.23.33.26) - base adresses
    • Credentials : adr_geo_user / d66,AdrGeoDev.User
    • Tables par département : cp22, cp23, etc.

Configuration

Dans src/Config/AppConfig.php :

'addresses_database' => [
    'host' => '13.23.33.46',  // Varie selon l'environnement
    'name' => 'adresses',
    'username' => 'adr_geo_user',
    'password' => 'd66,AdrGeoDev.User',
],

Gestion des contours départementaux

Table x_departements_contours

Création manuelle de la table (sans DROP permissions) :

CREATE TABLE IF NOT EXISTS `x_departements_contours` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `code_dept` varchar(3) NOT NULL,
    `nom_dept` varchar(100) NOT NULL,
    `contour` GEOMETRY NOT NULL,
    `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
    PRIMARY KEY (`id`),
    UNIQUE KEY `idx_code_dept` (`code_dept`),
    SPATIAL KEY `idx_contour` (`contour`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='Contours géographiques des départements français';

Import des contours

  1. Fichier source : docs/contour-des-departements.geojson (depuis data.gouv.fr)
  2. Import automatique : Uniquement lors de la connexion de l'admin d6soft
  3. Script : scripts/init_departements_contours.php
  4. Résultat : 96 départements importés avec support Polygon et MultiPolygon

Services principaux

AddressService

Gère la récupération des adresses depuis la base externe :

class AddressService {
    // Récupère toutes les adresses dans un polygone
    public function getAddressesInPolygon(array $coordinates, ?int $entityId = null): array

    // Compte les adresses dans un polygone
    public function countAddressesInPolygon(array $coordinates, ?int $entityId = null): int
}

Caractéristiques :

  • Détection automatique des départements touchés par le secteur
  • Interrogation de toutes les tables cp{dept} concernées
  • Gestion des secteurs multi-départements

DepartmentBoundaryService

Vérifie les limites départementales des secteurs :

class DepartmentBoundaryService {
    // Vérifie si un secteur est contenu dans un département
    public function checkSectorInDepartment(array $sectorCoordinates, string $departmentCode): array

    // Liste tous les départements touchés par un secteur
    public function getDepartmentsForSector(array $sectorCoordinates): array
}

Retour type :

[
    'is_contained' => bool,
    'message' => string,
    'intersecting_departments' => [
        ['code_dept' => '22', 'nom_dept' => 'Côtes-d\'Armor', 'percentage_overlap' => 75.5],
        ['code_dept' => '29', 'nom_dept' => 'Finistère', 'percentage_overlap' => 24.5]
    ]
]

Processus de création de secteur

1. Structure du payload

{
  "user_id": 123,
  "fk_entite": 45,
  "operation_id": 789,
  "sector": {
    "id": 0,
    "libelle": "Secteur Centre-Ville",
    "color": "#FF5733",
    "sector": "48.117266/-1.6777926#48.118500/-1.6750000#..."
  },
  "users": [12, 34, 56, 78]
}

2. Étapes de création

  1. Validation des données et de l'opération
  2. Vérification des limites départementales (warning si débordement)
  3. Début de transaction pour garantir la cohérence des données
  4. Insertion du secteur dans ope_sectors
  5. Affectation des utilisateurs dans ope_users_sectors avec :
    • fk_operation, fk_user, fk_sector
    • created_at, fk_user_creat, chk_active = 1
  6. Intégration des passages orphelins :
    • Recherche des passages avec fk_sector = 0 dans le polygone
    • Mise à jour de leur fk_sector vers le nouveau secteur
    • Exclusion des passages ayant déjà une fk_adresse
  7. Récupération des adresses via AddressService
  8. Stockage des adresses dans sectors_adresses
  9. Création des passages dans ope_pass pour chaque adresse :
    • Affectés au premier utilisateur de la liste
    • Avec toutes les FK nécessaires (entité, opération, secteur, user)
    • Données d'adresse complètes
  10. Commit de la transaction ou rollback en cas d'erreur

3. Réponse API pour CREATE

Format standardisé : Les données sont placées à la racine, sans groupe "data" intermédiaire.

{
  "status": "success",
  "message": "Secteur créé avec succès",
  "sector": {
    "id": 123,
    "libelle": "Secteur Centre-Ville",
    "color": "#FF5733",
    "sector": "48.117266/-1.6777926#48.118500/-1.6750000#..."
  },
  "passages_sector": [
    {
      "id": 456,
      "fk_operation": 789,
      "fk_sector": 123,
      "fk_user": 12,
      "fk_type": 2,
      "fk_adresse": "cp22.12345",
      "passed_at": null,
      "numero": "10",
      "rue": "Rue de la Paix",
      "rue_bis": "",
      "ville": "Saint-Brieuc",
      "residence": null,
      "fk_habitat": null,
      "appt": null,
      "niveau": null,
      "gps_lat": "48.117266",
      "gps_lng": "-1.6777926",
      "nom_recu": null,
      "name": "", // Décrypté depuis encrypted_name
      "remarque": null,
      "email": "", // Décrypté depuis encrypted_email
      "phone": "", // Décrypté depuis encrypted_phone
      "montant": null,
      "fk_type_reglement": null,
      "email_erreur": null,
      "nb_passages": null
    }
  ],
  "passages_integrated": 5, // Passages orphelins intégrés
  "passages_created": 10, // Nouveaux passages créés
  "users_sectors": [
    {
      "id": 12,
      "first_name": "Jean",
      "sect_name": "JDU",
      "fk_sector": 123,
      "name": "Dupont" // Décrypté depuis encrypted_name
    }
  ]
}

4. Réponse API pour UPDATE

La réponse est identique à CREATE avec des compteurs supplémentaires :

{
  "status": "success",
  "message": "Secteur modifié avec succès",
  "sector": {
    "id": 123,
    "libelle": "Secteur Centre-Ville Modifié",
    "color": "#FF5733",
    "sector": "48.117266/-1.6777926#48.118500/-1.6750000#..."
  },
  "passages_sector": [
    // Liste complète de TOUS les passages actuels du secteur
  ],
  "passages_orphaned": 3, // Passages mis en orphelin (hors polygone)
  "passages_updated": 5, // Passages mis à jour avec fk_adresse
  "passages_created": 10, // Nouveaux passages créés
  "passages_total": 25, // Nombre total de passages dans le secteur
  "users_sectors": [
    // Liste des utilisateurs affectés
  ]
}

Notes importantes :

  • Les champs sensibles (name, email, phone) sont stockés cryptés et décryptés à la volée
  • La structure est identique entre CREATE et UPDATE pour faciliter l'intégration
  • Tous les champs sont retournés, même s'ils sont null
  • Code HTTP : 201 pour CREATE, 200 pour UPDATE

Gestion des secteurs multi-départements

Détection automatique

Le système détecte automatiquement quand un secteur touche plusieurs départements :

  1. Analyse spatiale : Utilisation de ST_Intersects pour identifier tous les départements touchés
  2. Calcul de pourcentage : ST_Area(ST_Intersection) pour calculer le % de recouvrement
  3. Interrogation multi-tables : Requête sur toutes les tables cp{dept} concernées

Exemple de secteur multi-départements

// Secteur à cheval sur 22 (Côtes-d'Armor) et 29 (Finistère)
$coordinates = [
    [48.5778, -3.8280],  // Morlaix (29)
    [48.5778, -3.7280],  // Vers l'est (22)
    [48.4778, -3.7280],
    [48.4778, -3.8280]
];

// Le système va automatiquement :
// 1. Détecter que le secteur touche 22 et 29
// 2. Interroger cp22 et cp29 pour les adresses
// 3. Créer les passages pour toutes les adresses trouvées

Tables de données

ope_sectors

  • id : Identifiant unique
  • libelle : Nom du secteur
  • color : Couleur d'affichage
  • sector : Coordonnées (format lat/lng#lat/lng#...)
  • fk_entite : Lien vers l'entité

sectors_adresses

  • fk_sector : Lien vers le secteur
  • fk_address : ID de l'adresse dans la base externe
  • numero, voie, code_postal, commune
  • latitude, longitude

ope_pass (passages)

  • fk_entite, fk_operation, fk_sector, fk_user
  • numero, voie, code_postal, commune
  • latitude, longitude
  • created_at, fk_user_creat, chk_active

ope_users_sectors

  • fk_operation : Lien vers l'opération
  • fk_user : Lien vers l'utilisateur (ope_users)
  • fk_sector : Lien vers le secteur
  • created_at, fk_user_creat, chk_active

Logs et monitoring

Le système génère des logs détaillés pour :

  • Nombre d'adresses trouvées par département
  • Secteurs hors limites départementales
  • Passages créés avec succès
  • Erreurs de connexion aux bases d'adresses
  • Performance des requêtes spatiales

Scripts de test

  • test_sector_departments.php : Test des limites départementales
  • test_addresses_connection.php : Test de connexion à la base d'adresses

Notes importantes

  1. Fail-safe : La création de secteur continue même si la base d'adresses est inaccessible
  2. Transactions :
    • Toute la création est dans une transaction pour garantir la cohérence
    • Toujours vérifier inTransaction() avant d'appeler rollBack()
    • Gestion correcte des erreurs PDO avec try/catch
  3. Performance : Les requêtes spatiales utilisent des index spatiaux pour optimiser les performances
  4. Modification de secteur : Plus complexe car nécessite de gérer les passages existants (non implémenté)
  5. Paramètres SQL : Utiliser des noms uniques pour éviter l'erreur "Invalid parameter number"
  6. Jointures : Les données utilisateur viennent de la table users, pas ope_users (qui n'a pas nom/prenom)

Bilan de la gestion des adresses et passages

Vue d'ensemble du cycle de vie

Base Adresses (cp22, cp23...) → sectors_adresses → ope_pass

1. CRÉATION D'UN SECTEUR

Flux des données :

  1. Récupération des adresses depuis la base externe (AddressService)
  2. Intégration des passages orphelins (fk_sector = NULL) situés dans le polygone
  3. Stockage dans sectors_adresses de toutes les adresses du polygone
  4. Création automatique de passages (ope_pass) pour chaque adresse SAUF celles déjà utilisées par les passages orphelins

Détails :

  • Passages créés : fk_type = 2, encrypted_name = '' (vide), affectés au premier utilisateur
  • Passages orphelins : mis à jour avec le nouveau fk_sector
  • Évite les doublons : les adresses déjà utilisées par des passages orphelins ne génèrent pas de nouveau passage

2. MISE À JOUR D'UN SECTEUR

Processus de mise à jour :

  1. Mise à jour des attributs (libelle, color, sector)
  2. Mise à jour des membres affectés
  3. Suppression/recréation des adresses dans sectors_adresses
  4. Gestion intelligente des passages via updatePassagesForSector :

Gestion des passages lors de l'UPDATE :

a) Vérification géographique des passages existants
  • Pour chaque passage du secteur, vérification si ses coordonnées GPS sont dans le nouveau polygone
  • Si DANS le polygone : Conservation du passage
  • Si HORS du polygone : Mise en orphelin (fk_sector = NULL)
b) Traitement des nouvelles adresses

Pour chaque adresse dans sectors_adresses :

  1. Vérification primaire : Recherche par fk_adresse
  2. Vérification secondaire : Si pas trouvé, recherche par numero, rue_bis, rue, ville
    • Si trouvé → Mise à jour du fk_adresse dans le(s) passage(s)
  3. Création : Si aucun passage existant, création avec :
    • fk_type = 2, encrypted_name = ''
    • Affecté au premier utilisateur du secteur
    • Toutes les données de l'adresse

3. SUPPRESSION D'UN SECTEUR

Traitement différencié des passages :

  1. Passages "non visités" (fk_type = 2 ET encrypted_name vide) :

    • Suppression définitive de la base
    • Ces passages correspondent aux adresses non visitées
  2. Passages "visités" (tous les autres) :

    • Mise à jour : fk_sector = NULL
    • Deviennent des passages orphelins
    • Conservent toutes leurs données (contact, montant, etc.)

Autres suppressions :

  • Suppression des affectations membres (ope_users_sectors)
  • Suppression des adresses (sectors_adresses)
  • Suppression du secteur lui-même

Tableau récapitulatif

Action sectors_adresses ope_pass dans polygone ope_pass hors polygone Nouvelles adresses
CREATE Insertion depuis base externe - Intégration si orphelins Création automatique de passages
UPDATE Suppression/recréation Conservation Mise en orphelin Création si pas de passage existant
DELETE Suppression totale Suppression si non visités / Orphelin si visités - -

Points d'attention

  1. Cohérence géographique : Lors d'un UPDATE, le système vérifie automatiquement et met en orphelin les passages hors du nouveau périmètre
  2. Passages orphelins : Peuvent être réintégrés lors de la création d'un nouveau secteur englobant
  3. Mise à jour du fk_adresse : Lors d'un UPDATE, les passages existants peuvent recevoir leur fk_adresse s'ils correspondent à une adresse
  4. Performance : La création/mise à jour génère potentiellement des milliers de passages selon la densité d'adresses

Erreurs communes et solutions

"There is no active transaction"

  • Cause : Appel à rollBack() sans transaction active
  • Solution : Vérifier $db->inTransaction() avant rollback

"Column not found: fk_address"

  • Cause : La colonne s'appelle fk_adresse (avec 'e')
  • Solution : Corriger les noms de colonnes dans les requêtes

"Invalid parameter number"

  • Cause : Réutilisation du même nom de paramètre dans une requête
  • Solution : Utiliser des noms uniques (:param1, :param2, etc.)

"Unknown column 'ou.nom'"

  • Cause : La table ope_users n'a pas de colonnes nom/prenom
  • Solution : Joindre avec la table users qui contient encrypted_name et first_name

"Class 'ApiService' not found"

  • Cause : Import manquant dans le controller
  • Solution : Ajouter use App\Services\ApiService; et require_once