Files
pta/database/migration_v7_pta_annuel_equipes.sql
Loic Masi 5fbf76868f pour prod
2026-08-07 16:13:43 +02:00

314 lines
16 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- PTA V7 - Socle métier annuel, postes, contraintes et équipes
-- MySQL 8.0.16+
-- À exécuter après le schéma V5.20 / V6.
-- ============================================================================
START TRANSACTION;
-- --------------------------------------------------------------------------
-- 1. Postes éligibles au PTA
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS poste_agent (
id_poste INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(50) NOT NULL,
libelle VARCHAR(150) NOT NULL,
famille ENUM('ANIMATION', 'RESTAURATION_ENTRETIEN') NOT NULL,
heures_mercredi_minutes SMALLINT UNSIGNED NOT NULL,
heures_extrascolaire_minutes SMALLINT UNSIGNED NOT NULL,
autorise_preparation BOOLEAN NOT NULL DEFAULT TRUE,
peut_assurer_animation BOOLEAN NOT NULL DEFAULT TRUE,
preparation_lundi_si_100 BOOLEAN NOT NULL DEFAULT FALSE,
actif BOOLEAN NOT NULL DEFAULT TRUE,
ordre_affichage SMALLINT UNSIGNED NOT NULL DEFAULT 100,
CONSTRAINT uk_poste_agent_code UNIQUE (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO poste_agent (
code, libelle, famille, heures_mercredi_minutes, heures_extrascolaire_minutes,
autorise_preparation, peut_assurer_animation, preparation_lundi_si_100, ordre_affichage
) VALUES
('RESPONSABLE', 'Responsable daccueil', 'ANIMATION', 570, 570, TRUE, TRUE, TRUE, 10),
('ANIMATION_RELAIS', 'Agent danimation relais', 'ANIMATION', 570, 570, TRUE, TRUE, TRUE, 20),
('ANIMATION', 'Agent danimation', 'ANIMATION', 570, 570, TRUE, TRUE, TRUE, 30),
('RESTAURATION_ENTRETIEN', 'Agent de restauration collective et dentretien', 'RESTAURATION_ENTRETIEN', 420, 420, FALSE, FALSE, FALSE, 40)
ON DUPLICATE KEY UPDATE
libelle = VALUES(libelle),
famille = VALUES(famille),
heures_mercredi_minutes = VALUES(heures_mercredi_minutes),
heures_extrascolaire_minutes = VALUES(heures_extrascolaire_minutes),
autorise_preparation = VALUES(autorise_preparation),
peut_assurer_animation = VALUES(peut_assurer_animation),
preparation_lundi_si_100 = VALUES(preparation_lundi_si_100),
ordre_affichage = VALUES(ordre_affichage),
actif = TRUE;
-- --------------------------------------------------------------------------
-- 2. Compléments métier de lagent
-- --------------------------------------------------------------------------
ALTER TABLE agent
ADD COLUMN id_poste INT UNSIGNED NULL AFTER date_debut_contrat,
ADD COLUMN type_contrat ENUM('PERMANENT', 'TEMPORAIRE') NOT NULL DEFAULT 'PERMANENT' AFTER id_poste,
ADD COLUMN date_fin_contrat DATE NULL AFTER type_contrat,
ADD COLUMN formation_repartition_annuelle BOOLEAN NOT NULL DEFAULT FALSE AFTER date_fin_contrat,
ADD COLUMN commentaire_pta VARCHAR(1000) NULL AFTER formation_repartition_annuelle;
ALTER TABLE agent
ADD CONSTRAINT fk_agent_poste
FOREIGN KEY (id_poste) REFERENCES poste_agent(id_poste)
ON UPDATE CASCADE ON DELETE SET NULL,
ADD CONSTRAINT chk_agent_dates_contrat_v7
CHECK (date_fin_contrat IS NULL OR date_debut_contrat IS NULL OR date_fin_contrat >= date_debut_contrat);
CREATE INDEX idx_agent_poste ON agent(id_poste);
CREATE INDEX idx_agent_type_contrat ON agent(type_contrat, actif);
-- --------------------------------------------------------------------------
-- 3. PTA annuel de lagent
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS pta_annuel (
id_pta INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_agent INT UNSIGNED NOT NULL,
date_debut DATE NOT NULL,
date_fin DATE NOT NULL,
quotite_travail DECIMAL(5,2) NOT NULL,
quota_reference_minutes INT UNSIGNED NOT NULL DEFAULT 96420,
quota_cible_minutes INT UNSIGNED NOT NULL,
enveloppe_formation_minutes SMALLINT UNSIGNED NOT NULL DEFAULT 840,
conges_annuels_jours_cible DECIMAL(5,2) NOT NULL DEFAULT 25.00,
jours_fractionnement_cible TINYINT UNSIGNED NOT NULL DEFAULT 2,
statut ENUM('BROUILLON', 'A_CONTROLER', 'VALIDE') NOT NULL DEFAULT 'BROUILLON',
commentaire VARCHAR(1000) NULL,
date_validation DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_pta_annuel_agent
FOREIGN KEY (id_agent) REFERENCES agent(id_agent)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_pta_annuel_dates CHECK (date_fin >= date_debut),
CONSTRAINT chk_pta_annuel_quotite CHECK (quotite_travail > 0 AND quotite_travail <= 100),
CONSTRAINT chk_pta_annuel_quota CHECK (quota_reference_minutes > 0 AND quota_cible_minutes > 0),
CONSTRAINT uk_pta_annuel_agent_periode UNIQUE (id_agent, date_debut, date_fin),
INDEX idx_pta_annuel_agent_statut (id_agent, statut),
INDEX idx_pta_annuel_periode (date_debut, date_fin)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------------------------
-- 4. Préférences, interdictions et proximité des lieux
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS agent_structure_souhait (
id_souhait INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_agent INT UNSIGNED NOT NULL,
id_structure INT UNSIGNED NOT NULL,
type_souhait ENUM('PREFERENCE', 'INTERDICTION') NOT NULL,
priorite TINYINT UNSIGNED NOT NULL DEFAULT 1,
distance_km DECIMAL(7,2) NULL,
commentaire VARCHAR(500) NULL,
actif BOOLEAN NOT NULL DEFAULT TRUE,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_souhait_agent FOREIGN KEY (id_agent) REFERENCES agent(id_agent)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_souhait_structure FOREIGN KEY (id_structure) REFERENCES structure(id_structure)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_souhait_priorite CHECK (priorite BETWEEN 1 AND 5),
CONSTRAINT chk_souhait_distance CHECK (distance_km IS NULL OR distance_km >= 0),
CONSTRAINT uk_souhait_agent_structure UNIQUE (id_agent, id_structure),
INDEX idx_souhait_agent_type (id_agent, type_souhait, actif)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------------------------
-- 5. Contraintes médicales et temps partiels thérapeutiques
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS agent_contrainte (
id_contrainte INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_agent INT UNSIGNED NOT NULL,
type_contrainte ENUM('MEDICALE', 'TEMPS_PARTIEL_THERAPEUTIQUE') NOT NULL,
date_debut DATE NOT NULL,
date_fin DATE NULL,
quotite_temporaire DECIMAL(5,2) NULL,
maximum_minutes_jour SMALLINT UNSIGNED NULL,
maximum_minutes_semaine SMALLINT UNSIGNED NULL,
interdit_matin BOOLEAN NOT NULL DEFAULT FALSE,
interdit_midi BOOLEAN NOT NULL DEFAULT FALSE,
interdit_soir BOOLEAN NOT NULL DEFAULT FALSE,
commentaire VARCHAR(1000) NOT NULL,
actif BOOLEAN NOT NULL DEFAULT TRUE,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_contrainte_agent FOREIGN KEY (id_agent) REFERENCES agent(id_agent)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_contrainte_dates CHECK (date_fin IS NULL OR date_fin >= date_debut),
CONSTRAINT chk_contrainte_quotite CHECK (quotite_temporaire IS NULL OR (quotite_temporaire > 0 AND quotite_temporaire <= 100)),
CONSTRAINT chk_contrainte_max_jour CHECK (maximum_minutes_jour IS NULL OR maximum_minutes_jour > 0),
CONSTRAINT chk_contrainte_max_semaine CHECK (maximum_minutes_semaine IS NULL OR maximum_minutes_semaine > 0),
INDEX idx_contrainte_agent_dates (id_agent, actif, date_debut, date_fin)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------------------------
-- 6. Équipes périscolaires des mercredis et extrascolaires
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS equipe_pta (
id_equipe INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_structure INT UNSIGNED NOT NULL,
type_equipe ENUM('MERCREDI_PERISCOLAIRE', 'EXTRASCOLAIRE', 'PERISCOLAIRE_SEMAINE') NOT NULL,
libelle VARCHAR(180) NOT NULL,
date_debut DATE NOT NULL,
date_fin DATE NOT NULL,
nombre_enfants_moins_6 SMALLINT UNSIGNED NOT NULL DEFAULT 0,
nombre_enfants_6_plus SMALLINT UNSIGNED NOT NULL DEFAULT 0,
ratio_moins_6 TINYINT UNSIGNED NOT NULL DEFAULT 8,
ratio_6_plus TINYINT UNSIGNED NOT NULL DEFAULT 12,
minimum_agents TINYINT UNSIGNED NOT NULL DEFAULT 1,
pourcentage_diplomes_min DECIMAL(5,2) NOT NULL DEFAULT 50.00,
statut ENUM('BROUILLON', 'A_CONTROLER', 'VALIDEE') NOT NULL DEFAULT 'BROUILLON',
commentaire VARCHAR(1000) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_equipe_pta_structure FOREIGN KEY (id_structure) REFERENCES structure(id_structure)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_equipe_dates CHECK (date_fin >= date_debut),
CONSTRAINT chk_equipe_ratios CHECK (ratio_moins_6 > 0 AND ratio_6_plus > 0),
CONSTRAINT chk_equipe_minimum CHECK (minimum_agents > 0),
CONSTRAINT chk_equipe_diplomes CHECK (pourcentage_diplomes_min BETWEEN 0 AND 100),
INDEX idx_equipe_pta_type_dates (type_equipe, date_debut, date_fin),
INDEX idx_equipe_pta_structure_dates (id_structure, date_debut, date_fin)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE IF NOT EXISTS equipe_pta_membre (
id_equipe_membre INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_equipe INT UNSIGNED NOT NULL,
id_agent INT UNSIGNED NOT NULL,
fonction_equipe ENUM('RESPONSABLE', 'ANIMATION', 'RESTAURATION_ENTRETIEN') NOT NULL DEFAULT 'ANIMATION',
date_debut DATE NULL,
date_fin DATE NULL,
commentaire VARCHAR(500) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_equipe_membre_equipe FOREIGN KEY (id_equipe) REFERENCES equipe_pta(id_equipe)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_equipe_membre_agent FOREIGN KEY (id_agent) REFERENCES agent(id_agent)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_equipe_membre_dates CHECK (date_fin IS NULL OR date_debut IS NULL OR date_fin >= date_debut),
CONSTRAINT uk_equipe_membre UNIQUE (id_equipe, id_agent),
INDEX idx_equipe_membre_agent (id_agent, id_equipe)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------------------------
-- 7. Références horaires des lieux pour les automatismes de préparation
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS structure_creneau_reference (
id_reference INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
id_structure INT UNSIGNED NOT NULL,
code_plage ENUM('ALP_MATIN', 'RESTAURATION', 'ALP_SOIR') NOT NULL,
jour_semaine TINYINT UNSIGNED NOT NULL COMMENT '1=lundi ... 5=vendredi',
heure_debut TIME NOT NULL,
heure_fin TIME NOT NULL,
actif BOOLEAN NOT NULL DEFAULT TRUE,
CONSTRAINT fk_reference_structure FOREIGN KEY (id_structure) REFERENCES structure(id_structure)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_reference_jour CHECK (jour_semaine BETWEEN 1 AND 5),
CONSTRAINT chk_reference_ordre CHECK (heure_fin > heure_debut),
CONSTRAINT uk_reference_structure_plage_jour UNIQUE (id_structure, code_plage, jour_semaine),
INDEX idx_reference_structure_jour (id_structure, jour_semaine, actif)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------------------------
-- 8. Motifs détaillés du PTA
-- --------------------------------------------------------------------------
INSERT INTO motif_planning (code, libelle, compte_dans_quota, ordre_affichage, actif)
VALUES
('ALP_MATIN', 'ALP matin', TRUE, 10, TRUE),
('RESTAURATION', 'Restauration', TRUE, 20, TRUE),
('ALP_SOIR', 'ALP soir', TRUE, 30, TRUE),
('MERCREDI_PERISCOLAIRE', 'Mercredi périscolaire', TRUE, 40, TRUE),
('EXTRASCOLAIRE', 'Temps extrascolaire', TRUE, 50, TRUE),
('FORMATION', 'Formation', TRUE, 60, TRUE),
('CONGE', 'Congé annuel (non décompté)', FALSE, 70, TRUE),
('JOUR_FRACTIONNEMENT', 'Journée de fractionnement', TRUE, 80, TRUE),
('PREPARATION_PERISCOLAIRE', 'Préparation périscolaire', TRUE, 90, TRUE),
('PREPARATION_EXTRASCOLAIRE', 'Préparation extrascolaire', TRUE, 100, TRUE),
('PREPARATION_LUNDI', 'Préparation du lundi', TRUE, 110, TRUE),
('MENAGE_FOND', 'Ménage de fond', TRUE, 120, TRUE),
('JNT', 'Journée non travaillée', FALSE, 130, TRUE)
ON DUPLICATE KEY UPDATE
libelle = VALUES(libelle),
compte_dans_quota = VALUES(compte_dans_quota),
ordre_affichage = VALUES(ordre_affichage),
actif = TRUE;
-- La règle métier communiquée précise que les congés annuels ne sont pas
-- redécomptés du quota puisqu'ils sont déjà intégrés dans le calcul initial.
UPDATE motif_planning
SET libelle = 'Congé annuel (non décompté)', compte_dans_quota = FALSE
WHERE code = 'CONGE';
-- --------------------------------------------------------------------------
-- 9. Vues de synthèse
-- --------------------------------------------------------------------------
CREATE OR REPLACE VIEW v_pta_annuel_synthese AS
SELECT
pa.id_pta,
pa.id_agent,
a.matricule,
a.nom,
a.prenom,
po.code AS poste_code,
po.libelle AS poste_libelle,
a.type_contrat,
pa.date_debut,
pa.date_fin,
pa.quotite_travail,
pa.quota_cible_minutes,
COALESCE(SUM(CASE WHEN mp.compte_dans_quota = TRUE THEN TIMESTAMPDIFF(MINUTE, CONCAT(ch.date_jour, ' ', ch.heure_debut), CONCAT(ch.date_jour, ' ', ch.heure_fin)) ELSE 0 END), 0) AS minutes_decomptees,
pa.quota_cible_minutes - COALESCE(SUM(CASE WHEN mp.compte_dans_quota = TRUE THEN TIMESTAMPDIFF(MINUTE, CONCAT(ch.date_jour, ' ', ch.heure_debut), CONCAT(ch.date_jour, ' ', ch.heure_fin)) ELSE 0 END), 0) AS minutes_restantes,
pa.statut
FROM pta_annuel pa
JOIN agent a ON a.id_agent = pa.id_agent
LEFT JOIN poste_agent po ON po.id_poste = a.id_poste
LEFT JOIN planning p ON p.id_agent = pa.id_agent
LEFT JOIN creneau_horaire ch ON ch.id_planning = p.id_planning AND ch.date_jour BETWEEN pa.date_debut AND pa.date_fin
LEFT JOIN motif_planning mp ON mp.id_motif = ch.id_motif
GROUP BY pa.id_pta, pa.id_agent, a.matricule, a.nom, a.prenom, po.code, po.libelle,
a.type_contrat, pa.date_debut, pa.date_fin, pa.quotite_travail,
pa.quota_cible_minutes, pa.statut;
CREATE OR REPLACE VIEW v_equipe_pta_controle AS
SELECT
e.id_equipe,
e.libelle,
e.type_equipe,
e.id_structure,
s.nom AS structure_nom,
e.date_debut,
e.date_fin,
e.nombre_enfants_moins_6,
e.nombre_enfants_6_plus,
GREATEST(
e.minimum_agents,
CEIL(e.nombre_enfants_moins_6 / e.ratio_moins_6) + CEIL(e.nombre_enfants_6_plus / e.ratio_6_plus)
) AS agents_requis,
COUNT(em.id_agent) AS agents_affectes,
SUM(CASE WHEN a.est_diplome = TRUE THEN 1 ELSE 0 END) AS agents_diplomes,
CASE WHEN COUNT(em.id_agent) = 0 THEN 0
ELSE ROUND(100 * SUM(CASE WHEN a.est_diplome = TRUE THEN 1 ELSE 0 END) / COUNT(em.id_agent), 2)
END AS pourcentage_diplomes,
e.pourcentage_diplomes_min,
SUM(CASE WHEN em.fonction_equipe = 'RESPONSABLE' THEN 1 ELSE 0 END) AS responsables,
e.statut
FROM equipe_pta e
JOIN structure s ON s.id_structure = e.id_structure
LEFT JOIN equipe_pta_membre em ON em.id_equipe = e.id_equipe
LEFT JOIN agent a ON a.id_agent = em.id_agent
GROUP BY e.id_equipe, e.libelle, e.type_equipe, e.id_structure, s.nom,
e.date_debut, e.date_fin, e.nombre_enfants_moins_6, e.nombre_enfants_6_plus,
e.ratio_moins_6, e.ratio_6_plus, e.minimum_agents,
e.pourcentage_diplomes_min, e.statut;
COMMIT;