1736 lines
56 KiB
SQL
1736 lines
56 KiB
SQL
-- ============================================================
|
|
-- Base de donnees de test PTA
|
|
-- Compatible MySQL 8.0.16+
|
|
-- ============================================================
|
|
|
|
USE nfbhnajdb1;
|
|
|
|
-- ============================================================
|
|
-- 1. REFERENTIEL
|
|
-- ============================================================
|
|
|
|
CREATE TABLE agent (
|
|
id_agent INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
matricule VARCHAR(50) NOT NULL UNIQUE,
|
|
nom VARCHAR(100) NOT NULL,
|
|
prenom VARCHAR(100) NOT NULL,
|
|
email VARCHAR(255) NULL,
|
|
telephone VARCHAR(30) NULL,
|
|
adresse VARCHAR(255) NULL,
|
|
est_diplome BOOLEAN NOT NULL DEFAULT FALSE,
|
|
diplome_libelle VARCHAR(150) NULL,
|
|
date_debut_contrat DATE NULL,
|
|
id_structure INT UNSIGNED NULL,
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE TABLE structure (
|
|
id_structure INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
code VARCHAR(50) NOT NULL UNIQUE,
|
|
nom VARCHAR(150) NOT NULL,
|
|
adresse VARCHAR(255) NULL,
|
|
type_affectation ENUM('PERISCOLAIRE', 'EXTRASCOLAIRE') NOT NULL DEFAULT 'PERISCOLAIRE',
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE
|
|
) ENGINE = InnoDB;
|
|
|
|
ALTER TABLE agent
|
|
ADD CONSTRAINT fk_agent_structure_principale
|
|
FOREIGN KEY (id_structure) REFERENCES structure(id_structure)
|
|
ON UPDATE CASCADE
|
|
ON DELETE SET NULL;
|
|
|
|
CREATE INDEX idx_agent_structure_principale ON agent(id_structure);
|
|
|
|
CREATE TABLE structure_couverture_horaire (
|
|
id_couverture INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_structure INT UNSIGNED NOT NULL,
|
|
jour_semaine TINYINT UNSIGNED NOT NULL COMMENT '1=lundi, 5=vendredi',
|
|
heure_debut TIME NOT NULL,
|
|
heure_fin TIME NOT NULL,
|
|
minimum_agents TINYINT UNSIGNED NOT NULL DEFAULT 1,
|
|
|
|
CONSTRAINT fk_couverture_structure
|
|
FOREIGN KEY (id_structure) REFERENCES structure(id_structure)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
CONSTRAINT chk_couverture_jour CHECK (jour_semaine BETWEEN 1 AND 5),
|
|
CONSTRAINT chk_couverture_ordre CHECK (heure_fin > heure_debut),
|
|
CONSTRAINT chk_couverture_minimum CHECK (minimum_agents BETWEEN 1 AND 50),
|
|
CONSTRAINT chk_couverture_quart_heure CHECK (
|
|
MOD(MINUTE(heure_debut), 15) = 0
|
|
AND MOD(MINUTE(heure_fin), 15) = 0
|
|
AND SECOND(heure_debut) = 0
|
|
AND SECOND(heure_fin) = 0
|
|
)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE INDEX idx_couverture_structure_jour
|
|
ON structure_couverture_horaire(id_structure, jour_semaine, heure_debut, heure_fin);
|
|
|
|
CREATE TABLE tranche_age (
|
|
id_tranche_age INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
libelle VARCHAR(100) NOT NULL,
|
|
age_min_mois INT UNSIGNED NULL,
|
|
age_max_mois INT UNSIGNED NULL,
|
|
CONSTRAINT chk_tranche_age
|
|
CHECK (
|
|
age_min_mois IS NULL
|
|
OR age_max_mois IS NULL
|
|
OR age_max_mois >= age_min_mois
|
|
)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE TABLE structure_capacite (
|
|
id_structure INT UNSIGNED NOT NULL,
|
|
id_tranche_age INT UNSIGNED NOT NULL,
|
|
nombre_places INT UNSIGNED NOT NULL,
|
|
|
|
PRIMARY KEY (id_structure, id_tranche_age),
|
|
|
|
CONSTRAINT fk_capacite_structure
|
|
FOREIGN KEY (id_structure)
|
|
REFERENCES structure(id_structure),
|
|
|
|
CONSTRAINT fk_capacite_tranche_age
|
|
FOREIGN KEY (id_tranche_age)
|
|
REFERENCES tranche_age(id_tranche_age),
|
|
|
|
CONSTRAINT chk_nombre_places
|
|
CHECK (nombre_places > 0)
|
|
) ENGINE = InnoDB;
|
|
|
|
-- ============================================================
|
|
-- 2. CONTRATS ET QUOTAS
|
|
-- ============================================================
|
|
|
|
CREATE TABLE contrat_agent (
|
|
id_contrat INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_agent INT UNSIGNED NOT NULL,
|
|
date_debut DATE NOT NULL,
|
|
date_fin DATE NULL,
|
|
|
|
-- Quota total sur la periode du contrat, en minutes.
|
|
-- Le stockage en minutes evite les problemes d'arrondis.
|
|
quota_minutes INT UNSIGNED NOT NULL,
|
|
|
|
type_contrat VARCHAR(100) NULL,
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
|
|
|
CONSTRAINT fk_contrat_agent
|
|
FOREIGN KEY (id_agent)
|
|
REFERENCES agent(id_agent),
|
|
|
|
CONSTRAINT chk_dates_contrat
|
|
CHECK (date_fin IS NULL OR date_fin >= date_debut),
|
|
|
|
CONSTRAINT chk_quota_contrat
|
|
CHECK (quota_minutes > 0)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE INDEX idx_contrat_agent_dates
|
|
ON contrat_agent(id_agent, date_debut, date_fin);
|
|
|
|
-- ============================================================
|
|
-- 3. SEMAINES
|
|
-- ============================================================
|
|
|
|
CREATE TABLE semaine (
|
|
id_semaine INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
annee SMALLINT UNSIGNED NOT NULL,
|
|
numero_semaine TINYINT UNSIGNED NOT NULL,
|
|
date_debut DATE NOT NULL,
|
|
date_fin DATE NOT NULL,
|
|
statut ENUM('OUVERTE', 'VALIDEE', 'VERROUILLEE')
|
|
NOT NULL DEFAULT 'OUVERTE',
|
|
|
|
UNIQUE KEY uk_semaine_annee_numero (annee, numero_semaine),
|
|
UNIQUE KEY uk_semaine_dates (date_debut, date_fin),
|
|
|
|
CONSTRAINT chk_numero_semaine
|
|
CHECK (numero_semaine BETWEEN 1 AND 53),
|
|
|
|
CONSTRAINT chk_dates_semaine
|
|
CHECK (date_fin >= date_debut)
|
|
) ENGINE = InnoDB;
|
|
|
|
-- ============================================================
|
|
-- 4. PLANNINGS ET CRENEAUX
|
|
-- ============================================================
|
|
|
|
CREATE TABLE planning (
|
|
id_planning INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_agent INT UNSIGNED NOT NULL,
|
|
id_semaine INT UNSIGNED NOT NULL,
|
|
id_structure INT UNSIGNED NOT NULL,
|
|
|
|
statut ENUM('BROUILLON', 'VALIDE')
|
|
NOT NULL DEFAULT 'BROUILLON',
|
|
|
|
commentaire VARCHAR(500) NULL,
|
|
id_planning_source INT UNSIGNED NULL,
|
|
|
|
date_creation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
date_modification DATETIME NOT NULL
|
|
DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP,
|
|
date_validation DATETIME NULL,
|
|
|
|
CONSTRAINT fk_planning_agent
|
|
FOREIGN KEY (id_agent)
|
|
REFERENCES agent(id_agent),
|
|
|
|
CONSTRAINT fk_planning_semaine
|
|
FOREIGN KEY (id_semaine)
|
|
REFERENCES semaine(id_semaine),
|
|
|
|
CONSTRAINT fk_planning_structure
|
|
FOREIGN KEY (id_structure)
|
|
REFERENCES structure(id_structure),
|
|
|
|
CONSTRAINT fk_planning_source
|
|
FOREIGN KEY (id_planning_source)
|
|
REFERENCES planning(id_planning)
|
|
ON DELETE SET NULL,
|
|
|
|
UNIQUE KEY uk_planning_agent_semaine_structure
|
|
(id_agent, id_semaine, id_structure)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE INDEX idx_planning_agent_statut
|
|
ON planning(id_agent, statut);
|
|
|
|
CREATE TABLE creneau_horaire (
|
|
id_creneau INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_planning INT UNSIGNED NOT NULL,
|
|
date_jour DATE NOT NULL,
|
|
heure_debut TIME NOT NULL,
|
|
heure_fin TIME NOT NULL,
|
|
|
|
CONSTRAINT fk_creneau_planning
|
|
FOREIGN KEY (id_planning)
|
|
REFERENCES planning(id_planning)
|
|
ON DELETE CASCADE,
|
|
|
|
CONSTRAINT chk_creneau_ordre
|
|
CHECK (heure_fin > heure_debut),
|
|
|
|
-- Les heures doivent tomber sur un quart d'heure exact.
|
|
CONSTRAINT chk_creneau_quart_heure
|
|
CHECK (
|
|
MOD(MINUTE(heure_debut), 15) = 0
|
|
AND MOD(MINUTE(heure_fin), 15) = 0
|
|
AND SECOND(heure_debut) = 0
|
|
AND SECOND(heure_fin) = 0
|
|
)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE INDEX idx_creneau_planning_date
|
|
ON creneau_horaire(id_planning, date_jour, heure_debut, heure_fin);
|
|
|
|
-- ============================================================
|
|
-- 5. VUES DE SUIVI
|
|
-- ============================================================
|
|
|
|
CREATE OR REPLACE VIEW v_suivi_quota_contrat AS
|
|
SELECT
|
|
ca.id_contrat,
|
|
ca.id_agent,
|
|
a.matricule,
|
|
CONCAT(a.prenom, ' ', a.nom) AS agent,
|
|
ca.date_debut,
|
|
ca.date_fin,
|
|
ca.quota_minutes,
|
|
ROUND(ca.quota_minutes / 60, 2) AS quota_heures,
|
|
COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE'
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
) AS minutes_planifiees_validees,
|
|
ROUND(
|
|
COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE'
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
) / 60,
|
|
2
|
|
) AS heures_planifiees_validees,
|
|
ROUND(
|
|
(
|
|
ca.quota_minutes
|
|
- COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE'
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
)
|
|
) / 60,
|
|
2
|
|
) AS heures_restantes
|
|
FROM contrat_agent ca
|
|
JOIN agent a
|
|
ON a.id_agent = ca.id_agent
|
|
LEFT JOIN planning p
|
|
ON p.id_agent = ca.id_agent
|
|
LEFT JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
GROUP BY
|
|
ca.id_contrat,
|
|
ca.id_agent,
|
|
a.matricule,
|
|
a.prenom,
|
|
a.nom,
|
|
ca.date_debut,
|
|
ca.date_fin,
|
|
ca.quota_minutes;
|
|
|
|
CREATE OR REPLACE VIEW v_planning_agent_semaine AS
|
|
SELECT
|
|
p.id_planning,
|
|
p.id_agent,
|
|
a.matricule,
|
|
CONCAT(a.prenom, ' ', a.nom) AS agent,
|
|
s.annee,
|
|
s.numero_semaine,
|
|
st.nom AS structure,
|
|
p.statut,
|
|
ROUND(
|
|
COALESCE(
|
|
SUM(TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 3600),
|
|
0
|
|
),
|
|
2
|
|
) AS heures_planifiees
|
|
FROM planning p
|
|
JOIN agent a
|
|
ON a.id_agent = p.id_agent
|
|
JOIN semaine s
|
|
ON s.id_semaine = p.id_semaine
|
|
JOIN structure st
|
|
ON st.id_structure = p.id_structure
|
|
LEFT JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
GROUP BY
|
|
p.id_planning,
|
|
p.id_agent,
|
|
a.matricule,
|
|
a.prenom,
|
|
a.nom,
|
|
s.annee,
|
|
s.numero_semaine,
|
|
st.nom,
|
|
p.statut;
|
|
|
|
-- ============================================================
|
|
-- 6. PROCEDURES DE CONTROLE ET DE VALIDATION
|
|
-- ============================================================
|
|
|
|
DELIMITER $$
|
|
|
|
DROP PROCEDURE IF EXISTS sp_preparer_controles_planning$$
|
|
CREATE PROCEDURE sp_preparer_controles_planning(
|
|
IN p_id_planning INT UNSIGNED
|
|
)
|
|
controle: BEGIN
|
|
DECLARE v_planning_existe INT DEFAULT 0;
|
|
|
|
DROP TEMPORARY TABLE IF EXISTS tmp_controles_planning;
|
|
|
|
CREATE TEMPORARY TABLE tmp_controles_planning (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
niveau ENUM('ERREUR', 'ALERTE', 'INFO') NOT NULL,
|
|
code VARCHAR(100) NOT NULL,
|
|
message VARCHAR(1000) NOT NULL
|
|
);
|
|
|
|
SELECT COUNT(*)
|
|
INTO v_planning_existe
|
|
FROM planning
|
|
WHERE id_planning = p_id_planning;
|
|
|
|
IF v_planning_existe = 0 THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'ERREUR',
|
|
'PLANNING_INTROUVABLE',
|
|
CONCAT('Le planning #', p_id_planning, ' n''existe pas.')
|
|
);
|
|
|
|
LEAVE controle;
|
|
END IF;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 1 : le planning doit contenir au moins un creneau
|
|
-- --------------------------------------------------------
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM creneau_horaire
|
|
WHERE id_planning = p_id_planning
|
|
) THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'ERREUR',
|
|
'AUCUN_CRENEAU',
|
|
'Le planning ne contient aucun creneau horaire.'
|
|
);
|
|
END IF;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 2 : chaque creneau doit etre dans la semaine
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CRENEAU_HORS_SEMAINE',
|
|
CONCAT(
|
|
'Le creneau #', c.id_creneau,
|
|
' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
|
|
' est en dehors de la semaine ', s.numero_semaine,
|
|
' (', DATE_FORMAT(s.date_debut, '%d/%m/%Y'),
|
|
' au ', DATE_FORMAT(s.date_fin, '%d/%m/%Y'), ').'
|
|
)
|
|
FROM planning p
|
|
JOIN semaine s
|
|
ON s.id_semaine = p.id_semaine
|
|
JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
WHERE p.id_planning = p_id_planning
|
|
AND c.date_jour NOT BETWEEN s.date_debut AND s.date_fin;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 3 : chaque creneau doit etre couvert par un contrat
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'HORS_CONTRAT',
|
|
CONCAT(
|
|
'Le creneau #', c.id_creneau,
|
|
' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
|
|
' n''est couvert par aucun contrat actif pour cet agent.'
|
|
)
|
|
FROM planning p
|
|
JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
WHERE p.id_planning = p_id_planning
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM contrat_agent ca
|
|
WHERE ca.id_agent = p.id_agent
|
|
AND ca.actif = TRUE
|
|
AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
);
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 4 : chevauchements dans le meme planning
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CHEVAUCHEMENT_INTERNE',
|
|
CONCAT(
|
|
'Les creneaux #', c1.id_creneau,
|
|
' et #', c2.id_creneau,
|
|
' se chevauchent le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'),
|
|
' (', TIME_FORMAT(c1.heure_debut, '%H:%i'), '-', TIME_FORMAT(c1.heure_fin, '%H:%i'),
|
|
' et ', TIME_FORMAT(c2.heure_debut, '%H:%i'), '-', TIME_FORMAT(c2.heure_fin, '%H:%i'), ').'
|
|
)
|
|
FROM creneau_horaire c1
|
|
JOIN creneau_horaire c2
|
|
ON c2.id_planning = c1.id_planning
|
|
AND c2.date_jour = c1.date_jour
|
|
AND c2.id_creneau > c1.id_creneau
|
|
AND c1.heure_debut < c2.heure_fin
|
|
AND c1.heure_fin > c2.heure_debut
|
|
WHERE c1.id_planning = p_id_planning;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 5 : chevauchement avec un autre planning valide
|
|
-- pour le meme agent, meme si la structure est differente.
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CHEVAUCHEMENT_AUTRE_PLANNING',
|
|
CONCAT(
|
|
'Le creneau #', c1.id_creneau,
|
|
' chevauche le creneau #', c2.id_creneau,
|
|
' du planning valide #', p2.id_planning,
|
|
' le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'), '.'
|
|
)
|
|
FROM planning p1
|
|
JOIN creneau_horaire c1
|
|
ON c1.id_planning = p1.id_planning
|
|
JOIN planning p2
|
|
ON p2.id_agent = p1.id_agent
|
|
AND p2.id_planning <> p1.id_planning
|
|
AND p2.statut = 'VALIDE'
|
|
JOIN creneau_horaire c2
|
|
ON c2.id_planning = p2.id_planning
|
|
AND c2.date_jour = c1.date_jour
|
|
AND c1.heure_debut < c2.heure_fin
|
|
AND c1.heure_fin > c2.heure_debut
|
|
WHERE p1.id_planning = p_id_planning;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 6 : depassement du quota du contrat
|
|
-- Le quota est ici un quota total sur la periode du contrat.
|
|
-- Il s'agit d'une ALERTE et non d'une erreur bloquante.
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ALERTE',
|
|
'QUOTA_DEPASSE',
|
|
CONCAT(
|
|
'Le contrat #', ca.id_contrat,
|
|
' prevoit ', ROUND(ca.quota_minutes / 60, 2), ' h. ',
|
|
'Apres validation, ',
|
|
ROUND(
|
|
(
|
|
-- Minutes deja validees, hors planning courant
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
|
|
)
|
|
FROM planning pv
|
|
JOIN creneau_horaire cv
|
|
ON cv.id_planning = pv.id_planning
|
|
WHERE pv.id_agent = ca.id_agent
|
|
AND pv.statut = 'VALIDE'
|
|
AND pv.id_planning <> p_id_planning
|
|
AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
+
|
|
-- Minutes du planning courant appartenant a ce contrat
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
|
|
)
|
|
FROM creneau_horaire cc
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
) / 60,
|
|
2
|
|
),
|
|
' h seraient planifiees.'
|
|
)
|
|
FROM planning p
|
|
JOIN contrat_agent ca
|
|
ON ca.id_agent = p.id_agent
|
|
AND ca.actif = TRUE
|
|
WHERE p.id_planning = p_id_planning
|
|
AND EXISTS (
|
|
SELECT 1
|
|
FROM creneau_horaire cc
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
)
|
|
AND (
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
|
|
)
|
|
FROM planning pv
|
|
JOIN creneau_horaire cv
|
|
ON cv.id_planning = pv.id_planning
|
|
WHERE pv.id_agent = ca.id_agent
|
|
AND pv.statut = 'VALIDE'
|
|
AND pv.id_planning <> p_id_planning
|
|
AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
+
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
|
|
)
|
|
FROM creneau_horaire cc
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
) > ca.quota_minutes;
|
|
|
|
-- Si aucun probleme n'a ete detecte, on retourne explicitement OK.
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM tmp_controles_planning
|
|
) THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'INFO',
|
|
'OK',
|
|
'Aucune anomalie detectee. Le planning peut etre valide.'
|
|
);
|
|
END IF;
|
|
END$$
|
|
|
|
DROP PROCEDURE IF EXISTS sp_controler_planning$$
|
|
CREATE PROCEDURE sp_controler_planning(
|
|
IN p_id_planning INT UNSIGNED
|
|
)
|
|
BEGIN
|
|
CALL sp_preparer_controles_planning(p_id_planning);
|
|
|
|
SELECT
|
|
niveau,
|
|
code,
|
|
message
|
|
FROM tmp_controles_planning
|
|
ORDER BY
|
|
FIELD(niveau, 'ERREUR', 'ALERTE', 'INFO'),
|
|
id;
|
|
END$$
|
|
|
|
DROP PROCEDURE IF EXISTS sp_valider_planning$$
|
|
CREATE PROCEDURE sp_valider_planning(
|
|
IN p_id_planning INT UNSIGNED,
|
|
IN p_forcer_depassement_quota BOOLEAN
|
|
)
|
|
BEGIN
|
|
DECLARE v_nb_erreurs INT DEFAULT 0;
|
|
DECLARE v_nb_alertes_quota INT DEFAULT 0;
|
|
DECLARE v_message VARCHAR(1000);
|
|
|
|
CALL sp_preparer_controles_planning(p_id_planning);
|
|
|
|
SELECT COUNT(*)
|
|
INTO v_nb_erreurs
|
|
FROM tmp_controles_planning
|
|
WHERE niveau = 'ERREUR';
|
|
|
|
IF v_nb_erreurs > 0 THEN
|
|
SELECT message
|
|
INTO v_message
|
|
FROM tmp_controles_planning
|
|
WHERE niveau = 'ERREUR'
|
|
ORDER BY id
|
|
LIMIT 1;
|
|
|
|
SET v_message = CONCAT(
|
|
'Validation impossible. ',
|
|
v_nb_erreurs,
|
|
' erreur(s) detectee(s). Premiere erreur : ',
|
|
v_message
|
|
);
|
|
|
|
SIGNAL SQLSTATE '45000'
|
|
SET MESSAGE_TEXT = v_message;
|
|
END IF;
|
|
|
|
SELECT COUNT(*)
|
|
INTO v_nb_alertes_quota
|
|
FROM tmp_controles_planning
|
|
WHERE code = 'QUOTA_DEPASSE';
|
|
|
|
IF v_nb_alertes_quota > 0
|
|
AND COALESCE(p_forcer_depassement_quota, FALSE) = FALSE THEN
|
|
|
|
SET v_message = CONCAT(
|
|
'Validation suspendue : le quota contractuel serait depasse. ',
|
|
'Relancer avec p_forcer_depassement_quota = TRUE pour forcer la validation.'
|
|
);
|
|
|
|
SIGNAL SQLSTATE '45000'
|
|
SET MESSAGE_TEXT = v_message;
|
|
END IF;
|
|
|
|
UPDATE planning
|
|
SET
|
|
statut = 'VALIDE',
|
|
date_validation = CURRENT_TIMESTAMP
|
|
WHERE id_planning = p_id_planning;
|
|
|
|
SELECT
|
|
p.id_planning,
|
|
p.statut,
|
|
p.date_validation,
|
|
'Planning valide avec succes.' AS message
|
|
FROM planning p
|
|
WHERE p.id_planning = p_id_planning;
|
|
END$$
|
|
|
|
DELIMITER ;
|
|
|
|
-- ============================================================
|
|
-- 7. JEU D'ESSAI
|
|
-- ============================================================
|
|
|
|
INSERT INTO agent (
|
|
id_agent,
|
|
matricule,
|
|
nom,
|
|
prenom,
|
|
email,
|
|
telephone,
|
|
adresse,
|
|
est_diplome,
|
|
diplome_libelle,
|
|
date_debut_contrat
|
|
)
|
|
VALUES
|
|
(1, 'AG001', 'MARTIN', 'Alice', 'alice.martin@example.test', '06 10 20 30 40', '12 rue des Lilas, Orléans', TRUE, 'BAFA', '2026-07-01'),
|
|
(2, 'AG002', 'DURAND', 'Bruno', 'bruno.durand@example.test', '06 11 21 31 41', '8 avenue de Paris, Orléans', FALSE, NULL, '2026-07-01'),
|
|
(3, 'AG003', 'PETIT', 'Chloe', 'chloe.petit@example.test', '06 12 22 32 42', '5 rue Royale, Orléans', TRUE, 'CAP AEPE', '2026-07-22');
|
|
|
|
INSERT INTO structure (
|
|
id_structure,
|
|
code,
|
|
nom,
|
|
adresse,
|
|
type_affectation
|
|
)
|
|
VALUES
|
|
(1, 'STR001', 'Creche des Lilas', '10 rue des Lilas, Orleans', 'PERISCOLAIRE'),
|
|
(2, 'STR002', 'Accueil Madeleine', '36 rue du Faubourg Madeleine, Orleans', 'EXTRASCOLAIRE');
|
|
|
|
-- Plages de couverture de test. Elles sont configurables dans l'administration.
|
|
INSERT INTO structure_couverture_horaire (id_structure, jour_semaine, heure_debut, heure_fin, minimum_agents)
|
|
VALUES
|
|
(1, 1, '07:30:00', '09:00:00', 1),
|
|
(1, 1, '16:30:00', '18:30:00', 1),
|
|
(1, 2, '07:30:00', '09:00:00', 1),
|
|
(1, 2, '16:30:00', '18:30:00', 1),
|
|
(1, 3, '07:30:00', '09:00:00', 1),
|
|
(1, 3, '16:30:00', '18:30:00', 1),
|
|
(1, 4, '07:30:00', '09:00:00', 1),
|
|
(1, 4, '16:30:00', '18:30:00', 1),
|
|
(1, 5, '07:30:00', '09:00:00', 1),
|
|
(1, 5, '16:30:00', '18:30:00', 1),
|
|
(2, 1, '08:00:00', '18:00:00', 1),
|
|
(2, 2, '08:00:00', '18:00:00', 1),
|
|
(2, 3, '08:00:00', '18:00:00', 1),
|
|
(2, 4, '08:00:00', '18:00:00', 1),
|
|
(2, 5, '08:00:00', '18:00:00', 1);
|
|
|
|
-- Une structure principale par agent.
|
|
UPDATE agent SET id_structure = 1 WHERE id_agent IN (1, 2);
|
|
UPDATE agent SET id_structure = 2 WHERE id_agent = 3;
|
|
|
|
INSERT INTO tranche_age (
|
|
id_tranche_age,
|
|
libelle,
|
|
age_min_mois,
|
|
age_max_mois
|
|
)
|
|
VALUES
|
|
(1, '0 - 3 ans', 0, 35),
|
|
(2, '3 - 6 ans', 36, 71),
|
|
(3, '6 - 11 ans', 72, 131);
|
|
|
|
INSERT INTO structure_capacite (
|
|
id_structure,
|
|
id_tranche_age,
|
|
nombre_places
|
|
)
|
|
VALUES
|
|
(1, 1, 20),
|
|
(1, 2, 12),
|
|
(2, 2, 18),
|
|
(2, 3, 30);
|
|
|
|
-- Alice : 100 h sur juillet/aout pour permettre plusieurs plannings valides.
|
|
-- Bruno : 35 h au total, afin de provoquer un depassement de quota.
|
|
-- Chloe : contrat qui commence le 22/07, afin de tester un planning hors contrat.
|
|
INSERT INTO contrat_agent (
|
|
id_contrat,
|
|
id_agent,
|
|
date_debut,
|
|
date_fin,
|
|
quota_minutes,
|
|
type_contrat
|
|
)
|
|
VALUES
|
|
(1, 1, '2026-07-01', '2026-08-31', 6000, 'PTA ete - 100 h'),
|
|
(2, 2, '2026-07-01', '2026-07-31', 2100, 'PTA juillet - 35 h'),
|
|
(3, 3, '2026-07-22', '2026-08-31', 2100, 'PTA ete - 35 h');
|
|
|
|
INSERT INTO semaine (
|
|
id_semaine,
|
|
annee,
|
|
numero_semaine,
|
|
date_debut,
|
|
date_fin
|
|
)
|
|
VALUES
|
|
(1, 2026, 29, '2026-07-13', '2026-07-19'),
|
|
(2, 2026, 30, '2026-07-20', '2026-07-26'),
|
|
(3, 2026, 31, '2026-07-27', '2026-08-02');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #1 : Alice, semaine 29, 32 h, deja valide
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire,
|
|
date_validation
|
|
)
|
|
VALUES
|
|
(1, 1, 1, 1, 'VALIDE', 'Planning valide de reference - 32 h', CURRENT_TIMESTAMP);
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(1, '2026-07-13', '08:00:00', '12:00:00'),
|
|
(1, '2026-07-13', '13:00:00', '17:00:00'),
|
|
(1, '2026-07-14', '08:00:00', '12:00:00'),
|
|
(1, '2026-07-14', '13:00:00', '17:00:00'),
|
|
(1, '2026-07-15', '08:00:00', '12:00:00'),
|
|
(1, '2026-07-15', '13:00:00', '17:00:00'),
|
|
(1, '2026-07-16', '08:00:00', '12:00:00'),
|
|
(1, '2026-07-16', '13:00:00', '17:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #2 : Alice, semaine 30, 32 h, deja valide
|
|
-- Sert de planning de reference pour tester les chevauchements.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire,
|
|
date_validation
|
|
)
|
|
VALUES
|
|
(2, 1, 2, 1, 'VALIDE', 'Deuxieme planning valide de reference - 32 h', CURRENT_TIMESTAMP);
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(2, '2026-07-20', '08:00:00', '12:00:00'),
|
|
(2, '2026-07-20', '13:00:00', '17:00:00'),
|
|
(2, '2026-07-21', '08:00:00', '12:00:00'),
|
|
(2, '2026-07-21', '13:00:00', '17:00:00'),
|
|
(2, '2026-07-22', '08:00:00', '12:00:00'),
|
|
(2, '2026-07-22', '13:00:00', '17:00:00'),
|
|
(2, '2026-07-23', '08:00:00', '12:00:00'),
|
|
(2, '2026-07-23', '13:00:00', '17:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #3 : Alice, semaine 30, autre structure
|
|
-- ERREUR ATTENDUE : chevauchement avec le planning #2.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire
|
|
)
|
|
VALUES
|
|
(3, 1, 2, 2, 'BROUILLON', 'Test chevauchement entre deux structures');
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(3, '2026-07-20', '10:00:00', '12:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #4 : Bruno, semaine 29, 30 h, deja valide
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire,
|
|
date_validation
|
|
)
|
|
VALUES
|
|
(4, 2, 1, 1, 'VALIDE', 'Planning valide Bruno - 30 h sur quota de 35 h', CURRENT_TIMESTAMP);
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(4, '2026-07-13', '08:00:00', '12:00:00'),
|
|
(4, '2026-07-13', '13:00:00', '16:00:00'),
|
|
(4, '2026-07-14', '08:00:00', '12:00:00'),
|
|
(4, '2026-07-14', '13:00:00', '16:00:00'),
|
|
(4, '2026-07-15', '08:00:00', '12:00:00'),
|
|
(4, '2026-07-15', '13:00:00', '16:00:00'),
|
|
(4, '2026-07-16', '08:00:00', '12:00:00'),
|
|
(4, '2026-07-16', '13:00:00', '16:00:00'),
|
|
(4, '2026-07-17', '08:00:00', '10:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #5 : Bruno, semaine 30, 10 h
|
|
-- ALERTE ATTENDUE : quota depasse (30 h deja validees + 10 h = 40 h > 35 h).
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire
|
|
)
|
|
VALUES
|
|
(5, 2, 2, 1, 'BROUILLON', 'Test depassement de quota');
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(5, '2026-07-20', '08:00:00', '12:00:00'),
|
|
(5, '2026-07-21', '08:00:00', '12:00:00'),
|
|
(5, '2026-07-22', '08:00:00', '10:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #6 : Chloe, semaine 29
|
|
-- ERREUR ATTENDUE : hors contrat, son contrat commence le 22/07.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire
|
|
)
|
|
VALUES
|
|
(6, 3, 1, 1, 'BROUILLON', 'Test creneau avant le debut du contrat');
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(6, '2026-07-15', '08:00:00', '12:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #7 : Bruno, semaine 30, deuxieme structure
|
|
-- ERREUR ATTENDUE : deux creneaux se chevauchent dans le meme planning.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire
|
|
)
|
|
VALUES
|
|
(7, 2, 2, 2, 'BROUILLON', 'Test chevauchement interne');
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(7, '2026-07-23', '08:00:00', '12:00:00'),
|
|
(7, '2026-07-23', '11:00:00', '14:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #8 : Alice, semaine 31, 24 h
|
|
-- CAS VALIDE ATTENDU : 64 h deja validees + 24 h = 88 h sur quota de 100 h.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire,
|
|
id_planning_source
|
|
)
|
|
VALUES
|
|
(8, 1, 3, 1, 'BROUILLON', 'Cas nominal pouvant etre valide', 2);
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(8, '2026-07-27', '08:00:00', '12:00:00'),
|
|
(8, '2026-07-27', '13:00:00', '17:00:00'),
|
|
(8, '2026-07-28', '08:00:00', '12:00:00'),
|
|
(8, '2026-07-28', '13:00:00', '17:00:00'),
|
|
(8, '2026-07-29', '08:00:00', '12:00:00'),
|
|
(8, '2026-07-29', '13:00:00', '17:00:00');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- Planning #9 : Chloe, semaine 30
|
|
-- ERREUR ATTENDUE : le 27/07 appartient a la semaine 31, pas a la semaine 30.
|
|
-- Le creneau est volontairement couvert par le contrat pour isoler ce controle.
|
|
-- ------------------------------------------------------------
|
|
INSERT INTO planning (
|
|
id_planning,
|
|
id_agent,
|
|
id_semaine,
|
|
id_structure,
|
|
statut,
|
|
commentaire
|
|
)
|
|
VALUES
|
|
(9, 3, 2, 1, 'BROUILLON', 'Test date hors de la semaine du planning');
|
|
|
|
INSERT INTO creneau_horaire (id_planning, date_jour, heure_debut, heure_fin)
|
|
VALUES
|
|
(9, '2026-07-27', '08:00:00', '12:00:00');
|
|
|
|
-- ============================================================
|
|
-- 8. REQUETES DE TEST A EXECUTER MANUELLEMENT
|
|
-- ============================================================
|
|
|
|
-- CAS 1 : cas nominal, aucune anomalie attendue.
|
|
-- CALL sp_controler_planning(8);
|
|
|
|
-- CAS 2 : validation du cas nominal.
|
|
-- CALL sp_valider_planning(8, FALSE);
|
|
|
|
-- CAS 3 : chevauchement avec un autre planning valide.
|
|
-- CALL sp_controler_planning(3);
|
|
-- CALL sp_valider_planning(3, FALSE);
|
|
|
|
-- CAS 4 : depassement de quota.
|
|
-- CALL sp_controler_planning(5);
|
|
-- La validation ci-dessous doit etre refusee :
|
|
-- CALL sp_valider_planning(5, FALSE);
|
|
-- La validation peut etre forcee apres confirmation utilisateur :
|
|
-- CALL sp_valider_planning(5, TRUE);
|
|
|
|
-- CAS 5 : creneau hors contrat.
|
|
-- CALL sp_controler_planning(6);
|
|
-- CALL sp_valider_planning(6, FALSE);
|
|
|
|
-- CAS 6 : chevauchement interne.
|
|
-- CALL sp_controler_planning(7);
|
|
-- CALL sp_valider_planning(7, FALSE);
|
|
|
|
-- CAS 7 : date en dehors de la semaine selectionnee.
|
|
-- CALL sp_controler_planning(9);
|
|
-- CALL sp_valider_planning(9, FALSE);
|
|
|
|
-- CAS 8 : controle direct par contrainte SQL des quarts d'heure.
|
|
-- L'INSERT suivant doit echouer a cause du CHECK chk_creneau_quart_heure :
|
|
-- INSERT INTO creneau_horaire(id_planning, date_jour, heure_debut, heure_fin)
|
|
-- VALUES (8, '2026-07-30', '08:10:00', '10:00:00');
|
|
|
|
-- CAS 9 : consultation du suivi de quota par contrat.
|
|
-- SELECT * FROM v_suivi_quota_contrat ORDER BY id_contrat;
|
|
|
|
-- CAS 10 : synthese des heures par planning.
|
|
-- SELECT * FROM v_planning_agent_semaine ORDER BY id_planning;
|
|
|
|
-- ============================================================
|
|
-- 9. LIMITES FONCTIONNELLES VOLONTAIRES DU MVP
|
|
-- ============================================================
|
|
-- La capacite par tranche d'age est stockee dans structure_capacite,
|
|
-- mais elle ne peut pas encore etre controlee automatiquement avec le
|
|
-- modele actuel car aucun effectif d'enfants/usagers n'est associe a un
|
|
-- planning. Pour controler la capacite, il faudra ajouter une table
|
|
-- d'effectif ou d'affectation par structure, date et tranche d'age.
|
|
--
|
|
-- De meme, les heures ci-dessus sont des heures PLANIFIEES et non des
|
|
-- heures reellement EFFECTUEES. Pour distinguer les deux, il faudra
|
|
-- ajouter un module de pointage ou de realisation effective.
|
|
-- ============================================================
|
|
-- Extension de la base PTA pour l'interface de gestion planning
|
|
-- À exécuter APRES pta_mysql_test.sql
|
|
-- Compatible MySQL 8+
|
|
-- ============================================================
|
|
|
|
USE nfbhnajdb1;
|
|
|
|
-- 1. Motifs d'affectation
|
|
CREATE TABLE IF NOT EXISTS motif_planning (
|
|
id_motif INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
code VARCHAR(50) NOT NULL UNIQUE,
|
|
libelle VARCHAR(120) NOT NULL,
|
|
compte_dans_quota BOOLEAN NOT NULL DEFAULT FALSE,
|
|
ordre_affichage SMALLINT UNSIGNED NOT NULL DEFAULT 100,
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE
|
|
) ENGINE = InnoDB;
|
|
|
|
INSERT INTO motif_planning (code, libelle, compte_dans_quota, ordre_affichage)
|
|
VALUES
|
|
('TRAVAIL', 'Heure de travail', TRUE, 10),
|
|
('CONGE', 'Congé', FALSE, 20),
|
|
('MALADIE', 'Absence', TRUE, 30),
|
|
('FORMATION', 'Formation', TRUE, 40),
|
|
('AUTRE', 'Autre absence (non décomptée)', FALSE, 100)
|
|
ON DUPLICATE KEY UPDATE
|
|
libelle = VALUES(libelle),
|
|
compte_dans_quota = VALUES(compte_dans_quota),
|
|
ordre_affichage = VALUES(ordre_affichage),
|
|
actif = TRUE;
|
|
|
|
-- 2. Rattachement des agents aux structures
|
|
CREATE TABLE IF NOT EXISTS agent_structure (
|
|
id_agent_structure INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_agent INT UNSIGNED NOT NULL,
|
|
id_structure INT UNSIGNED NOT NULL,
|
|
date_debut DATE NULL,
|
|
date_fin DATE NULL,
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
|
|
|
CONSTRAINT fk_agent_structure_agent
|
|
FOREIGN KEY (id_agent) REFERENCES agent(id_agent),
|
|
|
|
CONSTRAINT fk_agent_structure_structure
|
|
FOREIGN KEY (id_structure) REFERENCES structure(id_structure),
|
|
|
|
CONSTRAINT chk_agent_structure_dates
|
|
CHECK (date_fin IS NULL OR date_debut IS NULL OR date_fin >= date_debut),
|
|
|
|
UNIQUE KEY uk_agent_structure_periode (id_agent, id_structure, date_debut)
|
|
) ENGINE = InnoDB;
|
|
|
|
-- Historique initial cohérent avec la structure principale de chaque agent.
|
|
INSERT INTO agent_structure (id_agent, id_structure, date_debut, date_fin, actif)
|
|
VALUES
|
|
(1, 1, '2026-01-01', NULL, TRUE),
|
|
(2, 1, '2026-01-01', NULL, TRUE),
|
|
(3, 2, '2026-01-01', NULL, TRUE)
|
|
ON DUPLICATE KEY UPDATE
|
|
date_fin = VALUES(date_fin),
|
|
actif = VALUES(actif);
|
|
|
|
-- 3. Ajout du motif à chaque créneau existant
|
|
SET @travail_id = (
|
|
SELECT id_motif FROM motif_planning WHERE code = 'TRAVAIL' LIMIT 1
|
|
);
|
|
|
|
SET @col_exists = (
|
|
SELECT COUNT(*)
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
AND TABLE_NAME = 'creneau_horaire'
|
|
AND COLUMN_NAME = 'id_motif'
|
|
);
|
|
|
|
SET @sql = IF(
|
|
@col_exists = 0,
|
|
'ALTER TABLE creneau_horaire ADD COLUMN id_motif INT UNSIGNED NULL AFTER id_planning',
|
|
'SELECT 1'
|
|
);
|
|
PREPARE stmt FROM @sql;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|
|
|
|
UPDATE creneau_horaire
|
|
SET id_motif = @travail_id
|
|
WHERE id_motif IS NULL;
|
|
|
|
SET @fk_exists = (
|
|
SELECT COUNT(*)
|
|
FROM information_schema.TABLE_CONSTRAINTS
|
|
WHERE CONSTRAINT_SCHEMA = DATABASE()
|
|
AND TABLE_NAME = 'creneau_horaire'
|
|
AND CONSTRAINT_NAME = 'fk_creneau_motif'
|
|
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
|
|
);
|
|
|
|
SET @sql = IF(
|
|
@fk_exists = 0,
|
|
'ALTER TABLE creneau_horaire ADD CONSTRAINT fk_creneau_motif FOREIGN KEY (id_motif) REFERENCES motif_planning(id_motif)',
|
|
'SELECT 1'
|
|
);
|
|
PREPARE stmt FROM @sql;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|
|
|
|
ALTER TABLE creneau_horaire
|
|
MODIFY id_motif INT UNSIGNED NOT NULL;
|
|
|
|
SET @idx_exists = (
|
|
SELECT COUNT(*)
|
|
FROM information_schema.STATISTICS
|
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
AND TABLE_NAME = 'creneau_horaire'
|
|
AND INDEX_NAME = 'idx_creneau_motif'
|
|
);
|
|
|
|
SET @sql = IF(
|
|
@idx_exists = 0,
|
|
'CREATE INDEX idx_creneau_motif ON creneau_horaire(id_motif)',
|
|
'SELECT 1'
|
|
);
|
|
PREPARE stmt FROM @sql;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|
|
|
|
-- 4. Vue de synthèse par agent en tenant compte du motif
|
|
CREATE OR REPLACE VIEW v_suivi_quota_contrat AS
|
|
SELECT
|
|
ca.id_contrat,
|
|
ca.id_agent,
|
|
a.matricule,
|
|
CONCAT(a.prenom, ' ', a.nom) AS agent,
|
|
ca.date_debut,
|
|
ca.date_fin,
|
|
ca.quota_minutes,
|
|
ROUND(ca.quota_minutes / 60, 2) AS quota_heures,
|
|
COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
) AS minutes_planifiees_validees,
|
|
ROUND(
|
|
COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
) / 60,
|
|
2
|
|
) AS heures_planifiees_validees,
|
|
ROUND(
|
|
(
|
|
ca.quota_minutes
|
|
- COALESCE(
|
|
SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
|
|
THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
|
|
ELSE 0
|
|
END
|
|
),
|
|
0
|
|
)
|
|
) / 60,
|
|
2
|
|
) AS heures_restantes
|
|
FROM contrat_agent ca
|
|
JOIN agent a ON a.id_agent = ca.id_agent
|
|
LEFT JOIN planning p ON p.id_agent = ca.id_agent
|
|
LEFT JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
LEFT JOIN motif_planning m ON m.id_motif = c.id_motif
|
|
GROUP BY
|
|
ca.id_contrat,
|
|
ca.id_agent,
|
|
a.matricule,
|
|
a.prenom,
|
|
a.nom,
|
|
ca.date_debut,
|
|
ca.date_fin,
|
|
ca.quota_minutes;
|
|
|
|
|
|
-- 5. Mise à jour du contrôle de quota : seuls les motifs marqués
|
|
-- compte_dans_quota = TRUE sont additionnés.
|
|
DELIMITER $$
|
|
|
|
DROP PROCEDURE IF EXISTS sp_preparer_controles_planning$$
|
|
CREATE PROCEDURE sp_preparer_controles_planning(
|
|
IN p_id_planning INT UNSIGNED
|
|
)
|
|
controle: BEGIN
|
|
DECLARE v_planning_existe INT DEFAULT 0;
|
|
|
|
DROP TEMPORARY TABLE IF EXISTS tmp_controles_planning;
|
|
|
|
CREATE TEMPORARY TABLE tmp_controles_planning (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
niveau ENUM('ERREUR', 'ALERTE', 'INFO') NOT NULL,
|
|
code VARCHAR(100) NOT NULL,
|
|
message VARCHAR(1000) NOT NULL
|
|
);
|
|
|
|
SELECT COUNT(*)
|
|
INTO v_planning_existe
|
|
FROM planning
|
|
WHERE id_planning = p_id_planning;
|
|
|
|
IF v_planning_existe = 0 THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'ERREUR',
|
|
'PLANNING_INTROUVABLE',
|
|
CONCAT('Le planning #', p_id_planning, ' n''existe pas.')
|
|
);
|
|
|
|
LEAVE controle;
|
|
END IF;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 1 : le planning doit contenir au moins un creneau
|
|
-- --------------------------------------------------------
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM creneau_horaire
|
|
WHERE id_planning = p_id_planning
|
|
) THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'ERREUR',
|
|
'AUCUN_CRENEAU',
|
|
'Le planning ne contient aucun creneau horaire.'
|
|
);
|
|
END IF;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 2 : chaque creneau doit etre dans la semaine
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CRENEAU_HORS_SEMAINE',
|
|
CONCAT(
|
|
'Le creneau #', c.id_creneau,
|
|
' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
|
|
' est en dehors de la semaine ', s.numero_semaine,
|
|
' (', DATE_FORMAT(s.date_debut, '%d/%m/%Y'),
|
|
' au ', DATE_FORMAT(s.date_fin, '%d/%m/%Y'), ').'
|
|
)
|
|
FROM planning p
|
|
JOIN semaine s
|
|
ON s.id_semaine = p.id_semaine
|
|
JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
WHERE p.id_planning = p_id_planning
|
|
AND c.date_jour NOT BETWEEN s.date_debut AND s.date_fin;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 3 : chaque creneau doit etre couvert par un contrat
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'HORS_CONTRAT',
|
|
CONCAT(
|
|
'Le creneau #', c.id_creneau,
|
|
' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
|
|
' n''est couvert par aucun contrat actif pour cet agent.'
|
|
)
|
|
FROM planning p
|
|
JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
WHERE p.id_planning = p_id_planning
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM contrat_agent ca
|
|
WHERE ca.id_agent = p.id_agent
|
|
AND ca.actif = TRUE
|
|
AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
);
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 4 : chevauchements dans le meme planning
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CHEVAUCHEMENT_INTERNE',
|
|
CONCAT(
|
|
'Les creneaux #', c1.id_creneau,
|
|
' et #', c2.id_creneau,
|
|
' se chevauchent le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'),
|
|
' (', TIME_FORMAT(c1.heure_debut, '%H:%i'), '-', TIME_FORMAT(c1.heure_fin, '%H:%i'),
|
|
' et ', TIME_FORMAT(c2.heure_debut, '%H:%i'), '-', TIME_FORMAT(c2.heure_fin, '%H:%i'), ').'
|
|
)
|
|
FROM creneau_horaire c1
|
|
JOIN creneau_horaire c2
|
|
ON c2.id_planning = c1.id_planning
|
|
AND c2.date_jour = c1.date_jour
|
|
AND c2.id_creneau > c1.id_creneau
|
|
AND c1.heure_debut < c2.heure_fin
|
|
AND c1.heure_fin > c2.heure_debut
|
|
WHERE c1.id_planning = p_id_planning;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 5 : chevauchement avec un autre planning valide
|
|
-- pour le meme agent, meme si la structure est differente.
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ERREUR',
|
|
'CHEVAUCHEMENT_AUTRE_PLANNING',
|
|
CONCAT(
|
|
'Le creneau #', c1.id_creneau,
|
|
' chevauche le creneau #', c2.id_creneau,
|
|
' du planning valide #', p2.id_planning,
|
|
' le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'), '.'
|
|
)
|
|
FROM planning p1
|
|
JOIN creneau_horaire c1
|
|
ON c1.id_planning = p1.id_planning
|
|
JOIN planning p2
|
|
ON p2.id_agent = p1.id_agent
|
|
AND p2.id_planning <> p1.id_planning
|
|
AND p2.statut = 'VALIDE'
|
|
JOIN creneau_horaire c2
|
|
ON c2.id_planning = p2.id_planning
|
|
AND c2.date_jour = c1.date_jour
|
|
AND c1.heure_debut < c2.heure_fin
|
|
AND c1.heure_fin > c2.heure_debut
|
|
WHERE p1.id_planning = p_id_planning;
|
|
|
|
-- --------------------------------------------------------
|
|
-- Controle 6 : depassement du quota du contrat
|
|
-- Le quota est ici un quota total sur la periode du contrat.
|
|
-- Il s'agit d'une ALERTE et non d'une erreur bloquante.
|
|
-- --------------------------------------------------------
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
SELECT
|
|
'ALERTE',
|
|
'QUOTA_DEPASSE',
|
|
CONCAT(
|
|
'Le contrat #', ca.id_contrat,
|
|
' prevoit ', ROUND(ca.quota_minutes / 60, 2), ' h. ',
|
|
'Apres validation, ',
|
|
ROUND(
|
|
(
|
|
-- Minutes deja validees, hors planning courant
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
|
|
)
|
|
FROM planning pv
|
|
JOIN creneau_horaire cv
|
|
ON cv.id_planning = pv.id_planning
|
|
JOIN motif_planning mv
|
|
ON mv.id_motif = cv.id_motif
|
|
AND mv.compte_dans_quota = TRUE
|
|
WHERE pv.id_agent = ca.id_agent
|
|
AND pv.statut = 'VALIDE'
|
|
AND pv.id_planning <> p_id_planning
|
|
AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
+
|
|
-- Minutes du planning courant appartenant a ce contrat
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
|
|
)
|
|
FROM creneau_horaire cc
|
|
JOIN motif_planning mc
|
|
ON mc.id_motif = cc.id_motif
|
|
AND mc.compte_dans_quota = TRUE
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
) / 60,
|
|
2
|
|
),
|
|
' h seraient planifiees.'
|
|
)
|
|
FROM planning p
|
|
JOIN contrat_agent ca
|
|
ON ca.id_agent = p.id_agent
|
|
AND ca.actif = TRUE
|
|
WHERE p.id_planning = p_id_planning
|
|
AND EXISTS (
|
|
SELECT 1
|
|
FROM creneau_horaire cc
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
)
|
|
AND (
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
|
|
)
|
|
FROM planning pv
|
|
JOIN creneau_horaire cv
|
|
ON cv.id_planning = pv.id_planning
|
|
JOIN motif_planning mv
|
|
ON mv.id_motif = cv.id_motif
|
|
AND mv.compte_dans_quota = TRUE
|
|
WHERE pv.id_agent = ca.id_agent
|
|
AND pv.statut = 'VALIDE'
|
|
AND pv.id_planning <> p_id_planning
|
|
AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
+
|
|
COALESCE((
|
|
SELECT SUM(
|
|
TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
|
|
)
|
|
FROM creneau_horaire cc
|
|
JOIN motif_planning mc
|
|
ON mc.id_motif = cc.id_motif
|
|
AND mc.compte_dans_quota = TRUE
|
|
WHERE cc.id_planning = p_id_planning
|
|
AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
|
|
), 0)
|
|
) > ca.quota_minutes;
|
|
|
|
-- Si aucun probleme n'a ete detecte, on retourne explicitement OK.
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM tmp_controles_planning
|
|
) THEN
|
|
INSERT INTO tmp_controles_planning(niveau, code, message)
|
|
VALUES (
|
|
'INFO',
|
|
'OK',
|
|
'Aucune anomalie detectee. Le planning peut etre valide.'
|
|
);
|
|
END IF;
|
|
END$$
|
|
|
|
|
|
DELIMITER ;
|
|
-- ============================================================
|
|
-- PTA - Quotas annuels et vues de pilotage
|
|
-- À exécuter APRÈS :
|
|
-- 1. pta_mysql_test.sql
|
|
-- 2. migration_planning_motifs.sql
|
|
-- Compatible MySQL 8+
|
|
-- ============================================================
|
|
|
|
USE nfbhnajdb1;
|
|
|
|
-- ------------------------------------------------------------
|
|
-- 1. Quota annuel d'un agent
|
|
-- 1607 h = 96 420 minutes à 100 %.
|
|
-- La quotité permet de proratiser automatiquement le quota cible.
|
|
-- ------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS quota_agent_annuel (
|
|
id_quota INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_agent INT UNSIGNED NOT NULL,
|
|
annee SMALLINT UNSIGNED NOT NULL,
|
|
quotite_travail DECIMAL(5,2) NOT NULL DEFAULT 100.00,
|
|
quota_reference_minutes INT UNSIGNED NOT NULL DEFAULT 96420,
|
|
quota_cible_minutes INT UNSIGNED NOT NULL,
|
|
commentaire VARCHAR(255) NULL,
|
|
|
|
CONSTRAINT fk_quota_annuel_agent
|
|
FOREIGN KEY (id_agent) REFERENCES agent(id_agent),
|
|
|
|
CONSTRAINT chk_quotite_travail
|
|
CHECK (quotite_travail > 0 AND quotite_travail <= 100),
|
|
|
|
CONSTRAINT chk_quota_annuel_positif
|
|
CHECK (quota_cible_minutes > 0),
|
|
|
|
UNIQUE KEY uk_quota_agent_annee (id_agent, annee)
|
|
) ENGINE = InnoDB;
|
|
|
|
-- Jeu d'essai 2026 : tous les agents existants à 100 %, soit 1607 h.
|
|
INSERT INTO quota_agent_annuel (
|
|
id_agent,
|
|
annee,
|
|
quotite_travail,
|
|
quota_reference_minutes,
|
|
quota_cible_minutes,
|
|
commentaire
|
|
)
|
|
SELECT
|
|
a.id_agent,
|
|
2026,
|
|
100.00,
|
|
96420,
|
|
96420,
|
|
'Quota annuel de référence à 100 %'
|
|
FROM agent a
|
|
WHERE NOT EXISTS (
|
|
SELECT 1
|
|
FROM quota_agent_annuel q
|
|
WHERE q.id_agent = a.id_agent
|
|
AND q.annee = 2026
|
|
);
|
|
|
|
-- Exemple de modification pour un agent à 80 % :
|
|
-- UPDATE quota_agent_annuel
|
|
-- SET quotite_travail = 80.00,
|
|
-- quota_cible_minutes = ROUND(96420 * 0.80)
|
|
-- WHERE id_agent = 2 AND annee = 2026;
|
|
|
|
-- ------------------------------------------------------------
|
|
-- 2. Règle de décompte des motifs
|
|
-- L'application reste paramétrable : le booléen compte_dans_quota
|
|
-- indique si le motif consomme le quota annuel.
|
|
-- Selon le besoin exprimé, les motifs principaux ci-dessous sont
|
|
-- comptés par défaut. Il est possible de changer cette règle ensuite.
|
|
-- ------------------------------------------------------------
|
|
UPDATE motif_planning
|
|
SET compte_dans_quota = TRUE
|
|
WHERE code IN ('TRAVAIL', 'FORMATION', 'CONGE', 'MALADIE');
|
|
|
|
-- ------------------------------------------------------------
|
|
-- 3. Vue annuelle de suivi des quotas
|
|
-- Les brouillons et les plannings validés sont distingués.
|
|
-- ------------------------------------------------------------
|
|
CREATE OR REPLACE VIEW v_suivi_quota_annuel AS
|
|
SELECT
|
|
q.id_agent,
|
|
a.matricule,
|
|
a.nom,
|
|
a.prenom,
|
|
q.annee,
|
|
q.quotite_travail,
|
|
q.quota_reference_minutes,
|
|
q.quota_cible_minutes,
|
|
ROUND(q.quota_cible_minutes / 60, 2) AS quota_cible_heures,
|
|
|
|
COALESCE(SUM(
|
|
CASE
|
|
WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
|
|
THEN TIMESTAMPDIFF(
|
|
MINUTE,
|
|
CONCAT(c.date_jour, ' ', c.heure_debut),
|
|
CONCAT(c.date_jour, ' ', c.heure_fin)
|
|
)
|
|
ELSE 0
|
|
END
|
|
), 0) AS minutes_valides,
|
|
|
|
COALESCE(SUM(
|
|
CASE
|
|
WHEN p.statut = 'BROUILLON' AND m.compte_dans_quota = TRUE
|
|
THEN TIMESTAMPDIFF(
|
|
MINUTE,
|
|
CONCAT(c.date_jour, ' ', c.heure_debut),
|
|
CONCAT(c.date_jour, ' ', c.heure_fin)
|
|
)
|
|
ELSE 0
|
|
END
|
|
), 0) AS minutes_brouillon,
|
|
|
|
q.quota_cible_minutes - COALESCE(SUM(
|
|
CASE
|
|
WHEN m.compte_dans_quota = TRUE
|
|
THEN TIMESTAMPDIFF(
|
|
MINUTE,
|
|
CONCAT(c.date_jour, ' ', c.heure_debut),
|
|
CONCAT(c.date_jour, ' ', c.heure_fin)
|
|
)
|
|
ELSE 0
|
|
END
|
|
), 0) AS minutes_restantes
|
|
|
|
FROM quota_agent_annuel q
|
|
INNER JOIN agent a ON a.id_agent = q.id_agent
|
|
LEFT JOIN planning p ON p.id_agent = q.id_agent
|
|
LEFT JOIN creneau_horaire c
|
|
ON c.id_planning = p.id_planning
|
|
AND YEAR(c.date_jour) = q.annee
|
|
LEFT JOIN motif_planning m ON m.id_motif = c.id_motif
|
|
GROUP BY
|
|
q.id_agent,
|
|
a.matricule,
|
|
a.nom,
|
|
a.prenom,
|
|
q.annee,
|
|
q.quotite_travail,
|
|
q.quota_reference_minutes,
|
|
q.quota_cible_minutes;
|
|
|
|
|
|
-- ============================================================
|
|
-- PTA V5.9 - Semaines types par agent
|
|
-- ============================================================
|
|
-- Un modèle mémorise les affectations d'une semaine de référence :
|
|
-- jour ouvré, lieu, motif et plage horaire.
|
|
|
|
CREATE TABLE IF NOT EXISTS modele_semaine_agent (
|
|
id_modele INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_agent INT UNSIGNED NOT NULL,
|
|
nom VARCHAR(120) NOT NULL,
|
|
date_creation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
date_modification DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT fk_modele_semaine_agent
|
|
FOREIGN KEY (id_agent)
|
|
REFERENCES agent(id_agent)
|
|
ON DELETE CASCADE,
|
|
|
|
UNIQUE KEY uk_modele_semaine_agent_nom (id_agent, nom),
|
|
KEY idx_modele_semaine_agent (id_agent, date_modification)
|
|
) ENGINE = InnoDB;
|
|
|
|
CREATE TABLE IF NOT EXISTS modele_semaine_creneau (
|
|
id_modele_creneau INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
id_modele INT UNSIGNED NOT NULL,
|
|
jour_semaine TINYINT UNSIGNED NOT NULL COMMENT '1=lundi ... 5=vendredi',
|
|
id_structure INT UNSIGNED NOT NULL,
|
|
id_motif INT UNSIGNED NOT NULL,
|
|
heure_debut TIME NOT NULL,
|
|
heure_fin TIME NOT NULL,
|
|
|
|
CONSTRAINT fk_modele_creneau_modele
|
|
FOREIGN KEY (id_modele)
|
|
REFERENCES modele_semaine_agent(id_modele)
|
|
ON DELETE CASCADE,
|
|
|
|
CONSTRAINT fk_modele_creneau_structure
|
|
FOREIGN KEY (id_structure)
|
|
REFERENCES structure(id_structure),
|
|
|
|
CONSTRAINT fk_modele_creneau_motif
|
|
FOREIGN KEY (id_motif)
|
|
REFERENCES motif_planning(id_motif),
|
|
|
|
CONSTRAINT chk_modele_creneau_jour
|
|
CHECK (jour_semaine BETWEEN 1 AND 5),
|
|
|
|
CONSTRAINT chk_modele_creneau_ordre
|
|
CHECK (heure_fin > heure_debut),
|
|
|
|
CONSTRAINT chk_modele_creneau_quart_heure
|
|
CHECK (
|
|
MOD(MINUTE(heure_debut), 15) = 0
|
|
AND MOD(MINUTE(heure_fin), 15) = 0
|
|
AND SECOND(heure_debut) = 0
|
|
AND SECOND(heure_fin) = 0
|
|
),
|
|
|
|
KEY idx_modele_creneau_modele_jour (id_modele, jour_semaine, heure_debut)
|
|
) ENGINE = InnoDB;
|
|
|
|
|
|
|
|
-- ============================================================
|
|
-- PTA V5.17 - Périodes de vacances scolaires
|
|
-- ============================================================
|
|
-- Les dates sont validées manuellement par l'utilisateur avant
|
|
-- d'être enregistrées, même lorsqu'elles proviennent de l'API
|
|
-- officielle du calendrier scolaire.
|
|
--
|
|
-- date_fin est stockée de façon INCLUSIVE dans PTA.
|
|
-- Pour une proposition issue de l'API, la date de reprise fournie
|
|
-- par l'API est convertie en dernier jour de vacances (veille).
|
|
|
|
CREATE TABLE IF NOT EXISTS periode_vacance (
|
|
id_periode INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
libelle VARCHAR(150) NOT NULL,
|
|
date_debut DATE NOT NULL,
|
|
date_fin DATE NOT NULL,
|
|
annee_scolaire VARCHAR(9) NOT NULL,
|
|
academie VARCHAR(100) NOT NULL DEFAULT '',
|
|
zone VARCHAR(50) NOT NULL DEFAULT '',
|
|
source ENUM('MANUEL', 'DATA_GOUV') NOT NULL DEFAULT 'MANUEL',
|
|
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
|
date_creation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT chk_periode_vacance_dates
|
|
CHECK (date_fin >= date_debut),
|
|
|
|
UNIQUE KEY uk_periode_vacance (
|
|
date_debut,
|
|
date_fin,
|
|
annee_scolaire,
|
|
academie
|
|
),
|
|
|
|
KEY idx_periode_vacance_dates (date_debut, date_fin),
|
|
KEY idx_periode_vacance_annee (annee_scolaire)
|
|
) ENGINE = InnoDB;
|
|
|
|
|
|
|
|
-- ============================================================
|
|
-- Historique V5.20 - date de début de contrat
|
|
-- IMPORTANT : depuis la V7.5, le quota PTA est suivi sur l'année civile
|
|
-- (01/01 -> 31/12). La date de contrat n'est plus l'ancre du quota.
|
|
-- ============================================================
|