pour prod

This commit is contained in:
Loic Masi
2026-08-07 16:13:43 +02:00
commit 5fbf76868f
157 changed files with 24085 additions and 0 deletions

View File

@@ -0,0 +1,313 @@
-- ============================================================================
-- 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;