# Module Planification des Temps — Conception

> **Statut** : Conception validée — en attente de développement  
> **Date** : Avril 2026  
> **Projet** : Vision ERP

---

## Table des matières

1. [Contexte & objectifs](#1-contexte--objectifs)
2. [Suppression de /saisietemps](#2-suppression-de-saisietemps)
3. [Modèle de données](#3-modèle-de-données)
4. [Paramètres](#4-paramètres)
5. [Référentiel Équipes](#5-référentiel-équipes)
6. [Droits d'accès](#6-droits-daccès)
7. [Interface Responsable](#7-interface-responsable)
8. [Interface Salarié](#8-interface-salarié)
9. [Règles métier](#9-règles-métier)
10. [Intégrations existantes](#10-intégrations-existantes)
11. [Rapports & traçabilité](#11-rapports--traçabilité)
12. [Ordre de développement](#12-ordre-de-développement)
13. [Points ouverts (à décider plus tard)](#13-points-ouverts-à-décider-plus-tard)

---

## 1. Contexte & objectifs

### Problème actuel
L'ancien module `/saisietemps` est obsolète, non utilisé et incompréhensible. Il est supprimé entièrement.

### Nouveaux objectifs
- Permettre à un **responsable** de planifier les tâches de son équipe semaine par semaine (ou au-delà)
- Le responsable affecte des **salariés sur des tâches de projet** avec un nombre d'heures par jour précis
- Le **salarié** voit sa planification dans son interface et ajuste ses heures réelles
- Tracer l'**écart planifié / réel** pour alimenter des rapports futurs

### Ce que ce module remplace
| Ancienne fonctionnalité | Remplacement |
|---|---|
| `/saisietemps` (saisie libre heures) | Interface salarié du nouveau module |
| Aucun outil de planification | Interface responsable du nouveau module |

---

## 2. Suppression de /saisietemps

### Fichiers à supprimer
- `app/Controllers/SaisieTempsController.php` (ou équivalent)
- `app/Views/blueline/saisietemps/` (répertoire complet)
- Routes associées dans `app/Config/Routes.php`
- Entrée dans le menu / sous-menus de navigation
- Droits d'accès liés au module dans la table des modules

### Tables à vérifier
Avant suppression, vérifier si les tables de saisie des temps contiennent des données historiques.  
Si oui : **archiver** avant de supprimer (décision à prendre avec le client).

---

## 3. Modèle de données

### 3.1 Nouvelles tables

#### `referentials` — catégorie `equipe` (table existante réutilisée)
Les équipes sont stockées dans la table `referentials` existante avec `category = 'equipe'`.
Pas de nouvelle table nécessaire pour la définition de l'équipe.

| Colonne | Usage |
|---|---|
| `category` | `'equipe'` (fixe) |
| `code` | Slug auto-généré (ex: `equipe_1746000000`) |
| `label` | Nom de l'équipe |
| `description` | Description |
| `color` | Couleur pour la grille planning (ex: `#0369a1`) |
| `icon` | Icône Font Awesome (ex: `fa-users`) |
| `is_active` | 1 = active, 0 = supprimée (soft delete) |

#### `planning_equipe_membres`
Rattache des salariés à une équipe.
```sql
CREATE TABLE planning_equipe_membres (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    id_equipe   BIGINT NOT NULL,        -- → referentials.id (category='equipe')
    id_salarie  INT UNSIGNED NOT NULL,  -- → salaries.id
    actif       TINYINT(1) DEFAULT 1,
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uq_equipe_salarie (id_equipe, id_salarie)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

#### `planning_equipe_responsables`
Un utilisateur peut être responsable de plusieurs équipes.
```sql
CREATE TABLE planning_equipe_responsables (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    id_equipe   BIGINT NOT NULL,        -- → referentials.id (category='equipe')
    id_user     INT UNSIGNED NOT NULL,  -- → users.id
    actif       TINYINT(1) DEFAULT 1,
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uq_equipe_user (id_equipe, id_user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

#### `planning_slots`
Une ligne = un salarié + une tâche (ticket) + un jour.  
C'est la table centrale du module.

```sql
CREATE TABLE planning_slots (
    id               INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    id_salarie       INT UNSIGNED NOT NULL,      -- → salaries.id
    id_ticket        INT UNSIGNED NULL,          -- → tickets.id (NULL si saisie libre)
    tache_libre      VARCHAR(255) NULL,          -- texte libre si id_ticket IS NULL
    date_slot        DATE NOT NULL,
    heures_planif    DECIMAL(4,2) NOT NULL DEFAULT 0,  -- saisie responsable
    heures_reelles   DECIMAL(4,2) NULL,                -- saisie salarié (NULL = non ajusté)
    note_salarie     TEXT NULL,                        -- explication écart, formation, support…
    created_by       INT UNSIGNED NOT NULL,     -- → users.id (responsable qui a planifié)
    created_at       DATETIME,
    updated_at       DATETIME,
    INDEX (id_salarie, date_slot),
    INDEX (id_ticket),
    INDEX (date_slot)
);
```

> **Règle** : `id_ticket IS NULL` ET `tache_libre IS NULL` → ligne invalide (contrainte applicative).

### 3.2 Modifications de tables existantes

#### Table `core` — nouveau champ (c'est la table settings réelle)
```sql
ALTER TABLE core
    ADD COLUMN heures_journee_max DECIMAL(4,2) NOT NULL DEFAULT 8.00;
```

#### Table `tickets` — lien projet obligatoire
- Le champ `id_project` (ou équivalent) doit être rendu **NOT NULL**
- À valider : vérifier les tickets existants sans projet avant la migration
- À la création d'un ticket : champ projet rendu obligatoire dans le formulaire

---

## 4. Paramètres

### Champ `heures_journee_max`
- Emplacement : **Paramètres Généraux** (`settings/index`)
- Valeur par défaut : **8.00**
- Exemples de valeurs saisonnières : 7h (été), 7h (Ramadan)
- Le responsable ne peut pas planifier plus que cette valeur par salarié par jour

> **Évolution future** : plafond par équipe (certaines équipes travaillent 7h, d'autres 8h).  
> Pour l'instant : un seul paramètre global.

---

## 5. Référentiel Équipes

### Emplacement
Nouvel onglet **"Équipes"** dans les paramètres (`/settings/equipes`).

### Fonctionnalités
| Action | Description |
|---|---|
| Créer une équipe | Nom, description, couleur, icône → `referentials` category=`equipe` |
| Modifier une équipe | Même champs |
| Supprimer une équipe | Soft delete : `is_active = 0` |
| Ajouter un membre | Sélectionner un salarié → `planning_equipe_membres` |
| Retirer un membre | `actif = 0` dans `planning_equipe_membres` |
| Désigner un responsable | Sélectionner un utilisateur (user, pas salarié) → `planning_equipe_responsables` |
| Retirer un responsable | `actif = 0` dans `planning_equipe_responsables` |

### Interface
- Liste des équipes avec compteur membres / responsables
- Modale de création/édition (design `modal-form.css`)
- Boutons action standard (`page-list.css`)
- Suppression via `#globalDeleteModal` centralisé (soft delete : `actif = 0`)

---

## 6. Droits d'accès

### Nouveau module dans la gestion des accès
```
Module : Planification
  ├── Sous-module : Planification équipe    ← vue responsable (grille équipe, créer/modifier slots)
  └── Sous-module : Mon planning            ← vue salarié (voir + ajuster ses propres heures)
```

### Règle d'affichage de `/planification`
```
L'utilisateur connecté a droit "Planification équipe"
  ET est responsable d'au moins une équipe active ?
    → Afficher la grille équipe + onglet "Mon planning" (si aussi salarié)

L'utilisateur connecté n'a PAS droit "Planification équipe"
  OU n'est responsable d'aucune équipe ?
    → Afficher uniquement "Mon planning"
```

> Un responsable qui est lui-même salarié voit les deux vues.

---

## 7. Interface Responsable

### URL : `/planification`

### En-tête de page
```
[< Semaine préc.]  Semaine du 28 avr. → 2 mai 2025  [Semaine suiv. >]
Équipe : [Dev Backend ▾]     Plafond : 8h/jour
```

### Grille semaine
```
┌────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ Salarié        │ Lun 28   │ Mar 29   │ Mer 30   │ Jeu 1    │ Ven 2    │
├────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ Ali Ben Salem  │  3h / 8h │  CONGÉ   │  5h / 8h │  8h ✓    │  2h / 8h │
│  Tâche #12     │  [3h]    │          │  [2h]    │  [4h]    │  [2h]    │
│  Tâche #18     │          │          │  [3h]    │  [4h]    │          │
│  [+ Ajouter]   │  [+]     │          │          │          │  [+]     │
├────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ Sana Kassem    │  FÉRIÉ   │  6h / 8h │  8h ✓    │  4h / 8h │  8h ✓    │
│  Tâche #07     │          │  [6h]    │  [8h]    │  [4h]    │  [3h]    │
│  [+ Ajouter]   │          │  [+]     │          │  [+]     │  [5h]    │
└────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
```

### Codes couleur des cellules jour
| Couleur | Condition |
|---|---|
| Gris + texte "CONGÉ" | Congé approuvé ce jour pour ce salarié |
| Gris + texte "FÉRIÉ" | Jour férié (table `jours_feries`) |
| Gris | Samedi / Dimanche |
| Blanc | Heures planifiées < 6h |
| Orange | 6h ≤ heures planifiées < plafond |
| Vert | Heures planifiées = plafond (journée complète) |
| Rouge | Dépassement (ne devrait pas arriver, bloqué en amont) |

### Modale d'ajout d'un slot (clic sur [+])
```
Salarié  : Ali Ben Salem       Date : Lundi 28 avril
Projet   : [Sélectionner ▾]
Tâche    : [Sélectionner ▾]    (filtrée par projet, statut ≠ Clôturé)
Heures   : [___] h             (max = plafond − heures déjà planifiées ce jour)
[Annuler]  [Planifier]
```

### Modale de modification d'un slot existant (clic sur [Xh])
- Mêmes champs, pré-remplis
- Bouton "Supprimer ce slot"

---

## 8. Interface Salarié

### URL : `/planification` (même URL, vue différente)

### Affichage
- Même grille semaine mais **pour le salarié connecté uniquement**
- Les tâches planifiées par le responsable apparaissent **en lecture** avec `heures_planif`
- Colonne supplémentaire : **heures réelles** (modifiable)

### Grille salarié (exemple)
```
┌──────────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ Tâche                │ Lun 28   │ Mar 29   │ Mer 30   │ Jeu 1    │ Ven 2    │
├──────────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ Tâche #12 — Fix API  │ Plan: 3h │          │ Plan: 2h │ Plan: 4h │ Plan: 2h │
│                      │ Réel: [_]│          │ Réel: [_]│ Réel: [_]│ Réel: [_]│
├──────────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ + Saisie libre       │  [+]     │          │  [+]     │  [+]     │  [+]     │
└──────────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
```

### Saisie libre (par le salarié)
- Clic sur [+] → modale simple
```
Date    : Lundi 28 avril
Tâche   : [____________] texte libre
Heures  : [___] h
Note    : [____________] (optionnel)
[Ajouter]
```
- Crée un `planning_slot` avec `id_ticket = NULL` et `tache_libre = "texte saisi"`
- `heures_planif = 0`, `heures_reelles = valeur saisie`

### Ajustement des heures réelles
- Le salarié clique sur une cellule "Réel" d'une tâche planifiée
- Saisit les heures réellement travaillées
- Peut ajouter une note (formation, support client, blocage…)
- `heures_reelles` ≠ `heures_planif` → écart enregistré, visible dans les rapports

---

## 9. Règles métier

| # | Règle |
|---|---|
| R1 | La somme de `heures_planif` par salarié par jour ne peut pas dépasser `heures_journee_max` |
| R2 | Une tâche (ticket) avec statut "Clôturé" n'apparaît pas dans le sélecteur |
| R3 | Les jours fériés et congés approuvés bloquent la planification (cellule non éditable) |
| R4 | Les samedis et dimanches sont affichés mais non éditables |
| R5 | Un responsable ne peut planifier que pour les membres de ses équipes |
| R6 | Un `planning_slot` doit avoir soit `id_ticket` soit `tache_libre` (pas les deux null) |
| R7 | `heures_reelles` peut dépasser `heures_planif` (le salarié a travaillé plus) |
| R8 | Pas de verrouillage du plan : responsable et salarié peuvent modifier à tout moment |
| R9 | Tout changement est tracé via `updated_at` + `created_by` (log plus fin à prévoir) |

---

## 10. Intégrations existantes

| Système | Utilisation dans le module |
|---|---|
| `jours_feries` | Bloquer les cellules des jours fériés à la planification et en saisie |
| `gestionconge` | Lire les congés approuvés (`statut = 'approuvé'`) pour griser les cellules |
| `tickets` | Liste des tâches disponibles (filtre `statut ≠ clôturé`, lien projet obligatoire) |
| `projets` | Sélecteur de projet dans la modale d'affectation |
| `salaries` | Liste des membres d'équipe |
| `users` | Identification du responsable connecté |
| Module droits accès | Sous-module "Planification équipe" pour séparer responsable / salarié |
| `settings.heures_journee_max` | Plafond journalier utilisé dans la validation |

---

## 11. Rapports & traçabilité

### Données disponibles immédiatement après développement
- Heures planifiées vs réelles par salarié, par semaine/mois
- Tâches systématiquement sous-estimées (écart récurrent)
- Taux de complétion par salarié

### Rapports futurs envisagés (hors scope v1)
- Rapport écart planifié/réel par salarié (identifier lenteur vs mauvaise planification)
- Taux de charge par équipe par semaine
- Historique des modifications de planning (log détaillé)
- Export PDF / Excel de la grille semaine

### Traçabilité v1
- `created_by` + `created_at` : qui a créé le slot et quand
- `updated_at` : dernière modification
- `heures_planif` vs `heures_reelles` : écart calculable
- `note_salarie` : justification textuelle de l'écart

---

## 12. Ordre de développement

| Étape | Description | Dépendances |
|---|---|---|
| **A** | Référentiel Équipes (tables + CRUD dans paramètres) | Aucune |
| **B** | Paramètre `heures_journee_max` dans Paramètres Généraux | Aucune |
| **C** | Nouveau module + sous-modules dans la gestion des droits | Aucune |
| **D** | Rendre le projet obligatoire à la création d'un ticket | Tickets existants |
| **E** | Suppression de `/saisietemps` | Archivage données si besoin |
| **F** | Contrôleur `PlanificationController` — vue responsable | A, B, C |
| **G** | Interface responsable — grille semaine + modales | F |
| **H** | Interface salarié — vue personnelle + saisie réelle + libre | F |
| **I** | Intégration jours fériés + congés dans la grille | G, H |
| **J** | Page rapports basiques (écart planifié/réel) | H |

---

## 13. Points ouverts (à décider plus tard)

| # | Sujet | Contexte |
|---|---|---|
| P1 | Plafond par équipe (pas seulement global) | Certaines équipes travaillent 7h, d'autres 8h |
| P2 | Verrouillage du plan après une date | Empêcher de modifier le passé au-delà de N jours |
| P3 | Log détaillé des changements de planning | Qui a modifié quoi et quand |
| P4 | Notification salarié quand une tâche lui est affectée | Email ou notification in-app |
| P5 | Vue mensuelle (en plus de la vue semaine) | Visibilité long terme |
| P6 | Export PDF/Excel de la grille | Impression planning équipe |
| P7 | Tickets sans projet — migration des données existantes | Avant de rendre le projet obligatoire |
| P8 | Intégration calendrier existant (`/calendar`) | Afficher les slots dans le calendrier global |
