diff --git a/CHANGELOG b/CHANGELOG
index fae02a0cef09216bfa797a2c470d51297e784f71..e3d73e945ad5f39e47a93132e58c5ba94248f1ae 100755
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,7 +3,7 @@ title: "Changements intervenus sur OSE"
 author: Laurent Lécluse - DSI - Unicaen
 ...
 
-#OSE 8.1
+#OSE 8.1 (béta)
 
 ## Corrections de bugs
 
@@ -16,6 +16,14 @@ author: Laurent Lécluse - DSI - Unicaen
 * Formule de calcul de l'université de Montpellier
 * Interface de test de la formule de calcul directmement intégrée dans OSE
 * Interface d'administration des motifs de modification de service dû
+* Interface d'administration des domaines fonctionnels
+
+## Notes de mise à jour
+
+* Modifiez la structure de votre base de données en exécutant dans SQL developer le script de mise à jour suivant :
+`data/Mises à jour/08.1.sql`
+Les mises à jour `data/Mises à jour/08.0.1.sql` et `data/Mises à jour/08.0.3.sql` sont inclues dans le précédent fichier.
+Inutile, donc, de les exécuter si vous mettez à jour depuis la 8.0.
 
 #OSE 8.0.3
 
diff --git a/code/GenDbStructure/DdlGen.php b/code/GenDbStructure/DdlGen.php
index e03c08bc369dcc37369f33da6eccfa684aa36919..4e59d3d8d40a79d4e9eab7c3008e94265b7e29ea 100755
--- a/code/GenDbStructure/DdlGen.php
+++ b/code/GenDbStructure/DdlGen.php
@@ -102,10 +102,16 @@ class DdlGen
                 $name = $ql['OBJECT_NAME'];
                 $ddl  = trim($ql['OBJECT_DDL']);
                 if (isset($query['callback'])) {
-                    $this->ddl[$type][$name] = $query['callback']($name, $ddl);
-                } else {
-                    $this->ddl[$type][$name] = $ddl;
+                    $ddl = $query['callback']($name, $ddl);
                 }
+                $ddla = explode("\n", $ddl );
+                $ddl = '';
+                foreach($ddla as $ddll){
+                    if ($ddl != '') $ddl .= "\n";
+                    $ddl .= rtrim($ddll);
+                }
+
+                $this->ddl[$type][$name] = $ddl;
             }
         }
     }
diff --git "a/data/D\303\251ploiement/ose-ddl.sql" "b/data/D\303\251ploiement/ose-ddl.sql"
index 66ab91a9c387308a42efdc899fbf7faebb2f1fa2..366160f4b6f0d78609bf1ede9d552efe79d51fcf 100755
--- "a/data/D\303\251ploiement/ose-ddl.sql"
+++ "b/data/D\303\251ploiement/ose-ddl.sql"
@@ -100,6 +100,9 @@ CREATE SEQUENCE FICHIER_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999
 -- FONCTION_REFERENTIEL_ID_SEQ
 CREATE SEQUENCE FONCTION_REFERENTIEL_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
 
+-- FORMULE_ID_SEQ
+CREATE SEQUENCE FORMULE_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
+
 -- FORMULE_RESULTAT_ID_SEQ
 CREATE SEQUENCE FORMULE_RESULTAT_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
 
@@ -112,6 +115,15 @@ CREATE SEQUENCE FORMULE_RESULTAT_VH_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999
 -- FORMULE_RESULTAT_VH_REF_ID_SEQ
 CREATE SEQUENCE FORMULE_RESULTAT_VH_REF_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
 
+-- FTEST_INTERVENANT_ID_SEQ
+CREATE SEQUENCE FTEST_INTERVENANT_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
+
+-- FTEST_STRUCTURE_ID_SEQ
+CREATE SEQUENCE FTEST_STRUCTURE_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
+
+-- FTEST_VOLUME_HORAIRE_ID_SEQ
+CREATE SEQUENCE FTEST_VOLUME_HORAIRE_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
+
 -- GRADE_ID_SEQ
 CREATE SEQUENCE GRADE_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
 
@@ -1064,6 +1076,16 @@ CREATE TABLE "FONCTION_REFERENTIEL"
 	 CONSTRAINT "FONCTION_REFERENTIEL_CODE_UN" UNIQUE ("CODE") ENABLE
    ) ;
 
+-- FORMULE
+CREATE TABLE "FORMULE"
+   (	"ID" NUMBER(*,0) NOT NULL ENABLE,
+	"LIBELLE" VARCHAR2(100 CHAR) NOT NULL ENABLE,
+	"PACKAGE_NAME" VARCHAR2(30 CHAR) NOT NULL ENABLE,
+	"PROCEDURE_NAME" VARCHAR2(30 CHAR) NOT NULL ENABLE,
+	 CONSTRAINT "FORMULE_PK" PRIMARY KEY ("ID") ENABLE,
+	 CONSTRAINT "FORMULE__UN" UNIQUE ("LIBELLE") ENABLE
+   ) ;
+
 -- FORMULE_RESULTAT
 CREATE TABLE "FORMULE_RESULTAT"
    (	"ID" NUMBER(*,0) NOT NULL ENABLE,
@@ -1143,6 +1165,82 @@ CREATE TABLE "FORMULE_RESULTAT_VH_REF"
 	 CONSTRAINT "FORMULE_RESULTAT_VH_REF_PK" PRIMARY KEY ("ID") ENABLE
    ) ;
 
+-- FORMULE_TEST_INTERVENANT
+CREATE TABLE "FORMULE_TEST_INTERVENANT"
+   (	"ID" NUMBER(*,0) NOT NULL ENABLE,
+	"LIBELLE" VARCHAR2(150 CHAR) NOT NULL ENABLE,
+	"FORMULE_ID" NUMBER(*,0) NOT NULL ENABLE,
+	"ANNEE_ID" NUMBER(*,0) NOT NULL ENABLE,
+	"TYPE_INTERVENANT_ID" NUMBER(*,0) DEFAULT 1 NOT NULL ENABLE,
+	"STRUCTURE_TEST_ID" NUMBER(*,0) NOT NULL ENABLE,
+	"TYPE_VOLUME_HORAIRE_ID" NUMBER(*,0) DEFAULT 1 NOT NULL ENABLE,
+	"ETAT_VOLUME_HORAIRE_ID" NUMBER(*,0) DEFAULT 1 NOT NULL ENABLE,
+	"HEURES_DECHARGE" FLOAT(126) NOT NULL ENABLE,
+	"HEURES_SERVICE_STATUTAIRE" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"HEURES_SERVICE_MODIFIE" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"DEPASSEMENT_SERVICE_DU_SANS_HC" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE,
+	"PARAM_1" VARCHAR2(50 CHAR),
+	"PARAM_2" VARCHAR2(50 CHAR),
+	"PARAM_3" VARCHAR2(50 CHAR),
+	"PARAM_4" VARCHAR2(50 CHAR),
+	"PARAM_5" VARCHAR2(50 CHAR),
+	"A_SERVICE_DU" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"C_SERVICE_DU" FLOAT(126),
+	"DEBUG_INFO" CLOB,
+	 CONSTRAINT "FORMULE_TEST_INTERVENANT_PK" PRIMARY KEY ("ID") ENABLE
+   ) ;
+
+-- FORMULE_TEST_STRUCTURE
+CREATE TABLE "FORMULE_TEST_STRUCTURE"
+   (	"ID" NUMBER(*,0) NOT NULL ENABLE,
+	"LIBELLE" VARCHAR2(80 CHAR) NOT NULL ENABLE,
+	"UNIVERSITE" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE,
+	 CONSTRAINT "FORMULE_TEST_STRUCTURE_PK" PRIMARY KEY ("ID") ENABLE,
+	 CONSTRAINT "FORMULE_TEST_STRUCTURE__UN" UNIQUE ("LIBELLE") ENABLE
+   ) ;
+
+-- FORMULE_TEST_VOLUME_HORAIRE
+CREATE TABLE "FORMULE_TEST_VOLUME_HORAIRE"
+   (	"ID" NUMBER(*,0) NOT NULL ENABLE,
+	"INTERVENANT_TEST_ID" NUMBER(*,0) NOT NULL ENABLE,
+	"STRUCTURE_TEST_ID" NUMBER(*,0) NOT NULL ENABLE,
+	"REFERENTIEL" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE,
+	"SERVICE_STATUTAIRE" NUMBER(1,0) DEFAULT 1 NOT NULL ENABLE,
+	"TAUX_FI" FLOAT(126) DEFAULT 1 NOT NULL ENABLE,
+	"TAUX_FA" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"TAUX_FC" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"TAUX_SERVICE_DU" FLOAT(126) DEFAULT 1 NOT NULL ENABLE,
+	"TAUX_SERVICE_COMPL" FLOAT(126) DEFAULT 1 NOT NULL ENABLE,
+	"PONDERATION_SERVICE_DU" FLOAT(126) DEFAULT 1 NOT NULL ENABLE,
+	"PONDERATION_SERVICE_COMPL" FLOAT(126) DEFAULT 1 NOT NULL ENABLE,
+	"PARAM_1" VARCHAR2(50 CHAR),
+	"PARAM_2" VARCHAR2(50 CHAR),
+	"PARAM_3" VARCHAR2(50 CHAR),
+	"PARAM_4" VARCHAR2(50 CHAR),
+	"PARAM_5" VARCHAR2(50 CHAR),
+	"HEURES" FLOAT(126) DEFAULT 0 NOT NULL ENABLE,
+	"A_SERVICE_FI" FLOAT(126),
+	"A_SERVICE_FA" FLOAT(126),
+	"A_SERVICE_FC" FLOAT(126),
+	"A_SERVICE_REFERENTIEL" FLOAT(126),
+	"A_HEURES_COMPL_FI" FLOAT(126),
+	"A_HEURES_COMPL_FA" FLOAT(126),
+	"A_HEURES_COMPL_FC" FLOAT(126),
+	"A_HEURES_COMPL_FC_MAJOREES" FLOAT(126),
+	"A_HEURES_COMPL_REFERENTIEL" FLOAT(126),
+	"C_SERVICE_FI" FLOAT(126),
+	"C_SERVICE_FA" FLOAT(126),
+	"C_SERVICE_FC" FLOAT(126),
+	"C_SERVICE_REFERENTIEL" FLOAT(126),
+	"C_HEURES_COMPL_FI" FLOAT(126),
+	"C_HEURES_COMPL_FA" FLOAT(126),
+	"C_HEURES_COMPL_FC" FLOAT(126),
+	"C_HEURES_COMPL_FC_MAJOREES" FLOAT(126),
+	"C_HEURES_COMPL_REFERENTIEL" FLOAT(126),
+	"DEBUG_INFO" CLOB,
+	 CONSTRAINT "FORMULE_TEST_VOLUME_HORAIRE_PK" PRIMARY KEY ("ID") ENABLE
+   ) ;
+
 -- GRADE
 CREATE TABLE "GRADE"
    (	"ID" NUMBER(*,0) NOT NULL ENABLE,
@@ -1327,6 +1425,12 @@ CREATE TABLE "LIEN"
 	 CONSTRAINT "LIEN_SRC_UN" UNIQUE ("SOURCE_CODE", "HISTO_DESTRUCTION") ENABLE
    ) ;
 
+-- LISTE_NOIRE
+CREATE TABLE "LISTE_NOIRE"
+   (	"CODE" VARCHAR2(50 CHAR) NOT NULL ENABLE,
+	 CONSTRAINT "INTERVENANT_LISTE_NOIRE_PK" PRIMARY KEY ("CODE") ENABLE
+   ) ;
+
 -- MISE_EN_PAIEMENT
 CREATE TABLE "MISE_EN_PAIEMENT"
    (	"ID" NUMBER(*,0) NOT NULL ENABLE,
@@ -2749,6 +2853,33 @@ CREATE TABLE "WF_ETAPE_DEP"
 -- Packages
 --------------------------------------------------
 
+-- FORMULE_MONTPELLIER
+CREATE OR REPLACE PACKAGE "FORMULE_MONTPELLIER" AS
+
+  PROCEDURE CALCUL_RESULTAT;
+
+  FUNCTION calcCell( c VARCHAR2, l NUMERIC ) RETURN FLOAT;
+
+END FORMULE_MONTPELLIER;
+
+/
+
+-- FORMULE_UNICAEN
+CREATE OR REPLACE PACKAGE "FORMULE_UNICAEN" AS
+  debug_enabled                BOOLEAN DEFAULT FALSE;
+  debug_etat_volume_horaire_id NUMERIC DEFAULT 1;
+  debug_volume_horaire_id      NUMERIC;
+  debug_volume_horaire_ref_id  NUMERIC;
+
+  PROCEDURE CALCUL_RESULTAT_V2;
+  PROCEDURE CALCUL_RESULTAT;
+
+  PROCEDURE PURGE_EM_NON_FC;
+
+END FORMULE_UNICAEN;
+
+/
+
 -- OSE_CHARGENS
 CREATE OR REPLACE PACKAGE "OSE_CHARGENS" AS
   ENABLE_TRIGGER_EFFECTIFS BOOLEAN DEFAULT TRUE;
@@ -2813,49 +2944,49 @@ END OSE_CHARGENS;
 -- OSE_DIVERS
 CREATE OR REPLACE PACKAGE "OSE_DIVERS" AS
 
-      PROCEDURE CALCULER_TABLEAUX_BORD;
+  PROCEDURE CALCULER_TABLEAUX_BORD;
 
-      FUNCTION GET_OSE_UTILISATEUR_ID RETURN NUMERIC;
-      FUNCTION GET_OSE_SOURCE_ID RETURN NUMERIC;
+  FUNCTION GET_OSE_UTILISATEUR_ID RETURN NUMERIC;
+  FUNCTION GET_OSE_SOURCE_ID RETURN NUMERIC;
 
-      FUNCTION INTERVENANT_HAS_PRIVILEGE( intervenant_id NUMERIC, privilege_name VARCHAR2 ) RETURN NUMERIC;
+  FUNCTION INTERVENANT_HAS_PRIVILEGE( intervenant_id NUMERIC, privilege_name VARCHAR2 ) RETURN NUMERIC;
 
-      FUNCTION implode(i_query VARCHAR2, i_seperator VARCHAR2 DEFAULT ',') RETURN VARCHAR2;
+  FUNCTION implode(i_query VARCHAR2, i_seperator VARCHAR2 DEFAULT ',') RETURN VARCHAR2;
 
-      PROCEDURE intervenant_horodatage_service( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, REFERENTIEL NUMERIC, HISTO_MODIFICATEUR_ID NUMERIC, HISTO_MODIFICATION DATE );
+  PROCEDURE intervenant_horodatage_service( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, REFERENTIEL NUMERIC, HISTO_MODIFICATEUR_ID NUMERIC, HISTO_MODIFICATION DATE );
 
-      FUNCTION NIVEAU_FORMATION_ID_CALC( gtf_id NUMERIC, gtf_pertinence_niveau NUMERIC, niveau NUMERIC DEFAULT NULL ) RETURN NUMERIC;
+  FUNCTION NIVEAU_FORMATION_ID_CALC( gtf_id NUMERIC, gtf_pertinence_niveau NUMERIC, niveau NUMERIC DEFAULT NULL ) RETURN NUMERIC;
 
-      FUNCTION STR_REDUCE( str CLOB ) RETURN CLOB;
+  FUNCTION STR_REDUCE( str CLOB ) RETURN CLOB;
 
-      FUNCTION STR_FIND( haystack CLOB, needle VARCHAR2 ) RETURN NUMERIC;
+  FUNCTION STR_FIND( haystack CLOB, needle VARCHAR2 ) RETURN NUMERIC;
 
-      FUNCTION LIKED( haystack CLOB, needle CLOB ) RETURN NUMERIC;
+  FUNCTION LIKED( haystack CLOB, needle CLOB ) RETURN NUMERIC;
 
-      FUNCTION CALCUL_TAUX_FI( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
+  FUNCTION CALCUL_TAUX_FI( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
 
-      FUNCTION CALCUL_TAUX_FC( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
+  FUNCTION CALCUL_TAUX_FC( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
 
-      FUNCTION CALCUL_TAUX_FA( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
+  FUNCTION CALCUL_TAUX_FA( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT;
 
-      PROCEDURE SYNC_LOG( msg CLOB );
+  PROCEDURE SYNC_LOG( msg CLOB );
 
-      FUNCTION FORMATTED_RIB (bic VARCHAR2, iban VARCHAR2) RETURN VARCHAR2;
+  FUNCTION FORMATTED_RIB (bic VARCHAR2, iban VARCHAR2) RETURN VARCHAR2;
 
-      FUNCTION FORMATTED_ADRESSE(
-            no_voie                VARCHAR2,
-            nom_voie               VARCHAR2,
-            batiment               VARCHAR2,
-            mention_complementaire VARCHAR2,
-            localite               VARCHAR2,
-            code_postal            VARCHAR2,
-            ville                  VARCHAR2,
-            pays_libelle           VARCHAR2)
-            RETURN VARCHAR2;
+  FUNCTION FORMATTED_ADRESSE(
+    no_voie                VARCHAR2,
+    nom_voie               VARCHAR2,
+    batiment               VARCHAR2,
+    mention_complementaire VARCHAR2,
+    localite               VARCHAR2,
+    code_postal            VARCHAR2,
+    ville                  VARCHAR2,
+    pays_libelle           VARCHAR2)
+  RETURN VARCHAR2;
 
-      PROCEDURE CALCUL_FEUILLE_DE_ROUTE( CONDS CLOB );
+  PROCEDURE CALCUL_FEUILLE_DE_ROUTE( CONDS CLOB );
 
-      FUNCTION GET_TRIGGER_BODY( TRIGGER_NAME VARCHAR2 ) RETURN VARCHAR2;
+  FUNCTION GET_TRIGGER_BODY( TRIGGER_NAME VARCHAR2 ) RETURN VARCHAR2;
 END OSE_DIVERS;
 
 /
@@ -2863,7 +2994,7 @@ END OSE_DIVERS;
 -- OSE_EVENT
 CREATE OR REPLACE PACKAGE "OSE_EVENT" AS
 
-      PROCEDURE ON_AFTER_FORMULE_CALC( INTERVENANT_ID NUMERIC );
+  PROCEDURE ON_AFTER_FORMULE_CALC( INTERVENANT_ID NUMERIC );
 
 END OSE_EVENT;
 
@@ -2872,70 +3003,83 @@ END OSE_EVENT;
 -- OSE_FORMULE
 CREATE OR REPLACE PACKAGE "OSE_FORMULE" AS
 
-      TYPE t_intervenant IS RECORD (
-      id                             NUMERIC,
-      annee_id                       NUMERIC,
-      structure_id                   NUMERIC,
-      type_volume_horaire_id         NUMERIC,
-      etat_volume_horaire_id         NUMERIC,
-
-      heures_decharge                FLOAT DEFAULT 0,
-      heures_service_statutaire      FLOAT DEFAULT 0,
-      heures_service_modifie         FLOAT DEFAULT 0,
-      depassement_service_du_sans_hc FLOAT DEFAULT 0,
-      type_intervenant_code          VARCHAR(2),
+  TYPE t_intervenant IS RECORD (
+    id                             NUMERIC,
+    annee_id                       NUMERIC,
+    structure_id                   NUMERIC,
+    type_volume_horaire_id         NUMERIC,
+    etat_volume_horaire_id         NUMERIC,
+
+    heures_decharge                FLOAT DEFAULT 0,
+    heures_service_statutaire      FLOAT DEFAULT 0,
+    heures_service_modifie         FLOAT DEFAULT 0,
+    depassement_service_du_sans_hc BOOLEAN DEFAULT FALSE,
+    type_intervenant_code          VARCHAR(2),
+
+    service_du                     FLOAT,
+    debug_info                     CLOB
+  );
 
-      service_du                     FLOAT
-      );
+  TYPE t_volume_horaire IS RECORD (
+    -- identifiants
+    volume_horaire_id          NUMERIC,
+    volume_horaire_ref_id      NUMERIC,
+    service_id                 NUMERIC,
+    service_referentiel_id     NUMERIC,
+    structure_id               NUMERIC,
+
+    -- paramètres
+    structure_is_affectation   BOOLEAN DEFAULT TRUE,
+    structure_is_univ          BOOLEAN DEFAULT FALSE,
+    service_statutaire         BOOLEAN DEFAULT TRUE,
+    taux_fi                    FLOAT DEFAULT 1,
+    taux_fa                    FLOAT DEFAULT 0,
+    taux_fc                    FLOAT DEFAULT 0,
+
+    -- pondérations et heures
+
+    taux_service_du            FLOAT DEFAULT 1, -- en fonction des types d'intervention
+    taux_service_compl         FLOAT DEFAULT 1, -- en fonction des types d'intervention
+    ponderation_service_du     FLOAT DEFAULT 1, -- relatif aux modulateurs
+    ponderation_service_compl  FLOAT DEFAULT 1, -- relatif aux modulateurs
+    heures                     FLOAT DEFAULT 0, -- heures réelles saisies
+
+    -- résultats
+    service_fi                 FLOAT DEFAULT 0,
+    service_fa                 FLOAT DEFAULT 0,
+    service_fc                 FLOAT DEFAULT 0,
+    service_referentiel        FLOAT DEFAULT 0,
+    heures_compl_fi            FLOAT DEFAULT 0,
+    heures_compl_fa            FLOAT DEFAULT 0,
+    heures_compl_fc            FLOAT DEFAULT 0,
+    heures_compl_fc_majorees   FLOAT DEFAULT 0,
+    heures_compl_referentiel   FLOAT DEFAULT 0,
+
+    debug_info                 CLOB
+  );
+  TYPE t_lst_volume_horaire IS TABLE OF t_volume_horaire INDEX BY PLS_INTEGER;
+  TYPE t_volumes_horaires IS RECORD (
+    length NUMERIC DEFAULT 0,
+    items t_lst_volume_horaire
+  );
 
-      TYPE t_volume_horaire IS RECORD (
-      volume_horaire_id          NUMERIC,
-      volume_horaire_ref_id      NUMERIC,
-      service_id                 NUMERIC,
-      service_referentiel_id     NUMERIC,
-      taux_fi                    FLOAT DEFAULT 1,
-      taux_fa                    FLOAT DEFAULT 0,
-      taux_fc                    FLOAT DEFAULT 0,
-      ponderation_service_du     FLOAT DEFAULT 1,
-      ponderation_service_compl  FLOAT DEFAULT 1,
-      structure_id               NUMERIC,
-      structure_is_affectation   BOOLEAN DEFAULT TRUE,
-      structure_is_univ          BOOLEAN,
-      service_statutaire         BOOLEAN DEFAULT TRUE,
-      heures                     FLOAT DEFAULT 0,
-      taux_service_du            FLOAT DEFAULT 1,
-      taux_service_compl         FLOAT DEFAULT 1,
-
-      service_fi                 FLOAT DEFAULT 0,
-      service_fa                 FLOAT DEFAULT 0,
-      service_fc                 FLOAT DEFAULT 0,
-      service_referentiel        FLOAT DEFAULT 0,
-      heures_compl_fi            FLOAT DEFAULT 0,
-      heures_compl_fa            FLOAT DEFAULT 0,
-      heures_compl_fc            FLOAT DEFAULT 0,
-      heures_compl_fc_majorees   FLOAT DEFAULT 0,
-      heures_compl_referentiel   FLOAT DEFAULT 0
-      );
-      TYPE t_lst_volume_horaire IS TABLE OF t_volume_horaire INDEX BY PLS_INTEGER;
-      TYPE t_volumes_horaires IS RECORD (
-      length NUMERIC DEFAULT 0,
-      items t_lst_volume_horaire
-      );
+  intervenant      t_intervenant;
+  volumes_horaires t_volumes_horaires;
 
-      intervenant      t_intervenant;
-      volumes_horaires t_volumes_horaires;
+  FUNCTION GET_INTERVENANT_ID RETURN NUMERIC;
 
-      FUNCTION GET_INTERVENANT_ID RETURN NUMERIC;
+  FUNCTION GET_TAUX_HORAIRE_HETD( DATE_OBS DATE DEFAULT NULL ) RETURN FLOAT;
+  PROCEDURE UPDATE_ANNEE_TAUX_HETD;
 
-      FUNCTION GET_TAUX_HORAIRE_HETD( DATE_OBS DATE DEFAULT NULL ) RETURN FLOAT;
-      PROCEDURE UPDATE_ANNEE_TAUX_HETD;
+  PROCEDURE CALCULER( INTERVENANT_ID NUMERIC );
+  PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL );        -- mise à jour de TOUTES les données ! ! ! !
+  PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS );
 
-      PROCEDURE CALCULER( INTERVENANT_ID NUMERIC );
-      PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL );        -- mise à jour de TOUTES les données ! ! ! !
-      PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS );
+  PROCEDURE TEST( INTERVENANT_TEST_ID NUMERIC );
+  PROCEDURE TEST_TOUT;
 
-      PROCEDURE DEBUG_INTERVENANT;
-      PROCEDURE DEBUG_VOLUMES_HORAIRES(VOLUME_HORAIRE_ID NUMERIC DEFAULT NULL);
+  PROCEDURE DEBUG_INTERVENANT;
+  PROCEDURE DEBUG_VOLUMES_HORAIRES(VOLUME_HORAIRE_ID NUMERIC DEFAULT NULL);
 END OSE_FORMULE;
 
 /
@@ -2973,14 +3117,10 @@ CREATE OR REPLACE PACKAGE "OSE_PARAMETRE" AS
   function get_annee return Numeric;
   function get_annee_import return Numeric;
   function get_ose_user return Numeric;
-  function get_drh_structure_id return Numeric;
-  function get_date_fin_saisie_permanents RETURN DATE;
-  function get_ddeb_saisie_serv_real RETURN DATE;
-  function get_dfin_saisie_serv_real RETURN DATE;
-  function get_formule_package_name RETURN VARCHAR2;
-  function get_formule_function_name RETURN VARCHAR2;
+  function get_formule RETURN formule%rowtype;
 
 END OSE_PARAMETRE;
+
 /
 
 -- OSE_TEST
@@ -3089,18 +3229,6 @@ CREATE OR REPLACE PACKAGE "UNICAEN_IMPORT" AS
 END UNICAEN_IMPORT;
 /
 
--- UNICAEN_OSE_FORMULE
-CREATE OR REPLACE PACKAGE "UNICAEN_OSE_FORMULE" AS
-
-  PROCEDURE CALCUL_RESULTAT_V2( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, ETAT_VOLUME_HORAIRE_ID NUMERIC );
-
-  PROCEDURE CALCUL_RESULTAT_V3( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, ETAT_VOLUME_HORAIRE_ID NUMERIC );
-
-  PROCEDURE PURGE_EM_NON_FC;
-
-END UNICAEN_OSE_FORMULE;
-/
-
 -- UNICAEN_TBL
 CREATE OR REPLACE PACKAGE "UNICAEN_TBL" AS
 
@@ -3617,130 +3745,134 @@ WHERE
   COALESCE(snsetp.dedoublement, tcsd.dedoublement)  IS NOT NULL;
 
 -- V_CONTRAT_MAIN
-CREATE OR REPLACE FORCE VIEW "V_CONTRAT_MAIN" ("CONTRAT_ID", "annee", "nom", "prenom", "civilite", "e", "dateNaissance", "adresse", "numInsee", "statut", "totalHETD", "tauxHoraireValeur", "tauxHoraireDate", "dateSignature", "contrat1", "avenant1", "n", "horodatage", "exemplaire1", "exemplaire2", "serviceTotal", "titre", "qualite", "titreCourt") AS
+CREATE OR REPLACE FORCE VIEW "V_CONTRAT_MAIN" ("CONTRAT_ID", "annee", "nom", "prenom", "civilite", "e", "dateNaissance", "adresse", "numInsee", "statut", "totalHETD", "tauxHoraireValeur", "tauxHoraireDate", "dateSignature", "modifieComplete", "contrat1", "avenant1", "n", "horodatage", "exemplaire1", "exemplaire2", "serviceTotal", "titre", "qualite", "titreCourt") AS
   WITH hs AS (
-      SELECT contrat_id, sum(heures) "serviceTotal" FROM V_CONTRAT_SERVICES GROUP BY contrat_id
-  )
-  SELECT
-         ct.id contrat_id,
-         ct."annee",
-         ct."nom",
-         ct."prenom",
-         ct."civilite",
-         ct."e",
-         ct."dateNaissance",
-         ct."adresse",
-         ct."numInsee",
-         ct."statut",
-         ct."totalHETD",
-         ct."tauxHoraireValeur",
-         ct."tauxHoraireDate",
-         ct."dateSignature",
-         CASE WHEN ct.est_contrat=1 THEN 1 ELSE null END "contrat1",
-         CASE WHEN ct.est_contrat=1 THEN null ELSE 1 END "avenant1",
-         CASE WHEN ct.est_contrat=1 THEN '3' ELSE '2' END "n",
-         to_char(SYSDATE, 'dd/mm/YYYY - hh24:mi:ss') "horodatage",
-         'Exemplaire à conserver' "exemplaire1",
-         'Exemplaire à retourner' || ct."exemplaire2" "exemplaire2",
-         ct."serviceTotal",
-
-         CASE ct.est_contrat
-           WHEN 1 THEN -- contrat
-             'Contrat de travail '
-           ELSE
-             'Avenant au contrat de travail initial modifiant le volume horaire initial'
-               || ' de recrutement en qualité '
-             END                                         "titre",
-         CASE WHEN ct.est_atv = 1 THEN
-             'd''agent temporaire vacataire'
-              ELSE
-             'de chargé' || ct."e" || ' d''enseignement vacataire'
-             END                                         "qualite",
+       SELECT contrat_id, sum(heures) "serviceTotal" FROM V_CONTRAT_SERVICES GROUP BY contrat_id
+   )
+   SELECT
+          ct.id contrat_id,
+          ct."annee",
+          ct."nom",
+          ct."prenom",
+          ct."civilite",
+          ct."e",
+          ct."dateNaissance",
+          ct."adresse",
+          ct."numInsee",
+          ct."statut",
+          ct."totalHETD",
+          ct."tauxHoraireValeur",
+          ct."tauxHoraireDate",
+          ct."dateSignature",
+          ct."modifieComplete",
+          CASE WHEN ct.est_contrat=1 THEN 1 ELSE null END "contrat1",
+          CASE WHEN ct.est_contrat=1 THEN null ELSE 1 END "avenant1",
+          CASE WHEN ct.est_contrat=1 THEN '3' ELSE '2' END "n",
+          to_char(SYSDATE, 'dd/mm/YYYY - hh24:mi:ss') "horodatage",
+          'Exemplaire à conserver' "exemplaire1",
+          'Exemplaire à retourner' || ct."exemplaire2" "exemplaire2",
+          ct."serviceTotal",
+
+          CASE ct.est_contrat
+             WHEN 1 THEN -- contrat
+              'Contrat de travail '
+             ELSE
+              'Avenant au contrat de travail initial modifiant le volume horaire initial'
+                 || ' de recrutement en qualité '
+              END                                         "titre",
+          CASE WHEN ct.est_atv = 1 THEN
+              'd''agent temporaire vacataire'
+               ELSE
+              'de chargé' || ct."e" || ' d''enseignement vacataire'
+              END                                         "qualite",
+
+          CASE
+             WHEN ct.est_projet = 1 AND ct.est_contrat = 1 THEN 'Projet de contrat'
+             WHEN ct.est_projet = 0 AND ct.est_contrat = 1 THEN 'Contrat n°' || ct.id
+             WHEN ct.est_projet = 1 AND ct.est_contrat = 0 THEN 'Projet d''avenant'
+             WHEN ct.est_projet = 0 AND ct.est_contrat = 0 THEN 'Avenant n°' || ct.contrat_id || '.' || ct.numero_avenant
+              END                                         "titreCourt"
+   FROM
+        (
+        SELECT
+               c.*,
+               a.libelle                                                                                     "annee",
+               COALESCE(d.nom_usuel,i.nom_usuel)                                                             "nom",
+               COALESCE(d.prenom,i.prenom)                                                                   "prenom",
+               civ.libelle_court                                                                             "civilite",
+               CASE WHEN civ.sexe = 'F' THEN 'e' ELSE '' END                                                 "e",
+               to_char(COALESCE(d.date_naissance,i.date_naissance), 'dd/mm/YYYY')                            "dateNaissance",
+               COALESCE(d.adresse,ose_divers.formatted_adresse(
+                                     ai.NO_VOIE, ai.NOM_VOIE, ai.BATIMENT, ai.MENTION_COMPLEMENTAIRE, ai.LOCALITE,
+                                     ai.CODE_POSTAL, ai.VILLE, ai.PAYS_LIBELLE))                                               "adresse",
+               COALESCE(d.numero_insee,i.numero_insee || ' ' || COALESCE(LPAD(i.numero_insee_cle,2,'0'),'')) "numInsee",
+               si.libelle                                                                                    "statut",
+               replace(ltrim(to_char(COALESCE(fr.total,0), '999999.00')),'.',',')                            "totalHETD",
+               replace(ltrim(to_char(COALESCE(th.valeur,0), '999999.00')),'.',',')                           "tauxHoraireValeur",
+               COALESCE(to_char(th.histo_creation, 'dd/mm/YYYY'), 'TAUX INTROUVABLE')                        "tauxHoraireDate",
+               to_char(COALESCE(v.histo_creation, c.histo_creation), 'dd/mm/YYYY')                           "dateSignature",
+               CASE WHEN c.structure_id <> COALESCE(cp.structure_id,0) THEN 'modifié' ELSE 'complété' END    "modifieComplete",
+               CASE WHEN s.aff_adresse_contrat = 1 THEN
+                   ' signé à l''adresse suivante :' || CHR(13) || CHR(10) ||
+                   s.libelle_court || ' - ' || REPLACE(ose_divers.formatted_adresse(
+                                                          astr.NO_VOIE, astr.NOM_VOIE, null, null, astr.LOCALITE,
+                                                          astr.CODE_POSTAL, astr.VILLE, null), CHR(13), ' - ')
+                    ELSE '' END                                                                                   "exemplaire2",
+               replace(ltrim(to_char(COALESCE(hs."serviceTotal",0), '999999.00')),'.',',')                   "serviceTotal",
+               CASE WHEN c.contrat_id IS NULL THEN 1 ELSE 0 END                                              est_contrat,
+               CASE WHEN v.id IS NULL THEN 1 ELSE 0 END                                                      est_projet,
+               si.tem_atv                                                                                    est_atv
 
-         CASE
-           WHEN ct.est_projet = 1 AND ct.est_contrat = 1 THEN 'Projet de contrat'
-           WHEN ct.est_projet = 0 AND ct.est_contrat = 1 THEN 'Contrat n°' || ct.id
-           WHEN ct.est_projet = 1 AND ct.est_contrat = 0 THEN 'Projet d''avenant'
-           WHEN ct.est_projet = 0 AND ct.est_contrat = 0 THEN 'Avenant n°' || ct.contrat_id || '.' || ct.numero_avenant
-             END                                         "titreCourt"
-  FROM
-       (
-       SELECT
-              c.*,
-              a.libelle                                                                                     "annee",
-              COALESCE(d.nom_usuel,i.nom_usuel)                                                             "nom",
-              COALESCE(d.prenom,i.prenom)                                                                   "prenom",
-              civ.libelle_court                                                                             "civilite",
-              CASE WHEN civ.sexe = 'F' THEN 'e' ELSE '' END                                                 "e",
-              to_char(COALESCE(d.date_naissance,i.date_naissance), 'dd/mm/YYYY')                            "dateNaissance",
-              COALESCE(d.adresse,ose_divers.formatted_adresse(
-                                   ai.NO_VOIE, ai.NOM_VOIE, ai.BATIMENT, ai.MENTION_COMPLEMENTAIRE, ai.LOCALITE,
-                                   ai.CODE_POSTAL, ai.VILLE, ai.PAYS_LIBELLE))                                               "adresse",
-              COALESCE(d.numero_insee,i.numero_insee || ' ' || COALESCE(LPAD(i.numero_insee_cle,2,'0'),'')) "numInsee",
-              si.libelle                                                                                    "statut",
-              replace(ltrim(to_char(COALESCE(fr.total,0), '999999.00')),'.',',')                                        "totalHETD",
-              replace(ltrim(to_char(COALESCE(th.valeur,0), '999999.00')),'.',',')                                       "tauxHoraireValeur",
-              COALESCE(to_char(th.histo_creation, 'dd/mm/YYYY'), 'TAUX INTROUVABLE')                        "tauxHoraireDate",
-              to_char(COALESCE(v.histo_creation, c.histo_creation), 'dd/mm/YYYY')                           "dateSignature",
-              CASE WHEN s.aff_adresse_contrat = 1 THEN
-                  ' signé à l''adresse suivante :' || CHR(13) || CHR(10) ||
-                  s.libelle_court || ' - ' || REPLACE(ose_divers.formatted_adresse(
-                                                        astr.NO_VOIE, astr.NOM_VOIE, null, null, astr.LOCALITE,
-                                                        astr.CODE_POSTAL, astr.VILLE, null), CHR(13), ' - ')
-                   ELSE '' END                                                                                   "exemplaire2",
-              replace(ltrim(to_char(COALESCE(hs."serviceTotal",0), '999999.00')),'.',',')                               "serviceTotal",
-              CASE WHEN c.contrat_id IS NULL THEN 1 ELSE 0 END                                              est_contrat,
-              CASE WHEN v.id IS NULL THEN 1 ELSE 0 END                                                      est_projet,
-              si.tem_atv                                                                                    est_atv
-
-       FROM
-            contrat               c
-              JOIN type_contrat         tc ON tc.id = c.type_contrat_id
-              JOIN intervenant           i ON i.id = c.intervenant_id
-              JOIN annee                 a ON a.id = i.annee_id
-              JOIN statut_intervenant   si ON si.id = i.statut_id
-              JOIN structure             s ON s.id = c.structure_id
-              LEFT JOIN adresse_structure  astr ON astr.structure_id = s.id AND astr.principale = 1 AND astr.histo_destruction IS NULL
-              LEFT JOIN dossier               d ON d.intervenant_id = i.id AND d.histo_destruction IS NULL
-              JOIN civilite            civ ON civ.id = COALESCE(d.civilite_id,i.civilite_id)
-              LEFT JOIN validation            v ON v.id = c.validation_id AND v.histo_destruction IS NULL
-              LEFT JOIN adresse_intervenant  ai ON ai.intervenant_id = i.id AND ai.histo_destruction IS NULL
-
-              JOIN type_volume_horaire tvh ON tvh.code = 'PREVU'
-              JOIN etat_volume_horaire evh ON evh.code = 'valide'
-              LEFT JOIN formule_resultat     fr ON fr.intervenant_id = i.id AND fr.type_volume_horaire_id = tvh.id AND fr.etat_volume_horaire_id = evh.id
-              LEFT JOIN taux_horaire_hetd    th ON c.histo_creation BETWEEN th.histo_creation AND COALESCE(th.histo_destruction,SYSDATE)
-              LEFT JOIN                      hs ON hs.contrat_id = c.id
-       WHERE
-           c.histo_destruction IS NULL
-       ) ct;
+        FROM
+             contrat               c
+                JOIN type_contrat         tc ON tc.id = c.type_contrat_id
+                JOIN intervenant           i ON i.id = c.intervenant_id
+                JOIN annee                 a ON a.id = i.annee_id
+                JOIN statut_intervenant   si ON si.id = i.statut_id
+                JOIN structure             s ON s.id = c.structure_id
+                LEFT JOIN adresse_structure  astr ON astr.structure_id = s.id AND astr.principale = 1 AND astr.histo_destruction IS NULL
+                LEFT JOIN dossier               d ON d.intervenant_id = i.id AND d.histo_destruction IS NULL
+                JOIN civilite            civ ON civ.id = COALESCE(d.civilite_id,i.civilite_id)
+                LEFT JOIN validation            v ON v.id = c.validation_id AND v.histo_destruction IS NULL
+                LEFT JOIN adresse_intervenant  ai ON ai.intervenant_id = i.id AND ai.histo_destruction IS NULL
+
+                JOIN type_volume_horaire tvh ON tvh.code = 'PREVU'
+                JOIN etat_volume_horaire evh ON evh.code = 'valide'
+                LEFT JOIN formule_resultat     fr ON fr.intervenant_id = i.id AND fr.type_volume_horaire_id = tvh.id AND fr.etat_volume_horaire_id = evh.id
+                LEFT JOIN taux_horaire_hetd    th ON c.histo_creation BETWEEN th.histo_creation AND COALESCE(th.histo_destruction,SYSDATE)
+                LEFT JOIN                      hs ON hs.contrat_id = c.id
+                LEFT JOIN contrat              cp ON cp.id = c.contrat_id
+        WHERE
+            c.histo_destruction IS NULL
+        ) ct;
 
 -- V_CONTRAT_SERVICES
 CREATE OR REPLACE FORCE VIEW "V_CONTRAT_SERVICES" ("CONTRAT_ID", "serviceComposante", "serviceCode", "serviceLibelle", "HEURES", "serviceHeures") AS
   SELECT
-             c.id                                             contrat_id,
-             str.libelle_court                                "serviceComposante",
-             ep.code                                          "serviceCode",
-             ep.libelle                                       "serviceLibelle",
-             sum(vh.heures)                                   heures,
-             replace(ltrim(to_char(sum(vh.heures), '999999.00')),'.',',') "serviceHeures"
-      FROM
-           contrat                  c
-                 JOIN intervenant              i ON i.id = c.intervenant_id
-                 JOIN type_volume_horaire    tvh ON tvh.code = 'PREVU'
-                 JOIN service                  s ON s.intervenant_id = i.id AND s.histo_destruction IS NULL
-                 JOIN volume_horaire          vh ON vh.service_id = s.id AND vh.histo_destruction IS NULL AND vh.type_volume_horaire_id = tvh.id
-                 LEFT JOIN validation_vol_horaire vvh ON vvh.volume_horaire_id = vh.id
-                 LEFT JOIN validation               v ON v.id = vvh.validation_id AND v.histo_destruction IS NULL
-                 LEFT JOIN validation              cv ON cv.id = c.validation_id AND cv.histo_destruction IS NULL
-                 LEFT JOIN element_pedagogique     ep ON ep.id = s.element_pedagogique_id
-                 JOIN structure              str ON str.id = COALESCE(ep.structure_id,i.structure_id)
-      WHERE
-          c.histo_destruction IS NULL
-        AND (cv.id IS NULL OR vh.contrat_id = c.id)
-        AND (vh.auto_validation = 1 OR v.id IS NOT NULL)
-      GROUP BY
-               c.id, str.libelle_court, ep.code, ep.libelle;
+  c.id                                             contrat_id,
+  str.libelle_court                                "serviceComposante",
+  ep.code                                          "serviceCode",
+  ep.libelle                                       "serviceLibelle",
+  sum(vh.heures)                                   heures,
+  replace(ltrim(to_char(sum(vh.heures), '999999.00')),'.',',') "serviceHeures"
+FROM
+            contrat                  c
+       JOIN intervenant              i ON i.id = c.intervenant_id
+       JOIN type_volume_horaire    tvh ON tvh.code = 'PREVU'
+       JOIN service                  s ON s.intervenant_id = i.id AND s.histo_destruction IS NULL
+       JOIN volume_horaire          vh ON vh.service_id = s.id AND vh.histo_destruction IS NULL AND vh.type_volume_horaire_id = tvh.id
+  LEFT JOIN validation_vol_horaire vvh ON vvh.volume_horaire_id = vh.id
+  LEFT JOIN validation               v ON v.id = vvh.validation_id AND v.histo_destruction IS NULL
+  LEFT JOIN validation              cv ON cv.id = c.validation_id AND cv.histo_destruction IS NULL
+  LEFT JOIN element_pedagogique     ep ON ep.id = s.element_pedagogique_id
+       JOIN structure              str ON str.id = COALESCE(ep.structure_id,i.structure_id)
+WHERE
+    c.histo_destruction IS NULL
+  AND (cv.id IS NULL OR vh.contrat_id = c.id)
+  AND (vh.auto_validation = 1 OR v.id IS NOT NULL)
+  --AND str.id = c.structure_id
+GROUP BY
+  c.id, str.libelle_court, ep.code, ep.libelle;
 
 -- V_CTL_SERVICES_ODF_HISTO
 CREATE OR REPLACE FORCE VIEW "V_CTL_SERVICES_ODF_HISTO" ("PRENOM", "NOM_USUEL", "ELEMENT", "ETAPE", "TYPE_INTERVENTION", "HEURES", "HAS_CONTRAT", "HAS_VALIDATION", "ELEMENT_SUPPRIME", "ETAPE_SUPPRIMEE", "ETABLISSEMENT_SUPPRIME") AS
@@ -4498,114 +4630,114 @@ ORDER BY
 -- V_EXPORT_PAIEMENT_WINPAIE
 CREATE OR REPLACE FORCE VIEW "V_EXPORT_PAIEMENT_WINPAIE" ("ANNEE_ID", "TYPE_INTERVENANT_ID", "STRUCTURE_ID", "PERIODE_ID", "INTERVENANT_ID", "INSEE", "NOM", "CARTE", "CODE_ORIGINE", "RETENUE", "SENS", "MC", "NBU", "MONTANT", "LIBELLE") AS
   SELECT
-             annee_id,
-             type_intervenant_id,
-             structure_id,
-             periode_id,
-             intervenant_id,
+    annee_id,
+    type_intervenant_id,
+    structure_id,
+    periode_id,
+    intervenant_id,
 
-             insee,
-             nom,
-             '20' carte,
-             code_origine,
-             '0204' retenue,
-             '0' sens,
-             'B' mc,
-             nbu,
-             montant,
-             libelle || ' ' || LPAD(TO_CHAR(FLOOR(nbu)),2,'00') || ' H' ||
-             CASE to_char(ROUND( nbu-FLOOR(nbu), 2 )*100,'00')
-                   WHEN ' 00' THEN '' ELSE ' ' || LPAD(ROUND( nbu-FLOOR(nbu), 2 )*100,2,'00') END libelle
-      FROM (
-           SELECT
-                  i.annee_id                                                                                          annee_id,
-                  si.type_intervenant_id                                                                              type_intervenant_id,
-                  t2.structure_id                                                                                     structure_id,
-                  t2.periode_paiement_id                                                                              periode_id,
-                  i.id                                                                                                intervenant_id,
-
-                  '''' || NVL(i.numero_insee,'') || TRIM(NVL(TO_CHAR(i.numero_insee_cle,'00'),''))                    insee,
-                  i.nom_usuel || ',' || i.prenom                                                                      nom,
-                  t2.code_origine                                                                                     code_origine,
-                  CASE WHEN ind <> CEIL(t2.nbu/max_nbu) THEN max_nbu ELSE t2.nbu - max_nbu*(ind-1) END                nbu,
-                  t2.nbu                                                                                              tnbu,
-                  OSE_FORMULE.GET_TAUX_HORAIRE_HETD( NVL(t2.date_mise_en_paiement,SYSDATE) )                          montant,
-                  COALESCE(t2.unite_budgetaire,'') || ' ' || to_char(i.annee_id) || ' ' || to_char(i.annee_id+1)      libelle
-           FROM (
-                SELECT
-                       structure_id,
-                       periode_paiement_id,
-                       intervenant_id,
-                       code_origine,
-                       ROUND( SUM(nbu), 2) nbu,
-                       unite_budgetaire,
-                       date_mise_en_paiement
-                FROM (
-                     WITH mep AS (
-                         SELECT
-                             -- pour les filtres
-                                mep.id,
-                                mis.structure_id,
-                                mep.periode_paiement_id,
-                                mis.intervenant_id,
-                                mep.heures,
-                                cc.unite_budgetaire,
-                                mep.date_mise_en_paiement
-                         FROM
-                              v_mep_intervenant_structure  mis
-                                    JOIN mise_en_paiement        mep ON mep.id = mis.mise_en_paiement_id AND mep.histo_destruction IS NULL
-                                    JOIN centre_cout              cc ON cc.id = mep.centre_cout_id
-                                    JOIN type_heures              th ON th.id = mep.type_heures_id
-                         WHERE
-                             mep.date_mise_en_paiement IS NOT NULL
-                           AND mep.periode_paiement_id IS NOT NULL
-                           AND th.eligible_extraction_paie = 1
-                     )
-                     SELECT
-                            mep.id,
-                            mep.structure_id,
-                            mep.periode_paiement_id,
-                            mep.intervenant_id,
-                            2 code_origine,
-                            mep.heures * 4 / 10 nbu,
-                            mep.unite_budgetaire,
-                            mep.date_mise_en_paiement
-                     FROM
-                          mep
-                     WHERE
-                         mep.heures * 4 / 10 > 0
+    insee,
+    nom,
+    '20' carte,
+    code_origine,
+    '0204' retenue,
+    '0' sens,
+    'B' mc,
+    nbu,
+    montant,
+    libelle || ' ' || LPAD(TO_CHAR(FLOOR(nbu)),2,'00') || ' H' ||
+      CASE to_char(ROUND( nbu-FLOOR(nbu), 2 )*100,'00')
+      WHEN ' 00' THEN '' ELSE ' ' || LPAD(ROUND( nbu-FLOOR(nbu), 2 )*100,2,'00') END libelle
+FROM (
+  SELECT
+    i.annee_id                                                                                          annee_id,
+    si.type_intervenant_id                                                                              type_intervenant_id,
+    t2.structure_id                                                                                     structure_id,
+    t2.periode_paiement_id                                                                              periode_id,
+    i.id                                                                                                intervenant_id,
+
+    '''' || NVL(i.numero_insee,'') || TRIM(NVL(TO_CHAR(i.numero_insee_cle,'00'),''))                    insee,
+    i.nom_usuel || ',' || i.prenom                                                                      nom,
+    t2.code_origine                                                                                     code_origine,
+    CASE WHEN ind <> CEIL(t2.nbu/max_nbu) THEN max_nbu ELSE t2.nbu - max_nbu*(ind-1) END                nbu,
+    t2.nbu                                                                                              tnbu,
+    OSE_FORMULE.GET_TAUX_HORAIRE_HETD( NVL(t2.date_mise_en_paiement,SYSDATE) )                          montant,
+    COALESCE(t2.unite_budgetaire,'') || ' ' || to_char(i.annee_id) || ' ' || to_char(i.annee_id+1)      libelle
+  FROM (
+    SELECT
+      structure_id,
+      periode_paiement_id,
+      intervenant_id,
+      code_origine,
+      ROUND( SUM(nbu), 2) nbu,
+      unite_budgetaire,
+      date_mise_en_paiement
+    FROM (
+      WITH mep AS (
+      SELECT
+        -- pour les filtres
+        mep.id,
+        mis.structure_id,
+        mep.periode_paiement_id,
+        mis.intervenant_id,
+        mep.heures,
+        cc.unite_budgetaire,
+        mep.date_mise_en_paiement
+      FROM
+        v_mep_intervenant_structure  mis
+        JOIN mise_en_paiement        mep ON mep.id = mis.mise_en_paiement_id AND mep.histo_destruction IS NULL
+        JOIN centre_cout              cc ON cc.id = mep.centre_cout_id
+        JOIN type_heures              th ON th.id = mep.type_heures_id
+      WHERE
+        mep.date_mise_en_paiement IS NOT NULL
+        AND mep.periode_paiement_id IS NOT NULL
+        AND th.eligible_extraction_paie = 1
+      )
+      SELECT
+        mep.id,
+        mep.structure_id,
+        mep.periode_paiement_id,
+        mep.intervenant_id,
+        2 code_origine,
+        mep.heures * 4 / 10 nbu,
+        mep.unite_budgetaire,
+        mep.date_mise_en_paiement
+      FROM
+        mep
+      WHERE
+        mep.heures * 4 / 10 > 0
 
-                     UNION
+      UNION
 
-                     SELECT
-                            mep.id,
-                            mep.structure_id,
-                            mep.periode_paiement_id,
-                            mep.intervenant_id,
-                            1 code_origine,
-                            mep.heures * 6 / 10 nbu,
-                            mep.unite_budgetaire,
-                            mep.date_mise_en_paiement
-                     FROM
-                          mep
-                     WHERE
-                         mep.heures * 6 / 10 > 0
-                     ) t1
-                GROUP BY
-                         structure_id,
-                         periode_paiement_id,
-                         intervenant_id,
-                         code_origine,
-                         unite_budgetaire,
-                         date_mise_en_paiement
-                ) t2
-                      JOIN (SELECT level ind, 99 max_nbu FROM dual CONNECT BY 1=1 AND LEVEL <= 11) tnbu ON ceil(t2.nbu / max_nbu) >= ind
-                      JOIN intervenant i ON i.id = t2.intervenant_id
-                      JOIN statut_intervenant si ON si.id = i.statut_id
-                      JOIN structure s ON s.id = t2.structure_id
-           ) t3
-      ORDER BY
-               annee_id, type_intervenant_id, structure_id, periode_id, nom, code_origine, nbu DESC;
+      SELECT
+        mep.id,
+        mep.structure_id,
+        mep.periode_paiement_id,
+        mep.intervenant_id,
+        1 code_origine,
+        mep.heures * 6 / 10 nbu,
+        mep.unite_budgetaire,
+        mep.date_mise_en_paiement
+      FROM
+        mep
+      WHERE
+        mep.heures * 6 / 10 > 0
+    ) t1
+    GROUP BY
+      structure_id,
+      periode_paiement_id,
+      intervenant_id,
+      code_origine,
+      unite_budgetaire,
+      date_mise_en_paiement
+  ) t2
+  JOIN (SELECT level ind, 99 max_nbu FROM dual CONNECT BY 1=1 AND LEVEL <= 11) tnbu ON ceil(t2.nbu / max_nbu) >= ind
+  JOIN intervenant i ON i.id = t2.intervenant_id
+  JOIN statut_intervenant si ON si.id = i.statut_id
+  JOIN structure s ON s.id = t2.structure_id
+) t3
+ORDER BY
+  annee_id, type_intervenant_id, structure_id, periode_id, nom, code_origine, nbu DESC;
 
 -- V_EXPORT_PILOTAGE_ECARTS_ETATS
 CREATE OR REPLACE FORCE VIEW "V_EXPORT_PILOTAGE_ECARTS_ETATS" ("ANNEE_ID", "ANNEE", "ETAT", "TYPE_HEURES_ID", "TYPE_HEURES", "STRUCTURE_ID", "STRUCTURE", "INTERVENANT_ID", "INTERVENANT_TYPE", "INTERVENANT_CODE", "INTERVENANT", "HETD_PAYABLES") AS
@@ -4836,7 +4968,7 @@ ORDER BY
 
 -- V_EXPORT_SERVICE
 CREATE OR REPLACE FORCE VIEW "V_EXPORT_SERVICE" ("ID", "SERVICE_ID", "INTERVENANT_ID", "TYPE_INTERVENANT_ID", "ANNEE_ID", "SERVICE_DATE_MODIFICATION", "TYPE_VOLUME_HORAIRE_ID", "ETAT_VOLUME_HORAIRE_ID", "ETABLISSEMENT_ID", "STRUCTURE_AFF_ID", "STRUCTURE_ENS_ID", "NIVEAU_FORMATION_ID", "ETAPE_ID", "ELEMENT_PEDAGOGIQUE_ID", "PERIODE_ID", "TYPE_INTERVENTION_ID", "FONCTION_REFERENTIEL_ID", "TYPE_ETAT", "INTERVENANT_CODE", "INTERVENANT_NOM", "INTERVENANT_DATE_NAISSANCE", "INTERVENANT_STATUT_LIBELLE", "INTERVENANT_TYPE_CODE", "INTERVENANT_TYPE_LIBELLE", "INTERVENANT_GRADE_CODE", "INTERVENANT_GRADE_LIBELLE", "INTERVENANT_DISCIPLINE_CODE", "INTERVENANT_DISCIPLINE_LIBELLE", "SERVICE_STRUCTURE_AFF_LIBELLE", "SERVICE_STRUCTURE_ENS_LIBELLE", "ETABLISSEMENT_LIBELLE", "GROUPE_TYPE_FORMATION_LIBELLE", "TYPE_FORMATION_LIBELLE", "ETAPE_NIVEAU", "ETAPE_CODE", "ETAPE_LIBELLE", "ELEMENT_CODE", "ELEMENT_LIBELLE", "ELEMENT_DISCIPLINE_CODE", "ELEMENT_DISCIPLINE_LIBELLE", "FONCTION_REFERENTIEL_LIBELLE", "ELEMENT_TAUX_FI", "ELEMENT_TAUX_FC", "ELEMENT_TAUX_FA", "SERVICE_REF_FORMATION", "COMMENTAIRES", "PERIODE_LIBELLE", "ELEMENT_PONDERATION_COMPL", "ELEMENT_SOURCE_LIBELLE", "HEURES", "HEURES_REF", "HEURES_NON_PAYEES", "SERVICE_STATUTAIRE", "SERVICE_DU_MODIFIE", "SERVICE_FI", "SERVICE_FA", "SERVICE_FC", "SERVICE_REFERENTIEL", "HEURES_COMPL_FI", "HEURES_COMPL_FA", "HEURES_COMPL_FC", "HEURES_COMPL_FC_MAJOREES", "HEURES_COMPL_REFERENTIEL", "TOTAL", "SOLDE", "DATE_CLOTURE_REALISE") AS
-WITH t AS ( SELECT
+  WITH t AS ( SELECT
   'vh_' || vh.id                    id,
   s.id                              service_id,
   s.intervenant_id                  intervenant_id,
@@ -5123,116 +5255,118 @@ FROM
 -- V_FORMULE_INTERVENANT
 CREATE OR REPLACE FORCE VIEW "V_FORMULE_INTERVENANT" ("INTERVENANT_ID", "ANNEE_ID", "STRUCTURE_ID", "TYPE_INTERVENANT_CODE", "HEURES_SERVICE_STATUTAIRE", "DEPASSEMENT_SERVICE_DU_SANS_HC", "HEURES_SERVICE_MODIFIE", "HEURES_DECHARGE") AS
   SELECT
-             i.id                                                                 intervenant_id,
-             i.annee_id                                                           annee_id,
-             CASE WHEN ti.code = 'P' THEN i.structure_id ELSE NULL END           structure_id,
-             ti.code                                                              type_intervenant_code,
-             si.service_statutaire                                                heures_service_statutaire,
-             si.depassement_service_du_sans_hc                                    depassement_service_du_sans_hc,
-             COALESCE( SUM( msd.heures * mms.multiplicateur ), 0 )                heures_service_modifie,
-             COALESCE( SUM( msd.heures * mms.multiplicateur * mms.decharge ), 0 ) heures_decharge
-      FROM
-           intervenant                  i
-                 LEFT JOIN modification_service_du    msd ON msd.intervenant_id = i.id AND msd.histo_destruction IS NULL
-                 LEFT JOIN motif_modification_service mms ON mms.id = msd.motif_id
-                 JOIN statut_intervenant          si ON si.id = i.statut_id
-                 JOIN type_intervenant            ti ON ti.id = si.type_intervenant_id
-      WHERE
-          i.histo_destruction IS NULL
-        AND i.id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, i.id )
-      GROUP BY
-               i.id, i.annee_id, i.structure_id, ti.code, si.service_statutaire, si.depassement_service_du_sans_hc;
+  i.id                                                                 intervenant_id,
+  i.annee_id                                                           annee_id,
+  CASE WHEN ti.code = 'P' THEN i.structure_id ELSE NULL END           structure_id,
+  ti.code                                                              type_intervenant_code,
+  si.service_statutaire                                                heures_service_statutaire,
+  si.depassement_service_du_sans_hc                                    depassement_service_du_sans_hc,
+  COALESCE( SUM( msd.heures * mms.multiplicateur ), 0 )                heures_service_modifie,
+  COALESCE( SUM( msd.heures * mms.multiplicateur * mms.decharge ), 0 ) heures_decharge
+FROM
+            intervenant                  i
+  LEFT JOIN modification_service_du    msd ON msd.intervenant_id = i.id AND msd.histo_destruction IS NULL
+  LEFT JOIN motif_modification_service mms ON mms.id = msd.motif_id
+       JOIN statut_intervenant          si ON si.id = i.statut_id
+       JOIN type_intervenant            ti ON ti.id = si.type_intervenant_id
+WHERE
+  i.histo_destruction IS NULL
+  AND i.id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, i.id )
+GROUP BY
+  i.id, i.annee_id, i.structure_id, ti.code, si.service_statutaire, si.depassement_service_du_sans_hc;
 
 -- V_FORMULE_VOLUME_HORAIRE
 CREATE OR REPLACE FORCE VIEW "V_FORMULE_VOLUME_HORAIRE" ("ID", "VOLUME_HORAIRE_ID", "VOLUME_HORAIRE_REF_ID", "SERVICE_ID", "SERVICE_REFERENTIEL_ID", "INTERVENANT_ID", "TYPE_INTERVENTION_ID", "TYPE_VOLUME_HORAIRE_ID", "ETAT_VOLUME_HORAIRE_ID", "TAUX_FI", "TAUX_FA", "TAUX_FC", "STRUCTURE_ID", "PONDERATION_SERVICE_DU", "PONDERATION_SERVICE_COMPL", "SERVICE_STATUTAIRE", "HEURES", "HORAIRE_DEBUT", "HORAIRE_FIN", "TAUX_SERVICE_DU", "TAUX_SERVICE_COMPL") AS
   SELECT
-             to_number( 1 || vh.id )                                            id,
-             vh.id                                                              volume_horaire_id,
-             null                                                               volume_horaire_ref_id,
-             s.id                                                               service_id,
-             null                                                               service_referentiel_id,
-             s.intervenant_id                                                   intervenant_id,
-             ti.id                                                              type_intervention_id,
-             vh.type_volume_horaire_id                                          type_volume_horaire_id,
-             vhe.etat_volume_horaire_id                                         etat_volume_horaire_id,
-
-             CASE WHEN ep.id IS NOT NULL THEN ep.taux_fi ELSE 1 END             taux_fi,
-             CASE WHEN ep.id IS NOT NULL THEN ep.taux_fa ELSE 0 END             taux_fa,
-             CASE WHEN ep.id IS NOT NULL THEN ep.taux_fc ELSE 0 END             taux_fc,
-             ep.structure_id                                                    structure_id,
-             MAX(COALESCE( m.ponderation_service_du, 1))                        ponderation_service_du,
-             MAX(COALESCE( m.ponderation_service_compl, 1))                     ponderation_service_compl,
-             COALESCE(tf.service_statutaire,1)                                  service_statutaire,
-
-             vh.heures                                                          heures,
-             vh.horaire_debut                                                   horaire_debut,
-             vh.horaire_fin                                                     horaire_fin,
-             COALESCE(ti.taux_hetd_service,1)                                   taux_service_du,
-             COALESCE(ti.taux_hetd_complementaire,1)                            taux_service_compl
-      FROM
-           volume_horaire            vh
-                 JOIN service                    s ON s.id = vh.service_id
-                 JOIN intervenant                i ON i.id = s.intervenant_id
-                 JOIN type_intervention         ti ON ti.id = vh.type_intervention_id
-                 JOIN v_volume_horaire_etat    vhe ON vhe.volume_horaire_id = vh.id
-
-                 LEFT JOIN element_pedagogique       ep ON ep.id = s.element_pedagogique_id
-                 LEFT JOIN etape                      e ON e.id = ep.etape_id
-                 LEFT JOIN type_formation            tf ON tf.id = e.type_formation_id
-                 LEFT JOIN element_modulateur        em ON em.element_id = s.element_pedagogique_id
-                                                                 AND em.histo_destruction IS NULL
-                 LEFT JOIN modulateur                 m ON m.id = em.modulateur_id
-      WHERE
-          vh.histo_destruction IS NULL
-        AND s.histo_destruction IS NULL
-        AND vh.heures <> 0
-        AND vh.motif_non_paiement_id IS NULL
-        AND s.intervenant_id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, s.intervenant_id )
-      GROUP BY
-               vh.id, s.id, s.intervenant_id, ti.id, vh.type_volume_horaire_id, vhe.etat_volume_horaire_id, ep.id,
-               ep.taux_fi, ep.taux_fa, ep.taux_fc, ep.structure_id, tf.service_statutaire, vh.heures,
-               vh.horaire_debut, vh.horaire_fin, ti.taux_hetd_service, ti.taux_hetd_complementaire
+  to_number( 1 || vh.id )                                              id,
+  vh.id                                                                volume_horaire_id,
+  null                                                                 volume_horaire_ref_id,
+  s.id                                                                 service_id,
+  null                                                                 service_referentiel_id,
+  s.intervenant_id                                                     intervenant_id,
+  ti.id                                                                type_intervention_id,
+  vh.type_volume_horaire_id                                            type_volume_horaire_id,
+  vhe.etat_volume_horaire_id                                           etat_volume_horaire_id,
+
+  CASE WHEN ep.id IS NOT NULL THEN ep.taux_fi ELSE 1 END               taux_fi,
+  CASE WHEN ep.id IS NOT NULL THEN ep.taux_fa ELSE 0 END               taux_fa,
+  CASE WHEN ep.id IS NOT NULL THEN ep.taux_fc ELSE 0 END               taux_fc,
+  ep.structure_id                                                      structure_id,
+  MAX(COALESCE( m.ponderation_service_du, 1))                          ponderation_service_du,
+  MAX(COALESCE( m.ponderation_service_compl, 1))                       ponderation_service_compl,
+  COALESCE(tf.service_statutaire,1)                                    service_statutaire,
+
+  vh.heures                                                            heures,
+  vh.horaire_debut                                                     horaire_debut,
+  vh.horaire_fin                                                       horaire_fin,
+  COALESCE(tis.taux_hetd_service,ti.taux_hetd_service,1)               taux_service_du,
+  COALESCE(tis.taux_hetd_complementaire,ti.taux_hetd_complementaire,1) taux_service_compl
+FROM
+            volume_horaire            vh
+       JOIN service                    s ON s.id = vh.service_id
+       JOIN intervenant                i ON i.id = s.intervenant_id
+       JOIN type_intervention         ti ON ti.id = vh.type_intervention_id
+       JOIN v_volume_horaire_etat    vhe ON vhe.volume_horaire_id = vh.id
+
+  LEFT JOIN element_pedagogique       ep ON ep.id = s.element_pedagogique_id
+  LEFT JOIN etape                      e ON e.id = ep.etape_id
+  LEFT JOIN type_formation            tf ON tf.id = e.type_formation_id
+  LEFT JOIN element_modulateur        em ON em.element_id = s.element_pedagogique_id
+                                        AND em.histo_destruction IS NULL
+  LEFT JOIN modulateur                 m ON m.id = em.modulateur_id
+  LEFT JOIN type_intervention_statut tis ON tis.type_intervention_id = ti.id AND tis.statut_intervenant_id = i.statut_id
+WHERE
+  vh.histo_destruction IS NULL
+  AND s.histo_destruction IS NULL
+  AND vh.heures <> 0
+  AND vh.motif_non_paiement_id IS NULL
+  AND s.intervenant_id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, s.intervenant_id )
+GROUP BY
+  vh.id, s.id, s.intervenant_id, ti.id, vh.type_volume_horaire_id, vhe.etat_volume_horaire_id, ep.id,
+  ep.taux_fi, ep.taux_fa, ep.taux_fc, ep.structure_id, tf.service_statutaire, vh.heures,
+  vh.horaire_debut, vh.horaire_fin, tis.taux_hetd_service, tis.taux_hetd_complementaire,
+  ti.taux_hetd_service, ti.taux_hetd_complementaire
 
-      UNION ALL
+UNION ALL
 
-      SELECT
-             to_number( 2 || vhr.id )          id,
-             null                              volume_horaire_id,
-             vhr.id                            volume_horaire_ref_id,
-             null                              service_id,
-             sr.id                             service_referentiel_id,
-             sr.intervenant_id                 intervenant_id,
-             null                              type_intervention_id,
-             vhr.type_volume_horaire_id        type_volume_horaire_id,
-             evh.id                            etat_volume_horaire_id,
-
-             0                                 taux_fi,
-             0                                 taux_fa,
-             0                                 taux_fc,
-             sr.structure_id                   structure_id,
-             1                                 ponderation_service_du,
-             1                                 ponderation_service_compl,
-             COALESCE(fr.service_statutaire,1) service_statutaire,
-
-             vhr.heures                        heures,
-             vhr.horaire_debut                 horaire_debut,
-             vhr.horaire_fin                   horaire_fin,
-             1                                 taux_service_du,
-             1                                 taux_service_compl
-      FROM
-           volume_horaire_ref               vhr
-                 JOIN service_referentiel          sr ON sr.id = vhr.service_referentiel_id
-                 JOIN v_volume_horaire_ref_etat  vher ON vher.volume_horaire_ref_id = vhr.id
-                 JOIN etat_volume_horaire         evh ON evh.id = vher.etat_volume_horaire_id
-                 JOIN fonction_referentiel         fr ON fr.id = sr.fonction_id
-      WHERE
-          vhr.histo_destruction IS NULL
-        AND sr.histo_destruction IS NULL
-        AND vhr.heures <> 0
-        AND sr.intervenant_id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, sr.intervenant_id )
+SELECT
+  to_number( 2 || vhr.id )          id,
+  null                              volume_horaire_id,
+  vhr.id                            volume_horaire_ref_id,
+  null                              service_id,
+  sr.id                             service_referentiel_id,
+  sr.intervenant_id                 intervenant_id,
+  null                              type_intervention_id,
+  vhr.type_volume_horaire_id        type_volume_horaire_id,
+  evh.id                            etat_volume_horaire_id,
 
-      ORDER BY
-            horaire_fin, horaire_debut, volume_horaire_id, volume_horaire_ref_id;
+  0                                 taux_fi,
+  0                                 taux_fa,
+  0                                 taux_fc,
+  sr.structure_id                   structure_id,
+  1                                 ponderation_service_du,
+  1                                 ponderation_service_compl,
+  COALESCE(fr.service_statutaire,1) service_statutaire,
+
+  vhr.heures                        heures,
+  vhr.horaire_debut                 horaire_debut,
+  vhr.horaire_fin                   horaire_fin,
+  1                                 taux_service_du,
+  1                                 taux_service_compl
+FROM
+  volume_horaire_ref               vhr
+  JOIN service_referentiel          sr ON sr.id = vhr.service_referentiel_id
+  JOIN v_volume_horaire_ref_etat  vher ON vher.volume_horaire_ref_id = vhr.id
+  JOIN etat_volume_horaire         evh ON evh.id = vher.etat_volume_horaire_id
+  JOIN fonction_referentiel         fr ON fr.id = sr.fonction_id
+WHERE
+  vhr.histo_destruction IS NULL
+  AND sr.histo_destruction IS NULL
+  AND vhr.heures <> 0
+  AND sr.intervenant_id = COALESCE( OSE_FORMULE.GET_INTERVENANT_ID, sr.intervenant_id )
+
+ORDER BY
+  horaire_fin, horaire_debut, volume_horaire_id, volume_horaire_ref_id;
 
 -- V_FR_SERVICE_CENTRE_COUT
 CREATE OR REPLACE FORCE VIEW "V_FR_SERVICE_CENTRE_COUT" ("FORMULE_RESULTAT_SERVICE_ID", "CENTRE_COUT_ID") AS
@@ -6042,38 +6176,38 @@ WHERE
 -- V_INDICATEUR_560
 CREATE OR REPLACE FORCE VIEW "V_INDICATEUR_560" ("ID", "ANNEE_ID", "INTERVENANT_ID", "STRUCTURE_ID", "PLAFOND", "HEURES") AS
   SELECT
-             rownum                              id,
-             i.annee_id                          annee_id,
-             i.id                                intervenant_id,
-             i.structure_id                      structure_id,
-             si.maximum_hetd                     plafond,
-             fr.total                            heures
-      FROM
-           intervenant                     i
-                 JOIN etat_volume_horaire      evh ON evh.code = 'saisi'
-                 JOIN formule_resultat          fr ON fr.intervenant_id = i.id AND fr.etat_volume_horaire_id = evh.id
-                 JOIN statut_intervenant        si ON si.id = i.statut_id
-                 JOIN type_volume_horaire      tvh ON tvh.id = fr.type_volume_horaire_id AND tvh.code= 'PREVU'
-      WHERE
-          fr.total - fr.heures_compl_fc_majorees > si.maximum_hetd;
+  rownum                              id,
+  i.annee_id                          annee_id,
+  i.id                                intervenant_id,
+  i.structure_id                      structure_id,
+  si.maximum_hetd                     plafond,
+  fr.total                            heures
+FROM
+  intervenant                     i
+  JOIN etat_volume_horaire      evh ON evh.code = 'saisi'
+  JOIN formule_resultat          fr ON fr.intervenant_id = i.id AND fr.etat_volume_horaire_id = evh.id
+  JOIN statut_intervenant        si ON si.id = i.statut_id
+  JOIN type_volume_horaire      tvh ON tvh.id = fr.type_volume_horaire_id AND tvh.code= 'PREVU'
+WHERE
+  fr.total - fr.heures_compl_fc_majorees > si.maximum_hetd;
 
 -- V_INDICATEUR_570
 CREATE OR REPLACE FORCE VIEW "V_INDICATEUR_570" ("ID", "ANNEE_ID", "INTERVENANT_ID", "STRUCTURE_ID", "PLAFOND", "HEURES") AS
   SELECT
-             rownum                              id,
-             i.annee_id                          annee_id,
-             i.id                                intervenant_id,
-             i.structure_id                      structure_id,
-             si.maximum_hetd                     plafond,
-             fr.total                            heures
-      FROM
-           intervenant                     i
-                 JOIN etat_volume_horaire      evh ON evh.code = 'saisi'
-                 JOIN formule_resultat          fr ON fr.intervenant_id = i.id AND fr.etat_volume_horaire_id = evh.id
-                 JOIN statut_intervenant        si ON si.id = i.statut_id
-                 JOIN type_volume_horaire      tvh ON tvh.id = fr.type_volume_horaire_id AND tvh.code= 'REALISE'
-      WHERE
-          fr.total - fr.heures_compl_fc_majorees > si.maximum_hetd;
+  rownum                              id,
+  i.annee_id                          annee_id,
+  i.id                                intervenant_id,
+  i.structure_id                      structure_id,
+  si.maximum_hetd                     plafond,
+  fr.total                            heures
+FROM
+  intervenant                     i
+  JOIN etat_volume_horaire      evh ON evh.code = 'saisi'
+  JOIN formule_resultat          fr ON fr.intervenant_id = i.id AND fr.etat_volume_horaire_id = evh.id
+  JOIN statut_intervenant        si ON si.id = i.statut_id
+  JOIN type_volume_horaire      tvh ON tvh.id = fr.type_volume_horaire_id AND tvh.code= 'REALISE'
+WHERE
+  fr.total - fr.heures_compl_fc_majorees > si.maximum_hetd;
 
 -- V_INDICATEUR_610
 CREATE OR REPLACE FORCE VIEW "V_INDICATEUR_610" ("ID", "ANNEE_ID", "INTERVENANT_ID", "STRUCTURE_ID", "STATUT_INTERVENANT_ID") AS
@@ -8329,4190 +8463,4725 @@ WHERE
 -- Packages Bodies
 --------------------------------------------------
 
--- OSE_CHARGENS
-CREATE OR REPLACE PACKAGE BODY "OSE_CHARGENS" AS
-  SCENARIO NUMERIC;
-  NOEUD NUMERIC;
-  old_enable BOOLEAN DEFAULT TRUE;
+-- FORMULE_MONTPELLIER
+CREATE OR REPLACE PACKAGE BODY "FORMULE_MONTPELLIER" AS
+  decalageLigne NUMERIC DEFAULT 20;
 
-  TYPE T_PRECALC_HEURES_PARAMS IS RECORD (
-    annee_id                       NUMERIC DEFAULT NULL,
-    structure_id                   NUMERIC DEFAULT NULL,
-    scenario_id                    NUMERIC DEFAULT NULL,
-    type_heures_id                 NUMERIC DEFAULT NULL,
-    etape_id                       NUMERIC DEFAULT NULL,
-    noeud_ids                      tnoeud_ids DEFAULT NULL
+
+  /* Stockage des valeurs intermédiaires */
+  TYPE t_cell IS RECORD (
+    valeur FLOAT,
+    enCalcul BOOLEAN DEFAULT FALSE
+  );
+  TYPE t_cells IS TABLE OF t_cell INDEX BY PLS_INTEGER;
+  TYPE t_coll IS RECORD (
+    cells t_cells
   );
+  TYPE t_colls IS TABLE OF t_coll INDEX BY VARCHAR2(50);
+  feuille t_colls;
 
-  PRECALC_HEURES_PARAMS T_PRECALC_HEURES_PARAMS;
+  debugActif BOOLEAN DEFAULT TRUE;
+  debugLine NUMERIC;
 
 
-  FUNCTION GET_SCENARIO RETURN NUMERIC IS
+  PROCEDURE dbg( val CLOB ) IS
   BEGIN
-    RETURN OSE_CHARGENS.SCENARIO;
+    ose_formule.volumes_horaires.items(debugLine).debug_info :=
+      ose_formule.volumes_horaires.items(debugLine).debug_info || val;
   END;
 
-  PROCEDURE SET_SCENARIO( SCENARIO NUMERIC ) IS
+
+  PROCEDURE dbgi( val CLOB ) IS
   BEGIN
-    OSE_CHARGENS.SCENARIO := SET_SCENARIO.SCENARIO;
+    ose_formule.intervenant.debug_info := ose_formule.intervenant.debug_info || val;
   END;
 
-
-
-  FUNCTION GET_NOEUD RETURN NUMERIC IS
+  PROCEDURE dbgDump( val CLOB ) IS
   BEGIN
-    RETURN OSE_CHARGENS.NOEUD;
+    dbg('
+' || val || '
+');
   END;
 
-  PROCEDURE SET_NOEUD( NOEUD NUMERIC ) IS
+  PROCEDURE dbgCell( c VARCHAR2, l NUMERIC, val FLOAT ) IS
+    ligne NUMERIC;
   BEGIN
-    OSE_CHARGENS.NOEUD := SET_NOEUD.NOEUD;
+    ligne := l;
+    IF l <> 0 THEN
+      ligne := ligne + decalageLigne;
+    END IF;
+
+    dbgi( '[cell|' || c || '|' || ligne || '|' || val );
   END;
 
+  PROCEDURE dbgCalc( fncName VARCHAR2, c VARCHAR2, res FLOAT ) IS
+  BEGIN
+    dbgi( '[calc|' || fncName || '|' || c || '|' || res );
+  END;
 
+  FUNCTION cell( c VARCHAR2, l NUMERIC DEFAULT 0 ) RETURN FLOAT IS
+    val FLOAT;
+  BEGIN
+    IF feuille.exists(c) THEN
+      IF feuille(c).cells.exists(l) THEN
+        IF feuille(c).cells(l).enCalcul THEN
+          raise_application_error( -20001, 'Dépendance cyclique : la cellule [' || c || ';' || l || '] est déjà en cours de calcul');
+        END IF;
+        RETURN feuille(c).cells(l).valeur;
+      END IF;
+    END IF;
 
+    feuille(c).cells(l).enCalcul := true;
+    val := calcCell( c, l );
+    IF debugActif THEN
+      dbgCell( c, l, val );
+    END IF;
+    feuille(c).cells(l).valeur := val;
+    feuille(c).cells(l).enCalcul := false;
 
+    RETURN val;
+  END;
 
-  FUNCTION CALC_COEF( choix_min NUMERIC, choix_max NUMERIC, poids NUMERIC, max_poids NUMERIC, total_poids NUMERIC, nb_choix NUMERIC ) RETURN FLOAT IS
-    cmin NUMERIC;
-    cmax NUMERIC;
-    coef_choix FLOAT;
-    coef_poids FLOAT;
-    max_coef_poids FLOAT;
-    correcteur FLOAT DEFAULT 1;
-    res FLOAT;
+  FUNCTION mainCell( libelle VARCHAR2, c VARCHAR2, l NUMERIC ) RETURN FLOAT IS
+    val FLOAT;
   BEGIN
-    cmin := choix_min;
-    cmax := choix_max;
+    debugLine := l;
+    val := cell(c,l);
 
-    IF total_poids = 0 THEN RETURN 0; END IF;
+    RETURN val;
+  END;
 
-    IF cmax IS NULL OR cmax > nb_choix THEN
-      cmax := nb_choix;
-    END IF;
-    IF cmin IS NULL THEN
-      cmin := nb_choix;
-    ELSIF cmin > cmax THEN
-      cmin := cmax;
+  FUNCTION calcFnc( fncName VARCHAR2, c VARCHAR2 ) RETURN FLOAT IS
+    val FLOAT;
+    cellRes FLOAT;
+  BEGIN
+    IF feuille.exists('__' || fncName || '__' || c || '__') THEN
+      IF feuille('__' || fncName || '__' || c || '__').cells.exists(1) THEN
+        RETURN feuille('__' || fncName || '__' || c || '__').cells(1).valeur;
+      END IF;
     END IF;
+    CASE
+    -- Liste des fonctions supportées
 
-      coef_choix := (cmin + cmax) / 2 / nb_choix;
-
-      coef_poids := poids / total_poids;
+    WHEN fncName = 'total' THEN
+      val := 0;
+      FOR l IN 1 .. ose_formule.volumes_horaires.length LOOP
+        val := val + COALESCE(cell(c, l),0);
+      END LOOP;
 
-      max_coef_poids := max_poids / total_poids;
+    WHEN fncName = 'max' THEN
+      val := NULL;
+      FOR l IN 1 .. ose_formule.volumes_horaires.length LOOP
+        cellRes := cell(c,l);
+        IF val IS NULL OR val < cellRes THEN
+          val := cellRes;
+        END IF;
+      END LOOP;
 
-      IF (coef_choix * nb_choix * max_coef_poids) <= 1 THEN
-        res := coef_choix * nb_choix * coef_poids;
-      ELSE
-        correcteur := 1;
-        res := coef_choix * nb_choix * (coef_poids + (((1/nb_choix)-coef_poids)*correcteur));
-      END IF;
+    -- fin de la liste des fonctions supportées
+    ELSE
+      raise_application_error( -20001, 'La formule "' || fncName || '" n''existe pas!');
+    END CASE;
+    IF debugActif THEN
+      dbgCalc(fncName, c, val );
+    END IF;
+    feuille('__' || fncName || '__' || c || '__').cells(1).valeur := val;
 
-      --ose_test.echo('choix_min= ' || cmin || ', choix_max= ' || cmax || ', poids = ' || poids || ', max_poids = ' || max_poids || ', total_poids = ' || total_poids || ', nb_choix = ' || nb_choix || ', RES = ' || res);
-      RETURN res;
+    RETURN val;
   END;
 
 
-  PROCEDURE DEM_CALC_SUB_EFFECTIF( scenario_noeud_id NUMERIC, type_heures_id NUMERIC, etape_id NUMERIC, effectif FLOAT ) IS
+  FUNCTION calcVersion RETURN NUMERIC IS
   BEGIN
-    INSERT INTO TMP_scenario_noeud_effectif(
-      scenario_noeud_id, type_heures_id, etape_id, effectif
-    ) VALUES(
-      scenario_noeud_id, type_heures_id, etape_id, effectif
-    );
+    RETURN 1;
   END;
 
 
 
-  PROCEDURE CALC_SUB_EFFECTIF_DEM IS
+  FUNCTION calcCell( c VARCHAR2, l NUMERIC ) RETURN FLOAT IS
+    vh ose_formule.t_volume_horaire;
+    v NUMERIC;
+    val FLOAT;
   BEGIN
-    DELETE FROM TMP_scenario_noeud_effectif;
-  END;
+    v := calcVersion;
 
+    IF l > 0 THEN
+      vh := ose_formule.volumes_horaires.items(l);
+    END IF;
+    CASE
 
-  PROCEDURE CALC_ALL_EFFECTIFS IS
-  BEGIN
-    FOR p IN (
 
-      SELECT
-        sn.noeud_id,
-        sn.scenario_id,
-        sne.type_heures_id,
-        sne.etape_id
-      FROM
-        scenario_noeud_effectif sne
-        JOIN scenario_noeud sn ON sn.id = sne.scenario_noeud_id
-        JOIN noeud n ON n.id = sn.noeud_id
-      WHERE
-        n.etape_id IS NOT NULL
+    -- J = SI(ESTVIDE(C21);0;RECHERCHEH(SI(ET(C21="TP";TP_vaut_TD="Oui");"TD";C21);types_intervention;2;0))
+    WHEN c = 'j' AND v >= 1 THEN
+      RETURN GREATEST(vh.taux_service_du * vh.ponderation_service_du,1);
 
-    ) LOOP
 
-      CALC_SUB_EFFECTIF2( p.noeud_id, p.scenario_id, p.type_heures_id, p.etape_id );
-    END LOOP;
 
-  END;
+    -- K = SI(H21="Oui";I21*J21;0)
+    WHEN c = 'k' AND v >= 1 THEN
+      IF vh.service_statutaire THEN
+        RETURN vh.heures * cell('j',l);
+      ELSE
+        RETURN 0;
+      END IF;
 
 
 
-  PROCEDURE CALC_EFFECTIF(
-    noeud_id       NUMERIC,
-    scenario_id    NUMERIC,
-    type_heures_id NUMERIC DEFAULT NULL,
-    etape_id       NUMERIC DEFAULT NULL
-  ) IS
-    snid  NUMERIC;
-  BEGIN
-    UPDATE scenario_noeud_effectif SET effectif = 0
-    WHERE
-      scenario_noeud_id = (
-        SELECT id FROM scenario_noeud WHERE noeud_id = CALC_EFFECTIF.noeud_id AND scenario_id = CALC_EFFECTIF.scenario_id
-      )
-      AND (type_heures_id = CALC_EFFECTIF.type_heures_id OR CALC_EFFECTIF.type_heures_id IS NULL)
-      AND (etape_id = CALC_EFFECTIF.etape_id OR CALC_EFFECTIF.etape_id IS NULL)
-    ;
+    -- l = SI(L20+K21>service_du;service_du;L20+K21)
+    WHEN c = 'l' AND v >= 1 THEN
+      IF l < 1 THEN
+        RETURN 0;
+      END IF;
+      IF cell('l', l-1) + cell('k',l) > ose_formule.intervenant.service_du THEN
+        RETURN ose_formule.intervenant.service_du;
+      ELSE
+        RETURN cell('l', l-1) + cell('k',l);
+      END IF;
 
-    FOR p IN (
 
-      SELECT
-        *
-      FROM
-        v_chargens_calc_effectif cce
-      WHERE
-        cce.noeud_id = CALC_EFFECTIF.noeud_id
-        AND cce.scenario_id = CALC_EFFECTIF.scenario_id
-        AND (cce.type_heures_id = CALC_EFFECTIF.type_heures_id OR CALC_EFFECTIF.type_heures_id IS NULL)
-        AND (cce.etape_id = CALC_EFFECTIF.etape_id OR CALC_EFFECTIF.etape_id IS NULL)
 
-    ) LOOP
-      snid := OSE_CHARGENS.GET_SCENARIO_NOEUD_ID( p.scenario_id, p.noeud_id );
-      IF snid IS NULL THEN
-        snid := OSE_CHARGENS.CREER_SCENARIO_NOEUD( p.scenario_id, p.noeud_id );
+    -- m = SI(J21>0;SI(L20+K21= 1 THEN
+      IF cell('j',l) > 0 THEN
+        IF cell('l',l-1) + cell('k',l) < ose_formule.intervenant.service_du THEN
+          RETURN 0;
+        ELSE
+          RETURN (cell('l',l-1) + cell('k',l) - ose_formule.intervenant.service_du) / cell('j',l);
+        END IF;
+      ELSE
+        RETURN 0;
       END IF;
-      ADD_SCENARIO_NOEUD_EFFECTIF( snid, p.type_heures_id, p.etape_id, p.effectif );
-    END LOOP;
-    CALC_SUB_EFFECTIF2( noeud_id, scenario_id, type_heures_id, etape_id );
-  END;
 
 
 
-  PROCEDURE CALC_SUB_EFFECTIF2( noeud_id NUMERIC, scenario_id NUMERIC, type_heures_id NUMERIC DEFAULT NULL, etape_id NUMERIC DEFAULT NULL) IS
-  BEGIN
-    FOR p IN (
+    -- n = SI(ESTVIDE(C21);0;RECHERCHEH(C21;types_intervention;3;0))
+    WHEN c = 'n' AND v >= 1 THEN
+      RETURN vh.taux_service_compl * vh.ponderation_service_compl;
 
-      SELECT *
-      FROM   V_CHARGENS_GRANDS_LIENS cgl
-      WHERE  cgl.noeud_sup_id = CALC_SUB_EFFECTIF2.noeud_id
 
-    ) LOOP
-      CALC_EFFECTIF( p.noeud_inf_id, scenario_id, type_heures_id, etape_id );
-    END LOOP;
-  END;
 
+    -- o = SI(OU(service_realise"Oui");0;(M21+SI(H21<>"Oui";I21;0))*N21)
+    -- service_realise = MAX($L$21:$L$50)
+    -- service_du = ose_formule.intervenant.service_du
+    -- HC_autorisees = ose_formule.intervenant.depassement_service_du_sans_hc = false
+    WHEN c = 'o' AND v >= 1 THEN
+      IF (calcFnc('max','l') < ose_formule.intervenant.service_du) OR ose_formule.intervenant.depassement_service_du_sans_hc THEN
+        RETURN 0;
+      ELSE
+        IF vh.service_statutaire THEN
+          RETURN cell('m',l) * cell('n',l);
+        ELSE
+          RETURN (cell('m',l) + vh.heures) * cell('n',l);
+        END IF;
+      END IF;
 
 
-  PROCEDURE DUPLIQUER( source_id NUMERIC, destination_id NUMERIC, utilisateur_id NUMERIC, structure_id NUMERIC, noeuds VARCHAR2 DEFAULT '', liens VARCHAR2 DEFAULT '' ) IS
-  BEGIN
 
-    /* Destruction de tous les liens antérieurs de la destination */
-    DELETE FROM
-      scenario_lien
-    WHERE
-      scenario_id = DUPLIQUER.destination_id
-      AND histo_destruction IS NULL
-      AND (DUPLIQUER.LIENS IS NULL OR DUPLIQUER.LIENS LIKE '%,' || lien_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR lien_id IN (
-        SELECT id FROM lien WHERE lien.structure_id = DUPLIQUER.STRUCTURE_ID
-      ))
-    ;
+    -- q =SI(ESTVIDE(C21);0;SI(C21="TP";1;RECHERCHEH(C21;types_intervention;2;0)))
+    -- Nouvelle interprêtation de la formule : on n'a pas 'TP' donc tout ce qui est <1 devient 1
+    WHEN c = 'q' AND v >= 1 THEN
+      RETURN GREATEST( vh.taux_service_compl, 1);
 
-    /* Duplication des liens */
-    INSERT INTO scenario_lien (
-      id,
-      scenario_id, lien_id,
-      actif, poids,
-      choix_minimum, choix_maximum,
-      source_id, source_code,
-      histo_creation, histo_createur_id,
-      histo_modification, histo_modificateur_id
-    ) SELECT
-      scenario_lien_id_seq.nextval,
-      DUPLIQUER.destination_id, sl.lien_id,
-      sl.actif, sl.poids,
-      sl.choix_minimum, sl.choix_maximum,
-      source.id, 'dupli_' || sl.id || '_' || sl.lien_id || '_' || trunc(dbms_random.value(1,10000000000000)),
-      sysdate, DUPLIQUER.utilisateur_id,
-      sysdate, DUPLIQUER.utilisateur_id
-    FROM
-      scenario_lien sl
-      JOIN lien l ON l.id = sl.lien_id
-      JOIN source ON source.code = 'OSE'
-    WHERE
-      sl.scenario_id = DUPLIQUER.source_id
-      AND sl.histo_destruction IS NULL
-      AND (DUPLIQUER.LIENS IS NULL OR DUPLIQUER.LIENS LIKE '%,' || lien_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR l.structure_id = DUPLIQUER.STRUCTURE_ID)
-    ;
 
 
-    /* Destruction de tous les noeuds antérieurs de la destination */
-    DELETE FROM
-      scenario_noeud
-    WHERE
-      scenario_id = DUPLIQUER.destination_id
-      AND histo_destruction IS NULL
-      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || noeud_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR scenario_noeud.noeud_id IN (
-        SELECT id FROM noeud WHERE noeud.structure_id = DUPLIQUER.STRUCTURE_ID
-      ))
-    ;
+    -- r =I21*Q21
+    WHEN c = 'r' AND v >= 1 THEN
+      RETURN vh.heures * cell('q',l);
 
-    /* Duplication des noeuds */
-    INSERT INTO scenario_noeud (
-      id,
-      scenario_id, noeud_id,
-      assiduite,
-      source_id, source_code,
-      histo_creation, histo_createur_id,
-      histo_modification, histo_modificateur_id
-    ) SELECT
-      scenario_noeud_id_seq.nextval,
-      DUPLIQUER.destination_id, sn.noeud_id,
-      sn.assiduite,
-      source.id, 'dupli_' || sn.id || '_' || sn.noeud_id || '_' || trunc(dbms_random.value(1,10000000000000)),
-      sysdate, DUPLIQUER.utilisateur_id,
-      sysdate, DUPLIQUER.utilisateur_id
-    FROM
-      scenario_noeud sn
-      JOIN noeud n ON n.id = sn.noeud_id
-      JOIN source ON source.code = 'OSE'
-    WHERE
-      sn.scenario_id = DUPLIQUER.source_id
-      AND sn.histo_destruction IS NULL
-      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || noeud_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
-    ;
 
-    /* Duplication des effectifs */
-    INSERT INTO scenario_noeud_effectif (
-      id,
-      scenario_noeud_id,
-      type_heures_id,
-      effectif,
-      etape_id
-    ) SELECT
-      scenario_noeud_effectif_id_seq.nextval,
-      sn_dst.id,
-      sne.type_heures_id,
-      sne.effectif,
-      sne.etape_id
-    FROM
-      scenario_noeud_effectif sne
-      JOIN scenario_noeud sn_src ON sn_src.id = sne.scenario_noeud_id
-      JOIN scenario_noeud sn_dst ON sn_dst.scenario_id = DUPLIQUER.destination_id AND sn_dst.noeud_id = sn_src.noeud_id
-      JOIN noeud n ON n.id = sn_src.noeud_id
-    WHERE
-      sn_src.scenario_id = DUPLIQUER.source_id
-      AND sn_src.histo_destruction IS NULL
-      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || sn_src.noeud_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
-    ;
 
-    /* Duplication des seuils */
-    INSERT INTO scenario_noeud_seuil (
-      id,
-      scenario_noeud_id,
-      type_intervention_id,
-      ouverture,
-      dedoublement
-    ) SELECT
-      scenario_noeud_seuil_id_seq.nextval,
-      sn_dst.id,
-      sns.type_intervention_id,
-      sns.ouverture,
-      sns.dedoublement
-    FROM
-      scenario_noeud_seuil sns
-      JOIN scenario_noeud sn_src ON sn_src.id = sns.scenario_noeud_id
-      JOIN scenario_noeud sn_dst ON sn_dst.scenario_id = DUPLIQUER.destination_id AND sn_dst.noeud_id = sn_src.noeud_id
-      JOIN noeud n ON n.id = sn_src.noeud_id
-    WHERE
-      sn_src.scenario_id = DUPLIQUER.source_id
-      AND sn_src.histo_destruction IS NULL
-      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || sn_src.noeud_id || ',%' )
-      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
-    ;
-  END;
+    -- r53 =SOMME.SI(B$21:B$50;composante_affectation;R$21:R$50)
+    WHEN c = 'r53' AND v >= 1 THEN
+      val := 0;
+      FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
+        IF ose_formule.volumes_horaires.items(i).structure_is_affectation THEN
+          val := val + cell('r',i);
+        END IF;
+      END LOOP;
+      RETURN val;
 
 
 
-  PROCEDURE CONTROLE_SEUIL( ouverture NUMERIC, dedoublement NUMERIC ) IS
-  BEGIN
-    IF ouverture IS NOT NULL THEN
-      IF ouverture < 1 THEN
-        raise_application_error(-20101, 'Le seuil d''ouverture doit être supérieur ou égal à 1');
+    -- r54 =SOMME.SI(B$21:B$50;"<>"&composante_affectation;R$21:R$50)
+    WHEN c = 'r54' AND v >= 1 THEN
+      val := 0;
+      FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
+        IF NOT ose_formule.volumes_horaires.items(i).structure_is_affectation THEN
+          val := val + cell('r',i);
+        END IF;
+      END LOOP;
+      RETURN val;
+
+
+
+    -- s =SI(B21=composante_affectation;SI($R$53=0;0;R21*$S$53/$R$53);SI($R$54=0;0;R21*$S$54/$R$54))
+    WHEN c = 's' AND v >= 1 THEN
+      IF vh.structure_is_affectation THEN
+        IF cell('r53') = 0 THEN
+          RETURN 0;
+        ELSE
+          RETURN cell('r',l) * cell('s53')/cell('r53');
+        END IF;
+      ELSE
+        IF cell('r54') = 0 THEN
+          RETURN 0;
+        ELSE
+          RETURN cell('r',l) * cell('s54')/cell('r54');
+        END IF;
       END IF;
-    END IF;
 
-    IF dedoublement IS NOT NULL THEN
-      IF dedoublement < 1 THEN
-        raise_application_error(-20101, 'Le seuil de dédoublement doit être supérieur ou égal à 1');
+
+
+    -- s53 =SI(OU(HC=0;R53= 1 THEN
+      IF calcFnc('total','o') = 0 OR cell('r53') < ose_formule.intervenant.service_du THEN
+        RETURN 0;
+      ELSE
+        RETURN cell('r53') - ose_formule.intervenant.service_du;
       END IF;
-    END IF;
 
-    IF ouverture IS NOT NULL AND dedoublement IS NOT NULL THEN
-      IF dedoublement < ouverture THEN
-        raise_application_error(-20101, 'Le seuil de dédoublement doit être supérieur ou égal au seuil d''ouverture');
+
+
+    -- s54 =SI(HC=0;0;R54*(HC-S53)/R54)
+    WHEN c = 's54' AND v >= 1 THEN
+      IF calcFnc('total','o') = 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN cell('r54')*(calcFnc('total','o')-cell('s53'))/cell('r54');
       END IF;
-    END IF;
-  END;
 
 
-  FUNCTION CREER_SCENARIO_NOEUD( scenario_id NUMERIC, noeud_id NUMERIC, assiduite FLOAT DEFAULT 1 ) RETURN NUMERIC IS
-    new_id NUMERIC;
-  BEGIN
-    new_id := SCENARIO_NOEUD_ID_SEQ.NEXTVAL;
---ose_test.echo(scenario_id || '-' || noeud_id);
-    INSERT INTO SCENARIO_NOEUD(
-      ID,
-      SCENARIO_ID,
-      NOEUD_ID,
-      ASSIDUITE,
-      SOURCE_ID,
-      SOURCE_CODE,
-      HEURES,
-      HISTO_CREATION,
-      HISTO_CREATEUR_ID,
-      HISTO_MODIFICATION,
-      HISTO_MODIFICATEUR_ID
-    ) VALUES (
-      new_id,
-      CREER_SCENARIO_NOEUD.scenario_id,
-      CREER_SCENARIO_NOEUD.noeud_id,
-      CREER_SCENARIO_NOEUD.assiduite,
-      OSE_DIVERS.GET_OSE_SOURCE_ID,
-      'OSE_NEW_SN_' || new_id,
-      null,
-      SYSDATE,
-      OSE_DIVERS.GET_OSE_UTILISATEUR_ID,
-      SYSDATE,
-      OSE_DIVERS.GET_OSE_UTILISATEUR_ID
-    );
-    RETURN new_id;
-  END;
 
+    -- u =SI(OU(ESTVIDE($C21);$C21="Référentiel");0;($K21-$M21)*D21)
+    WHEN c = 'u' AND v >= 1 THEN
+      IF vh.volume_horaire_ref_id IS NOT NULL THEN
+        RETURN 0;
+      ELSE
+        RETURN (cell('k',l)-cell('m',l))*vh.taux_fi;
+      END IF;
 
-  FUNCTION GET_SCENARIO_NOEUD_ID(scenario_id NUMERIC, noeud_id NUMERIC) RETURN NUMERIC IS
-    res NUMERIC;
-  BEGIN
-    SELECT
-      sn.id INTO res
-    FROM
-      scenario_noeud sn
-    WHERE
-      sn.noeud_id = GET_SCENARIO_NOEUD_ID.noeud_id
-      AND sn.scenario_id = GET_SCENARIO_NOEUD_ID.scenario_id
-      AND sn.histo_destruction IS NULL;
 
-    RETURN res;
 
-  EXCEPTION WHEN NO_DATA_FOUND THEN
-    RETURN NULL;
-  END;
+    -- v =SI(OU(ESTVIDE($C21);$C21="Référentiel");0;($K21-$M21)*E21)
+    WHEN c = 'v' AND v >= 1 THEN
+      IF vh.volume_horaire_ref_id IS NOT NULL THEN
+        RETURN 0;
+      ELSE
+        RETURN (cell('k',l)-cell('m',l))*vh.taux_fa;
+      END IF;
 
 
-  PROCEDURE ADD_SCENARIO_NOEUD_EFFECTIF( scenario_noeud_id NUMERIC, type_heures_id NUMERIC, etape_id NUMERIC, effectif FLOAT ) IS
-    old_enable BOOLEAN;
-  BEGIN
-    old_enable := ose_chargens.ENABLE_TRIGGER_EFFECTIFS;
-    ose_chargens.ENABLE_TRIGGER_EFFECTIFS := false;
 
-    MERGE INTO scenario_noeud_effectif sne USING dual ON (
 
-          sne.scenario_noeud_id = ADD_SCENARIO_NOEUD_EFFECTIF.scenario_noeud_id
-      AND sne.type_heures_id = ADD_SCENARIO_NOEUD_EFFECTIF.type_heures_id
-      AND sne.etape_id = ADD_SCENARIO_NOEUD_EFFECTIF.etape_id
+    -- w =SI(OU(ESTVIDE($C21);$C21="Référentiel");0;($K21-$M21)*F21)
+    WHEN c = 'w' AND v >= 1 THEN
+      IF vh.volume_horaire_ref_id IS NOT NULL THEN
+        RETURN 0;
+      ELSE
+        RETURN (cell('k',l)-cell('m',l))*vh.taux_fc;
+      END IF;
 
-    ) WHEN MATCHED THEN UPDATE SET
 
-      effectif = effectif + ADD_SCENARIO_NOEUD_EFFECTIF.effectif
 
-    WHEN NOT MATCHED THEN INSERT (
+    -- x =SI($C21="Référentiel";$K21-$M21;0)
+    WHEN c = 'x' AND v >= 1 THEN
+      IF vh.volume_horaire_ref_id IS NOT NULL THEN
+        RETURN cell('k',l) - cell('m',l);
+      ELSE
+        RETURN 0;
+      END IF;
 
-      ID,
-      SCENARIO_NOEUD_ID,
-      TYPE_HEURES_ID,
-      ETAPE_ID,
-      EFFECTIF
 
-    ) VALUES (
 
-      SCENARIO_NOEUD_EFFECTIF_ID_SEQ.NEXTVAL,
-      ADD_SCENARIO_NOEUD_EFFECTIF.scenario_noeud_id,
-      ADD_SCENARIO_NOEUD_EFFECTIF.type_heures_id,
-      ADD_SCENARIO_NOEUD_EFFECTIF.etape_id,
-      ADD_SCENARIO_NOEUD_EFFECTIF.effectif
+    -- y =SI($C21="Référentiel";0;$S21)
+    WHEN c = 'y' AND v >= 1 THEN
+      IF vh.volume_horaire_id IS NOT NULL THEN
+        RETURN cell('s',l);
+      ELSE
+        RETURN 0;
+      END IF;
 
-    );
 
-    DELETE FROM scenario_noeud_effectif WHERE effectif = 0;
 
-    ose_chargens.ENABLE_TRIGGER_EFFECTIFS := old_enable;
-  END;
+    -- z =0
+    WHEN c = 'z' AND v >= 1 THEN
+      RETURN 0;
 
 
 
-  PROCEDURE INIT_SCENARIO_NOEUD_EFFECTIF(
-    etape_id NUMERIC,
-    scenario_id NUMERIC,
-    type_heures_id NUMERIC,
-    effectif FLOAT,
-    surcharge BOOLEAN DEFAULT FALSE
-  ) IS
-    noeud_id NUMERIC;
-    scenario_noeud_id NUMERIC;
-    scenario_noeud_effectif_id NUMERIC;
-  BEGIN
-    SELECT
-      n.id, sn.id, sne.id
-    INTO
-      noeud_id, scenario_noeud_id, scenario_noeud_effectif_id
-    FROM
-                noeud                     n
-      LEFT JOIN scenario_noeud           sn ON sn.noeud_id = n.id
-                                           AND sn.histo_destruction IS NULL
-                                           AND sn.scenario_id = INIT_SCENARIO_NOEUD_EFFECTIF.scenario_id
+    -- aa =0
+    WHEN c = 'aa' AND v >= 1 THEN
+      RETURN 0;
 
-      LEFT JOIN scenario_noeud_effectif sne ON sne.scenario_noeud_id = sn.id
-                                           AND sne.type_heures_id = INIT_SCENARIO_NOEUD_EFFECTIF.type_heures_id
-    WHERE
-      n.etape_id = INIT_SCENARIO_NOEUD_EFFECTIF.etape_id
-      AND n.histo_destruction IS NULL
-    ;
 
-    IF noeud_id IS NULL THEN RETURN; END IF;
 
-    IF scenario_noeud_id IS NULL THEN
-      scenario_noeud_id := CREER_SCENARIO_NOEUD( scenario_id, noeud_id );
-    END IF;
+    -- ab =0
+    WHEN c = 'ab' AND v >= 1 THEN
+      RETURN 0;
 
-    IF scenario_noeud_effectif_id IS NULL THEN
-      scenario_noeud_effectif_id := SCENARIO_NOEUD_EFFECTIF_ID_SEQ.NEXTVAL;
-      INSERT INTO scenario_noeud_effectif (
-        id,
-        scenario_noeud_id,
-        type_heures_id,
-        effectif,
-        etape_id
-      ) VALUES (
-        scenario_noeud_effectif_id,
-        scenario_noeud_id,
-        INIT_SCENARIO_NOEUD_EFFECTIF.type_heures_id,
-        INIT_SCENARIO_NOEUD_EFFECTIF.effectif,
-        INIT_SCENARIO_NOEUD_EFFECTIF.etape_id
-      );
-    ELSIF surcharge THEN
-      UPDATE scenario_noeud_effectif SET effectif = INIT_SCENARIO_NOEUD_EFFECTIF.effectif WHERE id = scenario_noeud_effectif_id;
-    END IF;
 
-    CALC_SUB_EFFECTIF2( noeud_id, scenario_id, type_heures_id, etape_id );
 
-  EXCEPTION WHEN NO_DATA_FOUND THEN
-    RETURN;
-  END;
+    -- ac =SI($C21="Référentiel";$S21;0)
+    WHEN c = 'ac' AND v >= 1 THEN
+      IF vh.volume_horaire_ref_id IS NOT NULL THEN
+        RETURN cell('s',l);
+      ELSE
+        RETURN 0;
+      END IF;
 
 
 
-  PROCEDURE SET_PRECALC_HEURES_PARAMS(
-    annee_id                       NUMERIC DEFAULT NULL,
-    structure_id                   NUMERIC DEFAULT NULL,
-    scenario_id                    NUMERIC DEFAULT NULL,
-    type_heures_id                 NUMERIC DEFAULT NULL,
-    etape_id                       NUMERIC DEFAULT NULL,
-    noeud_ids                      tnoeud_ids DEFAULT NULL
-  ) IS
-  BEGIN
-    PRECALC_HEURES_PARAMS.ANNEE_ID       := ANNEE_ID;
-    PRECALC_HEURES_PARAMS.STRUCTURE_ID   := STRUCTURE_ID;
-    PRECALC_HEURES_PARAMS.SCENARIO_ID    := SCENARIO_ID;
-    PRECALC_HEURES_PARAMS.TYPE_HEURES_ID := TYPE_HEURES_ID;
-    PRECALC_HEURES_PARAMS.ETAPE_ID       := ETAPE_ID;
-    PRECALC_HEURES_PARAMS.NOEUD_IDS      := noeud_ids;
-  END;
 
 
+    ELSE
+      raise_application_error( -20001, 'La colonne c=' || c || ', l=' || l || ' n''existe pas!');
+  END CASE; END;
 
-  FUNCTION MATCH_PRECALC_HEURES_PARAMS(
-    annee_id                       NUMERIC DEFAULT NULL,
-    structure_id                   NUMERIC DEFAULT NULL,
-    scenario_id                    NUMERIC DEFAULT NULL,
-    type_heures_id                 NUMERIC DEFAULT NULL,
-    etape_id                       NUMERIC DEFAULT NULL,
-    noeud_id                       NUMERIC DEFAULT NULL
-  ) RETURN NUMERIC IS
-  BEGIN
 
-    IF PRECALC_HEURES_PARAMS.noeud_ids IS NOT NULL THEN
-      IF NOT (noeud_id MEMBER OF PRECALC_HEURES_PARAMS.noeud_ids) THEN
-        RETURN 0;
-      END IF;
-    END IF;
 
-    IF annee_id <> COALESCE(PRECALC_HEURES_PARAMS.annee_id, annee_id) THEN
-      RETURN 0;
-    END IF;
+  PROCEDURE CALCUL_RESULTAT IS
+  BEGIN
+    feuille.delete;
+
+    -- transmission des résultats aux volumes horaires et volumes horaires référentiel
+    FOR l IN 1 .. ose_formule.volumes_horaires.length LOOP
+      ose_formule.volumes_horaires.items(l).service_fi               := mainCell('Service FI', 'u',l);
+      ose_formule.volumes_horaires.items(l).service_fa               := mainCell('Service FA', 'v',l);
+      ose_formule.volumes_horaires.items(l).service_fc               := mainCell('Service FC', 'w',l);
+      ose_formule.volumes_horaires.items(l).service_referentiel      := mainCell('Service référentiel', 'x',l);
+      ose_formule.volumes_horaires.items(l).heures_compl_fi          := mainCell('Heures compl. FI', 'y',l);
+      ose_formule.volumes_horaires.items(l).heures_compl_fa          := mainCell('Heures compl. FA', 'z',l);
+      ose_formule.volumes_horaires.items(l).heures_compl_fc          := mainCell('Heures compl. FC', 'aa',l);
+      ose_formule.volumes_horaires.items(l).heures_compl_fc_majorees := mainCell('Heures compl. FC Maj.', 'ab',l);
+      ose_formule.volumes_horaires.items(l).heures_compl_referentiel := mainCell('Heures compl. référentiel', 'ac',l);
+    END LOOP;
+  END;
 
-    IF structure_id <> COALESCE(PRECALC_HEURES_PARAMS.structure_id, structure_id) THEN
-      RETURN 0;
-    END IF;
+END FORMULE_MONTPELLIER;
+/
 
-    IF scenario_id <> COALESCE(PRECALC_HEURES_PARAMS.scenario_id, scenario_id) THEN
-      RETURN 0;
-    END IF;
+-- FORMULE_UNICAEN
+CREATE OR REPLACE PACKAGE BODY "FORMULE_UNICAEN" AS
 
-    IF type_heures_id <> COALESCE(PRECALC_HEURES_PARAMS.type_heures_id, type_heures_id) THEN
-      RETURN 0;
-    END IF;
+  /* Stockage des valeurs intermédiaires */
+  TYPE t_valeurs IS TABLE OF FLOAT INDEX BY PLS_INTEGER;
+  TYPE t_tableau IS RECORD (
+    valeurs t_valeurs,
+    total   FLOAT DEFAULT 0
+  );
+  TYPE t_tableaux       IS TABLE OF t_tableau INDEX BY PLS_INTEGER;
+  TYPE t_tableau_config IS RECORD (
+    tableau NUMERIC,
+    version NUMERIC,
+    referentiel BOOLEAN DEFAULT FALSE,
+    setTotal BOOLEAN DEFAULT FALSE
+  );
+  TYPE t_tableaux_configs IS VARRAY(100) OF t_tableau_config;
 
-    IF etape_id <> COALESCE(PRECALC_HEURES_PARAMS.etape_id, etape_id) THEN
-      RETURN 0;
-    END IF;
+  t                     t_tableaux;
+  vh_index              NUMERIC;
 
-    RETURN 1;
-  END;
 
 
-  FUNCTION GET_PRECALC_HEURES_ANNEE RETURN NUMERIC IS
+  -- Crée une définition de tableau
+  FUNCTION TC( tableau NUMERIC, version NUMERIC, options VARCHAR2 DEFAULT NULL) RETURN t_tableau_config IS
+    tcRes t_tableau_config;
   BEGIN
-    RETURN PRECALC_HEURES_PARAMS.ANNEE_ID;
-  END;
-
+    tcRes.tableau := tableau;
+    tcRes.version := version;
+    CASE
+      WHEN options like '%t%' THEN tcRes.setTotal := TRUE;
+      WHEN options like '%r%' THEN tcRes.referentiel := TRUE;
+      ELSE RETURN tcRes;
+    END CASE;
 
+    RETURN tcRes;
+  END;
 
-  FUNCTION GET_PRECALC_HEURES_STRUCTURE RETURN NUMERIC IS
+  -- Setter d'une valeur intermédiaire au niveau case
+  PROCEDURE SV( tableau NUMERIC, valeur FLOAT ) IS
   BEGIN
-    RETURN PRECALC_HEURES_PARAMS.STRUCTURE_ID;
+    t(tableau).valeurs(vh_index) := valeur;
+    t(tableau).total             := t(tableau).total + valeur;
   END;
 
+  -- Setter d'une valeur intermédiaire au niveau tableau
+  PROCEDURE ST( tableau NUMERIC, valeur FLOAT ) IS
+  BEGIN
+    t(tableau).total      := valeur;
+  END;
 
+  -- Getter d'une valeur intermédiaire, au niveau case
+  FUNCTION GV( tableau NUMERIC ) RETURN FLOAT IS
+  BEGIN
+    IF NOT t.exists(tableau) THEN RETURN 0; END IF;
+    IF NOT t(tableau).valeurs.exists( vh_index ) THEN RETURN 0; END IF;
+    RETURN t(tableau).valeurs( vh_index );
+  END;
 
-  FUNCTION GET_PRECALC_HEURES_SCENARIO RETURN NUMERIC IS
+  -- Getter d'une valeur intermédiaire, au niveau tableau
+  FUNCTION GT( tableau NUMERIC ) RETURN FLOAT IS
   BEGIN
-    RETURN PRECALC_HEURES_PARAMS.SCENARIO_ID;
+    IF NOT t.exists(tableau) THEN RETURN 0; END IF;
+    RETURN t(tableau).total;
   END;
 
 
 
-  FUNCTION GET_PRECALC_HEURES_TYPE_HEURES RETURN NUMERIC IS
+
+  PROCEDURE DEBUG_VH IS
+    tableau NUMERIC;
+    vh ose_formule.t_volume_horaire;
   BEGIN
-    RETURN PRECALC_HEURES_PARAMS.TYPE_HEURES_ID;
+    IF NOT debug_enabled THEN RETURN; END IF;
+    IF ose_formule.intervenant.etat_volume_horaire_id <> debug_etat_volume_horaire_id THEN RETURN; END IF;
+
+    FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
+      vh_index := i;
+      vh := ose_formule.volumes_horaires.items(i);
+      IF vh.volume_horaire_id = debug_volume_horaire_id OR vh.volume_horaire_ref_id = debug_volume_horaire_ref_id THEN
+        ose_formule.DEBUG_INTERVENANT;
+        ose_test.echo('');
+        ose_test.echo('-- DEBUG DE VOLUME HORAIRE --');
+        ose_test.echo('volume_horaire_id         = ' || vh.volume_horaire_id);
+        ose_test.echo('volume_horaire_ref_id     = ' || vh.volume_horaire_ref_id);
+        ose_test.echo('service_id                = ' || vh.service_id);
+        ose_test.echo('service_referentiel_id    = ' || vh.service_referentiel_id);
+        ose_test.echo('taux_fi                   = ' || vh.taux_fi);
+        ose_test.echo('taux_fa                   = ' || vh.taux_fa);
+        ose_test.echo('taux_fc                   = ' || vh.taux_fc);
+        ose_test.echo('ponderation_service_du    = ' || vh.ponderation_service_du);
+        ose_test.echo('ponderation_service_compl = ' || vh.ponderation_service_compl);
+        ose_test.echo('structure_id              = ' || vh.structure_id);
+        ose_test.echo('structure_is_affectation  = ' || CASE WHEN vh.structure_is_affectation THEN 'OUI' ELSE 'NON' END);
+        ose_test.echo('structure_is_univ         = ' || CASE WHEN vh.structure_is_univ THEN 'OUI' ELSE 'NON' END);
+        ose_test.echo('service_statutaire        = ' || CASE WHEN vh.service_statutaire THEN 'OUI' ELSE 'NON' END);
+        ose_test.echo('heures                    = ' || vh.heures);
+        ose_test.echo('taux_service_du           = ' || vh.taux_service_du);
+        ose_test.echo('taux_service_compl        = ' || vh.taux_service_compl);
+
+        tableau := t.FIRST;
+        LOOP EXIT WHEN tableau IS NULL;
+          IF gv(tableau) <> 0 OR gt(tableau) <> 0 THEN
+            ose_test.echo('     t(' || LPAD(tableau,3,' ') || ') v=' || RPAD(round(gv(tableau),3),10,' ') || 't=' || round(gt(tableau),3));
+          END IF;
+          tableau := t.NEXT(tableau);
+        END LOOP;
+
+        ose_test.echo('service_fi                = ' || vh.service_fi);
+        ose_test.echo('service_fa                = ' || vh.service_fa);
+        ose_test.echo('service_fc                = ' || vh.service_fc);
+        ose_test.echo('service_referentiel       = ' || vh.service_referentiel);
+        ose_test.echo('heures_compl_fi           = ' || vh.heures_compl_fi);
+        ose_test.echo('heures_compl_fa           = ' || vh.heures_compl_fa);
+        ose_test.echo('heures_compl_fc           = ' || vh.heures_compl_fc);
+        ose_test.echo('heures_compl_fc_majorees  = ' || vh.heures_compl_fc_majorees);
+        ose_test.echo('heures_compl_referentiel  = ' || vh.heures_compl_referentiel);
+        ose_test.echo('-- FIN DE DEBUG DE VOLUME HORAIRE --');
+        ose_test.echo('');
+      END IF;
+    END LOOP;
   END;
 
 
 
-  FUNCTION GET_PRECALC_HEURES_ETAPE RETURN NUMERIC IS
+  -- Formule de calcul définie par tableaux
+  FUNCTION EXECFORMULE( tableau NUMERIC, version NUMERIC ) RETURN FLOAT IS
+    vh ose_formule.t_volume_horaire;
   BEGIN
-    RETURN PRECALC_HEURES_PARAMS.ETAPE_ID;
-  END;
+    vh := ose_formule.volumes_horaires.items(vh_index);
+    CASE
 
---  FUNCTION GET_PRECALC_HEURES_NOEUD RETURN NUMERIC IS
---  BEGIN
 
---  END;
+    WHEN tableau = 11 AND version = 2 THEN
+      IF vh.structure_is_affectation AND vh.taux_fc < 1 THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-END OSE_CHARGENS;
-/
 
--- OSE_DIVERS
-CREATE OR REPLACE PACKAGE BODY "OSE_DIVERS" AS
-      OSE_UTILISATEUR_ID NUMERIC;
-      OSE_SOURCE_ID NUMERIC;
-
-
-
-
-      PROCEDURE CALCULER_TABLEAUX_BORD IS
-            BEGIN
-                  FOR d IN (
-                  SELECT tbl_name
-                  FROM tbl
-                  WHERE tbl_name <> 'formule' -- TROP LONG !!
-                  ORDER BY ordre
-                  )
-                  LOOP
-                        UNICAEN_TBL.CALCULER(d.tbl_name);
-                        dbms_output.put_line('Calcul du tableau de bord "' || d.tbl_name || '" effectué');
-                        COMMIT;
-                  END LOOP;
-            END;
-
-
-
-      FUNCTION GET_OSE_UTILISATEUR_ID RETURN NUMERIC IS
-            BEGIN
-                  IF OSE_DIVERS.OSE_UTILISATEUR_ID IS NULL THEN
-                        SELECT
-                               to_number(valeur) INTO OSE_DIVERS.OSE_UTILISATEUR_ID
-                        FROM
-                             parametre
-                        WHERE
-                            nom = 'oseuser';
-                  END IF;
-
-                  RETURN OSE_DIVERS.OSE_UTILISATEUR_ID;
-            END;
-
-
-
-      FUNCTION GET_OSE_SOURCE_ID RETURN NUMERIC IS
-            BEGIN
-                  IF OSE_DIVERS.OSE_SOURCE_ID IS NULL THEN
-                        SELECT
-                               id INTO OSE_DIVERS.OSE_SOURCE_ID
-                        FROM
-                             source
-                        WHERE
-                            code = 'OSE';
-                  END IF;
-
-                  RETURN OSE_DIVERS.OSE_SOURCE_ID;
-            END;
-
-
-
-      FUNCTION INTERVENANT_HAS_PRIVILEGE( intervenant_id NUMERIC, privilege_name VARCHAR2 ) RETURN NUMERIC IS
-            statut statut_intervenant%rowtype;
-            itype  type_intervenant%rowtype;
-            res NUMERIC;
-            BEGIN
-                  res := 1;
-                  SELECT si.* INTO statut FROM statut_intervenant si JOIN intervenant i ON i.statut_id = si.id WHERE i.id = intervenant_id;
-                  SELECT ti.* INTO itype  FROM type_intervenant ti WHERE ti.id = statut.type_intervenant_id;
-
-                  /* DEPRECATED */
-                  IF 'saisie_service' = privilege_name THEN
-                        res := statut.peut_saisir_service;
-                        RETURN res;
-                  ELSIF 'saisie_service_exterieur' = privilege_name THEN
-                        --IF INTERVENANT_HAS_PRIVILEGE( intervenant_id, 'saisie_service' ) = 0 OR itype.code = 'E' THEN -- cascade
-                        IF itype.code = 'E' THEN
-                              res := 0;
-                        END IF;
-                        RETURN res;
-                  ELSIF 'saisie_service_referentiel' = privilege_name THEN
-                        IF itype.code = 'E' THEN
-                              res := 0;
-                        END IF;
-                        RETURN res;
-                  ELSIF 'saisie_service_referentiel_autre_structure' = privilege_name THEN
-                        res := 1;
-                        RETURN res;
-                  ELSIF 'saisie_motif_non_paiement' = privilege_name THEN
-                        res := statut.peut_saisir_motif_non_paiement;
-                        RETURN res;
-                  END IF;
-                  /* FIN DE DEPRECATED */
-
-                  SELECT
-                         count(*)
-                      INTO
-                            res
-                  FROM
-                       intervenant i
-                             JOIN statut_privilege sp ON sp.statut_id = i.statut_id
-                             JOIN privilege p ON p.id = sp.privilege_id
-                             JOIN categorie_privilege cp ON cp.id = p.categorie_id
-                  WHERE
-                      i.id = INTERVENANT_HAS_PRIVILEGE.intervenant_id
-                    AND cp.code || '-' || p.code = privilege_name;
-
-                  RETURN res;
-            END;
-
-      FUNCTION implode(i_query VARCHAR2, i_seperator VARCHAR2 DEFAULT ',') RETURN VARCHAR2 AS
-            l_return CLOB:='';
-            l_temp CLOB;
-            TYPE r_cursor is REF CURSOR;
-            rc r_cursor;
-            BEGIN
-                  OPEN rc FOR i_query;
-                  LOOP
-                        FETCH rc INTO L_TEMP;
-                        EXIT WHEN RC%NOTFOUND;
-                        l_return:=l_return||L_TEMP||i_seperator;
-                  END LOOP;
-                  RETURN RTRIM(l_return,i_seperator);
-            END;
-
-      PROCEDURE intervenant_horodatage_service( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, REFERENTIEL NUMERIC, HISTO_MODIFICATEUR_ID NUMERIC, HISTO_MODIFICATION DATE ) AS
-            BEGIN
-                  MERGE INTO histo_intervenant_service his USING dual ON (
-
-                        his.INTERVENANT_ID                = intervenant_horodatage_service.INTERVENANT_ID
-                        AND NVL(his.TYPE_VOLUME_HORAIRE_ID,0) = NVL(intervenant_horodatage_service.TYPE_VOLUME_HORAIRE_ID,0)
-                        AND his.REFERENTIEL                   = intervenant_horodatage_service.REFERENTIEL
-
-                  ) WHEN MATCHED THEN UPDATE SET
-
-                        HISTO_MODIFICATEUR_ID = intervenant_horodatage_service.HISTO_MODIFICATEUR_ID,
-                        HISTO_MODIFICATION = intervenant_horodatage_service.HISTO_MODIFICATION
-
-                  WHEN NOT MATCHED THEN INSERT (
-
-                        ID,
-                        INTERVENANT_ID,
-                        TYPE_VOLUME_HORAIRE_ID,
-                        REFERENTIEL,
-                        HISTO_MODIFICATEUR_ID,
-                        HISTO_MODIFICATION
-                  ) VALUES (
-                        HISTO_INTERVENANT_SERVI_ID_SEQ.NEXTVAL,
-                        intervenant_horodatage_service.INTERVENANT_ID,
-                        intervenant_horodatage_service.TYPE_VOLUME_HORAIRE_ID,
-                        intervenant_horodatage_service.REFERENTIEL,
-                        intervenant_horodatage_service.HISTO_MODIFICATEUR_ID,
-                        intervenant_horodatage_service.HISTO_MODIFICATION
-
-                  );
-            END;
-
-
-      FUNCTION NIVEAU_FORMATION_ID_CALC( gtf_id NUMERIC, gtf_pertinence_niveau NUMERIC, niveau NUMERIC DEFAULT NULL ) RETURN NUMERIC AS
-            BEGIN
-                  IF 1 <> gtf_pertinence_niveau OR niveau IS NULL OR niveau < 1 OR gtf_id < 1 THEN RETURN NULL; END IF;
-                  RETURN gtf_id * 256 + niveau;
-            END;
-
-      FUNCTION STR_REDUCE( str CLOB ) RETURN CLOB IS
-            BEGIN
-                  RETURN utl_raw.cast_to_varchar2((nlssort(str, 'nls_sort=binary_ai')));
-            END;
-
-      FUNCTION STR_FIND( haystack CLOB, needle VARCHAR2 ) RETURN NUMERIC IS
-            BEGIN
-                  IF STR_REDUCE( haystack ) LIKE STR_REDUCE( '%' || needle || '%' ) THEN RETURN 1; END IF;
-                  RETURN 0;
-            END;
-
-      FUNCTION LIKED( haystack CLOB, needle CLOB ) RETURN NUMERIC IS
-            BEGIN
-                  RETURN CASE WHEN STR_REDUCE(haystack) LIKE STR_REDUCE(needle) THEN 1 ELSE 0 END;
-            END;
-
-      PROCEDURE DO_NOTHING IS
-            BEGIN
-                  RETURN;
-            END;
-
-      PROCEDURE CALCUL_TAUX( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, r_fi OUT FLOAT, r_fc OUT FLOAT, r_fa OUT FLOAT, arrondi NUMERIC DEFAULT 15 ) IS
-            nt FLOAT;
-            bi FLOAT;
-            bc FLOAT;
-            ba FLOAT;
-            reste FLOAT;
-            BEGIN
-                  bi := eff_fi * fi;
-                  bc := eff_fc * fc;
-                  ba := eff_fa * fa;
-                  nt := bi + bc + ba;
-
-                  IF nt = 0 THEN -- au cas ou, alors on ne prend plus en compte les effectifs!!
-                        bi := fi;
-                        bc := fc;
-                        ba := fa;
-                        nt := bi + bc + ba;
-                  END IF;
-
-                  IF nt = 0 THEN -- toujours au cas ou...
-                        bi := 1;
-                        bc := 0;
-                        ba := 0;
-                        nt := bi + bc + ba;
-                  END IF;
-
-                  -- Calcul
-                  r_fi := bi / nt;
-                  r_fc := bc / nt;
-                  r_fa := ba / nt;
-
-                  -- Arrondis
-                  r_fi := ROUND( r_fi, arrondi );
-                  r_fc := ROUND( r_fc, arrondi );
-                  r_fa := ROUND( r_fa, arrondi );
-
-                  -- détermination du reste
-                  reste := 1 - r_fi - r_fc - r_fa;
-
-                  -- répartition éventuelle du reste
-                  IF reste <> 0 THEN
-                        IF r_fi > 0 THEN r_fi := r_fi + reste;
-                        ELSIF r_fc > 0 THEN r_fc := r_fc + reste;
-                        ELSE r_fa := r_fa + reste; END IF;
-                  END IF;
-
-            END;
-
-
-      FUNCTION CALCUL_TAUX_FI( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
-            ri FLOAT;
-            rc FLOAT;
-            ra FLOAT;
-            BEGIN
-                  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
-                  RETURN ri;
-            END;
-
-      FUNCTION CALCUL_TAUX_FC( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
-            ri FLOAT;
-            rc FLOAT;
-            ra FLOAT;
-            BEGIN
-                  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
-                  RETURN rc;
-            END;
-
-      FUNCTION CALCUL_TAUX_FA( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
-            ri FLOAT;
-            rc FLOAT;
-            ra FLOAT;
-            BEGIN
-                  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
-                  RETURN ra;
-            END;
-
-      PROCEDURE SYNC_LOG( msg CLOB ) IS
-            BEGIN
-                  INSERT INTO SYNC_LOG( id, date_sync, message ) VALUES ( sync_log_id_seq.nextval, systimestamp, msg );
-            END;
-
-      FUNCTION FORMATTED_RIB (bic VARCHAR2, iban VARCHAR2) RETURN VARCHAR2 IS
-            BEGIN
-                  if bic is null and iban is null then
-                        return null;
-                  end if;
-                  RETURN regexp_replace(bic, '[[:space:]]+', '') || '-' || regexp_replace(iban, '[[:space:]]+', '');
-            END;
-
-      FUNCTION FORMATTED_ADRESSE(
-            no_voie                VARCHAR2,
-            nom_voie               VARCHAR2,
-            batiment               VARCHAR2,
-            mention_complementaire VARCHAR2,
-            localite               VARCHAR2,
-            code_postal            VARCHAR2,
-            ville                  VARCHAR2,
-            pays_libelle           VARCHAR2)
-            RETURN VARCHAR2
-      IS
-            BEGIN
-                  return
-                  -- concaténation des éléments non null séparés par ', '
-                  trim(trim(',' FROM REPLACE(', ' || NVL(no_voie,'#') || ', ' || NVL(nom_voie,'#') || ', ' || NVL(batiment,'#') || ', ' || NVL(mention_complementaire,'#'), ', #', ''))) ||
-                  -- saut de ligne complet
-                  chr(13) || chr(10) ||
-                  -- concaténation des éléments non null séparés par ', '
-                  trim(trim(',' FROM REPLACE(', ' || NVL(localite,'#') || ', ' || NVL(code_postal,'#') || ', ' || NVL(ville,'#') || ', ' || NVL(pays_libelle,'#'), ', #', '')));
-            END;
-
-
-
-      PROCEDURE CALCUL_FEUILLE_DE_ROUTE( CONDS CLOB ) IS
-            BEGIN
-                  FOR d IN (
-                  SELECT   tbl_name
-                  FROM     tbl
-                  WHERE    feuille_de_route = 1
-                  ORDER BY ordre
-                  ) LOOP
-                        UNICAEN_TBL.CALCULER(d.tbl_name,CONDS);
-                  END LOOP;
-            END;
-
-
-
-      FUNCTION GET_TRIGGER_BODY( TRIGGER_NAME VARCHAR2 ) RETURN VARCHAR2 IS
-            vlong long;
-            BEGIN
-                  SELECT trigger_body INTO vlong FROM all_triggers WHERE trigger_name = GET_TRIGGER_BODY.TRIGGER_NAME;
-
-                  RETURN substr(vlong, 1, 32767);
-            END;
 
-END OSE_DIVERS;
+    WHEN tableau = 11 AND version = 3 THEN
+      IF vh.structure_is_affectation THEN
+        RETURN vh.heures * (vh.taux_fi + vh.taux_fa);
+      ELSE
+        RETURN 0;
+      END IF;
 
-/
 
--- OSE_EVENT
-CREATE OR REPLACE PACKAGE BODY "OSE_EVENT" AS
 
-      PROCEDURE ON_AFTER_FORMULE_CALC( INTERVENANT_ID NUMERIC ) IS
-            p unicaen_tbl.t_params;
-            BEGIN
-                  p := UNICAEN_TBL.make_params('INTERVENANT_ID', ON_AFTER_FORMULE_CALC.intervenant_id);
-                  /*
-                      UNICAEN_TBL.CALCULER( 'agrement', p );
-                      UNICAEN_TBL.CALCULER( 'paiement', p );
-                      UNICAEN_TBL.CALCULER( 'workflow', p );*/
-            END;
+    WHEN tableau = 12 AND version = 2 THEN
+      IF NOT vh.structure_is_affectation AND vh.taux_fc < 1 THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-END OSE_EVENT;
 
-/
 
--- OSE_FORMULE
-CREATE OR REPLACE PACKAGE BODY "OSE_FORMULE" AS
+    WHEN tableau = 12 AND version = 3 THEN
+      IF NOT vh.structure_is_affectation THEN
+        RETURN vh.heures * (vh.taux_fi + vh.taux_fa);
+      ELSE
+        RETURN 0;
+      END IF;
 
-      TYPE t_lst_vh_etats IS TABLE OF t_volumes_horaires INDEX BY PLS_INTEGER;
-      TYPE t_lst_vh_types IS TABLE OF t_lst_vh_etats INDEX BY PLS_INTEGER;
-
-      TYPE t_resultat IS RECORD (
-      id                         NUMERIC,
-      formule_resultat_id        NUMERIC,
-      type_volume_horaire_id     NUMERIC,
-      etat_volume_horaire_id     NUMERIC,
-      service_id                 NUMERIC,
-      service_referentiel_id     NUMERIC,
-      volume_horaire_id          NUMERIC,
-      volume_horaire_ref_id      NUMERIC,
-      structure_id               NUMERIC,
-
-      service_fi                 FLOAT DEFAULT 0,
-      service_fa                 FLOAT DEFAULT 0,
-      service_fc                 FLOAT DEFAULT 0,
-      service_referentiel        FLOAT DEFAULT 0,
-      heures_compl_fi            FLOAT DEFAULT 0,
-      heures_compl_fa            FLOAT DEFAULT 0,
-      heures_compl_fc            FLOAT DEFAULT 0,
-      heures_compl_fc_majorees   FLOAT DEFAULT 0,
-      heures_compl_referentiel   FLOAT DEFAULT 0,
-
-      changed                    BOOLEAN DEFAULT FALSE
-      );
-      TYPE t_resultats IS TABLE OF t_resultat INDEX BY VARCHAR2(15);
-
-      all_volumes_horaires t_lst_vh_types;
-      arrondi NUMERIC DEFAULT 2;
-      t_res t_resultats;
-
-
-
-      FUNCTION GET_INTERVENANT_ID RETURN NUMERIC IS
-            BEGIN
-                  RETURN intervenant.id;
-            END;
-
-
-
-      FUNCTION GET_TAUX_HORAIRE_HETD( DATE_OBS DATE DEFAULT NULL ) RETURN FLOAT IS
-            taux_hetd FLOAT;
-            BEGIN
-                  SELECT valeur INTO taux_hetd
-                  FROM taux_horaire_hetd t
-                  WHERE
-                      DATE_OBS BETWEEN t.histo_creation AND COALESCE(t.histo_destruction,GREATEST(SYSDATE,DATE_OBS))
-                    AND rownum = 1
-                  ORDER BY
-                           histo_creation DESC;
-                  RETURN taux_hetd;
-            END;
-
-
-
-      PROCEDURE UPDATE_ANNEE_TAUX_HETD IS
-            BEGIN
-                  UPDATE annee SET taux_hetd = GET_TAUX_HORAIRE_HETD(date_fin);
-            END;
-
-
-
-      PROCEDURE LOAD_INTERVENANT_FROM_BDD IS
-            BEGIN
-                  intervenant.service_du := 0;
-
-                  SELECT
-                         intervenant_id,
-                         annee_id,
-                         structure_id,
-                         type_intervenant_code,
-                         heures_service_statutaire,
-                         depassement_service_du_sans_hc,
-                         heures_service_modifie,
-                         heures_decharge
-                      INTO
-                            intervenant.id,
-                            intervenant.annee_id,
-                            intervenant.structure_id,
-                            intervenant.type_intervenant_code,
-                            intervenant.heures_service_statutaire,
-                            intervenant.depassement_service_du_sans_hc,
-                            intervenant.heures_service_modifie,
-                            intervenant.heures_decharge
-                  FROM
-                       v_formule_intervenant fi
-                  WHERE
-                      fi.intervenant_id = intervenant.id;
-
-                  intervenant.service_du := CASE
-                                            WHEN intervenant.depassement_service_du_sans_hc = 1 -- HC traitées comme du service
-                                                 OR intervenant.heures_decharge < 0 -- s'il y a une décharge => aucune HC
-
-                                                  THEN 9999
-                                            ELSE intervenant.heures_service_statutaire + intervenant.heures_service_modifie
-                                            END;
-
-                  EXCEPTION WHEN NO_DATA_FOUND THEN
-                  intervenant.id                             := NULL;
-                  intervenant.annee_id                       := null;
-                  intervenant.structure_id                   := null;
-                  intervenant.heures_service_statutaire      := 0;
-                  intervenant.depassement_service_du_sans_hc := 0;
-                  intervenant.heures_service_modifie         := 0;
-                  intervenant.heures_decharge                := 0;
-                  intervenant.type_intervenant_code          := 'E';
-                  intervenant.service_du                     := 0;
-            END;
-
-      PROCEDURE LOAD_VH_FROM_BDD IS
-            vh t_volume_horaire;
-            etat_volume_horaire_id NUMERIC DEFAULT 1;
-            structure_univ NUMERIC;
-            length NUMERIC;
-            BEGIN
-                  all_volumes_horaires.delete;
-
-                  SELECT to_number(valeur) INTO structure_univ FROM parametre WHERE nom = 'structure_univ';
-
-                  FOR d IN (
-                  SELECT *
-                  FROM   v_formule_volume_horaire fvh
-                  WHERE  fvh.intervenant_id = intervenant.id
-                  ) LOOP
-                        vh.volume_horaire_id         := d.volume_horaire_id;
-                        vh.volume_horaire_ref_id     := d.volume_horaire_ref_id;
-                        vh.service_id                := d.service_id;
-                        vh.service_referentiel_id    := d.service_referentiel_id;
-                        vh.taux_fi                   := d.taux_fi;
-                        vh.taux_fa                   := d.taux_fa;
-                        vh.taux_fc                   := d.taux_fc;
-                        vh.ponderation_service_du    := d.ponderation_service_du;
-                        vh.ponderation_service_compl := d.ponderation_service_compl;
-                        vh.structure_id              := d.structure_id;
-                        vh.structure_is_affectation  := NVL(d.structure_id,0) = NVL(intervenant.structure_id,-1);
-                        vh.structure_is_univ         := NVL(d.structure_id,0) = NVL(structure_univ,-1);
-                        vh.service_statutaire        := d.service_statutaire = 1;
-                        vh.heures                    := d.heures;
-                        vh.taux_service_du           := d.taux_service_du;
-                        vh.taux_service_compl        := d.taux_service_compl;
-
-                        FOR etat_volume_horaire_id IN 1 .. d.etat_volume_horaire_id LOOP
-                              BEGIN
-                                    length := all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).length;
-                                    EXCEPTION WHEN NO_DATA_FOUND THEN
-                                    length := 0;
-                              END;
-                              length := length + 1;
-                              all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).length := length;
-                              all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).items(length) := vh;
-                        END LOOP;
-                  END LOOP;
-            END;
-
-
-      PROCEDURE tres_add_heures( code VARCHAR2, vh t_volume_horaire, tvh NUMERIC, evh NUMERIC) IS
-            BEGIN
-                  IF NOT t_res.exists(code) THEN
-                        t_res(code).service_fi               := 0;
-                        t_res(code).service_fa               := 0;
-                        t_res(code).service_fc               := 0;
-                        t_res(code).service_referentiel      := 0;
-                        t_res(code).heures_compl_fi          := 0;
-                        t_res(code).heures_compl_fa          := 0;
-                        t_res(code).heures_compl_fc          := 0;
-                        t_res(code).heures_compl_fc_majorees := 0;
-                        t_res(code).heures_compl_referentiel := 0;
-                  END IF;
-
-                  t_res(code).service_fi               := t_res(code).service_fi               + vh.service_fi;
-                  t_res(code).service_fa               := t_res(code).service_fa               + vh.service_fa;
-                  t_res(code).service_fc               := t_res(code).service_fc               + vh.service_fc;
-                  t_res(code).service_referentiel      := t_res(code).service_referentiel      + vh.service_referentiel;
-                  t_res(code).heures_compl_fi          := t_res(code).heures_compl_fi          + vh.heures_compl_fi;
-                  t_res(code).heures_compl_fa          := t_res(code).heures_compl_fa          + vh.heures_compl_fa;
-                  t_res(code).heures_compl_fc          := t_res(code).heures_compl_fc          + vh.heures_compl_fc;
-                  t_res(code).heures_compl_fc_majorees := t_res(code).heures_compl_fc_majorees + vh.heures_compl_fc_majorees;
-                  t_res(code).heures_compl_referentiel := t_res(code).heures_compl_referentiel + vh.heures_compl_referentiel;
-
-                  t_res(code).type_volume_horaire_id := tvh;
-                  t_res(code).etat_volume_horaire_id := evh;
-            END;
-
-      PROCEDURE DEBUG_TRES IS
-            code varchar2(15);
-            table_name varchar2(30);
-            fr formule_resultat%rowtype;
-            frs formule_resultat_service%rowtype;
-            frsr formule_resultat_service_ref%rowtype;
-            frvh formule_resultat_vh%rowtype;
-            frvhr formule_resultat_vh_ref%rowtype;
-            BEGIN
-                  code := t_res.FIRST;
-                  LOOP EXIT WHEN code IS NULL;
-                        table_name := CASE
-                                      WHEN code LIKE '%-s-%' THEN 'FORMULE_RESULTAT_SERVICE'
-                                      WHEN code LIKE '%-sr-%' THEN 'FORMULE_RESULTAT_SERVICE_REF'
-                                      WHEN code LIKE '%-vh-%' THEN 'FORMULE_RESULTAT_VH'
-                                      WHEN code LIKE '%-vhr-%' THEN 'FORMULE_RESULTAT_VH_REF'
-                                      ELSE 'FORMULE_RESULTAT'
-                                      END;
-
-                        ose_test.echo('T_RES( ' || code || ' - Table ' || table_name || ' ) ');
-                        ose_test.echo('  id = ' || t_res(code).id);
-                        ose_test.echo('  formule_resultat_id      = ' || t_res(code).formule_resultat_id);
-                        ose_test.echo('  type_volume_horaire_id   = ' || t_res(code).type_volume_horaire_id);
-                        ose_test.echo('  etat_volume_horaire_id   = ' || t_res(code).etat_volume_horaire_id);
-                        ose_test.echo('  volume_horaire_id        = ' || t_res(code).volume_horaire_id);
-                        ose_test.echo('  volume_horaire_ref_id    = ' || t_res(code).volume_horaire_ref_id);
-                        ose_test.echo('  service_id               = ' || t_res(code).service_id);
-                        ose_test.echo('  service_referentiel_id   = ' || t_res(code).service_referentiel_id);
-                        ose_test.echo('  structure_id             = ' || t_res(code).structure_id);
-                        ose_test.echo('  service_fi               = ' || t_res(code).service_fi);
-                        ose_test.echo('  service_fa               = ' || t_res(code).service_fa);
-                        ose_test.echo('  service_fc               = ' || t_res(code).service_fc);
-                        ose_test.echo('  service_referentiel      = ' || t_res(code).service_referentiel);
-                        ose_test.echo('  heures_compl_fi          = ' || t_res(code).heures_compl_fi);
-                        ose_test.echo('  heures_compl_fa          = ' || t_res(code).heures_compl_fa);
-                        ose_test.echo('  heures_compl_fc          = ' || t_res(code).heures_compl_fc);
-                        ose_test.echo('  heures_compl_fc_majorees = ' || t_res(code).heures_compl_fc_majorees);
-                        ose_test.echo('  heures_compl_referentiel = ' || t_res(code).heures_compl_referentiel);
-
-                        code := t_res.NEXT(code);
-                  END LOOP;
-            END;
-
-      PROCEDURE SAVE_TO_BDD IS
-            bcode VARCHAR(15);
-            code VARCHAR(15);
-            type_volume_horaire_id NUMERIC;
-            etat_volume_horaire_id NUMERIC;
-            vh t_volume_horaire;
-            fr formule_resultat%rowtype;
-            frs formule_resultat_service%rowtype;
-            frsr formule_resultat_service_ref%rowtype;
-            frvh formule_resultat_vh%rowtype;
-            frvhr formule_resultat_vh_ref%rowtype;
-            BEGIN
-                  t_res.delete;
-
-                  /* On préinitialise avec ce qui existe déjà */
-                  FOR d IN (
-                  SELECT
-                         fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id code,
-                         fr.id                       id,
-                         fr.id                       formule_resultat_id,
-                         fr.type_volume_horaire_id   type_volume_horaire_id,
-                         fr.etat_volume_horaire_id   etat_volume_horaire_id,
-                         null                        service_id,
-                         null                        service_referentiel_id,
-                         null                        volume_horaire_id,
-                         null                        volume_horaire_ref_id
-
-                  FROM
-                       formule_resultat fr
-                  WHERE
-                      fr.intervenant_id = intervenant.id
-
-                  UNION ALL SELECT
-                                   fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-s-' || frs.service_id code,
-                                   frs.id                      id,
-                                   fr.id                       formule_resultat_id,
-                                   fr.type_volume_horaire_id   type_volume_horaire_id,
-                                   fr.etat_volume_horaire_id   etat_volume_horaire_id,
-                                   frs.service_id              service_id,
-                                   null                        service_referentiel_id,
-                                   null                        volume_horaire_id,
-                                   null                        volume_horaire_ref_id
-                            FROM
-                                 formule_resultat_service frs
-                                       JOIN formule_resultat fr ON fr.id = frs.formule_resultat_id
-                            WHERE
-                                fr.intervenant_id = intervenant.id
-
-                  UNION ALL SELECT
-                                   fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-sr-' || frsr.service_referentiel_id code,
-                                   frsr.id                     id,
-                                   fr.id                       formule_resultat_id,
-                                   fr.type_volume_horaire_id   type_volume_horaire_id,
-                                   fr.etat_volume_horaire_id   etat_volume_horaire_id,
-                                   null                        service_id,
-                                   frsr.service_referentiel_id service_referentiel_id,
-                                   null                        volume_horaire_id,
-                                   null                        volume_horaire_ref_id
-                            FROM
-                                 formule_resultat_service_ref frsr
-                                       JOIN formule_resultat fr ON fr.id = frsr.formule_resultat_id
-                            WHERE
-                                fr.intervenant_id = intervenant.id
-
-                  UNION ALL SELECT
-                                   fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-vh-' || frvh.volume_horaire_id code,
-                                   frvh.id                     id,
-                                   fr.id                       formule_resultat_id,
-                                   fr.type_volume_horaire_id   type_volume_horaire_id,
-                                   fr.etat_volume_horaire_id   etat_volume_horaire_id,
-                                   null                        service_id,
-                                   null                        service_referentiel_id,
-                                   frvh.volume_horaire_id      volume_horaire_id,
-                                   null                        volume_horaire_ref_id
-                            FROM
-                                 formule_resultat_vh frvh
-                                       JOIN formule_resultat fr ON fr.id = frvh.formule_resultat_id
-                            WHERE
-                                fr.intervenant_id = intervenant.id
-
-                  UNION ALL SELECT
-                                   fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-vhr-' || frvhr.volume_horaire_ref_id code,
-                                   frvhr.id                    id,
-                                   fr.id                       formule_resultat_id,
-                                   fr.type_volume_horaire_id   type_volume_horaire_id,
-                                   fr.etat_volume_horaire_id   etat_volume_horaire_id,
-                                   null                        service_id,
-                                   null                        service_referentiel_id,
-                                   null                        volume_horaire_id,
-                                   frvhr.volume_horaire_ref_id volume_horaire_ref_id
-                            FROM
-                                 formule_resultat_vh_ref frvhr
-                                       JOIN formule_resultat fr ON fr.id = frvhr.formule_resultat_id
-                            WHERE
-                                fr.intervenant_id = intervenant.id
-                  ) LOOP
-                        t_res(d.code).id                     := d.id;
-                        t_res(d.code).formule_resultat_id    := d.formule_resultat_id;
-                        t_res(d.code).type_volume_horaire_id := d.type_volume_horaire_id;
-                        t_res(d.code).etat_volume_horaire_id := d.etat_volume_horaire_id;
-                        t_res(d.code).service_id             := d.service_id;
-                        t_res(d.code).service_referentiel_id := d.service_referentiel_id;
-                        t_res(d.code).volume_horaire_id      := d.volume_horaire_id;
-                        t_res(d.code).volume_horaire_ref_id  := d.volume_horaire_ref_id;
-                  END LOOP;
-
-                  /* On charge avec les résultats de formule */
-                  type_volume_horaire_id := all_volumes_horaires.FIRST;
-                  LOOP EXIT WHEN type_volume_horaire_id IS NULL;
-                        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
-                        LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
-                              FOR i IN 1 .. all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).length LOOP
-                                    vh := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).items(i);
-                                    bcode := type_volume_horaire_id || '-' || etat_volume_horaire_id;
-
-                                    -- formule_resultat
-                                    code := bcode;
-                                    tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
-
-                                    -- formule_resultat_service
-                                    IF vh.service_id IS NOT NULL THEN
-                                          code := bcode || '-s-' || vh.service_id;
-                                          t_res(code).service_id := vh.service_id;
-                                          tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
-                                    END IF;
-
-                                    -- formule_resultat_service_ref
-                                    IF vh.service_referentiel_id IS NOT NULL THEN
-                                          code := bcode || '-sr-' || vh.service_referentiel_id;
-                                          t_res(code).service_referentiel_id := vh.service_referentiel_id;
-                                          tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
-                                    END IF;
-
-                                    -- formule_resultat_volume_horaire
-                                    IF vh.volume_horaire_id IS NOT NULL THEN
-                                          code := bcode || '-vh-' || vh.volume_horaire_id;
-                                          t_res(code).volume_horaire_id := vh.volume_horaire_id;
-                                          tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
-                                    END IF;
-
-                                    -- formule_resultat_volume_horaire_ref
-                                    IF vh.volume_horaire_ref_id IS NOT NULL THEN
-                                          code := bcode || '-vhr-' || vh.volume_horaire_ref_id;
-                                          t_res(code).volume_horaire_ref_id := vh.volume_horaire_ref_id;
-                                          tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
-                                    END IF;
-
-                              END LOOP;
-                              etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
-                        END LOOP;
-                        type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
-                  END LOOP;
-
-                  /* On fait la sauvegarde en BDD */
-                  /* D'abord le formule_resultat */
-                  code := t_res.FIRST;
-                  LOOP EXIT WHEN code IS NULL;
-                        IF code = (t_res(code).type_volume_horaire_id || '-' || t_res(code).etat_volume_horaire_id) THEN
-                              fr.id                       := t_res(code).id;
-                              fr.intervenant_id           := intervenant.id;
-                              fr.type_volume_horaire_id   := t_res(code).type_volume_horaire_id;
-                              fr.etat_volume_horaire_id   := t_res(code).etat_volume_horaire_id;
-                              fr.service_fi               := ROUND(t_res(code).service_fi,2);
-                              fr.service_fa               := ROUND(t_res(code).service_fa,2);
-                              fr.service_fc               := ROUND(t_res(code).service_fc,2);
-                              fr.service_referentiel      := ROUND(t_res(code).service_referentiel,2);
-                              fr.heures_compl_fi          := ROUND(t_res(code).heures_compl_fi,2);
-                              fr.heures_compl_fa          := ROUND(t_res(code).heures_compl_fa,2);
-                              fr.heures_compl_fc          := ROUND(t_res(code).heures_compl_fc,2);
-                              fr.heures_compl_fc_majorees := ROUND(t_res(code).heures_compl_fc_majorees,2);
-                              fr.heures_compl_referentiel := ROUND(t_res(code).heures_compl_referentiel,2);
-                              fr.total := fr.service_fi + fr.service_fa + fr.service_fc + fr.service_referentiel
-                                          + fr.heures_compl_fi + fr.heures_compl_fa + fr.heures_compl_fc
-                                          + fr.heures_compl_fc_majorees + fr.heures_compl_referentiel;
-
-                              fr.service_du := ROUND(CASE
-                                                     WHEN intervenant.depassement_service_du_sans_hc = 1 OR intervenant.heures_decharge < 0
-                                                           THEN GREATEST(fr.total, intervenant.heures_service_statutaire + intervenant.heures_service_modifie)
-                                                     ELSE intervenant.heures_service_statutaire + intervenant.heures_service_modifie
-                                                     END,2);
-
-                              fr.solde                    := fr.total - fr.service_du;
-                              IF fr.solde >= 0 THEN
-                                    fr.sous_service           := 0;
-                                    fr.heures_compl           := fr.solde;
-                              ELSE
-                                    fr.sous_service           := fr.solde * -1;
-                                    fr.heures_compl           := 0;
-                              END IF;
-                              fr.type_intervenant_code    := intervenant.type_intervenant_code;
-
-                              IF fr.id IS NULL THEN
-                                    fr.id := formule_resultat_id_seq.nextval;
-                                    t_res(code).id := fr.id;
-                                    INSERT INTO formule_resultat VALUES fr;
-                              ELSE
-                                    UPDATE formule_resultat SET ROW = fr WHERE id = fr.id;
-                              END IF;
-                        END IF;
-                        code := t_res.NEXT(code);
-                  END LOOP;
-
-                  --DEBUG_TRES;
-
-                  /* Ensuite toutes les dépendances... */
-                  code := t_res.FIRST;
-                  LOOP EXIT WHEN code IS NULL;
-                        bcode := t_res(code).type_volume_horaire_id || '-' || t_res(code).etat_volume_horaire_id;
-                        CASE
-                              WHEN code LIKE '%-s-%' THEN -- formule_resultat_service
-                              frs.id                         := t_res(code).id;
-                              frs.formule_resultat_id        := t_res(bcode).id;
-                              frs.service_id                 := t_res(code).service_id;
-                              frs.service_fi                 := ROUND(t_res(code).service_fi, 2);
-                              frs.service_fa                 := ROUND(t_res(code).service_fa, 2);
-                              frs.service_fc                 := ROUND(t_res(code).service_fc, 2);
-                              frs.heures_compl_fi            := ROUND(t_res(code).heures_compl_fi, 2);
-                              frs.heures_compl_fa            := ROUND(t_res(code).heures_compl_fa, 2);
-                              frs.heures_compl_fc            := ROUND(t_res(code).heures_compl_fc, 2);
-                              frs.heures_compl_fc_majorees   := ROUND(t_res(code).heures_compl_fc_majorees, 2);
-                              frs.total                      := frs.service_fi + frs.service_fa + frs.service_fc
-                                                                + frs.heures_compl_fi + frs.heures_compl_fa + frs.heures_compl_fc + frs.heures_compl_fc_majorees;
-                              IF frs.id IS NULL THEN
-                                    frs.id := formule_resultat_servic_id_seq.nextval;
-                                    INSERT INTO formule_resultat_service VALUES frs;
-                              ELSE
-                                    UPDATE formule_resultat_service SET ROW = frs WHERE id = frs.id;
-                              END IF;
-                              WHEN code LIKE '%-sr-%' THEN -- formule_resultat_service_ref
-                              frsr.id                        := t_res(code).id;
-                              frsr.formule_resultat_id       := t_res(bcode).id;
-                              frsr.service_referentiel_id    := t_res(code).service_referentiel_id;
-                              frsr.service_referentiel       := ROUND(t_res(code).service_referentiel, 2);
-                              frsr.heures_compl_referentiel  := ROUND(t_res(code).heures_compl_referentiel, 2);
-                              frsr.total                     := frsr.service_referentiel + frsr.heures_compl_referentiel;
-                              IF frsr.id IS NULL THEN
-                                    frsr.id := formule_resultat_servic_id_seq.nextval;
-                                    INSERT INTO formule_resultat_service_ref VALUES frsr;
-                              ELSE
-                                    UPDATE formule_resultat_service_ref SET ROW = frsr WHERE id = frsr.id;
-                              END IF;
-                              WHEN code LIKE '%-vh-%' THEN -- formule_resultat_vh
-                              frvh.id := t_res(code).id;
-                              frvh.formule_resultat_id       := t_res(bcode).id;
-                              frvh.volume_horaire_id         := t_res(code).volume_horaire_id;
-                              frvh.service_fi                := ROUND(t_res(code).service_fi, 2);
-                              frvh.service_fa                := ROUND(t_res(code).service_fa, 2);
-                              frvh.service_fc                := ROUND(t_res(code).service_fc, 2);
-                              frvh.heures_compl_fi           := ROUND(t_res(code).heures_compl_fi, 2);
-                              frvh.heures_compl_fa           := ROUND(t_res(code).heures_compl_fa, 2);
-                              frvh.heures_compl_fc           := ROUND(t_res(code).heures_compl_fc, 2);
-                              frvh.heures_compl_fc_majorees  := ROUND(t_res(code).heures_compl_fc_majorees, 2);
-                              frvh.total                     := frvh.service_fi + frvh.service_fa + frvh.service_fc
-                                                                + frvh.heures_compl_fi + frvh.heures_compl_fa + frvh.heures_compl_fc + frvh.heures_compl_fc_majorees;
-                              IF frvh.id IS NULL THEN
-                                    frvh.id := formule_resultat_vh_id_seq.nextval;
-                                    INSERT INTO formule_resultat_vh VALUES frvh;
-                              ELSE
-                                    UPDATE formule_resultat_vh SET ROW = frvh WHERE id = frvh.id;
-                              END IF;
-                              WHEN code LIKE '%-vhr-%' THEN -- formule_resultat_vh_ref
-                              frvhr.id := t_res(code).id;
-                              frvhr.formule_resultat_id      := t_res(bcode).id;
-                              frvhr.volume_horaire_ref_id    := t_res(code).volume_horaire_ref_id;
-                              frvhr.service_referentiel      := ROUND(t_res(code).service_referentiel, 2);
-                              frvhr.heures_compl_referentiel := ROUND(t_res(code).heures_compl_referentiel, 2);
-                              frvhr.total                    := frvhr.service_referentiel + frvhr.heures_compl_referentiel;
-                              IF frvhr.id IS NULL THEN
-                                    frvhr.id := formule_resultat_vh_ref_id_seq.nextval;
-                                    INSERT INTO formule_resultat_vh_ref VALUES frvhr;
-                              ELSE
-                                    UPDATE formule_resultat_vh_ref SET ROW = frvhr WHERE id = frvhr.id;
-                              END IF;
-                        ELSE code := code;
-                        END CASE;
-                        code := t_res.NEXT(code);
-                  END LOOP;
-            END;
-
-
-
-      PROCEDURE CALCULER( INTERVENANT_ID NUMERIC ) IS
-            type_volume_horaire_id NUMERIC;
-            etat_volume_horaire_id NUMERIC;
-
-            function_name VARCHAR2(30);
-            package_name VARCHAR2(30);
-            BEGIN
-                  package_name  := OSE_PARAMETRE.GET_FORMULE_PACKAGE_NAME;
-                  function_name := OSE_PARAMETRE.GET_FORMULE_FUNCTION_NAME;
-
-                  intervenant.id := intervenant_id;
-
-                  LOAD_INTERVENANT_FROM_BDD;
-                  LOAD_VH_FROM_BDD;
-
-                  type_volume_horaire_id := all_volumes_horaires.FIRST;
-                  LOOP EXIT WHEN type_volume_horaire_id IS NULL;
-                        intervenant.type_volume_horaire_id := type_volume_horaire_id;
-                        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
-                        LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
-                              intervenant.etat_volume_horaire_id := etat_volume_horaire_id;
-                              volumes_horaires := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id);
-                              EXECUTE IMMEDIATE 'BEGIN ' || package_name || '.' || function_name || '; END;';
-                              all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id) := volumes_horaires;
-                              etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
-                        END LOOP;
-                        type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
-                  END LOOP;
-
-                  SAVE_TO_BDD;
-
-                  OSE_EVENT.ON_AFTER_FORMULE_CALC( CALCULER.INTERVENANT_ID );
-            END;
-
-      PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL ) IS
-            a_id NUMERIC;
-            BEGIN
-                  a_id := NVL(CALCULER_TOUT.ANNEE_ID, OSE_PARAMETRE.GET_ANNEE);
-                  FOR mp IN (
-                  SELECT DISTINCT
-                                  intervenant_id
-                  FROM
-                       service s
-                             JOIN intervenant i ON i.id = s.intervenant_id
-                  WHERE
-                      s.histo_destruction IS NULL
-                    AND i.annee_id = a_id
-
-                  UNION ALL
-
-                  SELECT DISTINCT
-                                  intervenant_id
-                  FROM
-                       service_referentiel sr
-                             JOIN intervenant i ON i.id = sr.intervenant_id
-                  WHERE
-                      sr.histo_destruction IS NULL
-                    AND i.annee_id = a_id
-
-                  )
-                  LOOP
-                        CALCULER( mp.intervenant_id );
-                  END LOOP;
-            END;
-
-      PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS ) IS
-            intervenant_id NUMERIC;
-            TYPE r_cursor IS REF CURSOR;
-            diff_cur r_cursor;
-            BEGIN
-                  OPEN diff_cur FOR 'WITH interv AS (SELECT id intervenant_id, intervenant.* FROM intervenant)
-    SELECT intervenant_id FROM interv WHERE ' || unicaen_tbl.PARAMS_TO_CONDS( params );
-                  LOOP
-                        FETCH diff_cur INTO intervenant_id; EXIT WHEN diff_cur%NOTFOUND;
-                        BEGIN
-                              CALCULER( intervenant_id );
-                        END;
-                  END LOOP;
-                  CLOSE diff_cur;
-            END;
-
-
-
-      PROCEDURE DEBUG_INTERVENANT IS
-            BEGIN
-                  ose_test.echo('OSE Formule DEBUG Intervenant');
-                  ose_test.echo('id                             = ' || intervenant.id);
-                  ose_test.echo('annee_id                       = ' || intervenant.annee_id);
-                  ose_test.echo('structure_id                   = ' || intervenant.structure_id);
-                  ose_test.echo('type_volume_horaire_id         = ' || intervenant.type_volume_horaire_id);
-                  ose_test.echo('heures_decharge                = ' || intervenant.heures_decharge);
-                  ose_test.echo('heures_service_statutaire      = ' || intervenant.heures_service_statutaire);
-                  ose_test.echo('heures_service_modifie         = ' || intervenant.heures_service_modifie);
-                  ose_test.echo('depassement_service_du_sans_hc = ' || intervenant.depassement_service_du_sans_hc);
-                  ose_test.echo('service_du                     = ' || intervenant.service_du);
-            END;
-
-      PROCEDURE DEBUG_VOLUMES_HORAIRES(VOLUME_HORAIRE_ID NUMERIC DEFAULT NULL) IS
-            type_volume_horaire_id NUMERIC;
-            etat_volume_horaire_id NUMERIC;
-            vh t_volume_horaire;
-            BEGIN
-                  ose_test.echo('OSE Formule DEBUG Intervenant');
-
-                  type_volume_horaire_id := all_volumes_horaires.FIRST;
-                  LOOP EXIT WHEN type_volume_horaire_id IS NULL;
-                        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
-                        LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
-                              ose_test.echo('tvh=' || type_volume_horaire_id || ', evh=' || etat_volume_horaire_id);
-                              FOR i IN 1 .. all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).length LOOP
-                                    vh := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).items(i);
-                                    IF VOLUME_HORAIRE_ID IS NULL OR VOLUME_HORAIRE_ID = vh.volume_horaire_id OR VOLUME_HORAIRE_ID = vh.volume_horaire_ref_id THEN
-                                          ose_test.echo('volume_horaire_id         = ' || vh.volume_horaire_id);
-                                          ose_test.echo('volume_horaire_ref_id     = ' || vh.volume_horaire_ref_id);
-                                          ose_test.echo('service_id                = ' || vh.service_id);
-                                          ose_test.echo('service_referentiel_id    = ' || vh.service_referentiel_id);
-                                          ose_test.echo('taux_fi                   = ' || vh.taux_fi);
-                                          ose_test.echo('taux_fa                   = ' || vh.taux_fa);
-                                          ose_test.echo('taux_fc                   = ' || vh.taux_fc);
-                                          ose_test.echo('ponderation_service_du    = ' || vh.ponderation_service_du);
-                                          ose_test.echo('ponderation_service_compl = ' || vh.ponderation_service_compl);
-                                          ose_test.echo('structure_id              = ' || vh.structure_id);
-                                          ose_test.echo('structure_is_affectation  = ' || CASE WHEN vh.structure_is_affectation THEN 'OUI' ELSE 'NON' END);
-                                          ose_test.echo('structure_is_univ         = ' || CASE WHEN vh.structure_is_univ THEN 'OUI' ELSE 'NON' END);
-                                          ose_test.echo('service_statutaire        = ' || CASE WHEN vh.service_statutaire THEN 'OUI' ELSE 'NON' END);
-                                          ose_test.echo('heures                    = ' || vh.heures);
-                                          ose_test.echo('taux_service_du           = ' || vh.taux_service_du);
-                                          ose_test.echo('taux_service_compl        = ' || vh.taux_service_compl);
-                                          ose_test.echo('service_fi                = ' || vh.service_fi);
-                                          ose_test.echo('service_fa                = ' || vh.service_fa);
-                                          ose_test.echo('service_fc                = ' || vh.service_fc);
-                                          ose_test.echo('service_referentiel       = ' || vh.service_referentiel);
-                                          ose_test.echo('heures_compl_fi           = ' || vh.heures_compl_fi);
-                                          ose_test.echo('heures_compl_fa           = ' || vh.heures_compl_fa);
-                                          ose_test.echo('heures_compl_fc           = ' || vh.heures_compl_fc);
-                                          ose_test.echo('heures_compl_fc_majorees  = ' || vh.heures_compl_fc_majorees);
-                                          ose_test.echo('heures_compl_referentiel  = ' || vh.heures_compl_referentiel);
-                                          ose_test.echo('');
-                                    END IF;
-                              END LOOP;
-                              etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
-                        END LOOP;
-                        type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
-                  END LOOP;
-            END;
 
-END OSE_FORMULE;
 
-/
+    WHEN tableau = 13 AND version = 2 THEN
+      IF vh.structure_is_affectation AND vh.taux_fc = 1 THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
--- OSE_HISTO
-CREATE OR REPLACE PACKAGE BODY "OSE_HISTO" AS
 
-  FUNCTION FILTRE( histo_debut NUMERIC, histo_fin NUMERIC ) RETURN NUMERIC IS
-  BEGIN
-    RETURN 1;
-  END;
 
-END OSE_HISTO;
-/
+    WHEN tableau = 13 AND version = 3 THEN
+      IF vh.structure_is_affectation THEN
+        RETURN vh.heures * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
 
--- OSE_IMPORT
-CREATE OR REPLACE PACKAGE BODY "OSE_IMPORT" IS
 
-  PROCEDURE REFRESH_MV( mview_name varchar2 ) IS
-  BEGIN
-    DBMS_MVIEW.REFRESH(mview_name, 'C');
-  EXCEPTION WHEN OTHERS THEN
-    UNICAEN_IMPORT.SYNC_LOG( SQLERRM, mview_name );
-  END;
 
-  PROCEDURE REFRESH_MVS IS
-  BEGIN
-    -- Mise à jour des vues matérialisées
-    -- procédure à adapter aux besoins de chaque établissement
+    WHEN tableau = 14 AND version = 2 THEN
+      IF NOT vh.structure_is_affectation AND vh.taux_fc = 1 THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    REFRESH_MV('MV_UNICAEN_STRUCTURE_CODES');
-    REFRESH_MV('MV_AFFECTATION');
-    REFRESH_MV('MV_INTERVENANT');
-  END;
 
-  PROCEDURE SYNC_TABLES IS
-  BEGIN
-    -- SYNC COMMENT procédure à adapter aux besoins de chaque établissement
 
-    UNICAEN_IMPORT.SYNCHRONISATION('PAYS');
-    UNICAEN_IMPORT.SYNCHRONISATION('DEPARTEMENT');
+    WHEN tableau = 14 AND version = 3 THEN
+      IF NOT vh.structure_is_affectation THEN
+        RETURN vh.heures * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    UNICAEN_IMPORT.SYNCHRONISATION('ETABLISSEMENT');
-    UNICAEN_IMPORT.SYNCHRONISATION('STRUCTURE');
-    UNICAEN_IMPORT.SYNCHRONISATION('ADRESSE_STRUCTURE');
 
-    UNICAEN_IMPORT.SYNCHRONISATION('DOMAINE_FONCTIONNEL');
-    UNICAEN_IMPORT.SYNCHRONISATION('CENTRE_COUT');
-    UNICAEN_IMPORT.SYNCHRONISATION('CENTRE_COUT_STRUCTURE');
 
-    -- Import automatique des users des nouveaux directeurs
-    INSERT INTO utilisateur (
-      id, display_name, email, password, state, username
-    )
-    SELECT
-      utilisateur_id_seq.nextval id,
-      display_name,
-      email,
-      password,
-      state,
-      username
-    FROM
-      mv_affectation
-    WHERE
-      username not in (select username from utilisateur);
+    WHEN tableau = 15 AND version = 2 THEN
+      IF vh.structure_is_affectation THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    UNICAEN_IMPORT.SYNCHRONISATION('AFFECTATION');
 
-    UNICAEN_IMPORT.SYNCHRONISATION('CORPS');
-    UNICAEN_IMPORT.SYNCHRONISATION('GRADE');
 
-    UNICAEN_IMPORT.SYNCHRONISATION('INTERVENANT');
-    UNICAEN_IMPORT.SYNCHRONISATION('AFFECTATION_RECHERCHE');
-    UNICAEN_IMPORT.SYNCHRONISATION('ADRESSE_INTERVENANT');
+    WHEN tableau = 16 AND version = 2 THEN
+      IF NOT vh.structure_is_affectation AND NOT vh.structure_is_univ THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    UNICAEN_IMPORT.SYNCHRONISATION('GROUPE_TYPE_FORMATION');
-    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_FORMATION');
-    UNICAEN_IMPORT.SYNCHRONISATION('ETAPE');
-    UNICAEN_IMPORT.SYNCHRONISATION('ELEMENT_PEDAGOGIQUE');
-    UNICAEN_IMPORT.SYNCHRONISATION('EFFECTIFS');
-    --UNICAEN_IMPORT.SYNCHRONISATION('ELEMENT_TAUX_REGIMES');
-    UNICAEN_IMPORT.SYNCHRONISATION('CHEMIN_PEDAGOGIQUE');
 
-    UNICAEN_IMPORT.SYNCHRONISATION('VOLUME_HORAIRE_ENS');
-    UNICAEN_IMPORT.SYNCHRONISATION('NOEUD');
-    UNICAEN_IMPORT.SYNCHRONISATION('LIEN');
-    UNICAEN_IMPORT.SYNCHRONISATION('SCENARIO_LIEN');
 
-    REFRESH_MV('TBL_NOEUD');
-    UNICAEN_TBL.CALCULER('chargens');
+    WHEN tableau = 17 AND version = 2 THEN
+      IF vh.structure_is_univ THEN
+        RETURN vh.heures;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    -- Mise à jour des sources calculées en dernier
-    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_INTERVENTION_EP');
-    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_MODULATEUR_EP');
 
-    -- END SYNC COMMENT
-  END;
 
-  PROCEDURE SYNCHRONISATION IS
-  BEGIN
-    REFRESH_MVS;
-    SYNC_TABLES;
-  END SYNCHRONISATION;
+    WHEN tableau = 21 AND version = 2 THEN
+      RETURN gv(11) * vh.taux_service_du;
 
-END ose_import;
-/
 
--- OSE_PAIEMENT
-CREATE OR REPLACE PACKAGE BODY "OSE_PAIEMENT" AS
 
-  PROCEDURE CHECK_BAD_PAIEMENTS( FORMULE_RES_SERVICE_ID NUMERIC DEFAULT NULL, FORMULE_RES_SERVICE_REF_ID NUMERIC DEFAULT NULL ) IS
-    cc NUMERIC;
-  BEGIN
-    SELECT count(*) INTO cc
-    FROM mise_en_paiement mep
-    WHERE
-      mep.histo_destruction IS NULL
-      AND mep.formule_res_service_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_ID, mep.formule_res_service_id )
-      AND mep.formule_res_service_ref_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_REF_ID, mep.formule_res_service_ref_id )
-  ;
+    WHEN tableau = 22 AND version = 2 THEN
+      RETURN gv(12) * vh.taux_service_du;
 
-    IF (cc > 0) THEN
-      raise_application_error(-20101, 'Il est impossible d''effectuer cette action : des demandes de mise en paiement ont été saisies et ne peuvent pas être modifiées');
-    ELSE
-      DELETE FROM mise_en_paiement WHERE
-        histo_destruction IS NOT NULL
-        AND formule_res_service_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_ID, formule_res_service_id )
-        AND formule_res_service_ref_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_REF_ID, formule_res_service_ref_id )
-      ;
-    END IF;
-  END;
 
-END OSE_PAIEMENT;
-/
 
--- OSE_PARAMETRE
-CREATE OR REPLACE PACKAGE BODY "OSE_PARAMETRE" AS
+    WHEN tableau = 23 AND version = 2 THEN
+      RETURN gv(13) * vh.taux_service_du;
 
-  cache_ose_user NUMERIC;
-  cache_annee_id NUMERIC;
 
-  function get_etablissement return Numeric AS
-    etab_id numeric;
-  BEGIN
-    select to_number(valeur) into etab_id from parametre where nom = 'etablissement';
-    RETURN etab_id;
-  END get_etablissement;
 
-  function get_annee return Numeric AS
-    annee_id numeric;
-  BEGIN
-    IF cache_annee_id IS NOT NULL THEN RETURN cache_annee_id; END IF;
-    select to_number(valeur) into annee_id from parametre where nom = 'annee';
-    cache_annee_id := annee_id;
-    RETURN cache_annee_id;
-  END get_annee;
+    WHEN tableau = 24 AND version = 2 THEN
+      RETURN gv(14) * vh.taux_service_du;
 
-  FUNCTION get_annee_import RETURN NUMERIC AS
-    annee_id NUMERIC;
-  BEGIN
-    SELECT to_number(valeur) INTO annee_id FROM parametre WHERE nom = 'annee_import';
-    RETURN annee_id;
-  END get_annee_import;
 
-  function get_ose_user return Numeric AS
-    ose_user_id numeric;
-  BEGIN
-    IF cache_ose_user IS NOT NULL THEN RETURN cache_ose_user; END IF;
-    select to_number(valeur) into ose_user_id from parametre where nom = 'oseuser';
-    cache_ose_user := ose_user_id;
-    RETURN cache_ose_user;
-  END get_ose_user;
 
-  function get_drh_structure_id return Numeric AS
-    drh_structure_id numeric;
-  BEGIN
-    select to_number(valeur) into drh_structure_id from parametre where nom = 'drh_structure_id';
-    RETURN drh_structure_id;
-  END get_drh_structure_id;
+    WHEN tableau = 25 AND version = 2 THEN
+      RETURN gv(15);
 
-  FUNCTION get_date_fin_saisie_permanents RETURN DATE IS
-    date_fin_saisie_permanents date;
-  BEGIN
-    select TO_DATE(valeur, 'dd/mm/yyyy') into date_fin_saisie_permanents from parametre where nom = 'date_fin_saisie_permanents';
-    RETURN date_fin_saisie_permanents;
-  END;
 
-  FUNCTION get_ddeb_saisie_serv_real RETURN DATE IS
-    val date;
-  BEGIN
-    select TO_DATE(valeur, 'dd/mm/yyyy') into val from parametre where nom = 'date_debut_saisie_services_realises';
-    RETURN val;
-  END;
 
-  FUNCTION get_dfin_saisie_serv_real RETURN DATE IS
-    val date;
-  BEGIN
-    select TO_DATE(valeur, 'dd/mm/yyyy') into val from parametre where nom = 'date_fin_saisie_services_realises';
-    RETURN val;
-  END;
+    WHEN tableau = 26 AND version = 2 THEN
+      RETURN gv(16);
 
-  FUNCTION get_formule_package_name RETURN VARCHAR2 IS
-    formule_package_name VARCHAR2(30);
-  BEGIN
-    SELECT valeur INTO formule_package_name FROM parametre WHERE nom = 'formule_package_name';
-    RETURN formule_package_name;
-  END;
 
-  FUNCTION get_formule_function_name RETURN VARCHAR2 IS
-    formule_function_name VARCHAR2(30);
-  BEGIN
-    SELECT valeur INTO formule_function_name FROM parametre WHERE nom = 'formule_function_name';
-    RETURN formule_function_name;
-  END;
 
-END OSE_PARAMETRE;
-/
+    WHEN tableau = 27 AND version = 2 THEN
+      RETURN gv(17);
 
--- OSE_TEST
-CREATE OR REPLACE PACKAGE BODY "OSE_TEST" AS
-  TYPE OUT_LIST IS TABLE OF CLOB;
-  HTS TIMESTAMP;
 
-  SUCCES_SHOWN BOOLEAN DEFAULT TRUE;
-  T_SUCCES_COUNT NUMERIC DEFAULT 0;
-  T_ECHECS_COUNT NUMERIC DEFAULT 0;
-  A_SUCCES_COUNT NUMERIC DEFAULT 0;
-  A_ECHECS_COUNT NUMERIC DEFAULT 0;
-  CURRENT_TEST CLOB;
-  CURRENT_TEST_OUTPUT_BUFFER OUT_LIST := OUT_LIST();
-  CURRENT_TEST_OUTPUT_BUFFER_ERR BOOLEAN;
 
-  PROCEDURE SHOW_SUCCES IS
-  BEGIN
-    SUCCES_SHOWN := true;
-  END SHOW_SUCCES;
+    WHEN tableau = 31 AND version = 2 THEN
+      RETURN GREATEST( ose_formule.intervenant.service_du - gt(21), 0 );
 
-  PROCEDURE HIDE_SUCCES IS
-  BEGIN
-    SUCCES_SHOWN := false;
-  END HIDE_SUCCES;
 
-  PROCEDURE DEBUT( TEST_NAME CLOB ) IS
-  BEGIN
-    CURRENT_TEST := TEST_NAME;
-    CURRENT_TEST_OUTPUT_BUFFER_ERR := FALSE;
-    echo (' '); echo('TEST ' || TEST_NAME || ' >>>>>>>>>>' );
-  END;
 
-  PROCEDURE FIN IS
-    TEST_NAME CLOB;
-  BEGIN
-    IF CURRENT_TEST_OUTPUT_BUFFER_ERR THEN
-      T_ECHECS_COUNT := T_ECHECS_COUNT + 1;
-      echo('>>>>>>>>>> FIN DU TEST ' || CURRENT_TEST ); echo (' ');
-      CURRENT_TEST := NULL;
+    WHEN tableau = 32 AND version = 2 THEN
+      RETURN GREATEST( gt(31) - gt(22), 0 );
 
-      FOR i IN 1 .. CURRENT_TEST_OUTPUT_BUFFER.COUNT LOOP
-        echo( CURRENT_TEST_OUTPUT_BUFFER(i) );
-      END LOOP;
-    ELSE
-      T_SUCCES_COUNT := T_SUCCES_COUNT + 1;
-      TEST_NAME := CURRENT_TEST;
-      CURRENT_TEST := NULL;
-      echo('SUCCÈS DU TEST : ' || TEST_NAME );
-    END IF;
-    CURRENT_TEST_OUTPUT_BUFFER.DELETE; -- clear buffer
-  END;
 
-  PROCEDURE ECHO( MSG CLOB ) IS
-  BEGIN
-    IF CURRENT_TEST IS NULL THEN
-      dbms_output.put_line(MSG);
-    ELSE
-      CURRENT_TEST_OUTPUT_BUFFER.EXTEND;
-      CURRENT_TEST_OUTPUT_BUFFER (CURRENT_TEST_OUTPUT_BUFFER.LAST) := MSG;
-    END IF;
-  END;
 
-  PROCEDURE INIT IS
-  BEGIN
-    T_SUCCES_COUNT  := 0;
-    T_ECHECS_COUNT  := 0;
-    A_SUCCES_COUNT  := 0;
-    A_ECHECS_COUNT  := 0;
-    CURRENT_TEST    := NULL;
-  END INIT;
+    WHEN tableau = 33 AND version = 2 THEN
+      RETURN GREATEST( gt(32) - gt(23), 0 );
 
-  PROCEDURE SHOW_STATS IS
-  BEGIN
-    echo ( ' ' );
-    echo ( '********************************* STATISTIQUES *********************************' );
-    echo ( ' ' );
-    echo ( '   - nombre de tests passés avec succès :       ' || T_SUCCES_COUNT );
-    echo ( '   - nombre de tests ayant échoué :             ' || T_ECHECS_COUNT );
-    echo ( ' ' );
-    echo ( '   - nombre d''assertions passés avec succès :   ' || A_SUCCES_COUNT );
-    echo ( '   - nombre d''assertions ayant échoué :         ' || A_ECHECS_COUNT );
-    echo ( ' ' );
-    echo ( '********************************************************************************' );
-    echo ( ' ' );
-  END;
 
-  PROCEDURE ASSERT( condition BOOLEAN, MSG CLOB ) IS
-  BEGIN
-    IF condition THEN
-      A_SUCCES_COUNT := A_SUCCES_COUNT + 1;
-      IF SUCCES_SHOWN THEN
-        ECHO('        SUCCÈS : ' || MSG );
-      END IF;
-    ELSE
-      A_ECHECS_COUNT := A_ECHECS_COUNT + 1;
-      CURRENT_TEST_OUTPUT_BUFFER_ERR := TRUE;
-      ECHO('        ** ECHEC ** : ' || MSG );
-    END IF;
-  END;
 
-  PROCEDURE HOROINIT IS
-  BEGIN
-    HTS := systimestamp;
-  END;
+    WHEN tableau = 34 AND version = 2 THEN
+      RETURN GREATEST( gt(33) - gt(24), 0 );
 
-  PROCEDURE HORODATAGE( msg VARCHAR2 ) IS
-    diff INTERVAL DAY(9) TO SECOND(3);
-  BEGIN
-    IF HTS IS NULL THEN
-      HTS := systimestamp;
-      RETURN;
-    END IF;
 
-    diff := systimestamp - HTS;
-    HTS := systimestamp;
 
-    echo(msg || ' (' || diff || ')');
-  END;
+    WHEN tableau = 35 AND version = 2 THEN
+      RETURN GREATEST( gt(34) - gt(25), 0 );
 
-  FUNCTION GET_STRUCTURE_BY_ID( id NUMERIC ) RETURN structure%rowtype IS
-    res structure%rowtype;
-  BEGIN
-    IF ID IS NULL THEN RETURN res; END IF;
-    SELECT * INTO res FROM structure WHERE id = GET_STRUCTURE_BY_ID.id;
-    RETURN res;
-  END;
 
-END OSE_TEST;
-/
 
--- OSE_VALIDATION
-CREATE OR REPLACE PACKAGE BODY "OSE_VALIDATION" AS
+    WHEN tableau = 36 AND version = 2 THEN
+      RETURN GREATEST( gt(35) - gt(26), 0 );
 
-  FUNCTION can_devalider ( v validation%rowtype ) RETURN varchar2 IS
-    tv type_validation%rowtype;
-    nb NUMERIC;
-    result varchar2(500) default null;
-  BEGIN
 
-    SELECT * INTO tv FROM type_validation WHERE id = v.type_validation_id;
 
-    IF tv.code = 'SERVICES_PAR_COMP' THEN
+    WHEN tableau = 37 AND version = 2 THEN
+      RETURN GREATEST( gt(36) - gt(27), 0 );
 
-      SELECT
-        SUM(CASE WHEN c.id IS NOT NULL THEN 1 ELSE 0 END) INTO nb
-      FROM
-        validation_vol_horaire vvh
-        JOIN volume_horaire vh ON vh.id = vvh.volume_horaire_id
-        LEFT JOIN contrat c ON c.id = vh.contrat_id AND c.histo_destruction IS NULL
-      WHERE
-        vvh.validation_id = v.id;
 
-      -- Si des volumes horaires ont déjà fait l'objet de contrats alors pas de dévalidation possible des heures
-      IF nb > 0 THEN
-        result := 'La dévalidation est impossible car des contrats ont déjà été édités sur la base de ces heures.';
+
+    WHEN tableau = 41 AND version = 2 THEN
+      IF gt(21) <> 0 THEN
+        RETURN gv(21) / gt(21);
+      ELSE
+        RETURN 0;
       END IF;
 
-    END IF;
 
-    IF tv.code = 'CLOTURE_REALISE' THEN
 
-      SELECT
-        COUNT(*) INTO nb
-      FROM
-        tbl_paiement p
-      WHERE
-        p.periode_paiement_id IS NOT NULL
-        AND p.intervenant_id = v.intervenant_id
-        AND ROWNUM = 1;
+    WHEN tableau = 42 AND version = 2 THEN
+      IF gt(22) <> 0 THEN
+        RETURN gv(22) / gt(22);
+      ELSE
+        RETURN 0;
+      END IF;
 
-      IF nb > 0 THEN
-        result := 'La suppression de la clôture des services réalisés est impossible car des heures ont été payées ou bien le paiement a été demandé.';
+
+
+    WHEN tableau = 43 AND version = 2 THEN
+      IF gt(23) <> 0 THEN
+        RETURN gv(23) / gt(23);
+      ELSE
+        RETURN 0;
       END IF;
 
-    END IF;
 
-    RETURN result;
-  END;
 
-END OSE_VALIDATION;
-/
+    WHEN tableau = 44 AND version = 2 THEN
+      IF gt(24) <> 0 THEN
+        RETURN gv(24) / gt(24);
+      ELSE
+        RETURN 0;
+      END IF;
 
--- OSE_WORKFLOW
-CREATE OR REPLACE PACKAGE BODY "OSE_WORKFLOW" AS
-  INTERVENANT_ID NUMERIC DEFAULT NULL;
 
-  TYPE t_workflow IS TABLE OF tbl_workflow%rowtype INDEX BY PLS_INTEGER;
 
-  TYPE t_dep IS TABLE OF wf_etape_dep%rowtype INDEX BY PLS_INTEGER;
-  TYPE t_deps IS TABLE OF t_dep INDEX BY PLS_INTEGER;
-  TYPE t_deps_bloquantes IS TABLE OF wf_dep_bloquante%rowtype INDEX BY PLS_INTEGER;
+    WHEN tableau = 45 AND version = 2 THEN
+      IF gt(25) <> 0 THEN
+        RETURN gv(25) / gt(25);
+      ELSE
+        RETURN 0;
+      END IF;
 
-  -- propre au calcul courant ! !
-  etapes          t_workflow;
-  deps            t_deps;
-  deps_initialized boolean default false;
-  deps_bloquantes t_deps_bloquantes;
-  deps_bloquantes_index PLS_INTEGER DEFAULT 1;
 
 
+    WHEN tableau = 46 AND version = 2 THEN
+      IF gt(26) <> 0 THEN
+        RETURN gv(26) / gt(26);
+      ELSE
+        RETURN 0;
+      END IF;
 
 
-  FUNCTION ETAPE_FRANCHIE( etape tbl_workflow%rowtype, need_done boolean default false ) RETURN FLOAT IS
-    res FLOAT DEFAULT 0;
-  BEGIN
-    IF etape.objectif = 0 THEN
-      IF need_done THEN RETURN 0; ELSE RETURN 1; END IF;
-    END IF;
 
-    IF etape.atteignable = 0 THEN RETURN 0; END IF;
+    WHEN tableau = 47 AND version = 2 THEN
+      IF gt(27) <> 0 THEN
+        RETURN gv(27) / gt(27);
+      ELSE
+        RETURN 0;
+      END IF;
 
-    IF etape.objectif > 0 THEN
-      res := etape.realisation / etape.objectif;
-    END IF;
 
-    IF res > 1 THEN
-      res := 1;
-    END IF;
 
-    RETURN res;
-  END;
+    WHEN tableau = 51 AND version = 2 THEN
+      RETURN LEAST( ose_formule.intervenant.service_du, gt(21) ) * gv(41);
 
 
 
-  PROCEDURE POPULATE_ETAPES( INTERVENANT_ID NUMERIC ) IS
-    i NUMERIC DEFAULT 0;
-  BEGIN
-    etapes.delete; -- initialisation
+    WHEN tableau = 52 AND version = 2 THEN
+      RETURN LEAST( gt(31), gt(22) ) * gv(42);
 
-    FOR wie IN (
-      SELECT
-        wep.annee_id                                          annee_id,
-        e.id                                                  etape_id,
-        w.structure_id                                        structure_id,
-        ROUND(COALESCE(w.objectif,0),2)                       objectif,
-        CASE WHEN w.intervenant_id IS NULL THEN 0 ELSE 1 END  atteignable,
-        ROUND(COALESCE(w.realisation,0),2)                    realisation,
-        wep.etape_code                                        etape_code,
-        si.id                                                 statut_intervenant_id,
-        ti.id                                                 type_intervenant_id,
-        ti.code                                               type_intervenant_code
-      FROM
-        v_workflow_etape_pertinente wep
-        JOIN wf_etape                 e ON e.code = wep.etape_code
-        JOIN intervenant              i ON i.id = wep.intervenant_id
-        JOIN statut_intervenant      si ON si.id = i.statut_id
-        JOIN type_intervenant        ti ON ti.id = si.type_intervenant_id
-        LEFT JOIN v_tbl_workflow      w ON w.intervenant_id = wep.intervenant_id AND w.etape_code = wep.etape_code
-      WHERE
-        wep.intervenant_id = POPULATE_ETAPES.INTERVENANT_ID
-        AND (e.obligatoire = 1 OR w.intervenant_id IS NOT NULL)
-      ORDER BY
-        e.ordre
-    ) LOOP
-      etapes( i ).annee_id              := wie.annee_id;
-      etapes( i ).intervenant_id        := intervenant_id;
-      etapes( i ).etape_id              := wie.etape_id;
-      etapes( i ).structure_id          := wie.structure_id;
-      etapes( i ).atteignable           := wie.atteignable;
-      etapes( i ).objectif              := wie.objectif;
-      etapes( i ).realisation           := wie.realisation;
-      etapes( i ).etape_code            := wie.etape_code;
-      etapes( i ).statut_intervenant_id := wie.statut_intervenant_id;
-      etapes( i ).type_intervenant_id   := wie.type_intervenant_id;
-      etapes( i ).type_intervenant_code := wie.type_intervenant_code;
-      i := i + 1;
-    END LOOP;
-  END;
 
 
+    WHEN tableau = 53 AND version = 2 THEN
+      RETURN LEAST( gt(32), gt(23) ) * gv(43);
 
-  -- peuple l'arbre des dépendances entre étapes de workflow
-  PROCEDURE POPULATE_DEPS( INTERVENANT_ID NUMERIC ) IS
-    s PLS_INTEGER; -- index de l'étape suivante
-    p PLS_INTEGER; -- index de l'étape précédente
-  BEGIN
-    IF deps_initialized THEN RETURN; END IF;
 
-    FOR d IN (
-      SELECT
-        wed.*
-      FROM
-        wf_etape_dep wed
-        JOIN intervenant i ON i.id = POPULATE_DEPS.INTERVENANT_ID
-        JOIN statut_intervenant si ON si.id = i.statut_id
-      WHERE
-        active = 1
-        AND wed.type_intervenant_id IS NULL OR wed.type_intervenant_id = si.type_intervenant_id
-    ) LOOP
-      deps(d.etape_suiv_id)(d.etape_prec_id) := d;
-    END LOOP;
 
-    deps_initialized := true;
-  END;
+    WHEN tableau = 54 AND version = 2 THEN
+      RETURN LEAST( gt(33), gt(24) ) * gv(44);
 
 
 
-  PROCEDURE ADD_DEP_BLOQUANTE( wf_etape_dep_id NUMERIC, tbl_workflow_id NUMERIC ) IS
-  BEGIN
-    deps_bloquantes_index := deps_bloquantes_index + 1;
-    deps_bloquantes(deps_bloquantes_index).wf_etape_dep_id := wf_etape_dep_id;
-    deps_bloquantes(deps_bloquantes_index).tbl_workflow_id := tbl_workflow_id;
-  END;
+    WHEN tableau = 55 AND version = 2 THEN
+      RETURN LEAST( gt(34), gt(25) ) * gv(45);
 
 
 
-  PROCEDURE CALCUL_ATTEIGNABLE( s PLS_INTEGER, d wf_etape_dep%rowtype ) IS
-    count_tested PLS_INTEGER DEFAULT 0;
-    count_na     PLS_INTEGER DEFAULT 0;
-    p PLS_INTEGER; -- index de l'étape précédente
-  BEGIN
+    WHEN tableau = 56 AND version = 2 THEN
+      RETURN LEAST( gt(35), gt(26) ) * gv(46);
 
-    p := etapes.FIRST;
-    LOOP EXIT WHEN p IS NULL;
-      IF etapes(p).etape_id = d.etape_prec_id THEN
-        -- on restreint en fonction du périmètre visé :
-        --  - si la dépendance n'est pas locale alors on teste
-        --  - si les structures aussi bien de l'étape testée que de l'étape dépendante sont nulles alors on teste aussi car elles sont "universelles"
-        --  - si les structures sont équivalentes alors on teste, sinon elles ne sont pas dans le périmètre local
-        IF
-          (d.locale = 0)
-          OR etapes(s).structure_id IS NULL
-          OR etapes(p).structure_id IS NULL
-          OR etapes(s).structure_id = etapes(p).structure_id
-        THEN
-          count_tested := count_tested + 1;
 
-          -- on teste le type de franchissement désiré et si ce n'est pas bon alors on déclare l'étape courante non atteignable
 
-          --  - idem si on a besoin d'une dépendance partiellement franchie est qu'elle ne l'est pas
-          IF d.partielle = 1 THEN
-            IF ETAPE_FRANCHIE(etapes(p), d.obligatoire=1) = 0 THEN -- si le franchissement est totalement inexistant
-              count_na := count_na + 1;
-            END IF;
-          --  - si on a besoin d'une dépendance complètement franchie est qu'elle ne l'est pas alors ce n'est pas atteignable
-          ELSE
-            IF ETAPE_FRANCHIE(etapes(p), d.obligatoire=1) < 1 THEN
-              count_na := count_na + 1;
-            END IF;
-          END IF;
-        END IF;
+    WHEN tableau = 57 AND version = 2 THEN
+      RETURN LEAST( gt(36), gt(27) ) * gv(47);
 
-      END IF;
-      p := etapes.next(p);
-    END LOOP;
 
-    -- on applique le résultat uniquement si des étapes dépendantes ont été trouvées
-    IF count_tested > 0 THEN
 
-      -- si les étapes dépendantes ont été intégralement franchies
-      IF d.integrale = 1 THEN
-        -- si l'intégralité des étapes est atteignable = NON si au moins une ne l'est pas
-        IF count_na > 0 THEN
-          etapes(s).atteignable := 0;
-          ADD_DEP_BLOQUANTE( d.id, s );
-        END IF;
+    WHEN tableau = 61 AND version = 2 THEN
+      RETURN gv(51) * vh.taux_fi;
 
-      -- sinon...
+
+
+    WHEN tableau = 61 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(51) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
       ELSE
-        -- si au moins une étape est atteignable = NON si toutes ne sont pas atteignables
-        IF count_tested = count_na THEN
-          etapes(s).atteignable := 0;
-          ADD_DEP_BLOQUANTE( d.id, s );
-        END IF;
+        RETURN 0;
       END IF;
-    END IF;
-  END;
 
 
 
-  -- calcule si les étapes sont atteignables ou non
-  PROCEDURE CALCUL_ATTEIGNABLES IS
-    e PLS_INTEGER; -- index de l'étape courante
-    d PLS_INTEGER; -- ID de l'étape précédante
-  BEGIN
-    deps_bloquantes.delete;
-    e := etapes.FIRST;
-    LOOP EXIT WHEN e IS NULL;
-      IF deps.exists(etapes(e).etape_id) THEN -- s'il n'y a aucune dépendance alors pas de test!!
-        d := deps(etapes(e).etape_id).FIRST;
-        LOOP EXIT WHEN d IS NULL;
+    WHEN tableau = 62 AND version = 2 THEN
+      RETURN gv(52) * vh.taux_fi;
 
-          CALCUL_ATTEIGNABLE(e, deps(etapes(e).etape_id)(d));
 
-          d := deps(etapes(e).etape_id).next(d);
-        END LOOP;
+
+    WHEN tableau = 62 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(52) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
+      ELSE
+        RETURN 0;
       END IF;
-      e := etapes.next(e);
-    END LOOP;
-  END;
 
 
 
-  FUNCTION ENREGISTRER_ETAPE( e tbl_workflow%rowtype ) RETURN NUMERIC IS
-    n_etape_id NUMERIC;
-  BEGIN
+    WHEN tableau = 71 AND version = 2 THEN
+      RETURN gv(51) * vh.taux_fa;
 
-    MERGE INTO tbl_workflow w USING dual ON (
 
-          w.intervenant_id      = e.intervenant_id
-      AND w.etape_id            = e.etape_id
-      AND NVL(w.structure_id,0) = NVL(e.structure_id,0)
 
-    ) WHEN MATCHED THEN UPDATE SET
+    WHEN tableau = 71 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(51) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
+      ELSE
+        RETURN 0;
+      END IF;
 
-      atteignable                  = e.atteignable,
-      objectif                     = e.objectif,
-      realisation                  = e.realisation,
-      etape_code                   = e.etape_code,
-      statut_intervenant_id        = e.statut_intervenant_id,
-      type_intervenant_id          = e.type_intervenant_id,
-      type_intervenant_code        = e.type_intervenant_code,
-      to_delete                    = 0
 
-    WHEN NOT MATCHED THEN INSERT (
 
-      id,
-      annee_id,
-      intervenant_id,
-      etape_id,
-      structure_id,
-      atteignable,
-      objectif,
-      realisation,
-      etape_code,
-      statut_intervenant_id,
-      type_intervenant_id,
-      type_intervenant_code,
-      to_delete
+    WHEN tableau = 72 AND version = 2 THEN
+      RETURN gv(52) * vh.taux_fa;
 
-    ) VALUES (
 
-      TBL_WORKFLOW_ID_SEQ.NEXTVAL,
-      e.annee_id,
-      e.intervenant_id,
-      e.etape_id,
-      e.structure_id,
-      e.atteignable,
-      e.objectif,
-      e.realisation,
-      e.etape_code,
-      e.statut_intervenant_id,
-      e.type_intervenant_id,
-      e.type_intervenant_code,
-      0
 
-    );
+    WHEN tableau = 72 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(52) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
+      ELSE
+        RETURN 0;
+      END IF;
 
-    SELECT w.id INTO n_etape_id FROM tbl_workflow w WHERE
-      w.intervenant_id          = e.intervenant_id
-      AND w.etape_id            = e.etape_id
-      AND NVL(w.structure_id,0) = NVL(e.structure_id,0)
-    ;
 
-    RETURN n_etape_id;
-  END;
 
+    WHEN tableau = 81 AND version = 2 THEN
+      RETURN gv(51) * vh.taux_fc;
 
 
-  PROCEDURE ENREGISTRER_DEP_BLOQUANTE( db wf_dep_bloquante%rowtype ) IS
-  BEGIN
-    MERGE INTO wf_dep_bloquante wdb USING dual ON (
 
-          wdb.wf_etape_dep_id   = db.wf_etape_dep_id
-      AND wdb.tbl_workflow_id   = db.tbl_workflow_id
+    WHEN tableau = 82 AND version = 2 THEN
+      RETURN gv(52) * vh.taux_fc;
 
-    ) WHEN MATCHED THEN UPDATE SET
 
-      to_delete                 = 0
 
-    WHEN NOT MATCHED THEN INSERT (
+    WHEN tableau = 83 AND version = 2 THEN
+      RETURN gv(53) * vh.taux_fc;
 
-      id,
-      wf_etape_dep_id,
-      tbl_workflow_id,
-      to_delete
 
-    ) VALUES (
 
-      WF_DEP_BLOQUANTE_ID_SEQ.NEXTVAL,
-      db.wf_etape_dep_id,
-      db.tbl_workflow_id,
-      0
+    WHEN tableau = 83 AND version = 3 THEN
+      RETURN gv(53);
 
-    );
-  END;
 
 
+    WHEN tableau = 84 AND version = 2 THEN
+      RETURN gv(54) * vh.taux_fc;
 
-  PROCEDURE ENREGISTRER( INTERVENANT_ID NUMERIC ) IS
-    i PLS_INTEGER;
-  BEGIN
 
-    UPDATE tbl_workflow SET to_delete = 1 WHERE intervenant_id = ENREGISTRER.INTERVENANT_ID;
-    UPDATE wf_dep_bloquante SET to_delete = 1 WHERE tbl_workflow_id IN (SELECT id FROM tbl_workflow WHERE intervenant_id = ENREGISTRER.INTERVENANT_ID);
 
-    i := etapes.FIRST;
-    LOOP EXIT WHEN i IS NULL;
-      etapes(i).id := ENREGISTRER_ETAPE( etapes(i) );
-      i := etapes.NEXT(i);
-    END LOOP;
+    WHEN tableau = 84 AND version = 3 THEN
+      RETURN gv(54);
 
-    i := deps_bloquantes.FIRST;
-    LOOP EXIT WHEN i IS NULL;
-      deps_bloquantes(i).tbl_workflow_id := etapes(deps_bloquantes(i).tbl_workflow_id).id;
-      ENREGISTRER_DEP_BLOQUANTE( deps_bloquantes(i) );
-      i := deps_bloquantes.NEXT(i);
-    END LOOP;
 
-    DELETE FROM tbl_workflow WHERE TO_DELETE = 1 AND intervenant_id = ENREGISTRER.INTERVENANT_ID;
-    DELETE FROM wf_dep_bloquante WHERE TO_DELETE = 1;
-  END;
 
+    WHEN tableau = 91 AND version = 2 THEN
+      IF gv(21) <> 0 THEN
+        RETURN gv(51) / gv(21);
+      ELSE
+        RETURN 0;
+      END IF;
 
 
-  PROCEDURE DEP_CHECK( etape_suiv_id NUMERIC, etape_prec_id NUMERIC ) IS
-    eso NUMERIC;
-    epo NUMERIC;
-  BEGIN
-    SELECT ordre INTO eso FROM wf_etape WHERE id = etape_suiv_id;
-    SELECT ordre INTO epo FROM wf_etape WHERE id = etape_prec_id;
 
-    IF eso < epo THEN
-      raise_application_error(-20101, 'Une étape de Workflow ne peut dépendre d''une étape située en aval');
-    END IF;
-    IF eso = epo THEN
-      raise_application_error(-20101, 'Une étape de Workflow ne peut dépendre d''elle-même');
-    END IF;
-  END;
+    WHEN tableau = 92 AND version = 2 THEN
+      IF gv(22) <> 0 THEN
+        RETURN gv(52) / gv(22);
+      ELSE
+        RETURN 0;
+      END IF;
 
 
 
-  PROCEDURE DEBUG_CALCUL( INTERVENANT_ID NUMERIC ) IS
-    i PLS_INTEGER;
-    d PLS_INTEGER;
-    dep_desc VARCHAR2(200);
-  BEGIN
-    ose_test.echo('');
-    ose_test.echo('-- DEBUG WORKFLOW ETAPE INTERVENANT_ID='|| INTERVENANT_ID ||' --');
-    i := etapes.FIRST;
-    LOOP EXIT WHEN i IS NULL;
-      /*ose_test.echo(
-               'etape='       || RPAD( ose_test.get_wf_etape_by_id(etapes(i).etape_id).code, 30, ' ' )
-          || ', structure='   || RPAD( NVL(ose_test.get_structure_by_id(etapes(i).structure_id).libelle_court,' '), 20, ' ' )
-          || ', ' || CASE WHEN etapes(i).atteignable=1 THEN 'atteignable' ELSE 'na' END
-          || ', objectif= ' || ROUND(etapes(i).objectif)
-          || ', realisation= ' || ROUND(etapes(i).realisation)
-      );*/
+    WHEN tableau = 93 AND version = 2 THEN
+      IF gv(23) <> 0 THEN
+        RETURN gv(53) / gv(23);
+      ELSE
+        RETURN 0;
+      END IF;
 
-      d := deps_bloquantes.FIRST;
-      LOOP EXIT WHEN d IS NULL;
-        IF deps_bloquantes(d).tbl_workflow_id = i THEN
 
-          SELECT
-            we.desc_non_franchie INTO dep_desc
-          FROM
-            wf_etape_dep wed
-            JOIN wf_etape we ON we.id = wed.etape_prec_id
-          WHERE
-            wed.id = deps_bloquantes(d).wf_etape_dep_id;
 
-          ose_test.echo('    CAUSE =' || dep_desc);
-        END IF;
-        d := deps_bloquantes.NEXT(d);
-      END LOOP;
+    WHEN tableau = 94 AND version = 2 THEN
+      IF gv(24) <> 0 THEN
+        RETURN gv(54) / gv(24);
+      ELSE
+        RETURN 0;
+      END IF;
 
-      i := etapes.NEXT(i);
-    END LOOP;
-    ose_test.echo('');
-  END;
 
 
+    WHEN tableau = 95 AND version = 2 THEN
+      IF gv(25) <> 0 THEN
+        RETURN gv(55) / gv(25);
+      ELSE
+        RETURN 0;
+      END IF;
 
-  -- calcul du workflow pour un intervenant
-  PROCEDURE CALCULER( INTERVENANT_ID NUMERIC ) IS
-  BEGIN
-    set_intervenant(intervenant_id);
-    POPULATE_ETAPES( INTERVENANT_ID );
-    POPULATE_DEPS( INTERVENANT_ID );
-    CALCUL_ATTEIGNABLES;
-    IF OSE_TEST.DEBUG_ENABLED THEN
-      DEBUG_CALCUL( INTERVENANT_ID );
-    END IF;
-    ENREGISTRER( INTERVENANT_ID );
-    set_intervenant();
-  END;
 
 
+    WHEN tableau = 96 AND version = 2 THEN
+      IF gv(26) <> 0 THEN
+        RETURN gv(56) / gv(26);
+      ELSE
+        RETURN 0;
+      END IF;
 
-  PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL ) IS
-  BEGIN
-    FOR mp IN (
-      SELECT
-        id intervenant_id
-      FROM
-        intervenant i
-      WHERE
-        i.histo_destruction IS NULL
-        AND (CALCULER_TOUT.ANNEE_ID IS NULL OR i.annee_id = CALCULER_TOUT.ANNEE_ID)
-    )
-    LOOP
-      CALCULER( mp.intervenant_id );
-    END LOOP;
-  END;
 
 
+    WHEN tableau = 97 AND version = 2 THEN
+      IF gv(27) <> 0 THEN
+        RETURN gv(57) / gv(27);
+      ELSE
+        RETURN 0;
+      END IF;
 
-  PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS ) IS
-    intervenant_id NUMERIC;
-    TYPE r_cursor IS REF CURSOR;
-    diff_cur r_cursor;
-  BEGIN
-    OPEN diff_cur FOR 'WITH interv AS (SELECT id intervenant_id, intervenant.* FROM intervenant)
-    SELECT intervenant_id FROM interv WHERE ' || unicaen_tbl.PARAMS_TO_CONDS( params );
-    LOOP
-      FETCH diff_cur INTO intervenant_id; EXIT WHEN diff_cur%NOTFOUND;
-      BEGIN
-        CALCULER( intervenant_id );
-      END;
-    END LOOP;
-    CLOSE diff_cur;
-  END;
 
 
+    WHEN tableau = 101 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(91);
+      END IF;
 
-  FUNCTION GET_INTERVENANT RETURN NUMERIC IS
-  BEGIN
-    RETURN OSE_WORKFLOW.INTERVENANT_ID;
-  END;
 
-  PROCEDURE SET_INTERVENANT( INTERVENANT_ID NUMERIC DEFAULT NULL) IS
-  BEGIN
-    IF SET_INTERVENANT.INTERVENANT_ID = -1 THEN
-      OSE_WORKFLOW.INTERVENANT_ID := NULL;
-    ELSE
-      OSE_WORKFLOW.INTERVENANT_ID := SET_INTERVENANT.INTERVENANT_ID;
-    END IF;
-  END;
 
-  FUNCTION MATCH_INTERVENANT(INTERVENANT_ID NUMERIC DEFAULT NULL) RETURN NUMERIC IS
-  BEGIN
-    IF OSE_WORKFLOW.INTERVENANT_ID IS NULL OR OSE_WORKFLOW.INTERVENANT_ID = MATCH_INTERVENANT.INTERVENANT_ID THEN
-      RETURN 1;
-    ELSE
-      RETURN 0;
-    END IF;
-  END;
-END OSE_WORKFLOW;
-/
+    WHEN tableau = 102 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(92);
+      END IF;
 
--- UCBN_LDAP
-CREATE OR REPLACE PACKAGE BODY "UCBN_LDAP" AS
 
---===================================================================
---===================================================================
--- version()
---===================================================================
-FUNCTION version RETURN VARCHAR2 IS
-BEGIN
-  RETURN ' 0.7.1 (2017-05-16) ';
-END version;
 
+    WHEN tableau = 103 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(93);
+      END IF;
 
---===================================================================
---===================================================================
--- free()
---===================================================================
-FUNCTION free RETURN NUMBER IS
-  l_retval PLS_INTEGER ;
-BEGIN
-  BEGIN
-    l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
-    RETURN l_retval ;
-  EXCEPTION
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-    RETURN NULL ;
-  END;
-END free ;
 
 
---===================================================================
---===================================================================
--- ldap_connect()
---===================================================================
-FUNCTION ldap_connect RETURN NUMBER IS
-  ldap_host   VARCHAR2(256 char) := 'ldap.unicaen.fr';
-  ldap_port   VARCHAR2(3 char)   := '389';
-  ldap_user   VARCHAR2(256 char) := 'uid=oracle-ldap,ou=system,dc=unicaen,dc=fr' ;
-  ldap_passwd VARCHAR2(30 char)  := 'HBHOe2CQgrAI' ;
-  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
+    WHEN tableau = 104 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(94);
+      END IF;
 
-  l_retval  PLS_INTEGER ;
 
-  resultat  VARCHAR2(1024 char) := NULL ;
 
-BEGIN
-  -- Ouverture de connexion
-  BEGIN
-  ldap_sess := DBMS_LDAP.init(hostname => ldap_host,
-                              portnum  => ldap_port) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.INIT_FAILED THEN
-      RETURN 1 ;
-  END;
+    WHEN tableau = 105 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(95);
+      END IF;
 
 
-  -- Authentification
-  BEGIN
-  l_retval := DBMS_LDAP.simple_bind_s(ld     => ldap_sess,
-                                      dn     => ldap_user,
-                                      passwd => ldap_passwd) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.GENERAL_ERROR THEN
-      l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
-      RETURN 2 ;
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
-      RETURN 2 ;
-  END;
-  RETURN 0 ;
-END ldap_connect;
 
+    WHEN tableau = 106 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(96);
+      END IF;
 
 
 
+    WHEN tableau = 107 AND version = 2 THEN
+      IF gt(37) <> 0 THEN
+        RETURN 0;
+      ELSE
+        RETURN 1 - gv(97);
+      END IF;
 
 
---===================================================================
---===================================================================
--- get(filtre, attribut)
---===================================================================
-FUNCTION get(filtre IN VARCHAR2, attribut IN VARCHAR2, v_multi IN VARCHAR2 DEFAULT 'N', a_multi OUT ARRAY_STR) RETURN VARCHAR2 IS
-  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
-  l_retval  PLS_INTEGER ;
-  l_attrs   DBMS_LDAP.string_collection ;
-  l_message DBMS_LDAP.message ;
-  l_entry   DBMS_LDAP.message ;
-  l_attr_name VARCHAR2(256 char) ;
-  l_ber_element  DBMS_LDAP.ber_element;
-  l_vals         DBMS_LDAP.string_collection;
 
-  i         PLS_INTEGER ;
-  nb_res    PLS_INTEGER ;
-  probleme  EXCEPTION ;
-  resultat  VARCHAR2(1024 char) := NULL ;
+    WHEN tableau = 111 AND version = 2 THEN
+      RETURN gv(11) * vh.taux_service_compl * gv(101);
 
-  elapsed_since_used NUMBER ;
 
-BEGIN
 
-  -- On regarde depuis combien de temps la session n'a pas ete utilisee
-  elapsed_since_used:= to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) - last_used ;
-  last_used := to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) ;
+    WHEN tableau = 112 AND version = 2 THEN
+      RETURN gv(12) * vh.taux_service_compl * gv(102);
 
-  -- Si c'est trop vieux, on se reconnecte
-  IF elapsed_since_used > 10 THEN
-    l_retval := free() ;
-  END IF ;
 
-  -- Si on n'est pas connecte:
-  IF ldap_sess IS NULL THEN
-    DBMS_OUTPUT.PUT_LINE('Reconnexion au serveur LDAP...');
-    l_retval := ldap_connect() ;
-    CASE l_retval
-      WHEN 1 THEN RETURN '#Err 0010';
-      WHEN 2 THEN RETURN '#Err 0011';
-      ELSE NULL;
-    END CASE;
-  END IF ;
 
-  -- On cherche le mail seulement
-  l_attrs(1) := attribut ;
-  BEGIN
-  l_retval := DBMS_LDAP.search_s(ld       => ldap_sess,
-                                 base     => ldap_base,
-                                 scope    => DBMS_LDAP.SCOPE_SUBTREE,
-                                 filter   => filtre,
-                                 attrs    => l_attrs,
-                                 attronly => 0,
-                                 res      => l_message) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.GENERAL_ERROR THEN
-      DBMS_OUTPUT.PUT_LINE('Erreur: '||SQLERRM);
-      RETURN '#Err 0020' ;
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      RETURN '#Err 0021' ;
-    WHEN DBMS_LDAP.invalid_search_scope THEN
-      RETURN '#Err 0022' ;
-  END;
+    WHEN tableau = 113 AND version = 2 THEN
+      RETURN gv(13) * vh.taux_service_compl * gv(103);
 
 
-  BEGIN
-  nb_res := DBMS_LDAP.count_entries(ld => ldap_sess, msg => l_message) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      RETURN '#Err 0030' ;
-    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-      RETURN '#Err 0031' ;
-    WHEN DBMS_LDAP.count_entry_error THEN
-      RETURN '#Err 0032' ;
-  END;
 
-  IF nb_res < 1 THEN
-    -- Pas besoin de fermer la connexion puisqu'on en utilise qu'une...
-    -- l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
-    -- RETURN '#Err 0033'; -- On retourne NULL depuis la 0.4.1
-    RETURN NULL ;
-  END IF;
+    WHEN tableau = 114 AND version = 2 THEN
+      RETURN gv(14) * vh.taux_service_compl * gv(104);
 
-  -- Les entrees retournees
-  BEGIN
-  l_entry := DBMS_LDAP.first_entry(ld => ldap_sess, msg => l_message);
 
-  EXCEPTION
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      RETURN '#Err 0034' ;
-    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-      RETURN '#Err 0035' ;
-  END;
 
+    WHEN tableau = 115 AND version = 2 THEN
+      RETURN gv(15) * gv(105);
 
-  WHILE l_entry IS NOT NULL LOOP
-    -- Tous les attributs de l'entree:
-    BEGIN
-    l_attr_name := DBMS_LDAP.first_attribute(ld        => ldap_sess,
-                                             ldapentry => l_entry,
-                                             ber_elem  => l_ber_element);
-    EXCEPTION
-      WHEN DBMS_LDAP.INVALID_SESSION THEN
-        RETURN '#Err 0040' ;
-      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-        RETURN '#Err 0041' ;
-    END;
 
-    WHILE l_attr_name IS NOT NULL LOOP
-      -- Les valeurs de cet attribut
-      BEGIN
-      l_vals := DBMS_LDAP.get_values (ld        => ldap_sess,
-                                      ldapentry => l_entry,
-                                      attr      => l_attr_name);
-      EXCEPTION
-        WHEN DBMS_LDAP.INVALID_SESSION THEN
-          RETURN '#Err 0044' ;
-        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-          RETURN '#Err 0045' ;
-      END;
 
-      -- Si le nom de l'attribut ne correspond pas a l'attribut demande, next.
-      -- C'est pour empecher le retour de "supannAutreMail" quand on demande "mail" par exemple.
-      IF l_attr_name = attribut THEN
-        -- On ne retourne que la premiere valeur si mono-value
-        -- Sinon, on retourne le tableau a_multi
-        IF v_multi = 'N' THEN
-          resultat := l_vals(l_vals.FIRST) ;
-        ELSE
-          a_multi := ARRAY_STR() ; -- Initialisation du tableau
-          i := 0 ; -- tableau commence a 1 (d'ou i++ a l'entree du FOR)
-          FOR v IN l_vals.FIRST .. l_vals.LAST LOOP
-            i := i + 1 ;
-            a_multi.extend ;
-            a_multi(i) := l_vals(v) ;
-          END LOOP ;
-          resultat := '#Err Multi-value: '||i ;
-        END IF;
-      END IF;
+    WHEN tableau = 116 AND version = 2 THEN
+      RETURN gv(16) * gv(106);
 
-      EXIT WHEN resultat IS NOT NULL ;
 
-      -- Attribut suivant
-      BEGIN
-      l_attr_name := DBMS_LDAP.next_attribute(ld        => ldap_sess,
-                                              ldapentry => l_entry,
-                                              ber_elem  => l_ber_element);
-      EXCEPTION
-        WHEN DBMS_LDAP.INVALID_SESSION THEN
-          RETURN '#Err 0042' ;
-        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-          RETURN '#Err 0043' ;
-      END;
 
-    END LOOP ; -- LOOP Fin des attributs
-    IF l_ber_element IS NOT NULL THEN
-      DBMS_LDAP.ber_free(l_ber_element, 0) ;
-    END IF ;
-    EXIT WHEN resultat IS NOT NULL ;
-    BEGIN
-    l_entry := DBMS_LDAP.next_entry(ld  => ldap_sess,
-                                    msg => l_entry);
-    EXCEPTION
-      WHEN DBMS_LDAP.INVALID_SESSION THEN
-        RETURN '#Err 0036' ;
-      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-        RETURN '#Err 0037' ;
-    END;
-  END LOOP ; -- LOOP Fin des entrees
+    WHEN tableau = 117 AND version = 2 THEN
+      RETURN gv(17) * gv(107);
 
-  -- Liberation de la memoire
-  --l_retval := DBMS_LDAP.msgfree(l_message) ;
-  IF l_entry IS NOT NULL THEN
-    l_retval := DBMS_LDAP.msgfree(l_entry) ;
-  END IF ;
 
-  -- Pas de deconnexion (on la reutilisera)
-  --l_retval := DBMS_LDAP.unbind_s(ld => l_session);
-  --DBMS_OUTPUT.PUT_LINE('L_RETVAL: ' || l_retval);
 
-  RETURN resultat ;
+    WHEN tableau = 123 AND version = 2 THEN
+      IF vh.taux_fc = 1 THEN
+        RETURN gv(113) * vh.ponderation_service_compl;
+      ELSE
+        RETURN gv(113);
+      END IF;
 
-END get ;
 
 
+    WHEN tableau = 123 AND version = 3 THEN
+      IF vh.taux_fc > 0 THEN
+        RETURN gv(113) * vh.ponderation_service_compl;
+      ELSE
+        RETURN gv(113);
+      END IF;
 
---===================================================================
---===================================================================
--- alias2mail(alias)
---===================================================================
-FUNCTION alias2mail(ldap_alias IN VARCHAR2) RETURN VARCHAR2 IS
-BEGIN
-  RETURN get('supannAliasLogin='||ldap_alias, 'mail', 'N', a_multi) ;
-END alias2mail;
 
---===================================================================
---===================================================================
--- uid2mail(ldap_uid)
---===================================================================
-FUNCTION uid2mail(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
-BEGIN
-  RETURN get('uid='||ldap_uid, 'mail', 'N', a_multi) ;
-END uid2mail;
 
+    WHEN tableau = 124 AND version = 2 THEN
+      IF vh.taux_fc = 1 THEN
+        RETURN gv(114) * vh.ponderation_service_compl;
+      ELSE
+        RETURN gv(114);
+      END IF;
 
---===================================================================
---===================================================================
--- hid2mail(harpege_uid)
---===================================================================
-FUNCTION hid2mail(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
-BEGIN
-  RETURN get('uid=p'||to_char(harpege_uid,'FM00000000'), 'mail', 'N', a_multi) ;
-END hid2mail;
 
 
---===================================================================
---===================================================================
--- etu2mail(code_etu)
---===================================================================
-FUNCTION etu2mail(code_etu IN NUMBER) RETURN VARCHAR2 IS
-BEGIN
-  RETURN get('uid=e'||to_char(code_etu,'FM00000000'), 'mail', 'N', a_multi) ;
-END etu2mail;
+    WHEN tableau = 124 AND version = 3 THEN
+      IF vh.taux_fc > 0 THEN
+        RETURN gv(114) * vh.ponderation_service_compl;
+      ELSE
+        RETURN gv(114);
+      END IF;
 
 
---===================================================================
---===================================================================
--- uid2alias(ldap_uid)
---===================================================================
-FUNCTION uid2alias(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
-BEGIN
-  RETURN get('uid='||ldap_uid, 'supannAliasLogin', 'N', a_multi) ;
-END uid2alias;
 
---===================================================================
---===================================================================
--- hid2alias(harpege_uid)
---===================================================================
-FUNCTION hid2alias(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
+    WHEN tableau = 131 AND version = 2 THEN
+      RETURN gv(111) * vh.taux_fi;
+
+
+
+    WHEN tableau = 131 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(111) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 132 AND version = 2 THEN
+      RETURN gv(112) * vh.taux_fi;
+
+
+
+    WHEN tableau = 132 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(112) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 141 AND version = 2 THEN
+      RETURN gv(111) * vh.taux_fa;
+
+
+
+    WHEN tableau = 141 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(111) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 142 AND version = 2 THEN
+      RETURN gv(112) * vh.taux_fa;
+
+
+
+    WHEN tableau = 142 AND version = 3 THEN
+      IF vh.taux_fi + vh.taux_fa > 0 THEN
+        RETURN gv(112) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 151 AND version = 2 THEN
+      RETURN gv(111) * vh.taux_fc;
+
+
+
+    WHEN tableau = 152 AND version = 2 THEN
+      RETURN gv(112) * vh.taux_fc;
+
+
+
+    WHEN tableau = 153 AND version = 2 THEN
+      IF gv(123) = gv(113) THEN
+        RETURN gv(113) * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 153 AND version = 3 THEN
+      IF gv(123) = gv(113) THEN
+        RETURN gv(113);
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 154 AND version = 2 THEN
+      IF gv(124) = gv(114) THEN
+        RETURN gv(114) * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 154 AND version = 3 THEN
+      IF gv(124) = gv(114) THEN
+        RETURN gv(114);
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 163 AND version = 2 THEN
+      IF gv(123) <> gv(113) THEN
+        RETURN gv(123) * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 163 AND version = 3 THEN
+      IF gv(123) <> gv(113) THEN
+        RETURN gv(123);
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 164 AND version = 2 THEN
+      IF gv(124) <> gv(114) THEN
+        RETURN gv(124) * vh.taux_fc;
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    WHEN tableau = 164 AND version = 3 THEN
+      IF gv(124) <> gv(114) THEN
+        RETURN gv(124);
+      ELSE
+        RETURN 0;
+      END IF;
+
+
+
+    ELSE
+      raise_application_error( -20001, 'Le tableau ' || tableau || ' version ' || version || ' n''existe pas!');
+  END CASE; END;
+
+
+
+
+
+
+
+  PROCEDURE CALCUL_RESULTAT_V2 IS
+    tableaux       t_tableaux_configs;
+    valeur         FLOAT;
+  BEGIN
+
+    -- Définition des tableaux à utiliser
+    tableaux := t_tableaux_configs(
+      tc( 11,2    ), tc( 12,2    ), tc( 13,2    ), tc( 14,2    ), tc( 15,2,'r' ), tc( 16,2,'r' ), tc( 17,2,'r' ),
+      tc( 21,2    ), tc( 22,2    ), tc( 23,2    ), tc( 24,2    ), tc( 25,2,'r' ), tc( 26,2,'r' ), tc( 27,2,'r' ),
+      tc( 31,2,'t'), tc( 32,2,'t'), tc( 33,2,'t'), tc( 34,2,'t'), tc( 35,2,'tr'), tc( 36,2,'tr'), tc( 37,2,'tr'),
+      tc( 41,2    ), tc( 42,2    ), tc( 43,2    ), tc( 44,2    ), tc( 45,2,'r' ), tc( 46,2,'r' ), tc( 47,2,'r' ),
+      tc( 51,2    ), tc( 52,2    ), tc( 53,2    ), tc( 54,2    ), tc( 55,2,'r' ), tc( 56,2,'r' ), tc( 57,2,'r' ),
+      tc( 61,2    ), tc( 62,2    ),
+      tc( 71,2    ), tc( 72,2    ),
+      tc( 81,2    ), tc( 82,2    ), tc( 83,2    ), tc( 84,2    ),
+      tc( 91,2    ), tc( 92,2    ), tc( 93,2    ), tc( 94,2    ), tc( 95,2,'r' ), tc( 96,2,'r' ), tc( 97,2,'r' ),
+      tc(101,2    ), tc(102,2    ), tc(103,2    ), tc(104,2    ), tc(105,2,'r' ), tc(106,2,'r' ), tc(107,2,'r' ),
+      tc(111,2    ), tc(112,2    ), tc(113,2    ), tc(114,2    ), tc(115,2,'r' ), tc(116,2,'r' ), tc(117,2,'r' ),
+                                    tc(123,2    ), tc(124,2    ),
+      tc(131,2    ), tc(132,2    ),
+      tc(141,2    ), tc(142,2    ),
+      tc(151,2    ), tc(152,2    ), tc(153,2    ), tc(154,2    ),
+                                    tc(163,2    ), tc(164,2    )
+    );
+
+    -- calcul par tableau pour chaque volume horaire
+    t.delete;
+    FOR it IN tableaux.FIRST .. tableaux.LAST LOOP
+      FOR ivh IN 1 .. ose_formule.volumes_horaires.length LOOP
+        vh_index := ivh;
+        IF
+          ose_formule.volumes_horaires.items(ivh).service_id IS NOT NULL AND NOT tableaux(it).referentiel
+          OR ose_formule.volumes_horaires.items(ivh).service_referentiel_id IS NOT NULL AND tableaux(it).referentiel
+          OR tableaux(it).setTotal -- car on en a besoin tout le temps
+        THEN
+          valeur := EXECFORMULE(tableaux(it).tableau, tableaux(it).version);
+          IF tableaux(it).setTotal THEN
+            ST( tableaux(it).tableau, valeur );
+          ELSE
+            SV( tableaux(it).tableau, valeur );
+          END IF;
+        END IF;
+      END LOOP;
+    END LOOP;
+
+    -- transmisssion des résultats aux volumes horaires et volumes horaires référentiel
+    FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
+      vh_index := i;
+      IF ose_formule.volumes_horaires.items(i).service_id IS NOT NULL THEN
+        ose_formule.volumes_horaires.items(i).service_fi               := gv( 61) + gv( 62);
+        ose_formule.volumes_horaires.items(i).service_fa               := gv( 71) + gv( 72);
+        ose_formule.volumes_horaires.items(i).service_fc               := gv( 81) + gv( 82) + gv( 83) + gv( 84);
+        ose_formule.volumes_horaires.items(i).heures_compl_fi          := gv(131) + gv(132);
+        ose_formule.volumes_horaires.items(i).heures_compl_fa          := gv(141) + gv(142);
+        ose_formule.volumes_horaires.items(i).heures_compl_fc          := gv(151) + gv(152) + gv(153) + gv(154);
+        ose_formule.volumes_horaires.items(i).heures_compl_fc_majorees :=                     gv(163) + gv(164);
+      ELSIF ose_formule.volumes_horaires.items(i).service_referentiel_id IS NOT NULL THEN
+        ose_formule.volumes_horaires.items(i).service_referentiel      := gv( 55) + gv( 56) + gv( 57);
+        ose_formule.volumes_horaires.items(i).heures_compl_referentiel := gv(115) + gv(116) + gv(117);
+      END IF;
+    END LOOP;
+
+    DEBUG_VH;
+  END;
+
+
+
+  PROCEDURE CALCUL_RESULTAT IS
+    tableaux       t_tableaux_configs;
+    valeur         FLOAT;
+  BEGIN
+    -- si l'année est antérieure à 2016/2017 alors on utilise la V2!!
+    IF ose_formule.intervenant.annee_id < 2016 THEN
+      CALCUL_RESULTAT_V2;
+      RETURN;
+    END IF;
+
+
+    -- Définition des tableaux à utiliser
+    tableaux := t_tableaux_configs(
+      tc( 11,3    ), tc( 12,3    ), tc( 13,3    ), tc( 14,3    ), tc( 15,2,'r' ), tc( 16,2,'r' ), tc( 17,2,'r' ),
+      tc( 21,2    ), tc( 22,2    ), tc( 23,2    ), tc( 24,2    ), tc( 25,2,'r' ), tc( 26,2,'r' ), tc( 27,2,'r' ),
+      tc( 31,2,'t'), tc( 32,2,'t'), tc( 33,2,'t'), tc( 34,2,'t'), tc( 35,2,'tr'), tc( 36,2,'tr'), tc( 37,2,'tr'),
+      tc( 41,2    ), tc( 42,2    ), tc( 43,2    ), tc( 44,2    ), tc( 45,2,'r' ), tc( 46,2,'r' ), tc( 47,2,'r' ),
+      tc( 51,2    ), tc( 52,2    ), tc( 53,2    ), tc( 54,2    ), tc( 55,2,'r' ), tc( 56,2,'r' ), tc( 57,2,'r' ),
+      tc( 61,3    ), tc( 62,3    ),
+      tc( 71,3    ), tc( 72,3    ),
+                                    tc( 83,3    ), tc( 84,3    ),
+      tc( 91,2    ), tc( 92,2    ), tc( 93,2    ), tc( 94,2    ), tc( 95,2,'r' ), tc( 96,2,'r' ), tc( 97,2,'r' ),
+      tc(101,2    ), tc(102,2    ), tc(103,2    ), tc(104,2    ), tc(105,2,'r' ), tc(106,2,'r' ), tc(107,2,'r' ),
+      tc(111,2    ), tc(112,2    ), tc(113,2    ), tc(114,2    ), tc(115,2,'r' ), tc(116,2,'r' ), tc(117,2,'r' ),
+                                    tc(123,3    ), tc(124,3    ),
+      tc(131,3    ), tc(132,3    ),
+      tc(141,3    ), tc(142,3    ),
+                                    tc(153,3    ), tc(154,3    ),
+                                    tc(163,3    ), tc(164,3    )
+    );
+
+    -- calcul par tableau pour chaque volume horaire
+    t.delete;
+    FOR it IN tableaux.FIRST .. tableaux.LAST LOOP
+      FOR ivh IN 1 .. ose_formule.volumes_horaires.length LOOP
+        vh_index := ivh;
+        IF
+          ose_formule.volumes_horaires.items(ivh).service_id IS NOT NULL AND NOT tableaux(it).referentiel
+          OR ose_formule.volumes_horaires.items(ivh).service_referentiel_id IS NOT NULL AND tableaux(it).referentiel
+          OR tableaux(it).setTotal -- car on en a besoin tout le temps
+        THEN
+          valeur := EXECFORMULE(tableaux(it).tableau, tableaux(it).version);
+          IF tableaux(it).setTotal THEN
+            ST( tableaux(it).tableau, valeur );
+          ELSE
+            SV( tableaux(it).tableau, valeur );
+          END IF;
+        END IF;
+      END LOOP;
+    END LOOP;
+
+    -- transmission des résultats aux volumes horaires et volumes horaires référentiel
+    FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
+      vh_index := i;
+      IF ose_formule.volumes_horaires.items(i).service_id IS NOT NULL THEN
+        ose_formule.volumes_horaires.items(i).service_fi               := gv( 61) + gv( 62);
+        ose_formule.volumes_horaires.items(i).service_fa               := gv( 71) + gv( 72);
+        ose_formule.volumes_horaires.items(i).service_fc               := gv( 83) + gv( 84);
+        ose_formule.volumes_horaires.items(i).heures_compl_fi          := gv(131) + gv(132);
+        ose_formule.volumes_horaires.items(i).heures_compl_fa          := gv(141) + gv(142);
+        ose_formule.volumes_horaires.items(i).heures_compl_fc          := gv(153) + gv(154);
+        ose_formule.volumes_horaires.items(i).heures_compl_fc_majorees := gv(163) + gv(164);
+      ELSIF ose_formule.volumes_horaires.items(i).service_referentiel_id IS NOT NULL THEN
+        ose_formule.volumes_horaires.items(i).service_referentiel      := gv( 55) + gv( 56) + gv( 57);
+        ose_formule.volumes_horaires.items(i).heures_compl_referentiel := gv(115) + gv(116) + gv(117);
+      END IF;
+    END LOOP;
+
+    DEBUG_VH;
+  END;
+
+
+
+  PROCEDURE PURGE_EM_NON_FC IS
+  BEGIN
+    FOR em IN (
+      SELECT
+        em.id
+      FROM
+        ELEMENT_MODULATEUR em
+        JOIN element_pedagogique ep ON ep.id = em.element_id AND ep.histo_destruction IS NULL
+      WHERE
+        em.histo_destruction IS NULL
+        AND ep.taux_fc < 1
+    ) LOOP
+      UPDATE
+        element_modulateur
+      SET
+        histo_destruction = SYSDATE,
+        histo_destructeur_id = ose_parametre.get_ose_user
+      WHERE
+        id = em.id
+      ;
+    END LOOP;
+  END;
+
+
+END FORMULE_UNICAEN;
+
+/
+
+-- OSE_CHARGENS
+CREATE OR REPLACE PACKAGE BODY "OSE_CHARGENS" AS
+  SCENARIO NUMERIC;
+  NOEUD NUMERIC;
+  old_enable BOOLEAN DEFAULT TRUE;
+
+  TYPE T_PRECALC_HEURES_PARAMS IS RECORD (
+    annee_id                       NUMERIC DEFAULT NULL,
+    structure_id                   NUMERIC DEFAULT NULL,
+    scenario_id                    NUMERIC DEFAULT NULL,
+    type_heures_id                 NUMERIC DEFAULT NULL,
+    etape_id                       NUMERIC DEFAULT NULL,
+    noeud_ids                      tnoeud_ids DEFAULT NULL
+  );
+
+  PRECALC_HEURES_PARAMS T_PRECALC_HEURES_PARAMS;
+
+
+  FUNCTION GET_SCENARIO RETURN NUMERIC IS
+  BEGIN
+    RETURN OSE_CHARGENS.SCENARIO;
+  END;
+
+  PROCEDURE SET_SCENARIO( SCENARIO NUMERIC ) IS
+  BEGIN
+    OSE_CHARGENS.SCENARIO := SET_SCENARIO.SCENARIO;
+  END;
+
+
+
+  FUNCTION GET_NOEUD RETURN NUMERIC IS
+  BEGIN
+    RETURN OSE_CHARGENS.NOEUD;
+  END;
+
+  PROCEDURE SET_NOEUD( NOEUD NUMERIC ) IS
+  BEGIN
+    OSE_CHARGENS.NOEUD := SET_NOEUD.NOEUD;
+  END;
+
+
+
+
+
+  FUNCTION CALC_COEF( choix_min NUMERIC, choix_max NUMERIC, poids NUMERIC, max_poids NUMERIC, total_poids NUMERIC, nb_choix NUMERIC ) RETURN FLOAT IS
+    cmin NUMERIC;
+    cmax NUMERIC;
+    coef_choix FLOAT;
+    coef_poids FLOAT;
+    max_coef_poids FLOAT;
+    correcteur FLOAT DEFAULT 1;
+    res FLOAT;
+  BEGIN
+    cmin := choix_min;
+    cmax := choix_max;
+
+    IF total_poids = 0 THEN RETURN 0; END IF;
+
+    IF cmax IS NULL OR cmax > nb_choix THEN
+      cmax := nb_choix;
+    END IF;
+    IF cmin IS NULL THEN
+      cmin := nb_choix;
+    ELSIF cmin > cmax THEN
+      cmin := cmax;
+    END IF;
+
+      coef_choix := (cmin + cmax) / 2 / nb_choix;
+
+      coef_poids := poids / total_poids;
+
+      max_coef_poids := max_poids / total_poids;
+
+      IF (coef_choix * nb_choix * max_coef_poids) <= 1 THEN
+        res := coef_choix * nb_choix * coef_poids;
+      ELSE
+        correcteur := 1;
+        res := coef_choix * nb_choix * (coef_poids + (((1/nb_choix)-coef_poids)*correcteur));
+      END IF;
+
+      --ose_test.echo('choix_min= ' || cmin || ', choix_max= ' || cmax || ', poids = ' || poids || ', max_poids = ' || max_poids || ', total_poids = ' || total_poids || ', nb_choix = ' || nb_choix || ', RES = ' || res);
+      RETURN res;
+  END;
+
+
+  PROCEDURE DEM_CALC_SUB_EFFECTIF( scenario_noeud_id NUMERIC, type_heures_id NUMERIC, etape_id NUMERIC, effectif FLOAT ) IS
+  BEGIN
+    INSERT INTO TMP_scenario_noeud_effectif(
+      scenario_noeud_id, type_heures_id, etape_id, effectif
+    ) VALUES(
+      scenario_noeud_id, type_heures_id, etape_id, effectif
+    );
+  END;
+
+
+
+  PROCEDURE CALC_SUB_EFFECTIF_DEM IS
+  BEGIN
+    DELETE FROM TMP_scenario_noeud_effectif;
+  END;
+
+
+  PROCEDURE CALC_ALL_EFFECTIFS IS
+  BEGIN
+    FOR p IN (
+
+      SELECT
+        sn.noeud_id,
+        sn.scenario_id,
+        sne.type_heures_id,
+        sne.etape_id
+      FROM
+        scenario_noeud_effectif sne
+        JOIN scenario_noeud sn ON sn.id = sne.scenario_noeud_id
+        JOIN noeud n ON n.id = sn.noeud_id
+      WHERE
+        n.etape_id IS NOT NULL
+
+    ) LOOP
+
+      CALC_SUB_EFFECTIF2( p.noeud_id, p.scenario_id, p.type_heures_id, p.etape_id );
+    END LOOP;
+
+  END;
+
+
+
+  PROCEDURE CALC_EFFECTIF(
+    noeud_id       NUMERIC,
+    scenario_id    NUMERIC,
+    type_heures_id NUMERIC DEFAULT NULL,
+    etape_id       NUMERIC DEFAULT NULL
+  ) IS
+    snid  NUMERIC;
+  BEGIN
+    UPDATE scenario_noeud_effectif SET effectif = 0
+    WHERE
+      scenario_noeud_id = (
+        SELECT id FROM scenario_noeud WHERE noeud_id = CALC_EFFECTIF.noeud_id AND scenario_id = CALC_EFFECTIF.scenario_id
+      )
+      AND (type_heures_id = CALC_EFFECTIF.type_heures_id OR CALC_EFFECTIF.type_heures_id IS NULL)
+      AND (etape_id = CALC_EFFECTIF.etape_id OR CALC_EFFECTIF.etape_id IS NULL)
+    ;
+
+    FOR p IN (
+
+      SELECT
+        *
+      FROM
+        v_chargens_calc_effectif cce
+      WHERE
+        cce.noeud_id = CALC_EFFECTIF.noeud_id
+        AND cce.scenario_id = CALC_EFFECTIF.scenario_id
+        AND (cce.type_heures_id = CALC_EFFECTIF.type_heures_id OR CALC_EFFECTIF.type_heures_id IS NULL)
+        AND (cce.etape_id = CALC_EFFECTIF.etape_id OR CALC_EFFECTIF.etape_id IS NULL)
+
+    ) LOOP
+      snid := OSE_CHARGENS.GET_SCENARIO_NOEUD_ID( p.scenario_id, p.noeud_id );
+      IF snid IS NULL THEN
+        snid := OSE_CHARGENS.CREER_SCENARIO_NOEUD( p.scenario_id, p.noeud_id );
+      END IF;
+      ADD_SCENARIO_NOEUD_EFFECTIF( snid, p.type_heures_id, p.etape_id, p.effectif );
+    END LOOP;
+    CALC_SUB_EFFECTIF2( noeud_id, scenario_id, type_heures_id, etape_id );
+  END;
+
+
+
+  PROCEDURE CALC_SUB_EFFECTIF2( noeud_id NUMERIC, scenario_id NUMERIC, type_heures_id NUMERIC DEFAULT NULL, etape_id NUMERIC DEFAULT NULL) IS
+  BEGIN
+    FOR p IN (
+
+      SELECT *
+      FROM   V_CHARGENS_GRANDS_LIENS cgl
+      WHERE  cgl.noeud_sup_id = CALC_SUB_EFFECTIF2.noeud_id
+
+    ) LOOP
+      CALC_EFFECTIF( p.noeud_inf_id, scenario_id, type_heures_id, etape_id );
+    END LOOP;
+  END;
+
+
+
+  PROCEDURE DUPLIQUER( source_id NUMERIC, destination_id NUMERIC, utilisateur_id NUMERIC, structure_id NUMERIC, noeuds VARCHAR2 DEFAULT '', liens VARCHAR2 DEFAULT '' ) IS
+  BEGIN
+
+    /* Destruction de tous les liens antérieurs de la destination */
+    DELETE FROM
+      scenario_lien
+    WHERE
+      scenario_id = DUPLIQUER.destination_id
+      AND histo_destruction IS NULL
+      AND (DUPLIQUER.LIENS IS NULL OR DUPLIQUER.LIENS LIKE '%,' || lien_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR lien_id IN (
+        SELECT id FROM lien WHERE lien.structure_id = DUPLIQUER.STRUCTURE_ID
+      ))
+    ;
+
+    /* Duplication des liens */
+    INSERT INTO scenario_lien (
+      id,
+      scenario_id, lien_id,
+      actif, poids,
+      choix_minimum, choix_maximum,
+      source_id, source_code,
+      histo_creation, histo_createur_id,
+      histo_modification, histo_modificateur_id
+    ) SELECT
+      scenario_lien_id_seq.nextval,
+      DUPLIQUER.destination_id, sl.lien_id,
+      sl.actif, sl.poids,
+      sl.choix_minimum, sl.choix_maximum,
+      source.id, 'dupli_' || sl.id || '_' || sl.lien_id || '_' || trunc(dbms_random.value(1,10000000000000)),
+      sysdate, DUPLIQUER.utilisateur_id,
+      sysdate, DUPLIQUER.utilisateur_id
+    FROM
+      scenario_lien sl
+      JOIN lien l ON l.id = sl.lien_id
+      JOIN source ON source.code = 'OSE'
+    WHERE
+      sl.scenario_id = DUPLIQUER.source_id
+      AND sl.histo_destruction IS NULL
+      AND (DUPLIQUER.LIENS IS NULL OR DUPLIQUER.LIENS LIKE '%,' || lien_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR l.structure_id = DUPLIQUER.STRUCTURE_ID)
+    ;
+
+
+    /* Destruction de tous les noeuds antérieurs de la destination */
+    DELETE FROM
+      scenario_noeud
+    WHERE
+      scenario_id = DUPLIQUER.destination_id
+      AND histo_destruction IS NULL
+      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || noeud_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR scenario_noeud.noeud_id IN (
+        SELECT id FROM noeud WHERE noeud.structure_id = DUPLIQUER.STRUCTURE_ID
+      ))
+    ;
+
+    /* Duplication des noeuds */
+    INSERT INTO scenario_noeud (
+      id,
+      scenario_id, noeud_id,
+      assiduite,
+      source_id, source_code,
+      histo_creation, histo_createur_id,
+      histo_modification, histo_modificateur_id
+    ) SELECT
+      scenario_noeud_id_seq.nextval,
+      DUPLIQUER.destination_id, sn.noeud_id,
+      sn.assiduite,
+      source.id, 'dupli_' || sn.id || '_' || sn.noeud_id || '_' || trunc(dbms_random.value(1,10000000000000)),
+      sysdate, DUPLIQUER.utilisateur_id,
+      sysdate, DUPLIQUER.utilisateur_id
+    FROM
+      scenario_noeud sn
+      JOIN noeud n ON n.id = sn.noeud_id
+      JOIN source ON source.code = 'OSE'
+    WHERE
+      sn.scenario_id = DUPLIQUER.source_id
+      AND sn.histo_destruction IS NULL
+      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || noeud_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
+    ;
+
+    /* Duplication des effectifs */
+    INSERT INTO scenario_noeud_effectif (
+      id,
+      scenario_noeud_id,
+      type_heures_id,
+      effectif,
+      etape_id
+    ) SELECT
+      scenario_noeud_effectif_id_seq.nextval,
+      sn_dst.id,
+      sne.type_heures_id,
+      sne.effectif,
+      sne.etape_id
+    FROM
+      scenario_noeud_effectif sne
+      JOIN scenario_noeud sn_src ON sn_src.id = sne.scenario_noeud_id
+      JOIN scenario_noeud sn_dst ON sn_dst.scenario_id = DUPLIQUER.destination_id AND sn_dst.noeud_id = sn_src.noeud_id
+      JOIN noeud n ON n.id = sn_src.noeud_id
+    WHERE
+      sn_src.scenario_id = DUPLIQUER.source_id
+      AND sn_src.histo_destruction IS NULL
+      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || sn_src.noeud_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
+    ;
+
+    /* Duplication des seuils */
+    INSERT INTO scenario_noeud_seuil (
+      id,
+      scenario_noeud_id,
+      type_intervention_id,
+      ouverture,
+      dedoublement
+    ) SELECT
+      scenario_noeud_seuil_id_seq.nextval,
+      sn_dst.id,
+      sns.type_intervention_id,
+      sns.ouverture,
+      sns.dedoublement
+    FROM
+      scenario_noeud_seuil sns
+      JOIN scenario_noeud sn_src ON sn_src.id = sns.scenario_noeud_id
+      JOIN scenario_noeud sn_dst ON sn_dst.scenario_id = DUPLIQUER.destination_id AND sn_dst.noeud_id = sn_src.noeud_id
+      JOIN noeud n ON n.id = sn_src.noeud_id
+    WHERE
+      sn_src.scenario_id = DUPLIQUER.source_id
+      AND sn_src.histo_destruction IS NULL
+      AND (DUPLIQUER.NOEUDS IS NULL OR DUPLIQUER.NOEUDS LIKE '%,' || sn_src.noeud_id || ',%' )
+      AND (DUPLIQUER.STRUCTURE_ID IS NULL OR n.structure_id = DUPLIQUER.STRUCTURE_ID)
+    ;
+  END;
+
+
+
+  PROCEDURE CONTROLE_SEUIL( ouverture NUMERIC, dedoublement NUMERIC ) IS
+  BEGIN
+    IF ouverture IS NOT NULL THEN
+      IF ouverture < 1 THEN
+        raise_application_error(-20101, 'Le seuil d''ouverture doit être supérieur ou égal à 1');
+      END IF;
+    END IF;
+
+    IF dedoublement IS NOT NULL THEN
+      IF dedoublement < 1 THEN
+        raise_application_error(-20101, 'Le seuil de dédoublement doit être supérieur ou égal à 1');
+      END IF;
+    END IF;
+
+    IF ouverture IS NOT NULL AND dedoublement IS NOT NULL THEN
+      IF dedoublement < ouverture THEN
+        raise_application_error(-20101, 'Le seuil de dédoublement doit être supérieur ou égal au seuil d''ouverture');
+      END IF;
+    END IF;
+  END;
+
+
+  FUNCTION CREER_SCENARIO_NOEUD( scenario_id NUMERIC, noeud_id NUMERIC, assiduite FLOAT DEFAULT 1 ) RETURN NUMERIC IS
+    new_id NUMERIC;
+  BEGIN
+    new_id := SCENARIO_NOEUD_ID_SEQ.NEXTVAL;
+--ose_test.echo(scenario_id || '-' || noeud_id);
+    INSERT INTO SCENARIO_NOEUD(
+      ID,
+      SCENARIO_ID,
+      NOEUD_ID,
+      ASSIDUITE,
+      SOURCE_ID,
+      SOURCE_CODE,
+      HEURES,
+      HISTO_CREATION,
+      HISTO_CREATEUR_ID,
+      HISTO_MODIFICATION,
+      HISTO_MODIFICATEUR_ID
+    ) VALUES (
+      new_id,
+      CREER_SCENARIO_NOEUD.scenario_id,
+      CREER_SCENARIO_NOEUD.noeud_id,
+      CREER_SCENARIO_NOEUD.assiduite,
+      OSE_DIVERS.GET_OSE_SOURCE_ID,
+      'OSE_NEW_SN_' || new_id,
+      null,
+      SYSDATE,
+      OSE_DIVERS.GET_OSE_UTILISATEUR_ID,
+      SYSDATE,
+      OSE_DIVERS.GET_OSE_UTILISATEUR_ID
+    );
+    RETURN new_id;
+  END;
+
+
+  FUNCTION GET_SCENARIO_NOEUD_ID(scenario_id NUMERIC, noeud_id NUMERIC) RETURN NUMERIC IS
+    res NUMERIC;
+  BEGIN
+    SELECT
+      sn.id INTO res
+    FROM
+      scenario_noeud sn
+    WHERE
+      sn.noeud_id = GET_SCENARIO_NOEUD_ID.noeud_id
+      AND sn.scenario_id = GET_SCENARIO_NOEUD_ID.scenario_id
+      AND sn.histo_destruction IS NULL;
+
+    RETURN res;
+
+  EXCEPTION WHEN NO_DATA_FOUND THEN
+    RETURN NULL;
+  END;
+
+
+  PROCEDURE ADD_SCENARIO_NOEUD_EFFECTIF( scenario_noeud_id NUMERIC, type_heures_id NUMERIC, etape_id NUMERIC, effectif FLOAT ) IS
+    old_enable BOOLEAN;
+  BEGIN
+    old_enable := ose_chargens.ENABLE_TRIGGER_EFFECTIFS;
+    ose_chargens.ENABLE_TRIGGER_EFFECTIFS := false;
+
+    MERGE INTO scenario_noeud_effectif sne USING dual ON (
+
+          sne.scenario_noeud_id = ADD_SCENARIO_NOEUD_EFFECTIF.scenario_noeud_id
+      AND sne.type_heures_id = ADD_SCENARIO_NOEUD_EFFECTIF.type_heures_id
+      AND sne.etape_id = ADD_SCENARIO_NOEUD_EFFECTIF.etape_id
+
+    ) WHEN MATCHED THEN UPDATE SET
+
+      effectif = effectif + ADD_SCENARIO_NOEUD_EFFECTIF.effectif
+
+    WHEN NOT MATCHED THEN INSERT (
+
+      ID,
+      SCENARIO_NOEUD_ID,
+      TYPE_HEURES_ID,
+      ETAPE_ID,
+      EFFECTIF
+
+    ) VALUES (
+
+      SCENARIO_NOEUD_EFFECTIF_ID_SEQ.NEXTVAL,
+      ADD_SCENARIO_NOEUD_EFFECTIF.scenario_noeud_id,
+      ADD_SCENARIO_NOEUD_EFFECTIF.type_heures_id,
+      ADD_SCENARIO_NOEUD_EFFECTIF.etape_id,
+      ADD_SCENARIO_NOEUD_EFFECTIF.effectif
+
+    );
+
+    DELETE FROM scenario_noeud_effectif WHERE effectif = 0;
+
+    ose_chargens.ENABLE_TRIGGER_EFFECTIFS := old_enable;
+  END;
+
+
+
+  PROCEDURE INIT_SCENARIO_NOEUD_EFFECTIF(
+    etape_id NUMERIC,
+    scenario_id NUMERIC,
+    type_heures_id NUMERIC,
+    effectif FLOAT,
+    surcharge BOOLEAN DEFAULT FALSE
+  ) IS
+    noeud_id NUMERIC;
+    scenario_noeud_id NUMERIC;
+    scenario_noeud_effectif_id NUMERIC;
+  BEGIN
+    SELECT
+      n.id, sn.id, sne.id
+    INTO
+      noeud_id, scenario_noeud_id, scenario_noeud_effectif_id
+    FROM
+                noeud                     n
+      LEFT JOIN scenario_noeud           sn ON sn.noeud_id = n.id
+                                           AND sn.histo_destruction IS NULL
+                                           AND sn.scenario_id = INIT_SCENARIO_NOEUD_EFFECTIF.scenario_id
+
+      LEFT JOIN scenario_noeud_effectif sne ON sne.scenario_noeud_id = sn.id
+                                           AND sne.type_heures_id = INIT_SCENARIO_NOEUD_EFFECTIF.type_heures_id
+    WHERE
+      n.etape_id = INIT_SCENARIO_NOEUD_EFFECTIF.etape_id
+      AND n.histo_destruction IS NULL
+    ;
+
+    IF noeud_id IS NULL THEN RETURN; END IF;
+
+    IF scenario_noeud_id IS NULL THEN
+      scenario_noeud_id := CREER_SCENARIO_NOEUD( scenario_id, noeud_id );
+    END IF;
+
+    IF scenario_noeud_effectif_id IS NULL THEN
+      scenario_noeud_effectif_id := SCENARIO_NOEUD_EFFECTIF_ID_SEQ.NEXTVAL;
+      INSERT INTO scenario_noeud_effectif (
+        id,
+        scenario_noeud_id,
+        type_heures_id,
+        effectif,
+        etape_id
+      ) VALUES (
+        scenario_noeud_effectif_id,
+        scenario_noeud_id,
+        INIT_SCENARIO_NOEUD_EFFECTIF.type_heures_id,
+        INIT_SCENARIO_NOEUD_EFFECTIF.effectif,
+        INIT_SCENARIO_NOEUD_EFFECTIF.etape_id
+      );
+    ELSIF surcharge THEN
+      UPDATE scenario_noeud_effectif SET effectif = INIT_SCENARIO_NOEUD_EFFECTIF.effectif WHERE id = scenario_noeud_effectif_id;
+    END IF;
+
+    CALC_SUB_EFFECTIF2( noeud_id, scenario_id, type_heures_id, etape_id );
+
+  EXCEPTION WHEN NO_DATA_FOUND THEN
+    RETURN;
+  END;
+
+
+
+  PROCEDURE SET_PRECALC_HEURES_PARAMS(
+    annee_id                       NUMERIC DEFAULT NULL,
+    structure_id                   NUMERIC DEFAULT NULL,
+    scenario_id                    NUMERIC DEFAULT NULL,
+    type_heures_id                 NUMERIC DEFAULT NULL,
+    etape_id                       NUMERIC DEFAULT NULL,
+    noeud_ids                      tnoeud_ids DEFAULT NULL
+  ) IS
+  BEGIN
+    PRECALC_HEURES_PARAMS.ANNEE_ID       := ANNEE_ID;
+    PRECALC_HEURES_PARAMS.STRUCTURE_ID   := STRUCTURE_ID;
+    PRECALC_HEURES_PARAMS.SCENARIO_ID    := SCENARIO_ID;
+    PRECALC_HEURES_PARAMS.TYPE_HEURES_ID := TYPE_HEURES_ID;
+    PRECALC_HEURES_PARAMS.ETAPE_ID       := ETAPE_ID;
+    PRECALC_HEURES_PARAMS.NOEUD_IDS      := noeud_ids;
+  END;
+
+
+
+  FUNCTION MATCH_PRECALC_HEURES_PARAMS(
+    annee_id                       NUMERIC DEFAULT NULL,
+    structure_id                   NUMERIC DEFAULT NULL,
+    scenario_id                    NUMERIC DEFAULT NULL,
+    type_heures_id                 NUMERIC DEFAULT NULL,
+    etape_id                       NUMERIC DEFAULT NULL,
+    noeud_id                       NUMERIC DEFAULT NULL
+  ) RETURN NUMERIC IS
+  BEGIN
+
+    IF PRECALC_HEURES_PARAMS.noeud_ids IS NOT NULL THEN
+      IF NOT (noeud_id MEMBER OF PRECALC_HEURES_PARAMS.noeud_ids) THEN
+        RETURN 0;
+      END IF;
+    END IF;
+
+    IF annee_id <> COALESCE(PRECALC_HEURES_PARAMS.annee_id, annee_id) THEN
+      RETURN 0;
+    END IF;
+
+    IF structure_id <> COALESCE(PRECALC_HEURES_PARAMS.structure_id, structure_id) THEN
+      RETURN 0;
+    END IF;
+
+    IF scenario_id <> COALESCE(PRECALC_HEURES_PARAMS.scenario_id, scenario_id) THEN
+      RETURN 0;
+    END IF;
+
+    IF type_heures_id <> COALESCE(PRECALC_HEURES_PARAMS.type_heures_id, type_heures_id) THEN
+      RETURN 0;
+    END IF;
+
+    IF etape_id <> COALESCE(PRECALC_HEURES_PARAMS.etape_id, etape_id) THEN
+      RETURN 0;
+    END IF;
+
+    RETURN 1;
+  END;
+
+
+  FUNCTION GET_PRECALC_HEURES_ANNEE RETURN NUMERIC IS
+  BEGIN
+    RETURN PRECALC_HEURES_PARAMS.ANNEE_ID;
+  END;
+
+
+
+  FUNCTION GET_PRECALC_HEURES_STRUCTURE RETURN NUMERIC IS
+  BEGIN
+    RETURN PRECALC_HEURES_PARAMS.STRUCTURE_ID;
+  END;
+
+
+
+  FUNCTION GET_PRECALC_HEURES_SCENARIO RETURN NUMERIC IS
+  BEGIN
+    RETURN PRECALC_HEURES_PARAMS.SCENARIO_ID;
+  END;
+
+
+
+  FUNCTION GET_PRECALC_HEURES_TYPE_HEURES RETURN NUMERIC IS
+  BEGIN
+    RETURN PRECALC_HEURES_PARAMS.TYPE_HEURES_ID;
+  END;
+
+
+
+  FUNCTION GET_PRECALC_HEURES_ETAPE RETURN NUMERIC IS
+  BEGIN
+    RETURN PRECALC_HEURES_PARAMS.ETAPE_ID;
+  END;
+
+--  FUNCTION GET_PRECALC_HEURES_NOEUD RETURN NUMERIC IS
+--  BEGIN
+
+--  END;
+
+END OSE_CHARGENS;
+/
+
+-- OSE_DIVERS
+CREATE OR REPLACE PACKAGE BODY "OSE_DIVERS" AS
+  OSE_UTILISATEUR_ID NUMERIC;
+  OSE_SOURCE_ID NUMERIC;
+
+
+
+
+PROCEDURE CALCULER_TABLEAUX_BORD IS
 BEGIN
-  RETURN get('uid=p'||to_char(harpege_uid,'FM00000000'), 'supannAliasLogin', 'N', a_multi) ;
-END hid2alias;
+  FOR d IN (
+    SELECT tbl_name
+    FROM tbl
+    WHERE tbl_name <> 'formule' -- TROP LONG !!
+    ORDER BY ordre
+  )
+  LOOP
+    UNICAEN_TBL.CALCULER(d.tbl_name);
+    dbms_output.put_line('Calcul du tableau de bord "' || d.tbl_name || '" effectué');
+    COMMIT;
+  END LOOP;
+END;
 
 
---===================================================================
---===================================================================
--- uid2cn(ldap_uid)
---===================================================================
-FUNCTION uid2cn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+
+FUNCTION GET_OSE_UTILISATEUR_ID RETURN NUMERIC IS
 BEGIN
-  RETURN get('uid='||ldap_uid, 'cn', 'N', a_multi) ;
-END uid2cn;
+  IF OSE_DIVERS.OSE_UTILISATEUR_ID IS NULL THEN
+    SELECT
+      to_number(valeur) INTO OSE_DIVERS.OSE_UTILISATEUR_ID
+    FROM
+      parametre
+    WHERE
+      nom = 'oseuser';
+  END IF;
 
+  RETURN OSE_DIVERS.OSE_UTILISATEUR_ID;
+END;
 
---===================================================================
---===================================================================
--- uid2sn(ldap_uid)
---===================================================================
-FUNCTION uid2sn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+
+
+FUNCTION GET_OSE_SOURCE_ID RETURN NUMERIC IS
 BEGIN
-  RETURN get('uid='||ldap_uid, 'sn', 'N', a_multi) ;
-END uid2sn;
+  IF OSE_DIVERS.OSE_SOURCE_ID IS NULL THEN
+    SELECT
+      id INTO OSE_DIVERS.OSE_SOURCE_ID
+    FROM
+      source
+    WHERE
+      code = 'OSE';
+  END IF;
 
---===================================================================
---===================================================================
--- uid2givenname(ldap_uid)
---===================================================================
-FUNCTION uid2givenname(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+  RETURN OSE_DIVERS.OSE_SOURCE_ID;
+END;
+
+
+
+FUNCTION INTERVENANT_HAS_PRIVILEGE( intervenant_id NUMERIC, privilege_name VARCHAR2 ) RETURN NUMERIC IS
+  statut statut_intervenant%rowtype;
+  itype  type_intervenant%rowtype;
+  res NUMERIC;
 BEGIN
-  RETURN get('uid='||ldap_uid, 'givenname', 'N', a_multi) ;
-END uid2givenname;
+  res := 1;
+  SELECT si.* INTO statut FROM statut_intervenant si JOIN intervenant i ON i.statut_id = si.id WHERE i.id = intervenant_id;
+  SELECT ti.* INTO itype  FROM type_intervenant ti WHERE ti.id = statut.type_intervenant_id;
 
---===================================================================
---===================================================================
--- uid2gn(ldap_uid)
---===================================================================
-FUNCTION uid2gn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+  /* DEPRECATED */
+  IF 'saisie_service' = privilege_name THEN
+    res := statut.peut_saisir_service;
+    RETURN res;
+  ELSIF 'saisie_service_exterieur' = privilege_name THEN
+    --IF INTERVENANT_HAS_PRIVILEGE( intervenant_id, 'saisie_service' ) = 0 OR itype.code = 'E' THEN -- cascade
+    IF itype.code = 'E' THEN
+      res := 0;
+    END IF;
+    RETURN res;
+  ELSIF 'saisie_service_referentiel' = privilege_name THEN
+    IF itype.code = 'E' THEN
+      res := 0;
+    END IF;
+    RETURN res;
+  ELSIF 'saisie_service_referentiel_autre_structure' = privilege_name THEN
+    res := 1;
+    RETURN res;
+  ELSIF 'saisie_motif_non_paiement' = privilege_name THEN
+    res := statut.peut_saisir_motif_non_paiement;
+    RETURN res;
+  END IF;
+  /* FIN DE DEPRECATED */
+
+  SELECT
+    count(*)
+  INTO
+    res
+  FROM
+    intervenant i
+    JOIN statut_privilege sp ON sp.statut_id = i.statut_id
+    JOIN privilege p ON p.id = sp.privilege_id
+    JOIN categorie_privilege cp ON cp.id = p.categorie_id
+  WHERE
+    i.id = INTERVENANT_HAS_PRIVILEGE.intervenant_id
+    AND cp.code || '-' || p.code = privilege_name;
+
+  RETURN res;
+END;
+
+FUNCTION implode(i_query VARCHAR2, i_seperator VARCHAR2 DEFAULT ',') RETURN VARCHAR2 AS
+  l_return CLOB:='';
+  l_temp CLOB;
+  TYPE r_cursor is REF CURSOR;
+  rc r_cursor;
 BEGIN
-  RETURN get('uid='||ldap_uid, 'givenname', 'N', a_multi)||' '||get('uid='||ldap_uid, 'sn', 'N', a_multi) ;
-END uid2gn;
+  OPEN rc FOR i_query;
+  LOOP
+    FETCH rc INTO L_TEMP;
+    EXIT WHEN RC%NOTFOUND;
+    l_return:=l_return||L_TEMP||i_seperator;
+  END LOOP;
+  RETURN RTRIM(l_return,i_seperator);
+END;
 
+PROCEDURE intervenant_horodatage_service( INTERVENANT_ID NUMERIC, TYPE_VOLUME_HORAIRE_ID NUMERIC, REFERENTIEL NUMERIC, HISTO_MODIFICATEUR_ID NUMERIC, HISTO_MODIFICATION DATE ) AS
+BEGIN
+    MERGE INTO histo_intervenant_service his USING dual ON (
 
+          his.INTERVENANT_ID                = intervenant_horodatage_service.INTERVENANT_ID
+      AND NVL(his.TYPE_VOLUME_HORAIRE_ID,0) = NVL(intervenant_horodatage_service.TYPE_VOLUME_HORAIRE_ID,0)
+      AND his.REFERENTIEL                   = intervenant_horodatage_service.REFERENTIEL
 
---===================================================================
---===================================================================
--- hidIsPrimaryTeacher(harpege_uid)
---
--- Verifie eduPersonPrimaryAffiliation
---===================================================================
-FUNCTION hidIsPrimaryTeacher(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
-  l_resultat VARCHAR2(1024 char) := NULL ;
-  isTeacher VARCHAR2(1) := 'N' ;
+    ) WHEN MATCHED THEN UPDATE SET
+
+      HISTO_MODIFICATEUR_ID = intervenant_horodatage_service.HISTO_MODIFICATEUR_ID,
+      HISTO_MODIFICATION = intervenant_horodatage_service.HISTO_MODIFICATION
+
+    WHEN NOT MATCHED THEN INSERT (
+
+      ID,
+      INTERVENANT_ID,
+      TYPE_VOLUME_HORAIRE_ID,
+      REFERENTIEL,
+      HISTO_MODIFICATEUR_ID,
+      HISTO_MODIFICATION
+    ) VALUES (
+      HISTO_INTERVENANT_SERVI_ID_SEQ.NEXTVAL,
+      intervenant_horodatage_service.INTERVENANT_ID,
+      intervenant_horodatage_service.TYPE_VOLUME_HORAIRE_ID,
+      intervenant_horodatage_service.REFERENTIEL,
+      intervenant_horodatage_service.HISTO_MODIFICATEUR_ID,
+      intervenant_horodatage_service.HISTO_MODIFICATION
+
+    );
+END;
+
+
+FUNCTION NIVEAU_FORMATION_ID_CALC( gtf_id NUMERIC, gtf_pertinence_niveau NUMERIC, niveau NUMERIC DEFAULT NULL ) RETURN NUMERIC AS
+BEGIN
+  IF 1 <> gtf_pertinence_niveau OR niveau IS NULL OR niveau < 1 OR gtf_id < 1 THEN RETURN NULL; END IF;
+  RETURN gtf_id * 256 + niveau;
+END;
+
+FUNCTION STR_REDUCE( str CLOB ) RETURN CLOB IS
+BEGIN
+  RETURN utl_raw.cast_to_varchar2((nlssort(str, 'nls_sort=binary_ai')));
+END;
+
+FUNCTION STR_FIND( haystack CLOB, needle VARCHAR2 ) RETURN NUMERIC IS
+BEGIN
+  IF STR_REDUCE( haystack ) LIKE STR_REDUCE( '%' || needle || '%' ) THEN RETURN 1; END IF;
+  RETURN 0;
+END;
+
+FUNCTION LIKED( haystack CLOB, needle CLOB ) RETURN NUMERIC IS
+BEGIN
+  RETURN CASE WHEN STR_REDUCE(haystack) LIKE STR_REDUCE(needle) THEN 1 ELSE 0 END;
+END;
+
+PROCEDURE DO_NOTHING IS
+BEGIN
+  RETURN;
+END;
+
+PROCEDURE CALCUL_TAUX( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, r_fi OUT FLOAT, r_fc OUT FLOAT, r_fa OUT FLOAT, arrondi NUMERIC DEFAULT 15 ) IS
+  nt FLOAT;
+  bi FLOAT;
+  bc FLOAT;
+  ba FLOAT;
+  reste FLOAT;
 BEGIN
-  l_resultat := get('uid=p'||to_char(harpege_uid,'FM00000000'), 'eduPersonPrimaryAffiliation', 'N', a_multi) ;
+  bi := eff_fi * fi;
+  bc := eff_fc * fc;
+  ba := eff_fa * fa;
+  nt := bi + bc + ba;
+
+  IF nt = 0 THEN -- au cas ou, alors on ne prend plus en compte les effectifs!!
+    bi := fi;
+    bc := fc;
+    ba := fa;
+    nt := bi + bc + ba;
+  END IF;
 
-  IF l_resultat IS NULL THEN
-    RETURN NULL ;
-  END IF ;
+  IF nt = 0 THEN -- toujours au cas ou...
+    bi := 1;
+    bc := 0;
+    ba := 0;
+    nt := bi + bc + ba;
+  END IF;
 
-  IF SUBSTR( l_resultat, 1, 4 ) = '#Err' THEN
-    RETURN l_resultat ;
-  END IF ;
+  -- Calcul
+  r_fi := bi / nt;
+  r_fc := bc / nt;
+  r_fa := ba / nt;
 
-  IF l_resultat = 'teacher' THEN
-    isTeacher := 'O' ;
-  END IF ;
+  -- Arrondis
+  r_fi := ROUND( r_fi, arrondi );
+  r_fc := ROUND( r_fc, arrondi );
+  r_fa := ROUND( r_fa, arrondi );
 
-  RETURN isTeacher ;
+  -- détermination du reste
+  reste := 1 - r_fi - r_fc - r_fa;
 
-END hidIsPrimaryTeacher;
+  -- répartition éventuelle du reste
+  IF reste <> 0 THEN
+    IF r_fi > 0 THEN r_fi := r_fi + reste;
+    ELSIF r_fc > 0 THEN r_fc := r_fc + reste;
+    ELSE r_fa := r_fa + reste; END IF;
+  END IF;
 
+END;
 
---===================================================================
---===================================================================
--- hidIsTeacher(harpege_uid)
---
--- Retourne NULL si non trouve,
---             O si flag teacher ou faculty
---             N si pas ce flag.
---===================================================================
-FUNCTION hidIsTeacher(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
-  l_resultat VARCHAR2(1024 char) := NULL ;
-  isTeacher VARCHAR2(1) := 'N' ;
+
+FUNCTION CALCUL_TAUX_FI( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
+  ri FLOAT;
+  rc FLOAT;
+  ra FLOAT;
 BEGIN
-  l_resultat := get('uid=p'||to_char(harpege_uid,'FM00000000'), 'eduPersonAffiliation', 'Y', a_multi) ;
-  -- ici, l_resultat ne contient que '#Err Multi-value: i'
+  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
+  RETURN ri;
+END;
 
-  -- On verifie qu'on a bien obtenu des resultats
-  IF l_resultat IS NULL OR SUBSTR( l_resultat, 1, 18) != '#Err Multi-value: ' THEN
-    RETURN l_resultat ;
-  END IF ;
+FUNCTION CALCUL_TAUX_FC( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
+  ri FLOAT;
+  rc FLOAT;
+  ra FLOAT;
+BEGIN
+  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
+  RETURN rc;
+END;
 
-  -- Le Nombre de resultats
-  IF a_multi.count = 0 THEN
-    RETURN NULL ;
-  END IF ;
+FUNCTION CALCUL_TAUX_FA( eff_fi FLOAT, eff_fc FLOAT, eff_fa FLOAT, fi NUMERIC, fc NUMERIC, fa NUMERIC, arrondi NUMERIC DEFAULT 15 ) RETURN FLOAT IS
+  ri FLOAT;
+  rc FLOAT;
+  ra FLOAT;
+BEGIN
+  CALCUL_TAUX( eff_fi, eff_fc, eff_fa, fi, fc, fa, ri, rc, ra, arrondi );
+  RETURN ra;
+END;
 
-  FOR i IN 1 .. a_multi.count LOOP
-    IF a_multi(i)='teacher' THEN
-      isTeacher := 'O' ;
-    END IF ;
-  END LOOP ;
+PROCEDURE SYNC_LOG( msg CLOB ) IS
+BEGIN
+  INSERT INTO SYNC_LOG( id, date_sync, message ) VALUES ( sync_log_id_seq.nextval, systimestamp, msg );
+END;
 
-  RETURN isTeacher ;
+FUNCTION FORMATTED_RIB (bic VARCHAR2, iban VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  if bic is null and iban is null then
+    return null;
+  end if;
+  RETURN regexp_replace(bic, '[[:space:]]+', '') || '-' || regexp_replace(iban, '[[:space:]]+', '');
+END;
 
-END hidIsTeacher;
+FUNCTION FORMATTED_ADRESSE(
+    no_voie                VARCHAR2,
+    nom_voie               VARCHAR2,
+    batiment               VARCHAR2,
+    mention_complementaire VARCHAR2,
+    localite               VARCHAR2,
+    code_postal            VARCHAR2,
+    ville                  VARCHAR2,
+    pays_libelle           VARCHAR2)
+  RETURN VARCHAR2
+IS
+BEGIN
+  return
+    -- concaténation des éléments non null séparés par ', '
+    trim(trim(',' FROM REPLACE(', ' || NVL(no_voie,'#') || ', ' || NVL(nom_voie,'#') || ', ' || NVL(batiment,'#') || ', ' || NVL(mention_complementaire,'#'), ', #', ''))) ||
+    -- saut de ligne complet
+    chr(13) || chr(10) ||
+    -- concaténation des éléments non null séparés par ', '
+    trim(trim(',' FROM REPLACE(', ' || NVL(localite,'#') || ', ' || NVL(code_postal,'#') || ', ' || NVL(ville,'#') || ', ' || NVL(pays_libelle,'#'), ', #', '')));
+END;
 
 
---===================================================================
---===================================================================
--- getInvites()
---
--- Reprend la fonction "get" mais ecrit les resultats dans une table
---===================================================================
-PROCEDURE getInvites(l_table IN VARCHAR2) IS
-  -- Les valeurs qu'on recherche
-  l_uid VARCHAR2(10 char) ;
-  l_ucbnstatus VARCHAR2(12 char) ;
-  l_login VARCHAR2(32 char) ;
-  l_nom_usuel VARCHAR2(128 char) ;
-  l_prenom VARCHAR2(128 char) ;
-  l_d_naissance VARCHAR2(8 char) ;
-  l_d_fin_insc VARCHAR2(8 char) ;
-  l_affectation VARCHAR2(8 char) ;
-  l_parrain_dn VARCHAR2(64 char) ;
 
-  -- Les variables pour le requetage LDAP
-  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
-  ldap_filtre VARCHAR2(256 char) ;
-  l_retval  PLS_INTEGER ;
-  l_attrs   DBMS_LDAP.string_collection ;
-  l_message DBMS_LDAP.message ;
-  l_entry   DBMS_LDAP.message ;
-  l_attr_name VARCHAR2(256 char) ;
-  l_ber_element  DBMS_LDAP.ber_element;
-  l_vals         DBMS_LDAP.string_collection;
+PROCEDURE CALCUL_FEUILLE_DE_ROUTE( CONDS CLOB ) IS
+BEGIN
+  FOR d IN (
+    SELECT   tbl_name
+    FROM     tbl
+    WHERE    feuille_de_route = 1
+    ORDER BY ordre
+  ) LOOP
+    UNICAEN_TBL.CALCULER(d.tbl_name,CONDS);
+  END LOOP;
+END;
 
-  i         PLS_INTEGER ;
-  nb_res    PLS_INTEGER ;
-  probleme  EXCEPTION ;
-  resultat  VARCHAR2(1024 char) := NULL ;
-  requete   VARCHAR2(4000 char) ;
 
-  elapsed_since_used NUMBER ;
 
+FUNCTION GET_TRIGGER_BODY( TRIGGER_NAME VARCHAR2 ) RETURN VARCHAR2 IS
+  vlong long;
 BEGIN
-  -- On fabrique le filtre a partir de la date en cours:
-  ldap_filtre := '(&(|(ucbnStatus=LECTEUR_SCD)(ucbnStatus=APPRENANT)(ucbnStatus=INVITE))(dateFinInscription>='||to_char(SYSDATE,'YYYYMMDD')||'))' ;
+  SELECT trigger_body INTO vlong FROM all_triggers WHERE trigger_name = GET_TRIGGER_BODY.TRIGGER_NAME;
 
-  -- On regarde depuis combien de temps la session n'a pas ete utilisee
-  elapsed_since_used:= to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) - last_used ;
-  last_used := to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) ;
+  RETURN substr(vlong, 1, 32767);
+END;
 
-  -- Si c'est trop vieux, on se reconnecte
-  IF elapsed_since_used > 10 THEN
-    l_retval := free() ;
-  END IF ;
+END OSE_DIVERS;
 
-  -- Si on n'est pas connecte:
-  IF ldap_sess IS NULL THEN
-    DBMS_OUTPUT.PUT_LINE('Reconnexion au serveur LDAP...');
-    l_retval := ldap_connect() ;
-    CASE l_retval
-      WHEN 1 THEN DBMS_OUTPUT.PUT_LINE('#Err 0010') ;
-      WHEN 2 THEN DBMS_OUTPUT.PUT_LINE('#Err 0011') ;
-      ELSE NULL;
-    END CASE;
-  END IF ;
+/
 
-  -- Les attributs LDAP qu'on recherche:
-  l_attrs(1) := 'uid' ;
-  l_attrs(2) := 'sn' ;
-  l_attrs(3) := 'givenName' ;
-  l_attrs(4) := 'dateDeNaissance' ;
-  l_attrs(5) := 'dateFinInscription' ;
-  l_attrs(6) := 'supannAliasLogin' ;
-  l_attrs(7) := 'supannEntiteAffectation' ;
-  l_attrs(8) := 'ucbnStatus' ;
-  l_attrs(9) := 'supannParrainDN' ;
+-- OSE_EVENT
+CREATE OR REPLACE PACKAGE BODY "OSE_EVENT" AS
+
+  PROCEDURE ON_AFTER_FORMULE_CALC( INTERVENANT_ID NUMERIC ) IS
+    p unicaen_tbl.t_params;
   BEGIN
-  l_retval := DBMS_LDAP.search_s(ld       => ldap_sess,
-                                 base     => ldap_base,
-                                 scope    => DBMS_LDAP.SCOPE_SUBTREE,
-                                 filter   => ldap_filtre,
-                                 attrs    => l_attrs,
-                                 attronly => 0,
-                                 res      => l_message) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.GENERAL_ERROR THEN
-      DBMS_OUTPUT.PUT_LINE('Erreur: '||SQLERRM);
-      DBMS_OUTPUT.PUT_LINE('#Err 0020') ;
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0021') ;
-    WHEN DBMS_LDAP.invalid_search_scope THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0022') ;
+    p := UNICAEN_TBL.make_params('INTERVENANT_ID', ON_AFTER_FORMULE_CALC.intervenant_id);
+/*
+    UNICAEN_TBL.CALCULER( 'agrement', p );
+    UNICAEN_TBL.CALCULER( 'paiement', p );
+    UNICAEN_TBL.CALCULER( 'workflow', p );*/
   END;
 
+END OSE_EVENT;
+
+/
+
+-- OSE_FORMULE
+CREATE OR REPLACE PACKAGE BODY "OSE_FORMULE" AS
+
+  TYPE t_lst_vh_etats IS TABLE OF t_volumes_horaires INDEX BY PLS_INTEGER;
+  TYPE t_lst_vh_types IS TABLE OF t_lst_vh_etats INDEX BY PLS_INTEGER;
+
+  TYPE t_resultat IS RECORD (
+    id                         NUMERIC,
+    formule_resultat_id        NUMERIC,
+    type_volume_horaire_id     NUMERIC,
+    etat_volume_horaire_id     NUMERIC,
+    service_id                 NUMERIC,
+    service_referentiel_id     NUMERIC,
+    volume_horaire_id          NUMERIC,
+    volume_horaire_ref_id      NUMERIC,
+    structure_id               NUMERIC,
+
+    service_fi                 FLOAT DEFAULT 0,
+    service_fa                 FLOAT DEFAULT 0,
+    service_fc                 FLOAT DEFAULT 0,
+    service_referentiel        FLOAT DEFAULT 0,
+    heures_compl_fi            FLOAT DEFAULT 0,
+    heures_compl_fa            FLOAT DEFAULT 0,
+    heures_compl_fc            FLOAT DEFAULT 0,
+    heures_compl_fc_majorees   FLOAT DEFAULT 0,
+    heures_compl_referentiel   FLOAT DEFAULT 0,
+
+    changed                    BOOLEAN DEFAULT FALSE,
+    debug_info                 CLOB
+  );
+  TYPE t_resultats IS TABLE OF t_resultat INDEX BY VARCHAR2(15);
+
+  all_volumes_horaires t_lst_vh_types;
+  arrondi NUMERIC DEFAULT 2;
+  t_res t_resultats;
+
+
+
+  FUNCTION GET_INTERVENANT_ID RETURN NUMERIC IS
   BEGIN
-  nb_res := DBMS_LDAP.count_entries(ld => ldap_sess, msg => l_message) ;
-  EXCEPTION
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0030') ;
-    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0031') ;
-    WHEN DBMS_LDAP.count_entry_error THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0032') ;
+    RETURN intervenant.id;
   END;
 
-  IF nb_res < 1 THEN
-    -- Pas besoin de fermer la connexion puisqu'on en utilise qu'une...
-    -- l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
-    -- RETURN '#Err 0033'; -- On retourne NULL depuis la 0.4.1
-    DBMS_OUTPUT.PUT_LINE('#Err 0033') ;
-  END IF;
 
-  -- Les entrees retournees
-  BEGIN
-  l_entry := DBMS_LDAP.first_entry(ld => ldap_sess, msg => l_message);
 
-  EXCEPTION
-    WHEN DBMS_LDAP.INVALID_SESSION THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0034') ;
-    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-      DBMS_OUTPUT.PUT_LINE('#Err 0035') ;
+  FUNCTION GET_TAUX_HORAIRE_HETD( DATE_OBS DATE DEFAULT NULL ) RETURN FLOAT IS
+    taux_hetd FLOAT;
+  BEGIN
+    SELECT valeur INTO taux_hetd
+    FROM taux_horaire_hetd t
+    WHERE
+      DATE_OBS BETWEEN t.histo_creation AND COALESCE(t.histo_destruction,GREATEST(SYSDATE,DATE_OBS))
+      AND rownum = 1
+    ORDER BY
+      histo_creation DESC;
+    RETURN taux_hetd;
   END;
 
 
-  WHILE l_entry IS NOT NULL LOOP
-    -- Tous les attributs de l'entree:
-    BEGIN
-    l_attr_name := DBMS_LDAP.first_attribute(ld        => ldap_sess,
-                                             ldapentry => l_entry,
-                                             ber_elem  => l_ber_element);
-    EXCEPTION
-      WHEN DBMS_LDAP.INVALID_SESSION THEN
-        DBMS_OUTPUT.PUT_LINE('#Err 0040') ;
-      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-        DBMS_OUTPUT.PUT_LINE('#Err 0041') ;
-    END;
 
-    WHILE l_attr_name IS NOT NULL LOOP
-      -- Les valeurs de cet attribut
-      BEGIN
-      l_vals := DBMS_LDAP.get_values (ld        => ldap_sess,
-                                      ldapentry => l_entry,
-                                      attr      => l_attr_name);
-      EXCEPTION
-        WHEN DBMS_LDAP.INVALID_SESSION THEN
-          DBMS_OUTPUT.PUT_LINE('#Err 0044') ;
-        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-          DBMS_OUTPUT.PUT_LINE('#Err 0045') ;
-      END;
+  PROCEDURE UPDATE_ANNEE_TAUX_HETD IS
+  BEGIN
+    UPDATE annee SET taux_hetd = GET_TAUX_HORAIRE_HETD(date_fin);
+  END;
 
-      -- On ne retourne que la premiere valeur
-      CASE
-        WHEN l_attr_name = 'uid' THEN l_uid := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'ucbnStatus' THEN l_ucbnstatus := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'supannAliasLogin' THEN l_login := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'sn' THEN l_nom_usuel := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'givenName' THEN l_prenom := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'dateDeNaissance' THEN l_d_naissance := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'dateFinInscription' THEN l_d_fin_insc := l_vals(l_vals.FIRST) ;
-        WHEN l_attr_name = 'supannEntiteAffectation' THEN l_affectation := SUBSTR( l_vals(l_vals.FIRST), 4 ) ;
-        WHEN l_attr_name = 'supannParrainDN' THEN l_parrain_dn := l_vals(l_vals.FIRST) ;
-      END CASE ;
 
-      -- Attribut suivant
-      BEGIN
-      l_attr_name := DBMS_LDAP.next_attribute(ld        => ldap_sess,
-                                              ldapentry => l_entry,
-                                              ber_elem  => l_ber_element);
-      EXCEPTION
-        WHEN DBMS_LDAP.INVALID_SESSION THEN
-          DBMS_OUTPUT.PUT_LINE('#Err 0042') ;
-        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-          DBMS_OUTPUT.PUT_LINE('#Err 0043') ;
-      END;
-    END LOOP ; -- LOOP Fin des attributs
 
-    -- On insere les valeurs dans la table
-    dbms_output.put_line('uid= '||l_uid||' ('||l_affectation||')    fin: '||l_d_fin_insc) ;
-    requete := 'SELECT count(*) FROM '||l_table||' WHERE ldap_uid= :l_uid' ;
-    EXECUTE IMMEDIATE requete INTO nb_res USING l_uid ;
-    IF nb_res = 0 THEN
-      -- un INSERT
-      requete := 'INSERT INTO '||l_table||'(ucbnstatus, login, nom_usuel, prenom, d_naissance, d_fin_insc, affectation, parrain_dn, ldap_uid) VALUES ' ;
-      requete := requete||'(:l_ucbnstatus, :l_login, :l_nom_usuel, :l_prenom, to_date(:l_d_naissance,''YYYYMMDD''), to_date(:l_d_fin_insc,''YYYYMMDD''), :l_affectation, :l_parrain_dn, :l_uid )' ;
-      dbms_output.put_line('req= '||requete) ;
-    ELSE
-      -- un UPDATE
-      requete := 'UPDATE '||l_table||' SET ' ;
-      requete := requete||'ucbnstatus=:l_ucbnstatus, login=:l_login, nom_usuel=:l_nom_usuel, prenom=:l_prenom, d_naissance=to_date(:l_d_naissance,''YYYYMMDD''), ' ;
-      requete := requete||'d_fin_insc=to_date(:l_d_fin_insc,''YYYYMMDD''), affectation=:l_affectation, parrain_dn=:l_parrain_dn ' ;
-      requete := requete||'WHERE ldap_uid=:l_uid' ;
-      dbms_output.put_line('req= '||requete) ;
-    END IF ;
-    -- Execution de la mise a jour de la table:
-    -- Attention, les variables sont bindees selon l'ordre, pas le nom !!!!
-    -- http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/dynamic.htm#LNPLS631
-    EXECUTE IMMEDIATE requete USING l_ucbnstatus, l_login, l_nom_usuel, l_prenom, l_d_naissance, l_d_fin_insc, l_affectation, l_parrain_dn, l_uid ;
+  PROCEDURE LOAD_INTERVENANT_FROM_BDD IS
+    dsdushc NUMERIC DEFAULT 0;
+  BEGIN
+    intervenant.service_du := 0;
+
+    SELECT
+      intervenant_id,
+      annee_id,
+      structure_id,
+      type_intervenant_code,
+      heures_service_statutaire,
+      depassement_service_du_sans_hc,
+      heures_service_modifie,
+      heures_decharge
+    INTO
+      intervenant.id,
+      intervenant.annee_id,
+      intervenant.structure_id,
+      intervenant.type_intervenant_code,
+      intervenant.heures_service_statutaire,
+      dsdushc,
+      intervenant.heures_service_modifie,
+      intervenant.heures_decharge
+    FROM
+      v_formule_intervenant fi
+    WHERE
+      fi.intervenant_id = intervenant.id;
 
-    IF l_ber_element IS NOT NULL THEN
-      DBMS_LDAP.ber_free(l_ber_element, 0) ;
-    END IF ;
+    intervenant.depassement_service_du_sans_hc := (dsdushc = 1);
+    intervenant.service_du := CASE
+      WHEN intervenant.depassement_service_du_sans_hc -- HC traitées comme du service
+        OR intervenant.heures_decharge < 0 -- s'il y a une décharge => aucune HC
 
-    BEGIN
-    l_entry := DBMS_LDAP.next_entry(ld  => ldap_sess,
-                                    msg => l_entry);
-    EXCEPTION
-      WHEN DBMS_LDAP.INVALID_SESSION THEN
-        DBMS_OUTPUT.PUT_LINE('#Err 0036') ;
-      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
-        DBMS_OUTPUT.PUT_LINE('#Err 0037') ;
+      THEN 9999
+      ELSE intervenant.heures_service_statutaire + intervenant.heures_service_modifie
     END;
 
-  END LOOP ; -- LOOP Fin des entrees
-
-  -- Liberation de la memoire
-  IF l_entry IS NOT NULL THEN
-    l_retval := DBMS_LDAP.msgfree(l_entry) ;
-  END IF ;
+    EXCEPTION WHEN NO_DATA_FOUND THEN
+      intervenant.id                             := NULL;
+      intervenant.annee_id                       := null;
+      intervenant.structure_id                   := null;
+      intervenant.heures_service_statutaire      := 0;
+      intervenant.depassement_service_du_sans_hc := FALSE;
+      intervenant.heures_service_modifie         := 0;
+      intervenant.heures_decharge                := 0;
+      intervenant.type_intervenant_code          := 'E';
+      intervenant.service_du                     := 0;
+  END;
 
-  -- Pas de deconnexion (on la reutilisera)
-  -- Par contre on COMMIT :
-  commit ;
 
-END getInvites ;
 
+  PROCEDURE LOAD_INTERVENANT_FROM_TEST IS
+    dsdushc NUMERIC DEFAULT 0;
+  BEGIN
+    SELECT
+      fti.id,
+      fti.annee_id,
+      fti.structure_test_id,
+      fti.type_volume_horaire_id,
+      fti.etat_volume_horaire_id,
+      fti.heures_decharge,
+      fti.heures_service_statutaire,
+      fti.heures_service_modifie,
+      fti.depassement_service_du_sans_hc,
+      fti.a_service_du,
+      ti.code
+    INTO
+      intervenant.id,
+      intervenant.annee_id,
+      intervenant.structure_id,
+      intervenant.type_volume_horaire_id,
+      intervenant.etat_volume_horaire_id,
+      intervenant.heures_decharge,
+      intervenant.heures_service_statutaire,
+      intervenant.heures_service_modifie,
+      dsdushc,
+      intervenant.service_du,
+      intervenant.type_intervenant_code
+    FROM
+      formule_test_intervenant fti
+      JOIN type_intervenant ti ON ti.id = fti.type_intervenant_id
+    WHERE
+      fti.id = intervenant.id;
 
-END ucbn_ldap ;
-/
+    intervenant.depassement_service_du_sans_hc := (dsdushc = 1);
+    intervenant.service_du := CASE
+      WHEN intervenant.depassement_service_du_sans_hc -- HC traitées comme du service
+        OR intervenant.heures_decharge < 0 -- s'il y a une décharge => aucune HC
 
--- UNICAEN_IMPORT
-CREATE OR REPLACE PACKAGE BODY "UNICAEN_IMPORT" AS
+      THEN 9999
+      ELSE intervenant.heures_service_statutaire + intervenant.heures_service_modifie
+    END;
 
-  v_current_user INTEGER;
-  v_current_annee INTEGER;
+    EXCEPTION WHEN NO_DATA_FOUND THEN
+      intervenant.id                             := NULL;
+      intervenant.annee_id                       := null;
+      intervenant.structure_id                   := null;
+      intervenant.heures_service_statutaire      := 0;
+      intervenant.depassement_service_du_sans_hc := FALSE;
+      intervenant.heures_service_modifie         := 0;
+      intervenant.heures_decharge                := 0;
+      intervenant.type_intervenant_code          := 'E';
+      intervenant.service_du                     := 0;
+  END;
 
 
 
-  FUNCTION get_current_user RETURN INTEGER IS
+  PROCEDURE LOAD_VH_FROM_BDD IS
+    vh t_volume_horaire;
+    etat_volume_horaire_id NUMERIC DEFAULT 1;
+    structure_univ NUMERIC;
+    length NUMERIC;
   BEGIN
-    IF v_current_user IS NULL THEN
-      v_current_user := OSE_PARAMETRE.GET_OSE_USER();
-    END IF;
-    RETURN v_current_user;
-  END get_current_user;
+    all_volumes_horaires.delete;
 
-  PROCEDURE set_current_user (p_current_user INTEGER) is
+    SELECT to_number(valeur) INTO structure_univ FROM parametre WHERE nom = 'structure_univ';
+
+    FOR d IN (
+      SELECT *
+      FROM   v_formule_volume_horaire fvh
+      WHERE  fvh.intervenant_id = intervenant.id
+    ) LOOP
+      vh.volume_horaire_id         := d.volume_horaire_id;
+      vh.volume_horaire_ref_id     := d.volume_horaire_ref_id;
+      vh.service_id                := d.service_id;
+      vh.service_referentiel_id    := d.service_referentiel_id;
+      vh.taux_fi                   := d.taux_fi;
+      vh.taux_fa                   := d.taux_fa;
+      vh.taux_fc                   := d.taux_fc;
+      vh.ponderation_service_du    := d.ponderation_service_du;
+      vh.ponderation_service_compl := d.ponderation_service_compl;
+      vh.structure_id              := d.structure_id;
+      vh.structure_is_affectation  := NVL(d.structure_id,0) = NVL(intervenant.structure_id,-1);
+      vh.structure_is_univ         := NVL(d.structure_id,0) = NVL(structure_univ,-1);
+      vh.service_statutaire        := d.service_statutaire = 1;
+      vh.heures                    := d.heures;
+      vh.taux_service_du           := d.taux_service_du;
+      vh.taux_service_compl        := d.taux_service_compl;
+
+      FOR etat_volume_horaire_id IN 1 .. d.etat_volume_horaire_id LOOP
+        BEGIN
+          length := all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).length;
+        EXCEPTION WHEN NO_DATA_FOUND THEN
+          length := 0;
+        END;
+        length := length + 1;
+        all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).length := length;
+        all_volumes_horaires(d.type_volume_horaire_id)(etat_volume_horaire_id).items(length) := vh;
+      END LOOP;
+    END LOOP;
+  END;
+
+
+
+  PROCEDURE LOAD_VH_FROM_TEST IS
+    vh t_volume_horaire;
+    etat_volume_horaire_id NUMERIC DEFAULT 1;
+    structure_univ NUMERIC;
+    length NUMERIC;
   BEGIN
-    v_current_user := p_current_user;
-  END set_current_user;
+    volumes_horaires.items.delete;
+    length := 0;
+
+    SELECT id INTO structure_univ FROM formule_test_structure WHERE universite = 1;
+
+    FOR d IN (
+      SELECT *
+      FROM   formule_test_volume_horaire ftvh
+      WHERE  ftvh.intervenant_test_id = intervenant.id
+      ORDER BY ftvh.id
+    ) LOOP
+      length := length + 1;
+      volumes_horaires.length := length;
+
+      IF d.referentiel = 0 THEN
+        volumes_horaires.items(length).volume_horaire_id       := d.id;
+        volumes_horaires.items(length).service_id              := d.id;
+      ELSE
+        volumes_horaires.items(length).volume_horaire_ref_id   := d.id;
+        volumes_horaires.items(length).service_referentiel_id  := d.id;
+      END IF;
+      volumes_horaires.items(length).taux_fi                   := d.taux_fi;
+      volumes_horaires.items(length).taux_fa                   := d.taux_fa;
+      volumes_horaires.items(length).taux_fc                   := d.taux_fc;
+      volumes_horaires.items(length).ponderation_service_du    := d.ponderation_service_du;
+      volumes_horaires.items(length).ponderation_service_compl := d.ponderation_service_compl;
+      volumes_horaires.items(length).structure_id              := d.structure_test_id;
+      volumes_horaires.items(length).structure_is_affectation  := NVL(d.structure_test_id,0) = NVL(intervenant.structure_id,-1);
+      volumes_horaires.items(length).structure_is_univ         := NVL(d.structure_test_id,0) = NVL(structure_univ,-1);
+      volumes_horaires.items(length).service_statutaire        := d.service_statutaire = 1;
+      volumes_horaires.items(length).heures                    := d.heures;
+      volumes_horaires.items(length).taux_service_du           := d.taux_service_du;
+      volumes_horaires.items(length).taux_service_compl        := d.taux_service_compl;
+    END LOOP;
+  END;
 
 
 
-  FUNCTION get_current_annee RETURN INTEGER IS
+  PROCEDURE tres_add_heures( code VARCHAR2, vh t_volume_horaire, tvh NUMERIC, evh NUMERIC) IS
   BEGIN
-    IF v_current_annee IS NULL THEN
-      v_current_annee := OSE_PARAMETRE.GET_ANNEE_IMPORT();
+    IF NOT t_res.exists(code) THEN
+      t_res(code).service_fi               := 0;
+      t_res(code).service_fa               := 0;
+      t_res(code).service_fc               := 0;
+      t_res(code).service_referentiel      := 0;
+      t_res(code).heures_compl_fi          := 0;
+      t_res(code).heures_compl_fa          := 0;
+      t_res(code).heures_compl_fc          := 0;
+      t_res(code).heures_compl_fc_majorees := 0;
+      t_res(code).heures_compl_referentiel := 0;
     END IF;
-    RETURN v_current_annee;
-  END get_current_annee;
 
-  PROCEDURE set_current_annee (p_current_annee INTEGER) IS
-  BEGIN
-    v_current_annee := p_current_annee;
-  END set_current_annee;
+    t_res(code).service_fi               := t_res(code).service_fi               + vh.service_fi;
+    t_res(code).service_fa               := t_res(code).service_fa               + vh.service_fa;
+    t_res(code).service_fc               := t_res(code).service_fc               + vh.service_fc;
+    t_res(code).service_referentiel      := t_res(code).service_referentiel      + vh.service_referentiel;
+    t_res(code).heures_compl_fi          := t_res(code).heures_compl_fi          + vh.heures_compl_fi;
+    t_res(code).heures_compl_fa          := t_res(code).heures_compl_fa          + vh.heures_compl_fa;
+    t_res(code).heures_compl_fc          := t_res(code).heures_compl_fc          + vh.heures_compl_fc;
+    t_res(code).heures_compl_fc_majorees := t_res(code).heures_compl_fc_majorees + vh.heures_compl_fc_majorees;
+    t_res(code).heures_compl_referentiel := t_res(code).heures_compl_referentiel + vh.heures_compl_referentiel;
+
+    t_res(code).type_volume_horaire_id := tvh;
+    t_res(code).etat_volume_horaire_id := evh;
+  END;
 
+  PROCEDURE DEBUG_TRES IS
+    code varchar2(15);
+    table_name varchar2(30);
+    fr formule_resultat%rowtype;
+    frs formule_resultat_service%rowtype;
+    frsr formule_resultat_service_ref%rowtype;
+    frvh formule_resultat_vh%rowtype;
+    frvhr formule_resultat_vh_ref%rowtype;
+  BEGIN
+    code := t_res.FIRST;
+    LOOP EXIT WHEN code IS NULL;
+      table_name := CASE
+        WHEN code LIKE '%-s-%' THEN 'FORMULE_RESULTAT_SERVICE'
+        WHEN code LIKE '%-sr-%' THEN 'FORMULE_RESULTAT_SERVICE_REF'
+        WHEN code LIKE '%-vh-%' THEN 'FORMULE_RESULTAT_VH'
+        WHEN code LIKE '%-vhr-%' THEN 'FORMULE_RESULTAT_VH_REF'
+        ELSE 'FORMULE_RESULTAT'
+      END;
 
+      ose_test.echo('T_RES( ' || code || ' - Table ' || table_name || ' ) ');
+      ose_test.echo('  id = ' || t_res(code).id);
+      ose_test.echo('  formule_resultat_id      = ' || t_res(code).formule_resultat_id);
+      ose_test.echo('  type_volume_horaire_id   = ' || t_res(code).type_volume_horaire_id);
+      ose_test.echo('  etat_volume_horaire_id   = ' || t_res(code).etat_volume_horaire_id);
+      ose_test.echo('  volume_horaire_id        = ' || t_res(code).volume_horaire_id);
+      ose_test.echo('  volume_horaire_ref_id    = ' || t_res(code).volume_horaire_ref_id);
+      ose_test.echo('  service_id               = ' || t_res(code).service_id);
+      ose_test.echo('  service_referentiel_id   = ' || t_res(code).service_referentiel_id);
+      ose_test.echo('  structure_id             = ' || t_res(code).structure_id);
+      ose_test.echo('  service_fi               = ' || t_res(code).service_fi);
+      ose_test.echo('  service_fa               = ' || t_res(code).service_fa);
+      ose_test.echo('  service_fc               = ' || t_res(code).service_fc);
+      ose_test.echo('  service_referentiel      = ' || t_res(code).service_referentiel);
+      ose_test.echo('  heures_compl_fi          = ' || t_res(code).heures_compl_fi);
+      ose_test.echo('  heures_compl_fa          = ' || t_res(code).heures_compl_fa);
+      ose_test.echo('  heures_compl_fc          = ' || t_res(code).heures_compl_fc);
+      ose_test.echo('  heures_compl_fc_majorees = ' || t_res(code).heures_compl_fc_majorees);
+      ose_test.echo('  heures_compl_referentiel = ' || t_res(code).heures_compl_referentiel);
+
+      code := t_res.NEXT(code);
+    END LOOP;
+  END;
 
-  PROCEDURE SYNCHRONISATION( table_name VARCHAR2, SYNC_FILRE CLOB DEFAULT '', IGNORE_UPD_COLS CLOB DEFAULT '', FORCE_SYNC BOOLEAN DEFAULT FALSE ) IS
-    ok NUMERIC(1);
-    sync NUMERIC;
+  PROCEDURE SAVE_TO_BDD IS
+    bcode VARCHAR(15);
+    code VARCHAR(15);
+    type_volume_horaire_id NUMERIC;
+    etat_volume_horaire_id NUMERIC;
+    vh t_volume_horaire;
+    fr formule_resultat%rowtype;
+    frs formule_resultat_service%rowtype;
+    frsr formule_resultat_service_ref%rowtype;
+    frvh formule_resultat_vh%rowtype;
+    frvhr formule_resultat_vh_ref%rowtype;
   BEGIN
-    IF FORCE_SYNC THEN
-      sync := 1;
-    ELSE
-      sync := 0;
-    END IF;
+    t_res.delete;
 
-    SELECT COUNT(*) INTO ok FROM import_tables it WHERE it.table_name = SYNCHRONISATION.table_name AND (it.sync_enabled = 1 OR sync=1) AND rownum = 1;
+    /* On préinitialise avec ce qui existe déjà */
+    FOR d IN (
+      SELECT
+        fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id code,
+        fr.id                       id,
+        fr.id                       formule_resultat_id,
+        fr.type_volume_horaire_id   type_volume_horaire_id,
+        fr.etat_volume_horaire_id   etat_volume_horaire_id,
+        null                        service_id,
+        null                        service_referentiel_id,
+        null                        volume_horaire_id,
+        null                        volume_horaire_ref_id
 
-    IF 1 = ok THEN
-      z__SYNC_FILRE__z      := SYNCHRONISATION.SYNC_FILRE;
-      z__IGNORE_UPD_COLS__z := SYNCHRONISATION.IGNORE_UPD_COLS;
-      EXECUTE IMMEDIATE 'BEGIN UNICAEN_IMPORT_AUTOGEN_PROCS__.' || table_name || '(); END;';
-    END IF;
+      FROM
+        formule_resultat fr
+      WHERE
+        fr.intervenant_id = intervenant.id
+
+      UNION ALL SELECT
+        fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-s-' || frs.service_id code,
+        frs.id                      id,
+        fr.id                       formule_resultat_id,
+        fr.type_volume_horaire_id   type_volume_horaire_id,
+        fr.etat_volume_horaire_id   etat_volume_horaire_id,
+        frs.service_id              service_id,
+        null                        service_referentiel_id,
+        null                        volume_horaire_id,
+        null                        volume_horaire_ref_id
+      FROM
+        formule_resultat_service frs
+        JOIN formule_resultat fr ON fr.id = frs.formule_resultat_id
+      WHERE
+        fr.intervenant_id = intervenant.id
+
+      UNION ALL SELECT
+        fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-sr-' || frsr.service_referentiel_id code,
+        frsr.id                     id,
+        fr.id                       formule_resultat_id,
+        fr.type_volume_horaire_id   type_volume_horaire_id,
+        fr.etat_volume_horaire_id   etat_volume_horaire_id,
+        null                        service_id,
+        frsr.service_referentiel_id service_referentiel_id,
+        null                        volume_horaire_id,
+        null                        volume_horaire_ref_id
+      FROM
+        formule_resultat_service_ref frsr
+        JOIN formule_resultat fr ON fr.id = frsr.formule_resultat_id
+      WHERE
+        fr.intervenant_id = intervenant.id
+
+      UNION ALL SELECT
+        fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-vh-' || frvh.volume_horaire_id code,
+        frvh.id                     id,
+        fr.id                       formule_resultat_id,
+        fr.type_volume_horaire_id   type_volume_horaire_id,
+        fr.etat_volume_horaire_id   etat_volume_horaire_id,
+        null                        service_id,
+        null                        service_referentiel_id,
+        frvh.volume_horaire_id      volume_horaire_id,
+        null                        volume_horaire_ref_id
+      FROM
+        formule_resultat_vh frvh
+        JOIN formule_resultat fr ON fr.id = frvh.formule_resultat_id
+      WHERE
+        fr.intervenant_id = intervenant.id
+
+      UNION ALL SELECT
+        fr.type_volume_horaire_id || '-' || fr.etat_volume_horaire_id || '-vhr-' || frvhr.volume_horaire_ref_id code,
+        frvhr.id                    id,
+        fr.id                       formule_resultat_id,
+        fr.type_volume_horaire_id   type_volume_horaire_id,
+        fr.etat_volume_horaire_id   etat_volume_horaire_id,
+        null                        service_id,
+        null                        service_referentiel_id,
+        null                        volume_horaire_id,
+        frvhr.volume_horaire_ref_id volume_horaire_ref_id
+      FROM
+        formule_resultat_vh_ref frvhr
+        JOIN formule_resultat fr ON fr.id = frvhr.formule_resultat_id
+      WHERE
+        fr.intervenant_id = intervenant.id
+    ) LOOP
+      t_res(d.code).id                     := d.id;
+      t_res(d.code).formule_resultat_id    := d.formule_resultat_id;
+      t_res(d.code).type_volume_horaire_id := d.type_volume_horaire_id;
+      t_res(d.code).etat_volume_horaire_id := d.etat_volume_horaire_id;
+      t_res(d.code).service_id             := d.service_id;
+      t_res(d.code).service_referentiel_id := d.service_referentiel_id;
+      t_res(d.code).volume_horaire_id      := d.volume_horaire_id;
+      t_res(d.code).volume_horaire_ref_id  := d.volume_horaire_ref_id;
+    END LOOP;
+
+    /* On charge avec les résultats de formule */
+    type_volume_horaire_id := all_volumes_horaires.FIRST;
+    LOOP EXIT WHEN type_volume_horaire_id IS NULL;
+      etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
+      LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
+        FOR i IN 1 .. all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).length LOOP
+          vh := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).items(i);
+          bcode := type_volume_horaire_id || '-' || etat_volume_horaire_id;
+
+          -- formule_resultat
+          code := bcode;
+          tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
+
+          -- formule_resultat_service
+          IF vh.service_id IS NOT NULL THEN
+            code := bcode || '-s-' || vh.service_id;
+            t_res(code).service_id := vh.service_id;
+            tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
+          END IF;
+
+          -- formule_resultat_service_ref
+          IF vh.service_referentiel_id IS NOT NULL THEN
+            code := bcode || '-sr-' || vh.service_referentiel_id;
+            t_res(code).service_referentiel_id := vh.service_referentiel_id;
+            tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
+          END IF;
+
+          -- formule_resultat_volume_horaire
+          IF vh.volume_horaire_id IS NOT NULL THEN
+            code := bcode || '-vh-' || vh.volume_horaire_id;
+            t_res(code).volume_horaire_id := vh.volume_horaire_id;
+            tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
+          END IF;
+
+          -- formule_resultat_volume_horaire_ref
+          IF vh.volume_horaire_ref_id IS NOT NULL THEN
+            code := bcode || '-vhr-' || vh.volume_horaire_ref_id;
+            t_res(code).volume_horaire_ref_id := vh.volume_horaire_ref_id;
+            tres_add_heures(code,vh, type_volume_horaire_id, etat_volume_horaire_id);
+          END IF;
+
+        END LOOP;
+        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
+      END LOOP;
+      type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
+    END LOOP;
+
+    /* On fait la sauvegarde en BDD */
+    /* D'abord le formule_resultat */
+    code := t_res.FIRST;
+    LOOP EXIT WHEN code IS NULL;
+      IF code = (t_res(code).type_volume_horaire_id || '-' || t_res(code).etat_volume_horaire_id) THEN
+        fr.id                       := t_res(code).id;
+        fr.intervenant_id           := intervenant.id;
+        fr.type_volume_horaire_id   := t_res(code).type_volume_horaire_id;
+        fr.etat_volume_horaire_id   := t_res(code).etat_volume_horaire_id;
+        fr.service_fi               := ROUND(t_res(code).service_fi,2);
+        fr.service_fa               := ROUND(t_res(code).service_fa,2);
+        fr.service_fc               := ROUND(t_res(code).service_fc,2);
+        fr.service_referentiel      := ROUND(t_res(code).service_referentiel,2);
+        fr.heures_compl_fi          := ROUND(t_res(code).heures_compl_fi,2);
+        fr.heures_compl_fa          := ROUND(t_res(code).heures_compl_fa,2);
+        fr.heures_compl_fc          := ROUND(t_res(code).heures_compl_fc,2);
+        fr.heures_compl_fc_majorees := ROUND(t_res(code).heures_compl_fc_majorees,2);
+        fr.heures_compl_referentiel := ROUND(t_res(code).heures_compl_referentiel,2);
+        fr.total := fr.service_fi + fr.service_fa + fr.service_fc + fr.service_referentiel
+                  + fr.heures_compl_fi + fr.heures_compl_fa + fr.heures_compl_fc
+                  + fr.heures_compl_fc_majorees + fr.heures_compl_referentiel;
+
+        fr.service_du := ROUND(CASE
+          WHEN intervenant.depassement_service_du_sans_hc OR intervenant.heures_decharge < 0
+          THEN GREATEST(fr.total, intervenant.heures_service_statutaire + intervenant.heures_service_modifie)
+          ELSE intervenant.heures_service_statutaire + intervenant.heures_service_modifie
+        END,2);
+
+        fr.solde                    := fr.total - fr.service_du;
+        IF fr.solde >= 0 THEN
+          fr.sous_service           := 0;
+          fr.heures_compl           := fr.solde;
+        ELSE
+          fr.sous_service           := fr.solde * -1;
+          fr.heures_compl           := 0;
+        END IF;
+        fr.type_intervenant_code    := intervenant.type_intervenant_code;
+
+        IF fr.id IS NULL THEN
+          fr.id := formule_resultat_id_seq.nextval;
+          t_res(code).id := fr.id;
+          INSERT INTO formule_resultat VALUES fr;
+        ELSE
+          UPDATE formule_resultat SET ROW = fr WHERE id = fr.id;
+        END IF;
+      END IF;
+      code := t_res.NEXT(code);
+    END LOOP;
+
+    --DEBUG_TRES;
+
+    /* Ensuite toutes les dépendances... */
+    code := t_res.FIRST;
+    LOOP EXIT WHEN code IS NULL;
+      bcode := t_res(code).type_volume_horaire_id || '-' || t_res(code).etat_volume_horaire_id;
+      CASE
+        WHEN code LIKE '%-s-%' THEN -- formule_resultat_service
+          frs.id                         := t_res(code).id;
+          frs.formule_resultat_id        := t_res(bcode).id;
+          frs.service_id                 := t_res(code).service_id;
+          frs.service_fi                 := ROUND(t_res(code).service_fi, 2);
+          frs.service_fa                 := ROUND(t_res(code).service_fa, 2);
+          frs.service_fc                 := ROUND(t_res(code).service_fc, 2);
+          frs.heures_compl_fi            := ROUND(t_res(code).heures_compl_fi, 2);
+          frs.heures_compl_fa            := ROUND(t_res(code).heures_compl_fa, 2);
+          frs.heures_compl_fc            := ROUND(t_res(code).heures_compl_fc, 2);
+          frs.heures_compl_fc_majorees   := ROUND(t_res(code).heures_compl_fc_majorees, 2);
+          frs.total                      := frs.service_fi + frs.service_fa + frs.service_fc
+                 + frs.heures_compl_fi + frs.heures_compl_fa + frs.heures_compl_fc + frs.heures_compl_fc_majorees;
+          IF frs.id IS NULL THEN
+            frs.id := formule_resultat_servic_id_seq.nextval;
+            INSERT INTO formule_resultat_service VALUES frs;
+          ELSE
+            UPDATE formule_resultat_service SET ROW = frs WHERE id = frs.id;
+          END IF;
+        WHEN code LIKE '%-sr-%' THEN -- formule_resultat_service_ref
+          frsr.id                        := t_res(code).id;
+          frsr.formule_resultat_id       := t_res(bcode).id;
+          frsr.service_referentiel_id    := t_res(code).service_referentiel_id;
+          frsr.service_referentiel       := ROUND(t_res(code).service_referentiel, 2);
+          frsr.heures_compl_referentiel  := ROUND(t_res(code).heures_compl_referentiel, 2);
+          frsr.total                     := frsr.service_referentiel + frsr.heures_compl_referentiel;
+          IF frsr.id IS NULL THEN
+            frsr.id := formule_resultat_servic_id_seq.nextval;
+            INSERT INTO formule_resultat_service_ref VALUES frsr;
+          ELSE
+            UPDATE formule_resultat_service_ref SET ROW = frsr WHERE id = frsr.id;
+          END IF;
+        WHEN code LIKE '%-vh-%' THEN -- formule_resultat_vh
+          frvh.id := t_res(code).id;
+          frvh.formule_resultat_id       := t_res(bcode).id;
+          frvh.volume_horaire_id         := t_res(code).volume_horaire_id;
+          frvh.service_fi                := ROUND(t_res(code).service_fi, 2);
+          frvh.service_fa                := ROUND(t_res(code).service_fa, 2);
+          frvh.service_fc                := ROUND(t_res(code).service_fc, 2);
+          frvh.heures_compl_fi           := ROUND(t_res(code).heures_compl_fi, 2);
+          frvh.heures_compl_fa           := ROUND(t_res(code).heures_compl_fa, 2);
+          frvh.heures_compl_fc           := ROUND(t_res(code).heures_compl_fc, 2);
+          frvh.heures_compl_fc_majorees  := ROUND(t_res(code).heures_compl_fc_majorees, 2);
+          frvh.total                     := frvh.service_fi + frvh.service_fa + frvh.service_fc
+                  + frvh.heures_compl_fi + frvh.heures_compl_fa + frvh.heures_compl_fc + frvh.heures_compl_fc_majorees;
+          IF frvh.id IS NULL THEN
+            frvh.id := formule_resultat_vh_id_seq.nextval;
+            INSERT INTO formule_resultat_vh VALUES frvh;
+          ELSE
+            UPDATE formule_resultat_vh SET ROW = frvh WHERE id = frvh.id;
+          END IF;
+        WHEN code LIKE '%-vhr-%' THEN -- formule_resultat_vh_ref
+          frvhr.id := t_res(code).id;
+          frvhr.formule_resultat_id      := t_res(bcode).id;
+          frvhr.volume_horaire_ref_id    := t_res(code).volume_horaire_ref_id;
+          frvhr.service_referentiel      := ROUND(t_res(code).service_referentiel, 2);
+          frvhr.heures_compl_referentiel := ROUND(t_res(code).heures_compl_referentiel, 2);
+          frvhr.total                    := frvhr.service_referentiel + frvhr.heures_compl_referentiel;
+          IF frvhr.id IS NULL THEN
+            frvhr.id := formule_resultat_vh_ref_id_seq.nextval;
+            INSERT INTO formule_resultat_vh_ref VALUES frvhr;
+          ELSE
+            UPDATE formule_resultat_vh_ref SET ROW = frvhr WHERE id = frvhr.id;
+          END IF;
+        ELSE code := code;
+      END CASE;
+      code := t_res.NEXT(code);
+    END LOOP;
   END;
 
 
 
-  PROCEDURE REFRESH_MV( mview_name varchar2 ) IS
+  PROCEDURE SAVE_TO_TEST(passed NUMERIC) IS
+    vh t_volume_horaire;
   BEGIN
-    DBMS_MVIEW.REFRESH(mview_name, 'C');
-  EXCEPTION WHEN OTHERS THEN
-    SYNC_LOG( SQLERRM, mview_name );
+    UPDATE formule_test_intervenant SET
+      c_service_du = CASE WHEN passed = 1 THEN intervenant.service_du ELSE NULL END,
+      debug_info = intervenant.debug_info
+    WHERE id = intervenant.id;
+
+    FOR i IN 1 .. volumes_horaires.length LOOP
+      vh := volumes_horaires.items(i);
+      UPDATE formule_test_volume_horaire SET
+        c_service_fi               = CASE WHEN passed = 1 THEN vh.service_fi ELSE NULL END,
+        c_service_fa               = CASE WHEN passed = 1 THEN vh.service_fa ELSE NULL END,
+        c_service_fc               = CASE WHEN passed = 1 THEN vh.service_fc ELSE NULL END,
+        c_service_referentiel      = CASE WHEN passed = 1 THEN vh.service_referentiel ELSE NULL END,
+        c_heures_compl_fi          = CASE WHEN passed = 1 THEN vh.heures_compl_fi ELSE NULL END,
+        c_heures_compl_fa          = CASE WHEN passed = 1 THEN vh.heures_compl_fa ELSE NULL END,
+        c_heures_compl_fc          = CASE WHEN passed = 1 THEN vh.heures_compl_fc ELSE NULL END,
+        c_heures_compl_fc_majorees = CASE WHEN passed = 1 THEN vh.heures_compl_fc_majorees ELSE NULL END,
+        c_heures_compl_referentiel = CASE WHEN passed = 1 THEN vh.heures_compl_referentiel ELSE NULL END,
+        debug_info                 = vh.debug_info
+      WHERE
+        id = COALESCE(vh.volume_horaire_id,vh.volume_horaire_ref_id);
+    END LOOP;
   END;
 
 
 
-  PROCEDURE SYNC_LOG( message CLOB, table_name VARCHAR2 DEFAULT NULL, source_code VARCHAR2 DEFAULT NULL ) IS
+  PROCEDURE CALCULER( INTERVENANT_ID NUMERIC ) IS
+    type_volume_horaire_id NUMERIC;
+    etat_volume_horaire_id NUMERIC;
+    fdata formule%rowtype;
   BEGIN
-    INSERT INTO SYNC_LOG("ID","DATE_SYNC","MESSAGE","TABLE_NAME","SOURCE_CODE") VALUES (SYNC_LOG_ID_SEQ.NEXTVAL, SYSDATE, message,table_name,source_code);
-  END SYNC_LOG;
+    intervenant.id := intervenant_id;
+    fdata := ose_parametre.get_formule;
+
+    LOAD_INTERVENANT_FROM_BDD;
+    LOAD_VH_FROM_BDD;
+
+    type_volume_horaire_id := all_volumes_horaires.FIRST;
+    LOOP EXIT WHEN type_volume_horaire_id IS NULL;
+      intervenant.type_volume_horaire_id := type_volume_horaire_id;
+      etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
+      LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
+        intervenant.etat_volume_horaire_id := etat_volume_horaire_id;
+        volumes_horaires := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id);
+        EXECUTE IMMEDIATE 'BEGIN ' || fdata.package_name || '.' || fdata.procedure_name || '; END;';
+        all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id) := volumes_horaires;
+        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
+      END LOOP;
+      type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
+    END LOOP;
 
+    SAVE_TO_BDD;
 
+    OSE_EVENT.ON_AFTER_FORMULE_CALC( CALCULER.INTERVENANT_ID );
+  END;
 
-  FUNCTION IN_COLUMN_LIST( VALEUR VARCHAR2, CHAMPS CLOB ) RETURN NUMERIC IS
+  PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL ) IS
+    a_id NUMERIC;
   BEGIN
-    IF REGEXP_LIKE(CHAMPS, '(^|,)[ \t\r\n\v\f]*' || VALEUR || '[ \t\r\n\v\f]*(,|$)') THEN RETURN 1; END IF;
-    RETURN 0;
+    a_id := NVL(CALCULER_TOUT.ANNEE_ID, OSE_PARAMETRE.GET_ANNEE);
+    FOR mp IN (
+      SELECT DISTINCT
+        intervenant_id
+      FROM
+        service s
+        JOIN intervenant i ON i.id = s.intervenant_id
+      WHERE
+        s.histo_destruction IS NULL
+        AND i.annee_id = a_id
+
+      UNION ALL
+
+      SELECT DISTINCT
+        intervenant_id
+      FROM
+        service_referentiel sr
+        JOIN intervenant i ON i.id = sr.intervenant_id
+      WHERE
+        sr.histo_destruction IS NULL
+        AND i.annee_id = a_id
+
+    )
+    LOOP
+      CALCULER( mp.intervenant_id );
+    END LOOP;
   END;
 
-END UNICAEN_IMPORT;
-/
 
--- UNICAEN_OSE_FORMULE
-CREATE OR REPLACE PACKAGE BODY "UNICAEN_OSE_FORMULE" AS
 
-/* Stockage des valeurs intermédiaires */
-      TYPE t_valeurs IS TABLE OF FLOAT INDEX BY PLS_INTEGER;
-      TYPE t_tableau IS RECORD (
-      valeurs t_valeurs,
-      total   FLOAT DEFAULT 0
-      );
-      TYPE t_tableaux       IS TABLE OF t_tableau INDEX BY PLS_INTEGER;
-      TYPE t_tableau_config IS RECORD (
-      tableau NUMERIC,
-      version NUMERIC,
-      referentiel BOOLEAN DEFAULT FALSE,
-      setTotal BOOLEAN DEFAULT FALSE
-      );
-      TYPE t_tableaux_configs IS VARRAY(100) OF t_tableau_config;
-
-      t                     t_tableaux;
-      vh_index              NUMERIC;
-
-
-
-      -- Crée une définition de tableau
-      FUNCTION TC( tableau NUMERIC, version NUMERIC, options VARCHAR2 DEFAULT NULL) RETURN t_tableau_config IS
-            tcRes t_tableau_config;
-            BEGIN
-                  tcRes.tableau := tableau;
-                  tcRes.version := version;
-                  CASE
-                        WHEN options like '%t%' THEN tcRes.setTotal := TRUE;
-                        WHEN options like '%r%' THEN tcRes.referentiel := TRUE;
-                  ELSE RETURN tcRes;
-                  END CASE;
-
-                  RETURN tcRes;
-            END;
-
-      -- Setter d'une valeur intermédiaire au niveau case
-      PROCEDURE SV( tableau NUMERIC, valeur FLOAT ) IS
-            BEGIN
-                  t(tableau).valeurs(vh_index) := valeur;
-                  t(tableau).total             := t(tableau).total + valeur;
-            END;
-
-      -- Setter d'une valeur intermédiaire au niveau tableau
-      PROCEDURE ST( tableau NUMERIC, valeur FLOAT ) IS
-            BEGIN
-                  t(tableau).total      := valeur;
-            END;
-
-      -- Getter d'une valeur intermédiaire, au niveau case
-      FUNCTION GV( tableau NUMERIC ) RETURN FLOAT IS
-            BEGIN
-                  IF NOT t.exists(tableau) THEN RETURN 0; END IF;
-                  IF NOT t(tableau).valeurs.exists( vh_index ) THEN RETURN 0; END IF;
-                  RETURN t(tableau).valeurs( vh_index );
-            END;
-
-      -- Getter d'une valeur intermédiaire, au niveau tableau
-      FUNCTION GT( tableau NUMERIC ) RETURN FLOAT IS
-            BEGIN
-                  IF NOT t.exists(tableau) THEN RETURN 0; END IF;
-                  RETURN t(tableau).total;
-            END;
-
-
-
-
-      PROCEDURE DEBUG_VH IS
-            tableau NUMERIC;
-            vh ose_formule.t_volume_horaire;
-            BEGIN
-                  IF NOT debug_enabled THEN RETURN; END IF;
-                  IF ose_formule.intervenant.etat_volume_horaire_id <> debug_etat_volume_horaire_id THEN RETURN; END IF;
-
-                  FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
-                        vh_index := i;
-                        vh := ose_formule.volumes_horaires.items(i);
-                        IF vh.volume_horaire_id = debug_volume_horaire_id OR vh.volume_horaire_ref_id = debug_volume_horaire_ref_id THEN
-                              ose_formule.DEBUG_INTERVENANT;
-                              ose_test.echo('');
-                              ose_test.echo('-- DEBUG DE VOLUME HORAIRE --');
-                              ose_test.echo('volume_horaire_id         = ' || vh.volume_horaire_id);
-                              ose_test.echo('volume_horaire_ref_id     = ' || vh.volume_horaire_ref_id);
-                              ose_test.echo('service_id                = ' || vh.service_id);
-                              ose_test.echo('service_referentiel_id    = ' || vh.service_referentiel_id);
-                              ose_test.echo('taux_fi                   = ' || vh.taux_fi);
-                              ose_test.echo('taux_fa                   = ' || vh.taux_fa);
-                              ose_test.echo('taux_fc                   = ' || vh.taux_fc);
-                              ose_test.echo('ponderation_service_du    = ' || vh.ponderation_service_du);
-                              ose_test.echo('ponderation_service_compl = ' || vh.ponderation_service_compl);
-                              ose_test.echo('structure_id              = ' || vh.structure_id);
-                              ose_test.echo('structure_is_affectation  = ' || CASE WHEN vh.structure_is_affectation THEN 'OUI' ELSE 'NON' END);
-                              ose_test.echo('structure_is_univ         = ' || CASE WHEN vh.structure_is_univ THEN 'OUI' ELSE 'NON' END);
-                              ose_test.echo('service_statutaire        = ' || CASE WHEN vh.service_statutaire THEN 'OUI' ELSE 'NON' END);
-                              ose_test.echo('heures                    = ' || vh.heures);
-                              ose_test.echo('taux_service_du           = ' || vh.taux_service_du);
-                              ose_test.echo('taux_service_compl        = ' || vh.taux_service_compl);
-
-                              tableau := t.FIRST;
-                              LOOP EXIT WHEN tableau IS NULL;
-                                    IF gv(tableau) <> 0 OR gt(tableau) <> 0 THEN
-                                          ose_test.echo('     t(' || LPAD(tableau,3,' ') || ') v=' || RPAD(round(gv(tableau),3),10,' ') || 't=' || round(gt(tableau),3));
-                                    END IF;
-                                    tableau := t.NEXT(tableau);
-                              END LOOP;
-
-                              ose_test.echo('service_fi                = ' || vh.service_fi);
-                              ose_test.echo('service_fa                = ' || vh.service_fa);
-                              ose_test.echo('service_fc                = ' || vh.service_fc);
-                              ose_test.echo('service_referentiel       = ' || vh.service_referentiel);
-                              ose_test.echo('heures_compl_fi           = ' || vh.heures_compl_fi);
-                              ose_test.echo('heures_compl_fa           = ' || vh.heures_compl_fa);
-                              ose_test.echo('heures_compl_fc           = ' || vh.heures_compl_fc);
-                              ose_test.echo('heures_compl_fc_majorees  = ' || vh.heures_compl_fc_majorees);
-                              ose_test.echo('heures_compl_referentiel  = ' || vh.heures_compl_referentiel);
-                              ose_test.echo('-- FIN DE DEBUG DE VOLUME HORAIRE --');
-                              ose_test.echo('');
-                        END IF;
-                  END LOOP;
-            END;
-
-
-
-      -- Formule de calcul définie par tableaux
-      FUNCTION EXECFORMULE( tableau NUMERIC, version NUMERIC ) RETURN FLOAT IS
-            vh ose_formule.t_volume_horaire;
-            BEGIN
-                  vh := ose_formule.volumes_horaires.items(vh_index);
-                  CASE
+  PROCEDURE TEST( INTERVENANT_TEST_ID NUMERIC ) IS
+    procedure_name VARCHAR2(30);
+    package_name VARCHAR2(30);
+  BEGIN
+    intervenant.id := INTERVENANT_TEST_ID;
+
+    SELECT
+      package_name, procedure_name INTO package_name, procedure_name
+    FROM
+      formule f JOIN formule_test_intervenant fti ON fti.formule_id = f.id
+    WHERE
+      fti.id = intervenant.id;
+
+    LOAD_INTERVENANT_FROM_TEST;
+    LOAD_VH_FROM_TEST;
+
+    BEGIN
+      EXECUTE IMMEDIATE 'BEGIN ' || package_name || '.' || procedure_name || '; END;';
+      SAVE_TO_TEST(1);
+    EXCEPTION WHEN OTHERS THEN
+      SAVE_TO_TEST(0);
+      RAISE_APPLICATION_ERROR(-20001, dbms_utility.format_error_backtrace,true);
+    END;
+  END;
+
+
+
+  PROCEDURE TEST_TOUT IS
+  BEGIN
+    FOR d IN (SELECT id FROM formule_test_intervenant)
+    LOOP
+      TEST( d.id );
+    END LOOP;
+  END;
+
+
+
+  PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS ) IS
+    intervenant_id NUMERIC;
+    TYPE r_cursor IS REF CURSOR;
+    diff_cur r_cursor;
+  BEGIN
+    OPEN diff_cur FOR 'WITH interv AS (SELECT id intervenant_id, intervenant.* FROM intervenant)
+    SELECT intervenant_id FROM interv WHERE ' || unicaen_tbl.PARAMS_TO_CONDS( params );
+    LOOP
+      FETCH diff_cur INTO intervenant_id; EXIT WHEN diff_cur%NOTFOUND;
+      BEGIN
+        CALCULER( intervenant_id );
+      END;
+    END LOOP;
+    CLOSE diff_cur;
+  END;
 
 
-                        WHEN tableau = 11 AND version = 2 THEN
-                        IF vh.structure_is_affectation AND vh.taux_fc < 1 THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
+  PROCEDURE DEBUG_INTERVENANT IS
+  BEGIN
+    ose_test.echo('OSE Formule DEBUG Intervenant');
+    ose_test.echo('id                             = ' || intervenant.id);
+    ose_test.echo('annee_id                       = ' || intervenant.annee_id);
+    ose_test.echo('structure_id                   = ' || intervenant.structure_id);
+    ose_test.echo('type_volume_horaire_id         = ' || intervenant.type_volume_horaire_id);
+    ose_test.echo('heures_decharge                = ' || intervenant.heures_decharge);
+    ose_test.echo('heures_service_statutaire      = ' || intervenant.heures_service_statutaire);
+    ose_test.echo('heures_service_modifie         = ' || intervenant.heures_service_modifie);
+    ose_test.echo('depassement_service_du_sans_hc = ' || CASE WHEN intervenant.depassement_service_du_sans_hc THEN 'OUI' ELSE 'NON' END);
+    ose_test.echo('service_du                     = ' || intervenant.service_du);
+  END;
+
+  PROCEDURE DEBUG_VOLUMES_HORAIRES(VOLUME_HORAIRE_ID NUMERIC DEFAULT NULL) IS
+    type_volume_horaire_id NUMERIC;
+    etat_volume_horaire_id NUMERIC;
+    vh t_volume_horaire;
+  BEGIN
+    ose_test.echo('OSE Formule DEBUG Intervenant');
+
+    type_volume_horaire_id := all_volumes_horaires.FIRST;
+    LOOP EXIT WHEN type_volume_horaire_id IS NULL;
+      etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).FIRST;
+      LOOP EXIT WHEN etat_volume_horaire_id IS NULL;
+        ose_test.echo('tvh=' || type_volume_horaire_id || ', evh=' || etat_volume_horaire_id);
+        FOR i IN 1 .. all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).length LOOP
+          vh := all_volumes_horaires(type_volume_horaire_id)(etat_volume_horaire_id).items(i);
+          IF VOLUME_HORAIRE_ID IS NULL OR VOLUME_HORAIRE_ID = vh.volume_horaire_id OR VOLUME_HORAIRE_ID = vh.volume_horaire_ref_id THEN
+            ose_test.echo('volume_horaire_id         = ' || vh.volume_horaire_id);
+            ose_test.echo('volume_horaire_ref_id     = ' || vh.volume_horaire_ref_id);
+            ose_test.echo('service_id                = ' || vh.service_id);
+            ose_test.echo('service_referentiel_id    = ' || vh.service_referentiel_id);
+            ose_test.echo('taux_fi                   = ' || vh.taux_fi);
+            ose_test.echo('taux_fa                   = ' || vh.taux_fa);
+            ose_test.echo('taux_fc                   = ' || vh.taux_fc);
+            ose_test.echo('ponderation_service_du    = ' || vh.ponderation_service_du);
+            ose_test.echo('ponderation_service_compl = ' || vh.ponderation_service_compl);
+            ose_test.echo('structure_id              = ' || vh.structure_id);
+            ose_test.echo('structure_is_affectation  = ' || CASE WHEN vh.structure_is_affectation THEN 'OUI' ELSE 'NON' END);
+            ose_test.echo('structure_is_univ         = ' || CASE WHEN vh.structure_is_univ THEN 'OUI' ELSE 'NON' END);
+            ose_test.echo('service_statutaire        = ' || CASE WHEN vh.service_statutaire THEN 'OUI' ELSE 'NON' END);
+            ose_test.echo('heures                    = ' || vh.heures);
+            ose_test.echo('taux_service_du           = ' || vh.taux_service_du);
+            ose_test.echo('taux_service_compl        = ' || vh.taux_service_compl);
+            ose_test.echo('service_fi                = ' || vh.service_fi);
+            ose_test.echo('service_fa                = ' || vh.service_fa);
+            ose_test.echo('service_fc                = ' || vh.service_fc);
+            ose_test.echo('service_referentiel       = ' || vh.service_referentiel);
+            ose_test.echo('heures_compl_fi           = ' || vh.heures_compl_fi);
+            ose_test.echo('heures_compl_fa           = ' || vh.heures_compl_fa);
+            ose_test.echo('heures_compl_fc           = ' || vh.heures_compl_fc);
+            ose_test.echo('heures_compl_fc_majorees  = ' || vh.heures_compl_fc_majorees);
+            ose_test.echo('heures_compl_referentiel  = ' || vh.heures_compl_referentiel);
+            ose_test.echo('');
+          END IF;
+        END LOOP;
+        etat_volume_horaire_id := all_volumes_horaires(type_volume_horaire_id).NEXT(etat_volume_horaire_id);
+      END LOOP;
+      type_volume_horaire_id := all_volumes_horaires.NEXT(type_volume_horaire_id);
+    END LOOP;
+  END;
 
+END OSE_FORMULE;
 
-                        WHEN tableau = 11 AND version = 3 THEN
-                        IF vh.structure_is_affectation THEN
-                              RETURN vh.heures * (vh.taux_fi + vh.taux_fa);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+/
 
+-- OSE_HISTO
+CREATE OR REPLACE PACKAGE BODY "OSE_HISTO" AS
 
+  FUNCTION FILTRE( histo_debut NUMERIC, histo_fin NUMERIC ) RETURN NUMERIC IS
+  BEGIN
+    RETURN 1;
+  END;
 
-                        WHEN tableau = 12 AND version = 2 THEN
-                        IF NOT vh.structure_is_affectation AND vh.taux_fc < 1 THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+END OSE_HISTO;
+/
 
+-- OSE_IMPORT
+CREATE OR REPLACE PACKAGE BODY "OSE_IMPORT" IS
 
+  PROCEDURE REFRESH_MV( mview_name varchar2 ) IS
+  BEGIN
+    DBMS_MVIEW.REFRESH(mview_name, 'C');
+  EXCEPTION WHEN OTHERS THEN
+    UNICAEN_IMPORT.SYNC_LOG( SQLERRM, mview_name );
+  END;
 
-                        WHEN tableau = 12 AND version = 3 THEN
-                        IF NOT vh.structure_is_affectation THEN
-                              RETURN vh.heures * (vh.taux_fi + vh.taux_fa);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE REFRESH_MVS IS
+  BEGIN
+    -- Mise à jour des vues matérialisées
+    -- procédure à adapter aux besoins de chaque établissement
 
+    REFRESH_MV('MV_UNICAEN_STRUCTURE_CODES');
+    REFRESH_MV('MV_AFFECTATION');
+    REFRESH_MV('MV_INTERVENANT');
+  END;
 
+  PROCEDURE SYNC_TABLES IS
+  BEGIN
+    -- SYNC COMMENT procédure à adapter aux besoins de chaque établissement
 
-                        WHEN tableau = 13 AND version = 2 THEN
-                        IF vh.structure_is_affectation AND vh.taux_fc = 1 THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    UNICAEN_IMPORT.SYNCHRONISATION('PAYS');
+    UNICAEN_IMPORT.SYNCHRONISATION('DEPARTEMENT');
 
+    UNICAEN_IMPORT.SYNCHRONISATION('ETABLISSEMENT');
+    UNICAEN_IMPORT.SYNCHRONISATION('STRUCTURE');
+    UNICAEN_IMPORT.SYNCHRONISATION('ADRESSE_STRUCTURE');
 
+    UNICAEN_IMPORT.SYNCHRONISATION('DOMAINE_FONCTIONNEL');
+    UNICAEN_IMPORT.SYNCHRONISATION('CENTRE_COUT');
+    UNICAEN_IMPORT.SYNCHRONISATION('CENTRE_COUT_STRUCTURE');
 
-                        WHEN tableau = 13 AND version = 3 THEN
-                        IF vh.structure_is_affectation THEN
-                              RETURN vh.heures * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    -- Import automatique des users des nouveaux directeurs
+    INSERT INTO utilisateur (
+      id, display_name, email, password, state, username
+    )
+    SELECT
+      utilisateur_id_seq.nextval id,
+      display_name,
+      email,
+      password,
+      state,
+      username
+    FROM
+      mv_affectation
+    WHERE
+      username not in (select username from utilisateur);
 
+    UNICAEN_IMPORT.SYNCHRONISATION('AFFECTATION');
 
+    UNICAEN_IMPORT.SYNCHRONISATION('CORPS');
+    UNICAEN_IMPORT.SYNCHRONISATION('GRADE');
 
-                        WHEN tableau = 14 AND version = 2 THEN
-                        IF NOT vh.structure_is_affectation AND vh.taux_fc = 1 THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    UNICAEN_IMPORT.SYNCHRONISATION('INTERVENANT');
+    UNICAEN_IMPORT.SYNCHRONISATION('AFFECTATION_RECHERCHE');
+    UNICAEN_IMPORT.SYNCHRONISATION('ADRESSE_INTERVENANT');
 
+    UNICAEN_IMPORT.SYNCHRONISATION('GROUPE_TYPE_FORMATION');
+    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_FORMATION');
+    UNICAEN_IMPORT.SYNCHRONISATION('ETAPE');
+    UNICAEN_IMPORT.SYNCHRONISATION('ELEMENT_PEDAGOGIQUE');
+    UNICAEN_IMPORT.SYNCHRONISATION('EFFECTIFS');
+    --UNICAEN_IMPORT.SYNCHRONISATION('ELEMENT_TAUX_REGIMES');
+    UNICAEN_IMPORT.SYNCHRONISATION('CHEMIN_PEDAGOGIQUE');
 
+    UNICAEN_IMPORT.SYNCHRONISATION('VOLUME_HORAIRE_ENS');
+    UNICAEN_IMPORT.SYNCHRONISATION('NOEUD');
+    UNICAEN_IMPORT.SYNCHRONISATION('LIEN');
+    UNICAEN_IMPORT.SYNCHRONISATION('SCENARIO_LIEN');
 
-                        WHEN tableau = 14 AND version = 3 THEN
-                        IF NOT vh.structure_is_affectation THEN
-                              RETURN vh.heures * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    REFRESH_MV('TBL_NOEUD');
+    UNICAEN_TBL.CALCULER('chargens');
 
+    -- Mise à jour des sources calculées en dernier
+    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_INTERVENTION_EP');
+    UNICAEN_IMPORT.SYNCHRONISATION('TYPE_MODULATEUR_EP');
 
+    -- END SYNC COMMENT
+  END;
 
-                        WHEN tableau = 15 AND version = 2 THEN
-                        IF vh.structure_is_affectation THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE SYNCHRONISATION IS
+  BEGIN
+    REFRESH_MVS;
+    SYNC_TABLES;
+  END SYNCHRONISATION;
 
+END ose_import;
+/
 
+-- OSE_PAIEMENT
+CREATE OR REPLACE PACKAGE BODY "OSE_PAIEMENT" AS
 
-                        WHEN tableau = 16 AND version = 2 THEN
-                        IF NOT vh.structure_is_affectation AND NOT vh.structure_is_univ THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE CHECK_BAD_PAIEMENTS( FORMULE_RES_SERVICE_ID NUMERIC DEFAULT NULL, FORMULE_RES_SERVICE_REF_ID NUMERIC DEFAULT NULL ) IS
+    cc NUMERIC;
+  BEGIN
+    SELECT count(*) INTO cc
+    FROM mise_en_paiement mep
+    WHERE
+      mep.histo_destruction IS NULL
+      AND mep.formule_res_service_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_ID, mep.formule_res_service_id )
+      AND mep.formule_res_service_ref_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_REF_ID, mep.formule_res_service_ref_id )
+  ;
 
+    IF (cc > 0) THEN
+      raise_application_error(-20101, 'Il est impossible d''effectuer cette action : des demandes de mise en paiement ont été saisies et ne peuvent pas être modifiées');
+    ELSE
+      DELETE FROM mise_en_paiement WHERE
+        histo_destruction IS NOT NULL
+        AND formule_res_service_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_ID, formule_res_service_id )
+        AND formule_res_service_ref_id = NVL( CHECK_BAD_PAIEMENTS.FORMULE_RES_SERVICE_REF_ID, formule_res_service_ref_id )
+      ;
+    END IF;
+  END;
 
+END OSE_PAIEMENT;
+/
 
-                        WHEN tableau = 17 AND version = 2 THEN
-                        IF vh.structure_is_univ THEN
-                              RETURN vh.heures;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+-- OSE_PARAMETRE
+CREATE OR REPLACE PACKAGE BODY "OSE_PARAMETRE" AS
 
+  cache_ose_user NUMERIC;
+  cache_annee_id NUMERIC;
 
+  FUNCTION get_etablissement return Numeric AS
+    etab_id numeric;
+  BEGIN
+    select to_number(valeur) into etab_id from parametre where nom = 'etablissement';
+    RETURN etab_id;
+  END get_etablissement;
 
-                        WHEN tableau = 21 AND version = 2 THEN
-                        RETURN gv(11) * vh.taux_service_du;
+  FUNCTION get_annee return Numeric AS
+    annee_id numeric;
+  BEGIN
+    IF cache_annee_id IS NOT NULL THEN RETURN cache_annee_id; END IF;
+    select to_number(valeur) into annee_id from parametre where nom = 'annee';
+    cache_annee_id := annee_id;
+    RETURN cache_annee_id;
+  END get_annee;
 
+  FUNCTION get_annee_import RETURN NUMERIC AS
+    annee_id NUMERIC;
+  BEGIN
+    SELECT to_number(valeur) INTO annee_id FROM parametre WHERE nom = 'annee_import';
+    RETURN annee_id;
+  END get_annee_import;
 
+  FUNCTION get_ose_user return NUMERIC AS
+    ose_user_id numeric;
+  BEGIN
+    IF cache_ose_user IS NOT NULL THEN RETURN cache_ose_user; END IF;
+    select to_number(valeur) into ose_user_id from parametre where nom = 'oseuser';
+    cache_ose_user := ose_user_id;
+    RETURN cache_ose_user;
+  END get_ose_user;
 
-                        WHEN tableau = 22 AND version = 2 THEN
-                        RETURN gv(12) * vh.taux_service_du;
+  FUNCTION get_formule RETURN formule%rowtype IS
+    fdata formule%rowtype;
+  BEGIN
+    SELECT
+      f.* INTO fdata
+    FROM
+      formule f
+      JOIN parametre p ON f.id = to_number(p.valeur)
+    WHERE p.nom = 'formule';
+    RETURN fdata;
+  END;
 
+END OSE_PARAMETRE;
 
+/
 
-                        WHEN tableau = 23 AND version = 2 THEN
-                        RETURN gv(13) * vh.taux_service_du;
+-- OSE_TEST
+CREATE OR REPLACE PACKAGE BODY "OSE_TEST" AS
+  TYPE OUT_LIST IS TABLE OF CLOB;
+  HTS TIMESTAMP;
 
+  SUCCES_SHOWN BOOLEAN DEFAULT TRUE;
+  T_SUCCES_COUNT NUMERIC DEFAULT 0;
+  T_ECHECS_COUNT NUMERIC DEFAULT 0;
+  A_SUCCES_COUNT NUMERIC DEFAULT 0;
+  A_ECHECS_COUNT NUMERIC DEFAULT 0;
+  CURRENT_TEST CLOB;
+  CURRENT_TEST_OUTPUT_BUFFER OUT_LIST := OUT_LIST();
+  CURRENT_TEST_OUTPUT_BUFFER_ERR BOOLEAN;
 
+  PROCEDURE SHOW_SUCCES IS
+  BEGIN
+    SUCCES_SHOWN := true;
+  END SHOW_SUCCES;
 
-                        WHEN tableau = 24 AND version = 2 THEN
-                        RETURN gv(14) * vh.taux_service_du;
+  PROCEDURE HIDE_SUCCES IS
+  BEGIN
+    SUCCES_SHOWN := false;
+  END HIDE_SUCCES;
 
+  PROCEDURE DEBUT( TEST_NAME CLOB ) IS
+  BEGIN
+    CURRENT_TEST := TEST_NAME;
+    CURRENT_TEST_OUTPUT_BUFFER_ERR := FALSE;
+    echo (' '); echo('TEST ' || TEST_NAME || ' >>>>>>>>>>' );
+  END;
 
+  PROCEDURE FIN IS
+    TEST_NAME CLOB;
+  BEGIN
+    IF CURRENT_TEST_OUTPUT_BUFFER_ERR THEN
+      T_ECHECS_COUNT := T_ECHECS_COUNT + 1;
+      echo('>>>>>>>>>> FIN DU TEST ' || CURRENT_TEST ); echo (' ');
+      CURRENT_TEST := NULL;
 
-                        WHEN tableau = 25 AND version = 2 THEN
-                        RETURN gv(15);
+      FOR i IN 1 .. CURRENT_TEST_OUTPUT_BUFFER.COUNT LOOP
+        echo( CURRENT_TEST_OUTPUT_BUFFER(i) );
+      END LOOP;
+    ELSE
+      T_SUCCES_COUNT := T_SUCCES_COUNT + 1;
+      TEST_NAME := CURRENT_TEST;
+      CURRENT_TEST := NULL;
+      echo('SUCCÈS DU TEST : ' || TEST_NAME );
+    END IF;
+    CURRENT_TEST_OUTPUT_BUFFER.DELETE; -- clear buffer
+  END;
 
+  PROCEDURE ECHO( MSG CLOB ) IS
+  BEGIN
+    IF CURRENT_TEST IS NULL THEN
+      dbms_output.put_line(MSG);
+    ELSE
+      CURRENT_TEST_OUTPUT_BUFFER.EXTEND;
+      CURRENT_TEST_OUTPUT_BUFFER (CURRENT_TEST_OUTPUT_BUFFER.LAST) := MSG;
+    END IF;
+  END;
 
+  PROCEDURE INIT IS
+  BEGIN
+    T_SUCCES_COUNT  := 0;
+    T_ECHECS_COUNT  := 0;
+    A_SUCCES_COUNT  := 0;
+    A_ECHECS_COUNT  := 0;
+    CURRENT_TEST    := NULL;
+  END INIT;
 
-                        WHEN tableau = 26 AND version = 2 THEN
-                        RETURN gv(16);
+  PROCEDURE SHOW_STATS IS
+  BEGIN
+    echo ( ' ' );
+    echo ( '********************************* STATISTIQUES *********************************' );
+    echo ( ' ' );
+    echo ( '   - nombre de tests passés avec succès :       ' || T_SUCCES_COUNT );
+    echo ( '   - nombre de tests ayant échoué :             ' || T_ECHECS_COUNT );
+    echo ( ' ' );
+    echo ( '   - nombre d''assertions passés avec succès :   ' || A_SUCCES_COUNT );
+    echo ( '   - nombre d''assertions ayant échoué :         ' || A_ECHECS_COUNT );
+    echo ( ' ' );
+    echo ( '********************************************************************************' );
+    echo ( ' ' );
+  END;
 
+  PROCEDURE ASSERT( condition BOOLEAN, MSG CLOB ) IS
+  BEGIN
+    IF condition THEN
+      A_SUCCES_COUNT := A_SUCCES_COUNT + 1;
+      IF SUCCES_SHOWN THEN
+        ECHO('        SUCCÈS : ' || MSG );
+      END IF;
+    ELSE
+      A_ECHECS_COUNT := A_ECHECS_COUNT + 1;
+      CURRENT_TEST_OUTPUT_BUFFER_ERR := TRUE;
+      ECHO('        ** ECHEC ** : ' || MSG );
+    END IF;
+  END;
 
+  PROCEDURE HOROINIT IS
+  BEGIN
+    HTS := systimestamp;
+  END;
 
-                        WHEN tableau = 27 AND version = 2 THEN
-                        RETURN gv(17);
+  PROCEDURE HORODATAGE( msg VARCHAR2 ) IS
+    diff INTERVAL DAY(9) TO SECOND(3);
+  BEGIN
+    IF HTS IS NULL THEN
+      HTS := systimestamp;
+      RETURN;
+    END IF;
 
+    diff := systimestamp - HTS;
+    HTS := systimestamp;
 
+    echo(msg || ' (' || diff || ')');
+  END;
 
-                        WHEN tableau = 31 AND version = 2 THEN
-                        RETURN GREATEST( ose_formule.intervenant.service_du - gt(21), 0 );
+  FUNCTION GET_STRUCTURE_BY_ID( id NUMERIC ) RETURN structure%rowtype IS
+    res structure%rowtype;
+  BEGIN
+    IF ID IS NULL THEN RETURN res; END IF;
+    SELECT * INTO res FROM structure WHERE id = GET_STRUCTURE_BY_ID.id;
+    RETURN res;
+  END;
 
+END OSE_TEST;
+/
 
+-- OSE_VALIDATION
+CREATE OR REPLACE PACKAGE BODY "OSE_VALIDATION" AS
 
-                        WHEN tableau = 32 AND version = 2 THEN
-                        RETURN GREATEST( gt(31) - gt(22), 0 );
+  FUNCTION can_devalider ( v validation%rowtype ) RETURN varchar2 IS
+    tv type_validation%rowtype;
+    nb NUMERIC;
+    result varchar2(500) default null;
+  BEGIN
 
+    SELECT * INTO tv FROM type_validation WHERE id = v.type_validation_id;
 
+    IF tv.code = 'SERVICES_PAR_COMP' THEN
 
-                        WHEN tableau = 33 AND version = 2 THEN
-                        RETURN GREATEST( gt(32) - gt(23), 0 );
+      SELECT
+        SUM(CASE WHEN c.id IS NOT NULL THEN 1 ELSE 0 END) INTO nb
+      FROM
+        validation_vol_horaire vvh
+        JOIN volume_horaire vh ON vh.id = vvh.volume_horaire_id
+        LEFT JOIN contrat c ON c.id = vh.contrat_id AND c.histo_destruction IS NULL
+      WHERE
+        vvh.validation_id = v.id;
 
+      -- Si des volumes horaires ont déjà fait l'objet de contrats alors pas de dévalidation possible des heures
+      IF nb > 0 THEN
+        result := 'La dévalidation est impossible car des contrats ont déjà été édités sur la base de ces heures.';
+      END IF;
 
+    END IF;
 
-                        WHEN tableau = 34 AND version = 2 THEN
-                        RETURN GREATEST( gt(33) - gt(24), 0 );
+    IF tv.code = 'CLOTURE_REALISE' THEN
 
+      SELECT
+        COUNT(*) INTO nb
+      FROM
+        tbl_paiement p
+      WHERE
+        p.periode_paiement_id IS NOT NULL
+        AND p.intervenant_id = v.intervenant_id
+        AND ROWNUM = 1;
 
+      IF nb > 0 THEN
+        result := 'La suppression de la clôture des services réalisés est impossible car des heures ont été payées ou bien le paiement a été demandé.';
+      END IF;
 
-                        WHEN tableau = 35 AND version = 2 THEN
-                        RETURN GREATEST( gt(34) - gt(25), 0 );
+    END IF;
 
+    RETURN result;
+  END;
 
+END OSE_VALIDATION;
+/
 
-                        WHEN tableau = 36 AND version = 2 THEN
-                        RETURN GREATEST( gt(35) - gt(26), 0 );
+-- OSE_WORKFLOW
+CREATE OR REPLACE PACKAGE BODY "OSE_WORKFLOW" AS
+  INTERVENANT_ID NUMERIC DEFAULT NULL;
 
+  TYPE t_workflow IS TABLE OF tbl_workflow%rowtype INDEX BY PLS_INTEGER;
 
+  TYPE t_dep IS TABLE OF wf_etape_dep%rowtype INDEX BY PLS_INTEGER;
+  TYPE t_deps IS TABLE OF t_dep INDEX BY PLS_INTEGER;
+  TYPE t_deps_bloquantes IS TABLE OF wf_dep_bloquante%rowtype INDEX BY PLS_INTEGER;
 
-                        WHEN tableau = 37 AND version = 2 THEN
-                        RETURN GREATEST( gt(36) - gt(27), 0 );
+  -- propre au calcul courant ! !
+  etapes          t_workflow;
+  deps            t_deps;
+  deps_initialized boolean default false;
+  deps_bloquantes t_deps_bloquantes;
+  deps_bloquantes_index PLS_INTEGER DEFAULT 1;
 
 
 
-                        WHEN tableau = 41 AND version = 2 THEN
-                        IF gt(21) <> 0 THEN
-                              RETURN gv(21) / gt(21);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
+  FUNCTION ETAPE_FRANCHIE( etape tbl_workflow%rowtype, need_done boolean default false ) RETURN FLOAT IS
+    res FLOAT DEFAULT 0;
+  BEGIN
+    IF etape.objectif = 0 THEN
+      IF need_done THEN RETURN 0; ELSE RETURN 1; END IF;
+    END IF;
 
+    IF etape.atteignable = 0 THEN RETURN 0; END IF;
 
-                        WHEN tableau = 42 AND version = 2 THEN
-                        IF gt(22) <> 0 THEN
-                              RETURN gv(22) / gt(22);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    IF etape.objectif > 0 THEN
+      res := etape.realisation / etape.objectif;
+    END IF;
 
+    IF res > 1 THEN
+      res := 1;
+    END IF;
 
+    RETURN res;
+  END;
 
-                        WHEN tableau = 43 AND version = 2 THEN
-                        IF gt(23) <> 0 THEN
-                              RETURN gv(23) / gt(23);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
 
+  PROCEDURE POPULATE_ETAPES( INTERVENANT_ID NUMERIC ) IS
+    i NUMERIC DEFAULT 0;
+  BEGIN
+    etapes.delete; -- initialisation
 
-                        WHEN tableau = 44 AND version = 2 THEN
-                        IF gt(24) <> 0 THEN
-                              RETURN gv(24) / gt(24);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    FOR wie IN (
+      SELECT
+        wep.annee_id                                          annee_id,
+        e.id                                                  etape_id,
+        w.structure_id                                        structure_id,
+        ROUND(COALESCE(w.objectif,0),2)                       objectif,
+        CASE WHEN w.intervenant_id IS NULL THEN 0 ELSE 1 END  atteignable,
+        ROUND(COALESCE(w.realisation,0),2)                    realisation,
+        wep.etape_code                                        etape_code,
+        si.id                                                 statut_intervenant_id,
+        ti.id                                                 type_intervenant_id,
+        ti.code                                               type_intervenant_code
+      FROM
+        v_workflow_etape_pertinente wep
+        JOIN wf_etape                 e ON e.code = wep.etape_code
+        JOIN intervenant              i ON i.id = wep.intervenant_id
+        JOIN statut_intervenant      si ON si.id = i.statut_id
+        JOIN type_intervenant        ti ON ti.id = si.type_intervenant_id
+        LEFT JOIN v_tbl_workflow      w ON w.intervenant_id = wep.intervenant_id AND w.etape_code = wep.etape_code
+      WHERE
+        wep.intervenant_id = POPULATE_ETAPES.INTERVENANT_ID
+        AND (e.obligatoire = 1 OR w.intervenant_id IS NOT NULL)
+      ORDER BY
+        e.ordre
+    ) LOOP
+      etapes( i ).annee_id              := wie.annee_id;
+      etapes( i ).intervenant_id        := intervenant_id;
+      etapes( i ).etape_id              := wie.etape_id;
+      etapes( i ).structure_id          := wie.structure_id;
+      etapes( i ).atteignable           := wie.atteignable;
+      etapes( i ).objectif              := wie.objectif;
+      etapes( i ).realisation           := wie.realisation;
+      etapes( i ).etape_code            := wie.etape_code;
+      etapes( i ).statut_intervenant_id := wie.statut_intervenant_id;
+      etapes( i ).type_intervenant_id   := wie.type_intervenant_id;
+      etapes( i ).type_intervenant_code := wie.type_intervenant_code;
+      i := i + 1;
+    END LOOP;
+  END;
 
 
 
-                        WHEN tableau = 45 AND version = 2 THEN
-                        IF gt(25) <> 0 THEN
-                              RETURN gv(25) / gt(25);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  -- peuple l'arbre des dépendances entre étapes de workflow
+  PROCEDURE POPULATE_DEPS( INTERVENANT_ID NUMERIC ) IS
+    s PLS_INTEGER; -- index de l'étape suivante
+    p PLS_INTEGER; -- index de l'étape précédente
+  BEGIN
+    IF deps_initialized THEN RETURN; END IF;
 
+    FOR d IN (
+      SELECT
+        wed.*
+      FROM
+        wf_etape_dep wed
+        JOIN intervenant i ON i.id = POPULATE_DEPS.INTERVENANT_ID
+        JOIN statut_intervenant si ON si.id = i.statut_id
+      WHERE
+        active = 1
+        AND wed.type_intervenant_id IS NULL OR wed.type_intervenant_id = si.type_intervenant_id
+    ) LOOP
+      deps(d.etape_suiv_id)(d.etape_prec_id) := d;
+    END LOOP;
 
+    deps_initialized := true;
+  END;
 
-                        WHEN tableau = 46 AND version = 2 THEN
-                        IF gt(26) <> 0 THEN
-                              RETURN gv(26) / gt(26);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
 
+  PROCEDURE ADD_DEP_BLOQUANTE( wf_etape_dep_id NUMERIC, tbl_workflow_id NUMERIC ) IS
+  BEGIN
+    deps_bloquantes_index := deps_bloquantes_index + 1;
+    deps_bloquantes(deps_bloquantes_index).wf_etape_dep_id := wf_etape_dep_id;
+    deps_bloquantes(deps_bloquantes_index).tbl_workflow_id := tbl_workflow_id;
+  END;
 
-                        WHEN tableau = 47 AND version = 2 THEN
-                        IF gt(27) <> 0 THEN
-                              RETURN gv(27) / gt(27);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
 
+  PROCEDURE CALCUL_ATTEIGNABLE( s PLS_INTEGER, d wf_etape_dep%rowtype ) IS
+    count_tested PLS_INTEGER DEFAULT 0;
+    count_na     PLS_INTEGER DEFAULT 0;
+    p PLS_INTEGER; -- index de l'étape précédente
+  BEGIN
 
-                        WHEN tableau = 51 AND version = 2 THEN
-                        RETURN LEAST( ose_formule.intervenant.service_du, gt(21) ) * gv(41);
+    p := etapes.FIRST;
+    LOOP EXIT WHEN p IS NULL;
+      IF etapes(p).etape_id = d.etape_prec_id THEN
+        -- on restreint en fonction du périmètre visé :
+        --  - si la dépendance n'est pas locale alors on teste
+        --  - si les structures aussi bien de l'étape testée que de l'étape dépendante sont nulles alors on teste aussi car elles sont "universelles"
+        --  - si les structures sont équivalentes alors on teste, sinon elles ne sont pas dans le périmètre local
+        IF
+          (d.locale = 0)
+          OR etapes(s).structure_id IS NULL
+          OR etapes(p).structure_id IS NULL
+          OR etapes(s).structure_id = etapes(p).structure_id
+        THEN
+          count_tested := count_tested + 1;
 
+          -- on teste le type de franchissement désiré et si ce n'est pas bon alors on déclare l'étape courante non atteignable
 
+          --  - idem si on a besoin d'une dépendance partiellement franchie est qu'elle ne l'est pas
+          IF d.partielle = 1 THEN
+            IF ETAPE_FRANCHIE(etapes(p), d.obligatoire=1) = 0 THEN -- si le franchissement est totalement inexistant
+              count_na := count_na + 1;
+            END IF;
+          --  - si on a besoin d'une dépendance complètement franchie est qu'elle ne l'est pas alors ce n'est pas atteignable
+          ELSE
+            IF ETAPE_FRANCHIE(etapes(p), d.obligatoire=1) < 1 THEN
+              count_na := count_na + 1;
+            END IF;
+          END IF;
+        END IF;
 
-                        WHEN tableau = 52 AND version = 2 THEN
-                        RETURN LEAST( gt(31), gt(22) ) * gv(42);
+      END IF;
+      p := etapes.next(p);
+    END LOOP;
 
+    -- on applique le résultat uniquement si des étapes dépendantes ont été trouvées
+    IF count_tested > 0 THEN
 
+      -- si les étapes dépendantes ont été intégralement franchies
+      IF d.integrale = 1 THEN
+        -- si l'intégralité des étapes est atteignable = NON si au moins une ne l'est pas
+        IF count_na > 0 THEN
+          etapes(s).atteignable := 0;
+          ADD_DEP_BLOQUANTE( d.id, s );
+        END IF;
 
-                        WHEN tableau = 53 AND version = 2 THEN
-                        RETURN LEAST( gt(32), gt(23) ) * gv(43);
+      -- sinon...
+      ELSE
+        -- si au moins une étape est atteignable = NON si toutes ne sont pas atteignables
+        IF count_tested = count_na THEN
+          etapes(s).atteignable := 0;
+          ADD_DEP_BLOQUANTE( d.id, s );
+        END IF;
+      END IF;
+    END IF;
+  END;
 
 
 
-                        WHEN tableau = 54 AND version = 2 THEN
-                        RETURN LEAST( gt(33), gt(24) ) * gv(44);
+  -- calcule si les étapes sont atteignables ou non
+  PROCEDURE CALCUL_ATTEIGNABLES IS
+    e PLS_INTEGER; -- index de l'étape courante
+    d PLS_INTEGER; -- ID de l'étape précédante
+  BEGIN
+    deps_bloquantes.delete;
+    e := etapes.FIRST;
+    LOOP EXIT WHEN e IS NULL;
+      IF deps.exists(etapes(e).etape_id) THEN -- s'il n'y a aucune dépendance alors pas de test!!
+        d := deps(etapes(e).etape_id).FIRST;
+        LOOP EXIT WHEN d IS NULL;
 
+          CALCUL_ATTEIGNABLE(e, deps(etapes(e).etape_id)(d));
 
+          d := deps(etapes(e).etape_id).next(d);
+        END LOOP;
+      END IF;
+      e := etapes.next(e);
+    END LOOP;
+  END;
 
-                        WHEN tableau = 55 AND version = 2 THEN
-                        RETURN LEAST( gt(34), gt(25) ) * gv(45);
 
 
+  FUNCTION ENREGISTRER_ETAPE( e tbl_workflow%rowtype ) RETURN NUMERIC IS
+    n_etape_id NUMERIC;
+  BEGIN
 
-                        WHEN tableau = 56 AND version = 2 THEN
-                        RETURN LEAST( gt(35), gt(26) ) * gv(46);
+    MERGE INTO tbl_workflow w USING dual ON (
 
+          w.intervenant_id      = e.intervenant_id
+      AND w.etape_id            = e.etape_id
+      AND NVL(w.structure_id,0) = NVL(e.structure_id,0)
 
+    ) WHEN MATCHED THEN UPDATE SET
 
-                        WHEN tableau = 57 AND version = 2 THEN
-                        RETURN LEAST( gt(36), gt(27) ) * gv(47);
+      atteignable                  = e.atteignable,
+      objectif                     = e.objectif,
+      realisation                  = e.realisation,
+      etape_code                   = e.etape_code,
+      statut_intervenant_id        = e.statut_intervenant_id,
+      type_intervenant_id          = e.type_intervenant_id,
+      type_intervenant_code        = e.type_intervenant_code,
+      to_delete                    = 0
 
+    WHEN NOT MATCHED THEN INSERT (
 
+      id,
+      annee_id,
+      intervenant_id,
+      etape_id,
+      structure_id,
+      atteignable,
+      objectif,
+      realisation,
+      etape_code,
+      statut_intervenant_id,
+      type_intervenant_id,
+      type_intervenant_code,
+      to_delete
 
-                        WHEN tableau = 61 AND version = 2 THEN
-                        RETURN gv(51) * vh.taux_fi;
+    ) VALUES (
 
+      TBL_WORKFLOW_ID_SEQ.NEXTVAL,
+      e.annee_id,
+      e.intervenant_id,
+      e.etape_id,
+      e.structure_id,
+      e.atteignable,
+      e.objectif,
+      e.realisation,
+      e.etape_code,
+      e.statut_intervenant_id,
+      e.type_intervenant_id,
+      e.type_intervenant_code,
+      0
 
+    );
 
-                        WHEN tableau = 61 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(51) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    SELECT w.id INTO n_etape_id FROM tbl_workflow w WHERE
+      w.intervenant_id          = e.intervenant_id
+      AND w.etape_id            = e.etape_id
+      AND NVL(w.structure_id,0) = NVL(e.structure_id,0)
+    ;
 
+    RETURN n_etape_id;
+  END;
 
 
-                        WHEN tableau = 62 AND version = 2 THEN
-                        RETURN gv(52) * vh.taux_fi;
 
+  PROCEDURE ENREGISTRER_DEP_BLOQUANTE( db wf_dep_bloquante%rowtype ) IS
+  BEGIN
+    MERGE INTO wf_dep_bloquante wdb USING dual ON (
 
+          wdb.wf_etape_dep_id   = db.wf_etape_dep_id
+      AND wdb.tbl_workflow_id   = db.tbl_workflow_id
 
-                        WHEN tableau = 62 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(52) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    ) WHEN MATCHED THEN UPDATE SET
 
+      to_delete                 = 0
 
+    WHEN NOT MATCHED THEN INSERT (
 
-                        WHEN tableau = 71 AND version = 2 THEN
-                        RETURN gv(51) * vh.taux_fa;
+      id,
+      wf_etape_dep_id,
+      tbl_workflow_id,
+      to_delete
 
+    ) VALUES (
 
+      WF_DEP_BLOQUANTE_ID_SEQ.NEXTVAL,
+      db.wf_etape_dep_id,
+      db.tbl_workflow_id,
+      0
 
-                        WHEN tableau = 71 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(51) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    );
+  END;
 
 
 
-                        WHEN tableau = 72 AND version = 2 THEN
-                        RETURN gv(52) * vh.taux_fa;
+  PROCEDURE ENREGISTRER( INTERVENANT_ID NUMERIC ) IS
+    i PLS_INTEGER;
+  BEGIN
 
+    UPDATE tbl_workflow SET to_delete = 1 WHERE intervenant_id = ENREGISTRER.INTERVENANT_ID;
+    UPDATE wf_dep_bloquante SET to_delete = 1 WHERE tbl_workflow_id IN (SELECT id FROM tbl_workflow WHERE intervenant_id = ENREGISTRER.INTERVENANT_ID);
 
+    i := etapes.FIRST;
+    LOOP EXIT WHEN i IS NULL;
+      etapes(i).id := ENREGISTRER_ETAPE( etapes(i) );
+      i := etapes.NEXT(i);
+    END LOOP;
 
-                        WHEN tableau = 72 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(52) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    i := deps_bloquantes.FIRST;
+    LOOP EXIT WHEN i IS NULL;
+      deps_bloquantes(i).tbl_workflow_id := etapes(deps_bloquantes(i).tbl_workflow_id).id;
+      ENREGISTRER_DEP_BLOQUANTE( deps_bloquantes(i) );
+      i := deps_bloquantes.NEXT(i);
+    END LOOP;
 
+    DELETE FROM tbl_workflow WHERE TO_DELETE = 1 AND intervenant_id = ENREGISTRER.INTERVENANT_ID;
+    DELETE FROM wf_dep_bloquante WHERE TO_DELETE = 1;
+  END;
 
 
-                        WHEN tableau = 81 AND version = 2 THEN
-                        RETURN gv(51) * vh.taux_fc;
 
+  PROCEDURE DEP_CHECK( etape_suiv_id NUMERIC, etape_prec_id NUMERIC ) IS
+    eso NUMERIC;
+    epo NUMERIC;
+  BEGIN
+    SELECT ordre INTO eso FROM wf_etape WHERE id = etape_suiv_id;
+    SELECT ordre INTO epo FROM wf_etape WHERE id = etape_prec_id;
 
+    IF eso < epo THEN
+      raise_application_error(-20101, 'Une étape de Workflow ne peut dépendre d''une étape située en aval');
+    END IF;
+    IF eso = epo THEN
+      raise_application_error(-20101, 'Une étape de Workflow ne peut dépendre d''elle-même');
+    END IF;
+  END;
 
-                        WHEN tableau = 82 AND version = 2 THEN
-                        RETURN gv(52) * vh.taux_fc;
 
 
+  PROCEDURE DEBUG_CALCUL( INTERVENANT_ID NUMERIC ) IS
+    i PLS_INTEGER;
+    d PLS_INTEGER;
+    dep_desc VARCHAR2(200);
+  BEGIN
+    ose_test.echo('');
+    ose_test.echo('-- DEBUG WORKFLOW ETAPE INTERVENANT_ID='|| INTERVENANT_ID ||' --');
+    i := etapes.FIRST;
+    LOOP EXIT WHEN i IS NULL;
+      /*ose_test.echo(
+               'etape='       || RPAD( ose_test.get_wf_etape_by_id(etapes(i).etape_id).code, 30, ' ' )
+          || ', structure='   || RPAD( NVL(ose_test.get_structure_by_id(etapes(i).structure_id).libelle_court,' '), 20, ' ' )
+          || ', ' || CASE WHEN etapes(i).atteignable=1 THEN 'atteignable' ELSE 'na' END
+          || ', objectif= ' || ROUND(etapes(i).objectif)
+          || ', realisation= ' || ROUND(etapes(i).realisation)
+      );*/
 
-                        WHEN tableau = 83 AND version = 2 THEN
-                        RETURN gv(53) * vh.taux_fc;
+      d := deps_bloquantes.FIRST;
+      LOOP EXIT WHEN d IS NULL;
+        IF deps_bloquantes(d).tbl_workflow_id = i THEN
 
+          SELECT
+            we.desc_non_franchie INTO dep_desc
+          FROM
+            wf_etape_dep wed
+            JOIN wf_etape we ON we.id = wed.etape_prec_id
+          WHERE
+            wed.id = deps_bloquantes(d).wf_etape_dep_id;
 
+          ose_test.echo('    CAUSE =' || dep_desc);
+        END IF;
+        d := deps_bloquantes.NEXT(d);
+      END LOOP;
 
-                        WHEN tableau = 83 AND version = 3 THEN
-                        RETURN gv(53);
+      i := etapes.NEXT(i);
+    END LOOP;
+    ose_test.echo('');
+  END;
 
 
 
-                        WHEN tableau = 84 AND version = 2 THEN
-                        RETURN gv(54) * vh.taux_fc;
+  -- calcul du workflow pour un intervenant
+  PROCEDURE CALCULER( INTERVENANT_ID NUMERIC ) IS
+  BEGIN
+    set_intervenant(intervenant_id);
+    POPULATE_ETAPES( INTERVENANT_ID );
+    POPULATE_DEPS( INTERVENANT_ID );
+    CALCUL_ATTEIGNABLES;
+    IF OSE_TEST.DEBUG_ENABLED THEN
+      DEBUG_CALCUL( INTERVENANT_ID );
+    END IF;
+    ENREGISTRER( INTERVENANT_ID );
+    set_intervenant();
+  END;
 
 
 
-                        WHEN tableau = 84 AND version = 3 THEN
-                        RETURN gv(54);
+  PROCEDURE CALCULER_TOUT( ANNEE_ID NUMERIC DEFAULT NULL ) IS
+  BEGIN
+    FOR mp IN (
+      SELECT
+        id intervenant_id
+      FROM
+        intervenant i
+      WHERE
+        i.histo_destruction IS NULL
+        AND (CALCULER_TOUT.ANNEE_ID IS NULL OR i.annee_id = CALCULER_TOUT.ANNEE_ID)
+    )
+    LOOP
+      CALCULER( mp.intervenant_id );
+    END LOOP;
+  END;
 
 
 
-                        WHEN tableau = 91 AND version = 2 THEN
-                        IF gv(21) <> 0 THEN
-                              RETURN gv(51) / gv(21);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE CALCULER_TBL( PARAMS UNICAEN_TBL.T_PARAMS ) IS
+    intervenant_id NUMERIC;
+    TYPE r_cursor IS REF CURSOR;
+    diff_cur r_cursor;
+  BEGIN
+    OPEN diff_cur FOR 'WITH interv AS (SELECT id intervenant_id, intervenant.* FROM intervenant)
+    SELECT intervenant_id FROM interv WHERE ' || unicaen_tbl.PARAMS_TO_CONDS( params );
+    LOOP
+      FETCH diff_cur INTO intervenant_id; EXIT WHEN diff_cur%NOTFOUND;
+      BEGIN
+        CALCULER( intervenant_id );
+      END;
+    END LOOP;
+    CLOSE diff_cur;
+  END;
 
 
 
-                        WHEN tableau = 92 AND version = 2 THEN
-                        IF gv(22) <> 0 THEN
-                              RETURN gv(52) / gv(22);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  FUNCTION GET_INTERVENANT RETURN NUMERIC IS
+  BEGIN
+    RETURN OSE_WORKFLOW.INTERVENANT_ID;
+  END;
 
+  PROCEDURE SET_INTERVENANT( INTERVENANT_ID NUMERIC DEFAULT NULL) IS
+  BEGIN
+    IF SET_INTERVENANT.INTERVENANT_ID = -1 THEN
+      OSE_WORKFLOW.INTERVENANT_ID := NULL;
+    ELSE
+      OSE_WORKFLOW.INTERVENANT_ID := SET_INTERVENANT.INTERVENANT_ID;
+    END IF;
+  END;
 
+  FUNCTION MATCH_INTERVENANT(INTERVENANT_ID NUMERIC DEFAULT NULL) RETURN NUMERIC IS
+  BEGIN
+    IF OSE_WORKFLOW.INTERVENANT_ID IS NULL OR OSE_WORKFLOW.INTERVENANT_ID = MATCH_INTERVENANT.INTERVENANT_ID THEN
+      RETURN 1;
+    ELSE
+      RETURN 0;
+    END IF;
+  END;
+END OSE_WORKFLOW;
+/
 
-                        WHEN tableau = 93 AND version = 2 THEN
-                        IF gv(23) <> 0 THEN
-                              RETURN gv(53) / gv(23);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+-- UCBN_LDAP
+CREATE OR REPLACE PACKAGE BODY "UCBN_LDAP" AS
 
+--===================================================================
+--===================================================================
+-- version()
+--===================================================================
+FUNCTION version RETURN VARCHAR2 IS
+BEGIN
+  RETURN ' 0.7.1 (2017-05-16) ';
+END version;
 
 
-                        WHEN tableau = 94 AND version = 2 THEN
-                        IF gv(24) <> 0 THEN
-                              RETURN gv(54) / gv(24);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+--===================================================================
+--===================================================================
+-- free()
+--===================================================================
+FUNCTION free RETURN NUMBER IS
+  l_retval PLS_INTEGER ;
+BEGIN
+  BEGIN
+    l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
+    RETURN l_retval ;
+  EXCEPTION
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+    RETURN NULL ;
+  END;
+END free ;
 
 
+--===================================================================
+--===================================================================
+-- ldap_connect()
+--===================================================================
+FUNCTION ldap_connect RETURN NUMBER IS
+  ldap_host   VARCHAR2(256 char) := 'ldap.unicaen.fr';
+  ldap_port   VARCHAR2(3 char)   := '389';
+  ldap_user   VARCHAR2(256 char) := 'uid=oracle-ldap,ou=system,dc=unicaen,dc=fr' ;
+  ldap_passwd VARCHAR2(30 char)  := 'HBHOe2CQgrAI' ;
+  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
 
-                        WHEN tableau = 95 AND version = 2 THEN
-                        IF gv(25) <> 0 THEN
-                              RETURN gv(55) / gv(25);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  l_retval  PLS_INTEGER ;
 
+  resultat  VARCHAR2(1024 char) := NULL ;
 
+BEGIN
+  -- Ouverture de connexion
+  BEGIN
+  ldap_sess := DBMS_LDAP.init(hostname => ldap_host,
+                              portnum  => ldap_port) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.INIT_FAILED THEN
+      RETURN 1 ;
+  END;
 
-                        WHEN tableau = 96 AND version = 2 THEN
-                        IF gv(26) <> 0 THEN
-                              RETURN gv(56) / gv(26);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
+  -- Authentification
+  BEGIN
+  l_retval := DBMS_LDAP.simple_bind_s(ld     => ldap_sess,
+                                      dn     => ldap_user,
+                                      passwd => ldap_passwd) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.GENERAL_ERROR THEN
+      l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
+      RETURN 2 ;
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
+      RETURN 2 ;
+  END;
+  RETURN 0 ;
+END ldap_connect;
 
 
-                        WHEN tableau = 97 AND version = 2 THEN
-                        IF gv(27) <> 0 THEN
-                              RETURN gv(57) / gv(27);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
 
 
-                        WHEN tableau = 101 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(91);
-                        END IF;
 
+--===================================================================
+--===================================================================
+-- get(filtre, attribut)
+--===================================================================
+FUNCTION get(filtre IN VARCHAR2, attribut IN VARCHAR2, v_multi IN VARCHAR2 DEFAULT 'N', a_multi OUT ARRAY_STR) RETURN VARCHAR2 IS
+  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
+  l_retval  PLS_INTEGER ;
+  l_attrs   DBMS_LDAP.string_collection ;
+  l_message DBMS_LDAP.message ;
+  l_entry   DBMS_LDAP.message ;
+  l_attr_name VARCHAR2(256 char) ;
+  l_ber_element  DBMS_LDAP.ber_element;
+  l_vals         DBMS_LDAP.string_collection;
 
+  i         PLS_INTEGER ;
+  nb_res    PLS_INTEGER ;
+  probleme  EXCEPTION ;
+  resultat  VARCHAR2(1024 char) := NULL ;
 
-                        WHEN tableau = 102 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(92);
-                        END IF;
+  elapsed_since_used NUMBER ;
 
+BEGIN
 
+  -- On regarde depuis combien de temps la session n'a pas ete utilisee
+  elapsed_since_used:= to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) - last_used ;
+  last_used := to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) ;
 
-                        WHEN tableau = 103 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(93);
-                        END IF;
+  -- Si c'est trop vieux, on se reconnecte
+  IF elapsed_since_used > 10 THEN
+    l_retval := free() ;
+  END IF ;
 
+  -- Si on n'est pas connecte:
+  IF ldap_sess IS NULL THEN
+    DBMS_OUTPUT.PUT_LINE('Reconnexion au serveur LDAP...');
+    l_retval := ldap_connect() ;
+    CASE l_retval
+      WHEN 1 THEN RETURN '#Err 0010';
+      WHEN 2 THEN RETURN '#Err 0011';
+      ELSE NULL;
+    END CASE;
+  END IF ;
 
+  -- On cherche le mail seulement
+  l_attrs(1) := attribut ;
+  BEGIN
+  l_retval := DBMS_LDAP.search_s(ld       => ldap_sess,
+                                 base     => ldap_base,
+                                 scope    => DBMS_LDAP.SCOPE_SUBTREE,
+                                 filter   => filtre,
+                                 attrs    => l_attrs,
+                                 attronly => 0,
+                                 res      => l_message) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.GENERAL_ERROR THEN
+      DBMS_OUTPUT.PUT_LINE('Erreur: '||SQLERRM);
+      RETURN '#Err 0020' ;
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      RETURN '#Err 0021' ;
+    WHEN DBMS_LDAP.invalid_search_scope THEN
+      RETURN '#Err 0022' ;
+  END;
 
-                        WHEN tableau = 104 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(94);
-                        END IF;
 
+  BEGIN
+  nb_res := DBMS_LDAP.count_entries(ld => ldap_sess, msg => l_message) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      RETURN '#Err 0030' ;
+    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+      RETURN '#Err 0031' ;
+    WHEN DBMS_LDAP.count_entry_error THEN
+      RETURN '#Err 0032' ;
+  END;
 
+  IF nb_res < 1 THEN
+    -- Pas besoin de fermer la connexion puisqu'on en utilise qu'une...
+    -- l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
+    -- RETURN '#Err 0033'; -- On retourne NULL depuis la 0.4.1
+    RETURN NULL ;
+  END IF;
 
-                        WHEN tableau = 105 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(95);
-                        END IF;
+  -- Les entrees retournees
+  BEGIN
+  l_entry := DBMS_LDAP.first_entry(ld => ldap_sess, msg => l_message);
 
+  EXCEPTION
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      RETURN '#Err 0034' ;
+    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+      RETURN '#Err 0035' ;
+  END;
 
 
-                        WHEN tableau = 106 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(96);
-                        END IF;
+  WHILE l_entry IS NOT NULL LOOP
+    -- Tous les attributs de l'entree:
+    BEGIN
+    l_attr_name := DBMS_LDAP.first_attribute(ld        => ldap_sess,
+                                             ldapentry => l_entry,
+                                             ber_elem  => l_ber_element);
+    EXCEPTION
+      WHEN DBMS_LDAP.INVALID_SESSION THEN
+        RETURN '#Err 0040' ;
+      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+        RETURN '#Err 0041' ;
+    END;
 
+    WHILE l_attr_name IS NOT NULL LOOP
+      -- Les valeurs de cet attribut
+      BEGIN
+      l_vals := DBMS_LDAP.get_values (ld        => ldap_sess,
+                                      ldapentry => l_entry,
+                                      attr      => l_attr_name);
+      EXCEPTION
+        WHEN DBMS_LDAP.INVALID_SESSION THEN
+          RETURN '#Err 0044' ;
+        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+          RETURN '#Err 0045' ;
+      END;
 
+      -- Si le nom de l'attribut ne correspond pas a l'attribut demande, next.
+      -- C'est pour empecher le retour de "supannAutreMail" quand on demande "mail" par exemple.
+      IF l_attr_name = attribut THEN
+        -- On ne retourne que la premiere valeur si mono-value
+        -- Sinon, on retourne le tableau a_multi
+        IF v_multi = 'N' THEN
+          resultat := l_vals(l_vals.FIRST) ;
+        ELSE
+          a_multi := ARRAY_STR() ; -- Initialisation du tableau
+          i := 0 ; -- tableau commence a 1 (d'ou i++ a l'entree du FOR)
+          FOR v IN l_vals.FIRST .. l_vals.LAST LOOP
+            i := i + 1 ;
+            a_multi.extend ;
+            a_multi(i) := l_vals(v) ;
+          END LOOP ;
+          resultat := '#Err Multi-value: '||i ;
+        END IF;
+      END IF;
 
-                        WHEN tableau = 107 AND version = 2 THEN
-                        IF gt(37) <> 0 THEN
-                              RETURN 0;
-                        ELSE
-                              RETURN 1 - gv(97);
-                        END IF;
+      EXIT WHEN resultat IS NOT NULL ;
 
+      -- Attribut suivant
+      BEGIN
+      l_attr_name := DBMS_LDAP.next_attribute(ld        => ldap_sess,
+                                              ldapentry => l_entry,
+                                              ber_elem  => l_ber_element);
+      EXCEPTION
+        WHEN DBMS_LDAP.INVALID_SESSION THEN
+          RETURN '#Err 0042' ;
+        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+          RETURN '#Err 0043' ;
+      END;
 
+    END LOOP ; -- LOOP Fin des attributs
+    IF l_ber_element IS NOT NULL THEN
+      DBMS_LDAP.ber_free(l_ber_element, 0) ;
+    END IF ;
+    EXIT WHEN resultat IS NOT NULL ;
+    BEGIN
+    l_entry := DBMS_LDAP.next_entry(ld  => ldap_sess,
+                                    msg => l_entry);
+    EXCEPTION
+      WHEN DBMS_LDAP.INVALID_SESSION THEN
+        RETURN '#Err 0036' ;
+      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+        RETURN '#Err 0037' ;
+    END;
+  END LOOP ; -- LOOP Fin des entrees
 
-                        WHEN tableau = 111 AND version = 2 THEN
-                        RETURN gv(11) * vh.taux_service_compl * gv(101);
+  -- Liberation de la memoire
+  --l_retval := DBMS_LDAP.msgfree(l_message) ;
+  IF l_entry IS NOT NULL THEN
+    l_retval := DBMS_LDAP.msgfree(l_entry) ;
+  END IF ;
 
+  -- Pas de deconnexion (on la reutilisera)
+  --l_retval := DBMS_LDAP.unbind_s(ld => l_session);
+  --DBMS_OUTPUT.PUT_LINE('L_RETVAL: ' || l_retval);
 
+  RETURN resultat ;
 
-                        WHEN tableau = 112 AND version = 2 THEN
-                        RETURN gv(12) * vh.taux_service_compl * gv(102);
+END get ;
 
 
 
-                        WHEN tableau = 113 AND version = 2 THEN
-                        RETURN gv(13) * vh.taux_service_compl * gv(103);
+--===================================================================
+--===================================================================
+-- alias2mail(alias)
+--===================================================================
+FUNCTION alias2mail(ldap_alias IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('supannAliasLogin='||ldap_alias, 'mail', 'N', a_multi) ;
+END alias2mail;
 
+--===================================================================
+--===================================================================
+-- uid2mail(ldap_uid)
+--===================================================================
+FUNCTION uid2mail(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'mail', 'N', a_multi) ;
+END uid2mail;
 
 
-                        WHEN tableau = 114 AND version = 2 THEN
-                        RETURN gv(14) * vh.taux_service_compl * gv(104);
+--===================================================================
+--===================================================================
+-- hid2mail(harpege_uid)
+--===================================================================
+FUNCTION hid2mail(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid=p'||to_char(harpege_uid,'FM00000000'), 'mail', 'N', a_multi) ;
+END hid2mail;
 
 
+--===================================================================
+--===================================================================
+-- etu2mail(code_etu)
+--===================================================================
+FUNCTION etu2mail(code_etu IN NUMBER) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid=e'||to_char(code_etu,'FM00000000'), 'mail', 'N', a_multi) ;
+END etu2mail;
 
-                        WHEN tableau = 115 AND version = 2 THEN
-                        RETURN gv(15) * gv(105);
 
+--===================================================================
+--===================================================================
+-- uid2alias(ldap_uid)
+--===================================================================
+FUNCTION uid2alias(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'supannAliasLogin', 'N', a_multi) ;
+END uid2alias;
 
+--===================================================================
+--===================================================================
+-- hid2alias(harpege_uid)
+--===================================================================
+FUNCTION hid2alias(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid=p'||to_char(harpege_uid,'FM00000000'), 'supannAliasLogin', 'N', a_multi) ;
+END hid2alias;
 
-                        WHEN tableau = 116 AND version = 2 THEN
-                        RETURN gv(16) * gv(106);
 
+--===================================================================
+--===================================================================
+-- uid2cn(ldap_uid)
+--===================================================================
+FUNCTION uid2cn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'cn', 'N', a_multi) ;
+END uid2cn;
 
 
-                        WHEN tableau = 117 AND version = 2 THEN
-                        RETURN gv(17) * gv(107);
+--===================================================================
+--===================================================================
+-- uid2sn(ldap_uid)
+--===================================================================
+FUNCTION uid2sn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'sn', 'N', a_multi) ;
+END uid2sn;
 
+--===================================================================
+--===================================================================
+-- uid2givenname(ldap_uid)
+--===================================================================
+FUNCTION uid2givenname(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'givenname', 'N', a_multi) ;
+END uid2givenname;
 
+--===================================================================
+--===================================================================
+-- uid2gn(ldap_uid)
+--===================================================================
+FUNCTION uid2gn(ldap_uid IN VARCHAR2) RETURN VARCHAR2 IS
+BEGIN
+  RETURN get('uid='||ldap_uid, 'givenname', 'N', a_multi)||' '||get('uid='||ldap_uid, 'sn', 'N', a_multi) ;
+END uid2gn;
 
-                        WHEN tableau = 123 AND version = 2 THEN
-                        IF vh.taux_fc = 1 THEN
-                              RETURN gv(113) * vh.ponderation_service_compl;
-                        ELSE
-                              RETURN gv(113);
-                        END IF;
 
 
+--===================================================================
+--===================================================================
+-- hidIsPrimaryTeacher(harpege_uid)
+--
+-- Verifie eduPersonPrimaryAffiliation
+--===================================================================
+FUNCTION hidIsPrimaryTeacher(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
+  l_resultat VARCHAR2(1024 char) := NULL ;
+  isTeacher VARCHAR2(1) := 'N' ;
+BEGIN
+  l_resultat := get('uid=p'||to_char(harpege_uid,'FM00000000'), 'eduPersonPrimaryAffiliation', 'N', a_multi) ;
 
-                        WHEN tableau = 123 AND version = 3 THEN
-                        IF vh.taux_fc > 0 THEN
-                              RETURN gv(113) * vh.ponderation_service_compl;
-                        ELSE
-                              RETURN gv(113);
-                        END IF;
+  IF l_resultat IS NULL THEN
+    RETURN NULL ;
+  END IF ;
 
+  IF SUBSTR( l_resultat, 1, 4 ) = '#Err' THEN
+    RETURN l_resultat ;
+  END IF ;
 
+  IF l_resultat = 'teacher' THEN
+    isTeacher := 'O' ;
+  END IF ;
 
-                        WHEN tableau = 124 AND version = 2 THEN
-                        IF vh.taux_fc = 1 THEN
-                              RETURN gv(114) * vh.ponderation_service_compl;
-                        ELSE
-                              RETURN gv(114);
-                        END IF;
+  RETURN isTeacher ;
 
+END hidIsPrimaryTeacher;
 
 
-                        WHEN tableau = 124 AND version = 3 THEN
-                        IF vh.taux_fc > 0 THEN
-                              RETURN gv(114) * vh.ponderation_service_compl;
-                        ELSE
-                              RETURN gv(114);
-                        END IF;
+--===================================================================
+--===================================================================
+-- hidIsTeacher(harpege_uid)
+--
+-- Retourne NULL si non trouve,
+--             O si flag teacher ou faculty
+--             N si pas ce flag.
+--===================================================================
+FUNCTION hidIsTeacher(harpege_uid IN NUMBER) RETURN VARCHAR2 IS
+  l_resultat VARCHAR2(1024 char) := NULL ;
+  isTeacher VARCHAR2(1) := 'N' ;
+BEGIN
+  l_resultat := get('uid=p'||to_char(harpege_uid,'FM00000000'), 'eduPersonAffiliation', 'Y', a_multi) ;
+  -- ici, l_resultat ne contient que '#Err Multi-value: i'
 
+  -- On verifie qu'on a bien obtenu des resultats
+  IF l_resultat IS NULL OR SUBSTR( l_resultat, 1, 18) != '#Err Multi-value: ' THEN
+    RETURN l_resultat ;
+  END IF ;
 
+  -- Le Nombre de resultats
+  IF a_multi.count = 0 THEN
+    RETURN NULL ;
+  END IF ;
 
-                        WHEN tableau = 131 AND version = 2 THEN
-                        RETURN gv(111) * vh.taux_fi;
+  FOR i IN 1 .. a_multi.count LOOP
+    IF a_multi(i)='teacher' THEN
+      isTeacher := 'O' ;
+    END IF ;
+  END LOOP ;
 
+  RETURN isTeacher ;
 
+END hidIsTeacher;
 
-                        WHEN tableau = 131 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(111) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
+--===================================================================
+--===================================================================
+-- getInvites()
+--
+-- Reprend la fonction "get" mais ecrit les resultats dans une table
+--===================================================================
+PROCEDURE getInvites(l_table IN VARCHAR2) IS
+  -- Les valeurs qu'on recherche
+  l_uid VARCHAR2(10 char) ;
+  l_ucbnstatus VARCHAR2(12 char) ;
+  l_login VARCHAR2(32 char) ;
+  l_nom_usuel VARCHAR2(128 char) ;
+  l_prenom VARCHAR2(128 char) ;
+  l_d_naissance VARCHAR2(8 char) ;
+  l_d_fin_insc VARCHAR2(8 char) ;
+  l_affectation VARCHAR2(8 char) ;
+  l_parrain_dn VARCHAR2(64 char) ;
 
+  -- Les variables pour le requetage LDAP
+  ldap_base   VARCHAR2(256 char) := 'ou=people,dc=unicaen,dc=fr';
+  ldap_filtre VARCHAR2(256 char) ;
+  l_retval  PLS_INTEGER ;
+  l_attrs   DBMS_LDAP.string_collection ;
+  l_message DBMS_LDAP.message ;
+  l_entry   DBMS_LDAP.message ;
+  l_attr_name VARCHAR2(256 char) ;
+  l_ber_element  DBMS_LDAP.ber_element;
+  l_vals         DBMS_LDAP.string_collection;
 
-                        WHEN tableau = 132 AND version = 2 THEN
-                        RETURN gv(112) * vh.taux_fi;
+  i         PLS_INTEGER ;
+  nb_res    PLS_INTEGER ;
+  probleme  EXCEPTION ;
+  resultat  VARCHAR2(1024 char) := NULL ;
+  requete   VARCHAR2(4000 char) ;
 
+  elapsed_since_used NUMBER ;
 
+BEGIN
+  -- On fabrique le filtre a partir de la date en cours:
+  ldap_filtre := '(&(|(ucbnStatus=LECTEUR_SCD)(ucbnStatus=APPRENANT)(ucbnStatus=INVITE))(dateFinInscription>='||to_char(SYSDATE,'YYYYMMDD')||'))' ;
 
-                        WHEN tableau = 132 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(112) / (vh.taux_fi + vh.taux_fa) * vh.taux_fi;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  -- On regarde depuis combien de temps la session n'a pas ete utilisee
+  elapsed_since_used:= to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) - last_used ;
+  last_used := to_number( to_char( SYSDATE,'yyyymmddhh24miss' ) ) ;
 
+  -- Si c'est trop vieux, on se reconnecte
+  IF elapsed_since_used > 10 THEN
+    l_retval := free() ;
+  END IF ;
 
+  -- Si on n'est pas connecte:
+  IF ldap_sess IS NULL THEN
+    DBMS_OUTPUT.PUT_LINE('Reconnexion au serveur LDAP...');
+    l_retval := ldap_connect() ;
+    CASE l_retval
+      WHEN 1 THEN DBMS_OUTPUT.PUT_LINE('#Err 0010') ;
+      WHEN 2 THEN DBMS_OUTPUT.PUT_LINE('#Err 0011') ;
+      ELSE NULL;
+    END CASE;
+  END IF ;
 
-                        WHEN tableau = 141 AND version = 2 THEN
-                        RETURN gv(111) * vh.taux_fa;
+  -- Les attributs LDAP qu'on recherche:
+  l_attrs(1) := 'uid' ;
+  l_attrs(2) := 'sn' ;
+  l_attrs(3) := 'givenName' ;
+  l_attrs(4) := 'dateDeNaissance' ;
+  l_attrs(5) := 'dateFinInscription' ;
+  l_attrs(6) := 'supannAliasLogin' ;
+  l_attrs(7) := 'supannEntiteAffectation' ;
+  l_attrs(8) := 'ucbnStatus' ;
+  l_attrs(9) := 'supannParrainDN' ;
+  BEGIN
+  l_retval := DBMS_LDAP.search_s(ld       => ldap_sess,
+                                 base     => ldap_base,
+                                 scope    => DBMS_LDAP.SCOPE_SUBTREE,
+                                 filter   => ldap_filtre,
+                                 attrs    => l_attrs,
+                                 attronly => 0,
+                                 res      => l_message) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.GENERAL_ERROR THEN
+      DBMS_OUTPUT.PUT_LINE('Erreur: '||SQLERRM);
+      DBMS_OUTPUT.PUT_LINE('#Err 0020') ;
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0021') ;
+    WHEN DBMS_LDAP.invalid_search_scope THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0022') ;
+  END;
 
+  BEGIN
+  nb_res := DBMS_LDAP.count_entries(ld => ldap_sess, msg => l_message) ;
+  EXCEPTION
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0030') ;
+    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0031') ;
+    WHEN DBMS_LDAP.count_entry_error THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0032') ;
+  END;
 
+  IF nb_res < 1 THEN
+    -- Pas besoin de fermer la connexion puisqu'on en utilise qu'une...
+    -- l_retval := DBMS_LDAP.unbind_s(ld => ldap_sess);
+    -- RETURN '#Err 0033'; -- On retourne NULL depuis la 0.4.1
+    DBMS_OUTPUT.PUT_LINE('#Err 0033') ;
+  END IF;
 
-                        WHEN tableau = 141 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(111) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  -- Les entrees retournees
+  BEGIN
+  l_entry := DBMS_LDAP.first_entry(ld => ldap_sess, msg => l_message);
 
+  EXCEPTION
+    WHEN DBMS_LDAP.INVALID_SESSION THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0034') ;
+    WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+      DBMS_OUTPUT.PUT_LINE('#Err 0035') ;
+  END;
 
 
-                        WHEN tableau = 142 AND version = 2 THEN
-                        RETURN gv(112) * vh.taux_fa;
+  WHILE l_entry IS NOT NULL LOOP
+    -- Tous les attributs de l'entree:
+    BEGIN
+    l_attr_name := DBMS_LDAP.first_attribute(ld        => ldap_sess,
+                                             ldapentry => l_entry,
+                                             ber_elem  => l_ber_element);
+    EXCEPTION
+      WHEN DBMS_LDAP.INVALID_SESSION THEN
+        DBMS_OUTPUT.PUT_LINE('#Err 0040') ;
+      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+        DBMS_OUTPUT.PUT_LINE('#Err 0041') ;
+    END;
 
+    WHILE l_attr_name IS NOT NULL LOOP
+      -- Les valeurs de cet attribut
+      BEGIN
+      l_vals := DBMS_LDAP.get_values (ld        => ldap_sess,
+                                      ldapentry => l_entry,
+                                      attr      => l_attr_name);
+      EXCEPTION
+        WHEN DBMS_LDAP.INVALID_SESSION THEN
+          DBMS_OUTPUT.PUT_LINE('#Err 0044') ;
+        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+          DBMS_OUTPUT.PUT_LINE('#Err 0045') ;
+      END;
 
+      -- On ne retourne que la premiere valeur
+      CASE
+        WHEN l_attr_name = 'uid' THEN l_uid := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'ucbnStatus' THEN l_ucbnstatus := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'supannAliasLogin' THEN l_login := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'sn' THEN l_nom_usuel := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'givenName' THEN l_prenom := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'dateDeNaissance' THEN l_d_naissance := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'dateFinInscription' THEN l_d_fin_insc := l_vals(l_vals.FIRST) ;
+        WHEN l_attr_name = 'supannEntiteAffectation' THEN l_affectation := SUBSTR( l_vals(l_vals.FIRST), 4 ) ;
+        WHEN l_attr_name = 'supannParrainDN' THEN l_parrain_dn := l_vals(l_vals.FIRST) ;
+      END CASE ;
 
-                        WHEN tableau = 142 AND version = 3 THEN
-                        IF vh.taux_fi + vh.taux_fa > 0 THEN
-                              RETURN gv(112) / (vh.taux_fi + vh.taux_fa) * vh.taux_fa;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+      -- Attribut suivant
+      BEGIN
+      l_attr_name := DBMS_LDAP.next_attribute(ld        => ldap_sess,
+                                              ldapentry => l_entry,
+                                              ber_elem  => l_ber_element);
+      EXCEPTION
+        WHEN DBMS_LDAP.INVALID_SESSION THEN
+          DBMS_OUTPUT.PUT_LINE('#Err 0042') ;
+        WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+          DBMS_OUTPUT.PUT_LINE('#Err 0043') ;
+      END;
+    END LOOP ; -- LOOP Fin des attributs
 
+    -- On insere les valeurs dans la table
+    dbms_output.put_line('uid= '||l_uid||' ('||l_affectation||')    fin: '||l_d_fin_insc) ;
+    requete := 'SELECT count(*) FROM '||l_table||' WHERE ldap_uid= :l_uid' ;
+    EXECUTE IMMEDIATE requete INTO nb_res USING l_uid ;
+    IF nb_res = 0 THEN
+      -- un INSERT
+      requete := 'INSERT INTO '||l_table||'(ucbnstatus, login, nom_usuel, prenom, d_naissance, d_fin_insc, affectation, parrain_dn, ldap_uid) VALUES ' ;
+      requete := requete||'(:l_ucbnstatus, :l_login, :l_nom_usuel, :l_prenom, to_date(:l_d_naissance,''YYYYMMDD''), to_date(:l_d_fin_insc,''YYYYMMDD''), :l_affectation, :l_parrain_dn, :l_uid )' ;
+      dbms_output.put_line('req= '||requete) ;
+    ELSE
+      -- un UPDATE
+      requete := 'UPDATE '||l_table||' SET ' ;
+      requete := requete||'ucbnstatus=:l_ucbnstatus, login=:l_login, nom_usuel=:l_nom_usuel, prenom=:l_prenom, d_naissance=to_date(:l_d_naissance,''YYYYMMDD''), ' ;
+      requete := requete||'d_fin_insc=to_date(:l_d_fin_insc,''YYYYMMDD''), affectation=:l_affectation, parrain_dn=:l_parrain_dn ' ;
+      requete := requete||'WHERE ldap_uid=:l_uid' ;
+      dbms_output.put_line('req= '||requete) ;
+    END IF ;
+    -- Execution de la mise a jour de la table:
+    -- Attention, les variables sont bindees selon l'ordre, pas le nom !!!!
+    -- http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/dynamic.htm#LNPLS631
+    EXECUTE IMMEDIATE requete USING l_ucbnstatus, l_login, l_nom_usuel, l_prenom, l_d_naissance, l_d_fin_insc, l_affectation, l_parrain_dn, l_uid ;
 
+    IF l_ber_element IS NOT NULL THEN
+      DBMS_LDAP.ber_free(l_ber_element, 0) ;
+    END IF ;
 
-                        WHEN tableau = 151 AND version = 2 THEN
-                        RETURN gv(111) * vh.taux_fc;
+    BEGIN
+    l_entry := DBMS_LDAP.next_entry(ld  => ldap_sess,
+                                    msg => l_entry);
+    EXCEPTION
+      WHEN DBMS_LDAP.INVALID_SESSION THEN
+        DBMS_OUTPUT.PUT_LINE('#Err 0036') ;
+      WHEN DBMS_LDAP.INVALID_MESSAGE THEN
+        DBMS_OUTPUT.PUT_LINE('#Err 0037') ;
+    END;
 
+  END LOOP ; -- LOOP Fin des entrees
 
+  -- Liberation de la memoire
+  IF l_entry IS NOT NULL THEN
+    l_retval := DBMS_LDAP.msgfree(l_entry) ;
+  END IF ;
 
-                        WHEN tableau = 152 AND version = 2 THEN
-                        RETURN gv(112) * vh.taux_fc;
+  -- Pas de deconnexion (on la reutilisera)
+  -- Par contre on COMMIT :
+  commit ;
 
+END getInvites ;
 
 
-                        WHEN tableau = 153 AND version = 2 THEN
-                        IF gv(123) = gv(113) THEN
-                              RETURN gv(113) * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+END ucbn_ldap ;
+/
 
+-- UNICAEN_IMPORT
+CREATE OR REPLACE PACKAGE BODY "UNICAEN_IMPORT" AS
 
+  v_current_user INTEGER;
+  v_current_annee INTEGER;
 
-                        WHEN tableau = 153 AND version = 3 THEN
-                        IF gv(123) = gv(113) THEN
-                              RETURN gv(113);
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
 
+  FUNCTION get_current_user RETURN INTEGER IS
+  BEGIN
+    IF v_current_user IS NULL THEN
+      v_current_user := OSE_PARAMETRE.GET_OSE_USER();
+    END IF;
+    RETURN v_current_user;
+  END get_current_user;
 
-                        WHEN tableau = 154 AND version = 2 THEN
-                        IF gv(124) = gv(114) THEN
-                              RETURN gv(114) * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE set_current_user (p_current_user INTEGER) is
+  BEGIN
+    v_current_user := p_current_user;
+  END set_current_user;
 
 
 
-                        WHEN tableau = 154 AND version = 3 THEN
-                        IF gv(124) = gv(114) THEN
-                              RETURN gv(114);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  FUNCTION get_current_annee RETURN INTEGER IS
+  BEGIN
+    IF v_current_annee IS NULL THEN
+      v_current_annee := OSE_PARAMETRE.GET_ANNEE_IMPORT();
+    END IF;
+    RETURN v_current_annee;
+  END get_current_annee;
 
+  PROCEDURE set_current_annee (p_current_annee INTEGER) IS
+  BEGIN
+    v_current_annee := p_current_annee;
+  END set_current_annee;
 
 
-                        WHEN tableau = 163 AND version = 2 THEN
-                        IF gv(123) <> gv(113) THEN
-                              RETURN gv(123) * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
 
+  PROCEDURE SYNCHRONISATION( table_name VARCHAR2, SYNC_FILRE CLOB DEFAULT '', IGNORE_UPD_COLS CLOB DEFAULT '', FORCE_SYNC BOOLEAN DEFAULT FALSE ) IS
+    ok NUMERIC(1);
+    sync NUMERIC;
+  BEGIN
+    IF FORCE_SYNC THEN
+      sync := 1;
+    ELSE
+      sync := 0;
+    END IF;
 
+    SELECT COUNT(*) INTO ok FROM import_tables it WHERE it.table_name = SYNCHRONISATION.table_name AND (it.sync_enabled = 1 OR sync=1) AND rownum = 1;
 
-                        WHEN tableau = 163 AND version = 3 THEN
-                        IF gv(123) <> gv(113) THEN
-                              RETURN gv(123);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+    IF 1 = ok THEN
+      z__SYNC_FILRE__z      := SYNCHRONISATION.SYNC_FILRE;
+      z__IGNORE_UPD_COLS__z := SYNCHRONISATION.IGNORE_UPD_COLS;
+      EXECUTE IMMEDIATE 'BEGIN UNICAEN_IMPORT_AUTOGEN_PROCS__.' || table_name || '(); END;';
+    END IF;
+  END;
 
 
 
-                        WHEN tableau = 164 AND version = 2 THEN
-                        IF gv(124) <> gv(114) THEN
-                              RETURN gv(124) * vh.taux_fc;
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE REFRESH_MV( mview_name varchar2 ) IS
+  BEGIN
+    DBMS_MVIEW.REFRESH(mview_name, 'C');
+  EXCEPTION WHEN OTHERS THEN
+    SYNC_LOG( SQLERRM, mview_name );
+  END;
 
 
 
-                        WHEN tableau = 164 AND version = 3 THEN
-                        IF gv(124) <> gv(114) THEN
-                              RETURN gv(124);
-                        ELSE
-                              RETURN 0;
-                        END IF;
+  PROCEDURE SYNC_LOG( message CLOB, table_name VARCHAR2 DEFAULT NULL, source_code VARCHAR2 DEFAULT NULL ) IS
+  BEGIN
+    INSERT INTO SYNC_LOG("ID","DATE_SYNC","MESSAGE","TABLE_NAME","SOURCE_CODE") VALUES (SYNC_LOG_ID_SEQ.NEXTVAL, SYSDATE, message,table_name,source_code);
+  END SYNC_LOG;
 
 
 
-                  ELSE
-                        raise_application_error( -20001, 'Le tableau ' || tableau || ' version ' || version || ' n''existe pas!');
-                  END CASE; END;
-
-
-
-
-
-
-
-      PROCEDURE CALCUL_RESULTAT_V2 IS
-            tableaux       t_tableaux_configs;
-            valeur         FLOAT;
-            BEGIN
-
-                  -- Définition des tableaux à utiliser
-                  tableaux := t_tableaux_configs(
-                      tc( 11,2    ), tc( 12,2    ), tc( 13,2    ), tc( 14,2    ), tc( 15,2,'r' ), tc( 16,2,'r' ), tc( 17,2,'r' ),
-                      tc( 21,2    ), tc( 22,2    ), tc( 23,2    ), tc( 24,2    ), tc( 25,2,'r' ), tc( 26,2,'r' ), tc( 27,2,'r' ),
-                      tc( 31,2,'t'), tc( 32,2,'t'), tc( 33,2,'t'), tc( 34,2,'t'), tc( 35,2,'tr'), tc( 36,2,'tr'), tc( 37,2,'tr'),
-                      tc( 41,2    ), tc( 42,2    ), tc( 43,2    ), tc( 44,2    ), tc( 45,2,'r' ), tc( 46,2,'r' ), tc( 47,2,'r' ),
-                      tc( 51,2    ), tc( 52,2    ), tc( 53,2    ), tc( 54,2    ), tc( 55,2,'r' ), tc( 56,2,'r' ), tc( 57,2,'r' ),
-                      tc( 61,2    ), tc( 62,2    ),
-                      tc( 71,2    ), tc( 72,2    ),
-                      tc( 81,2    ), tc( 82,2    ), tc( 83,2    ), tc( 84,2    ),
-                      tc( 91,2    ), tc( 92,2    ), tc( 93,2    ), tc( 94,2    ), tc( 95,2,'r' ), tc( 96,2,'r' ), tc( 97,2,'r' ),
-                      tc(101,2    ), tc(102,2    ), tc(103,2    ), tc(104,2    ), tc(105,2,'r' ), tc(106,2,'r' ), tc(107,2,'r' ),
-                      tc(111,2    ), tc(112,2    ), tc(113,2    ), tc(114,2    ), tc(115,2,'r' ), tc(116,2,'r' ), tc(117,2,'r' ),
-                      tc(123,2    ), tc(124,2    ),
-                      tc(131,2    ), tc(132,2    ),
-                      tc(141,2    ), tc(142,2    ),
-                      tc(151,2    ), tc(152,2    ), tc(153,2    ), tc(154,2    ),
-                      tc(163,2    ), tc(164,2    )
-                  );
-
-                  -- calcul par tableau pour chaque volume horaire
-                  t.delete;
-                  FOR it IN tableaux.FIRST .. tableaux.LAST LOOP
-                        FOR ivh IN 1 .. ose_formule.volumes_horaires.length LOOP
-                              vh_index := ivh;
-                              IF
-                              ose_formule.volumes_horaires.items(ivh).service_id IS NOT NULL AND NOT tableaux(it).referentiel
-                              OR ose_formule.volumes_horaires.items(ivh).service_referentiel_id IS NOT NULL AND tableaux(it).referentiel
-                              OR tableaux(it).setTotal -- car on en a besoin tout le temps
-                              THEN
-                                    valeur := EXECFORMULE(tableaux(it).tableau, tableaux(it).version);
-                                    IF tableaux(it).setTotal THEN
-                                          ST( tableaux(it).tableau, valeur );
-                                    ELSE
-                                          SV( tableaux(it).tableau, valeur );
-                                    END IF;
-                              END IF;
-                        END LOOP;
-                  END LOOP;
-
-                  -- transmisssion des résultats aux volumes horaires et volumes horaires référentiel
-                  FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
-                        vh_index := i;
-                        IF ose_formule.volumes_horaires.items(i).service_id IS NOT NULL THEN
-                              ose_formule.volumes_horaires.items(i).service_fi               := gv( 61) + gv( 62);
-                              ose_formule.volumes_horaires.items(i).service_fa               := gv( 71) + gv( 72);
-                              ose_formule.volumes_horaires.items(i).service_fc               := gv( 81) + gv( 82) + gv( 83) + gv( 84);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fi          := gv(131) + gv(132);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fa          := gv(141) + gv(142);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fc          := gv(151) + gv(152) + gv(153) + gv(154);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fc_majorees :=                     gv(163) + gv(164);
-                        ELSIF ose_formule.volumes_horaires.items(i).service_referentiel_id IS NOT NULL THEN
-                              ose_formule.volumes_horaires.items(i).service_referentiel      := gv( 55) + gv( 56) + gv( 57);
-                              ose_formule.volumes_horaires.items(i).heures_compl_referentiel := gv(115) + gv(116) + gv(117);
-                        END IF;
-                  END LOOP;
-
-                  DEBUG_VH;
-            END;
-
-
-
-      PROCEDURE CALCUL_RESULTAT_V3 IS
-            tableaux       t_tableaux_configs;
-            valeur         FLOAT;
-            BEGIN
-                  -- si l'année est antérieure à 2016/2017 alors on utilise la V2!!
-                  IF ose_formule.intervenant.annee_id < 2016 THEN
-                        CALCUL_RESULTAT_V2;
-                        RETURN;
-                  END IF;
-
-
-                  -- Définition des tableaux à utiliser
-                  tableaux := t_tableaux_configs(
-                      tc( 11,3    ), tc( 12,3    ), tc( 13,3    ), tc( 14,3    ), tc( 15,2,'r' ), tc( 16,2,'r' ), tc( 17,2,'r' ),
-                      tc( 21,2    ), tc( 22,2    ), tc( 23,2    ), tc( 24,2    ), tc( 25,2,'r' ), tc( 26,2,'r' ), tc( 27,2,'r' ),
-                      tc( 31,2,'t'), tc( 32,2,'t'), tc( 33,2,'t'), tc( 34,2,'t'), tc( 35,2,'tr'), tc( 36,2,'tr'), tc( 37,2,'tr'),
-                      tc( 41,2    ), tc( 42,2    ), tc( 43,2    ), tc( 44,2    ), tc( 45,2,'r' ), tc( 46,2,'r' ), tc( 47,2,'r' ),
-                      tc( 51,2    ), tc( 52,2    ), tc( 53,2    ), tc( 54,2    ), tc( 55,2,'r' ), tc( 56,2,'r' ), tc( 57,2,'r' ),
-                      tc( 61,3    ), tc( 62,3    ),
-                      tc( 71,3    ), tc( 72,3    ),
-                      tc( 83,3    ), tc( 84,3    ),
-                      tc( 91,2    ), tc( 92,2    ), tc( 93,2    ), tc( 94,2    ), tc( 95,2,'r' ), tc( 96,2,'r' ), tc( 97,2,'r' ),
-                      tc(101,2    ), tc(102,2    ), tc(103,2    ), tc(104,2    ), tc(105,2,'r' ), tc(106,2,'r' ), tc(107,2,'r' ),
-                      tc(111,2    ), tc(112,2    ), tc(113,2    ), tc(114,2    ), tc(115,2,'r' ), tc(116,2,'r' ), tc(117,2,'r' ),
-                      tc(123,3    ), tc(124,3    ),
-                      tc(131,3    ), tc(132,3    ),
-                      tc(141,3    ), tc(142,3    ),
-                      tc(153,3    ), tc(154,3    ),
-                      tc(163,3    ), tc(164,3    )
-                  );
-
-                  -- calcul par tableau pour chaque volume horaire
-                  t.delete;
-                  FOR it IN tableaux.FIRST .. tableaux.LAST LOOP
-                        FOR ivh IN 1 .. ose_formule.volumes_horaires.length LOOP
-                              vh_index := ivh;
-                              IF
-                              ose_formule.volumes_horaires.items(ivh).service_id IS NOT NULL AND NOT tableaux(it).referentiel
-                              OR ose_formule.volumes_horaires.items(ivh).service_referentiel_id IS NOT NULL AND tableaux(it).referentiel
-                              OR tableaux(it).setTotal -- car on en a besoin tout le temps
-                              THEN
-                                    valeur := EXECFORMULE(tableaux(it).tableau, tableaux(it).version);
-                                    IF tableaux(it).setTotal THEN
-                                          ST( tableaux(it).tableau, valeur );
-                                    ELSE
-                                          SV( tableaux(it).tableau, valeur );
-                                    END IF;
-                              END IF;
-                        END LOOP;
-                  END LOOP;
-
-                  -- transmission des résultats aux volumes horaires et volumes horaires référentiel
-                  FOR i IN 1 .. ose_formule.volumes_horaires.length LOOP
-                        vh_index := i;
-                        IF ose_formule.volumes_horaires.items(i).service_id IS NOT NULL THEN
-                              ose_formule.volumes_horaires.items(i).service_fi               := gv( 61) + gv( 62);
-                              ose_formule.volumes_horaires.items(i).service_fa               := gv( 71) + gv( 72);
-                              ose_formule.volumes_horaires.items(i).service_fc               := gv( 83) + gv( 84);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fi          := gv(131) + gv(132);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fa          := gv(141) + gv(142);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fc          := gv(153) + gv(154);
-                              ose_formule.volumes_horaires.items(i).heures_compl_fc_majorees := gv(163) + gv(164);
-                        ELSIF ose_formule.volumes_horaires.items(i).service_referentiel_id IS NOT NULL THEN
-                              ose_formule.volumes_horaires.items(i).service_referentiel      := gv( 55) + gv( 56) + gv( 57);
-                              ose_formule.volumes_horaires.items(i).heures_compl_referentiel := gv(115) + gv(116) + gv(117);
-                        END IF;
-                  END LOOP;
-
-                  DEBUG_VH;
-            END;
-
-
-
-      PROCEDURE PURGE_EM_NON_FC IS
-            BEGIN
-                  FOR em IN (
-                  SELECT
-                         em.id
-                  FROM
-                       ELEMENT_MODULATEUR em
-                             JOIN element_pedagogique ep ON ep.id = em.element_id AND ep.histo_destruction IS NULL
-                  WHERE
-                      em.histo_destruction IS NULL
-                    AND ep.taux_fc < 1
-                  ) LOOP
-                        UPDATE
-                            element_modulateur
-                        SET
-                            histo_destruction = SYSDATE,
-                            histo_destructeur_id = ose_parametre.get_ose_user
-                        WHERE
-                            id = em.id
-                  ;
-                  END LOOP;
-            END;
-
-
-END UNICAEN_OSE_FORMULE;
+  FUNCTION IN_COLUMN_LIST( VALEUR VARCHAR2, CHAMPS CLOB ) RETURN NUMERIC IS
+  BEGIN
+    IF REGEXP_LIKE(CHAMPS, '(^|,)[ \t\r\n\v\f]*' || VALEUR || '[ \t\r\n\v\f]*(,|$)') THEN RETURN 1; END IF;
+    RETURN 0;
+  END;
 
+END UNICAEN_IMPORT;
 /
 
 -- UNICAEN_TBL
@@ -15288,6 +15957,38 @@ ALTER TABLE "FORMULE_RESULTAT_VH_REF" ADD CONSTRAINT "FRVHR_VOLUME_HORAIRE_REF_F
 ALTER TABLE "FORMULE_RESULTAT_VH" ADD CONSTRAINT "FRVH_VOLUME_HORAIRE_FK" FOREIGN KEY ("VOLUME_HORAIRE_ID")
 	  REFERENCES "VOLUME_HORAIRE" ("ID") ON DELETE CASCADE ENABLE;
 
+-- FTI_ANNEE_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_ANNEE_FK" FOREIGN KEY ("ANNEE_ID")
+	  REFERENCES "ANNEE" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTI_ETAT_VOLUME_HORAIRE_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_ETAT_VOLUME_HORAIRE_FK" FOREIGN KEY ("ETAT_VOLUME_HORAIRE_ID")
+	  REFERENCES "ETAT_VOLUME_HORAIRE" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTI_FORMULE_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_FORMULE_FK" FOREIGN KEY ("FORMULE_ID")
+	  REFERENCES "FORMULE" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTI_FORMULE_TEST_STRUCTURE_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_FORMULE_TEST_STRUCTURE_FK" FOREIGN KEY ("STRUCTURE_TEST_ID")
+	  REFERENCES "FORMULE_TEST_STRUCTURE" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTI_TYPE_INTERVENANT_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_TYPE_INTERVENANT_FK" FOREIGN KEY ("TYPE_INTERVENANT_ID")
+	  REFERENCES "TYPE_INTERVENANT" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTI_TYPE_VOLUME_HORAIRE_FK
+ALTER TABLE "FORMULE_TEST_INTERVENANT" ADD CONSTRAINT "FTI_TYPE_VOLUME_HORAIRE_FK" FOREIGN KEY ("TYPE_VOLUME_HORAIRE_ID")
+	  REFERENCES "TYPE_VOLUME_HORAIRE" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTVH_FORMULE_TEST_INTERV_FK
+ALTER TABLE "FORMULE_TEST_VOLUME_HORAIRE" ADD CONSTRAINT "FTVH_FORMULE_TEST_INTERV_FK" FOREIGN KEY ("INTERVENANT_TEST_ID")
+	  REFERENCES "FORMULE_TEST_INTERVENANT" ("ID") ON DELETE CASCADE ENABLE;
+
+-- FTVH_FORMULE_TEST_STRUCTURE_FK
+ALTER TABLE "FORMULE_TEST_VOLUME_HORAIRE" ADD CONSTRAINT "FTVH_FORMULE_TEST_STRUCTURE_FK" FOREIGN KEY ("STRUCTURE_TEST_ID")
+	  REFERENCES "FORMULE_TEST_STRUCTURE" ("ID") ON DELETE CASCADE ENABLE;
+
 -- GRADE_CORPS_FK
 ALTER TABLE "GRADE" ADD CONSTRAINT "GRADE_CORPS_FK" FOREIGN KEY ("CORPS_ID")
 	  REFERENCES "CORPS" ("ID") ENABLE;
@@ -17242,9 +17943,14 @@ ALTER TRIGGER "F_MOTIF_MODIFICATION_SERVICE_S" ENABLE;
 
 -- F_STATUT_INTERVENANT
 CREATE OR REPLACE TRIGGER "F_STATUT_INTERVENANT"
-  AFTER UPDATE OF SERVICE_STATUTAIRE, DEPASSEMENT, TYPE_INTERVENANT_ID, NON_AUTORISE ON "STATUT_INTERVENANT"
-  REFERENCING FOR EACH ROW
-  BEGIN
+AFTER UPDATE OF
+  service_statutaire,
+  depassement,
+  type_intervenant_id,
+  non_autorise
+ON STATUT_INTERVENANT
+FOR EACH ROW
+BEGIN return; /* Désactivation du trigger... */
 
   IF NOT UNICAEN_TBL.ACTIV_TRIGGERS THEN RETURN; END IF;
 
@@ -17324,260 +18030,260 @@ ALTER TRIGGER "F_TYPE_INTERVENTION_S" ENABLE;
 
 -- INDIC_TRG_MODIF_DOSSIER
 CREATE OR REPLACE TRIGGER "INDIC_TRG_MODIF_DOSSIER"
-      AFTER INSERT OR UPDATE OF NOM_USUEL, NOM_PATRONYMIQUE, PRENOM, CIVILITE_ID, ADRESSE, RIB, DATE_NAISSANCE ON "DOSSIER"
-
-      FOR EACH ROW
-      /**
-       * But : mettre à jour la liste des PJ attendues.
-       */
-      DECLARE
-            i integer := 1;
-            intervenantId NUMERIC;
-            found integer;
-            estCreationDossier integer;
-            type array_t is table of varchar2(1024);
-
-            attrNames     array_t := array_t();
-            attrOldVals   array_t := array_t();
-            attrNewVals   array_t := array_t();
-
-            -- valeurs importées (format texte) :
-            impSourceName source.libelle%type;
-            impNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
-            impRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
-            -- anciennes valeurs dans le dossier (format texte) :
-            oldSourceName source.libelle%type;
-            oldNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
-            oldRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
-            -- nouvelles valeurs dans le dossier (format texte) :
-            newSourceName source.libelle%type;
-            newNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
-            newRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
-      BEGIN
-            --
-            -- Témoin indiquant s'il s'agit d'une création de dossier (insert).
-            --
-            estCreationDossier := case when inserting then 1 else 0 end;
-
-            --
-            -- Fetch source OSE.
-            --
-            select s.libelle into newSourceName from source s where s.code = 'OSE';
-
-            --
-            -- Fetch et formattage texte des valeurs importées.
-            --
-            select
-                   i.id,
-                   s.libelle,
-                   nvl(i.NOM_USUEL, '(Aucun)'),
-                   nvl(i.NOM_PATRONYMIQUE, '(Aucun)'),
-                   nvl(i.PRENOM, '(Aucun)'),
-                   nvl(c.libelle_court, '(Aucune)'),
-                   nvl(to_char(i.DATE_NAISSANCE, 'DD/MM/YYYY'), '(Aucune)'),
-                   nvl(ose_divers.formatted_rib(i.bic, i.iban), '(Aucun)'),
-                   case when a.id is not null
-                             then ose_divers.formatted_adresse(a.NO_VOIE, a.NOM_VOIE, a.BATIMENT, a.MENTION_COMPLEMENTAIRE, a.LOCALITE, a.CODE_POSTAL, a.VILLE, a.PAYS_LIBELLE)
-                        else '(Aucune)'
-                       end
-                into
-                      intervenantId,
-                      oldSourceName,
-                      impNomUsuel,
-                      impNomPatro,
-                      impPrenom,
-                      impCivilite,
-                      impDateNaiss,
-                      impRib,
-                      impAdresse
-            from intervenant i
-                       join source s on s.id = i.source_id
-                       left join civilite c on c.id = i.civilite_id
-                       left join adresse_intervenant a on a.intervenant_id = i.id AND a.histo_destruction IS NULL
-            where i.id = :NEW.intervenant_id;
-
-            --
-            -- Anciennes valeurs dans le cas d'une création de dossier : ce sont les valeurs importées.
-            --
-            if (1 = estCreationDossier) then
-                  --dbms_output.put_line('inserting');
-                  oldNomUsuel  := impNomUsuel;
-                  oldNomPatro  := impNomPatro;
-                  oldPrenom    := impPrenom;
-                  oldCivilite  := impCivilite;
-                  oldDateNaiss := impDateNaiss;
-                  oldAdresse   := impAdresse;
-                  oldRib       := impRib;
-            --
-            -- Anciennes valeurs dans le cas d'une mise à jour du dossier.
-            --
-            else
-                  --dbms_output.put_line('updating');
-                  oldNomUsuel     := trim(:OLD.NOM_USUEL);
-                  oldNomPatro     := trim(:OLD.NOM_PATRONYMIQUE);
-                  oldPrenom       := trim(:OLD.PRENOM);
-                  oldDateNaiss    := case when :OLD.DATE_NAISSANCE is null then '(Aucune)' else to_char(:OLD.DATE_NAISSANCE, 'DD/MM/YYYY') end;
-                  oldAdresse      := trim(:OLD.ADRESSE);
-                  oldRib          := trim(:OLD.RIB);
-                  if :OLD.CIVILITE_ID is not null then
-                        select c.libelle_court into oldCivilite from civilite c where c.id = :OLD.CIVILITE_ID;
-                  else
-                        oldCivilite := '(Aucune)';
-                  end if;
-                  select s.libelle into oldSourceName from source s where s.code = 'OSE';
-            end if;
-
-            --
-            -- Nouvelles valeurs saisies.
-            --
-            newNomUsuel   := trim(:NEW.NOM_USUEL);
-            newNomPatro   := trim(:NEW.NOM_PATRONYMIQUE);
-            newPrenom     := trim(:NEW.PRENOM);
-            newDateNaiss  := case when :NEW.DATE_NAISSANCE is null then '(Aucune)' else to_char(:NEW.DATE_NAISSANCE, 'DD/MM/YYYY') end;
-            newAdresse    := trim(:NEW.ADRESSE);
-            newRib        := trim(:NEW.RIB);
-            if :NEW.CIVILITE_ID is not null then
-                  select c.libelle_court into newCivilite from civilite c where c.id = :NEW.CIVILITE_ID;
-            else
-                  newCivilite := '(Aucune)';
-            end if;
-
-            --
-            -- Détection des différences.
-            --
-            if newNomUsuel <> oldNomUsuel then
-                  --dbms_output.put_line('NOM_USUEL ' || sourceLib || ' = ' || oldNomUsuel || ' --> NOM_USUEL OSE = ' || :NEW.NOM_USUEL);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Nom usuel';
-                  attrOldVals(i) := oldNomUsuel;
-                  attrNewVals(i) := newNomUsuel;
-                  i := i + 1;
-            end if;
-            if newNomPatro <> oldNomPatro then
-                  --dbms_output.put_line('NOM_PATRONYMIQUE ' || sourceLib || ' = ' || oldNomPatro || ' --> NOM_PATRONYMIQUE OSE = ' || :NEW.NOM_PATRONYMIQUE);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Nom de naissance';
-                  attrOldVals(i) := oldNomPatro;
-                  attrNewVals(i) := newNomPatro;
-                  i := i + 1;
-            end if;
-            if newPrenom <> oldPrenom then
-                  --dbms_output.put_line('PRENOM ' || sourceLib || ' = ' || oldPrenom || ' --> PRENOM OSE = ' || :NEW.PRENOM);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Prénom';
-                  attrOldVals(i) := oldPrenom;
-                  attrNewVals(i) := newPrenom;
-                  i := i + 1;
-            end if;
-            if newCivilite <> oldCivilite then
-                  --dbms_output.put_line('CIVILITE_ID ' || sourceLib || ' = ' || oldCivilite || ' --> CIVILITE_ID OSE = ' || :NEW.CIVILITE_ID);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Civilité';
-                  attrOldVals(i) := oldCivilite;
-                  attrNewVals(i) := newCivilite;
-                  i := i + 1;
-            end if;
-            if newDateNaiss <> oldDateNaiss then
-                  --dbms_output.put_line('DATE_NAISSANCE ' || sourceLib || ' = ' || oldDateNaiss || ' --> DATE_NAISSANCE OSE = ' || :NEW.DATE_NAISSANCE);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Date de naissance';
-                  attrOldVals(i) := oldDateNaiss;
-                  attrNewVals(i) := newDateNaiss;
-                  i := i + 1;
-            end if;
-            if newAdresse <> oldAdresse then
-                  --dbms_output.put_line('ADRESSE ' || sourceLib || ' = ' || oldAdresse || ' --> ADRESSE OSE = ' || :NEW.ADRESSE);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'Adresse postale';
-                  attrOldVals(i) := oldAdresse;
-                  attrNewVals(i) := newAdresse;
-                  i := i + 1;
-            end if;
-            if oldRib is null or newRib <> oldRib then
-                  --dbms_output.put_line('RIB ' || sourceLib || ' = ' || oldRib || ' --> RIB OSE = ' || :NEW.RIB);
-                  attrNames.extend(1);
-                  attrOldVals.extend(1);
-                  attrNewVals.extend(1);
-                  attrNames(i)   := 'RIB';
-                  attrOldVals(i) := oldRib;
-                  attrNewVals(i) := newRib;
-                  i := i + 1;
-            end if;
-
-            --
-            -- Enregistrement des différences.
-            --
-            for i in 1 .. attrNames.count loop
-                  --dbms_output.put_line(attrNames(i) || ' ' || oldSourceName || ' = ' || attrOldVals(i) || ' --> ' || attrNames(i) || ' ' || newSourceName || ' = ' || attrNewVals(i));
-
-                  -- vérification que la même modif n'est pas déjà consignée
-                  select count(*) into found from indic_modif_dossier
-                  where INTERVENANT_ID = intervenantId
-                    and ATTR_NAME = attrNames(i)
-                    and ATTR_OLD_VALUE = to_char(attrOldVals(i))
-                    and ATTR_NEW_VALUE = to_char(attrNewVals(i));
-                  if found > 0 then
-                        continue;
-                  end if;
-
-                  insert into INDIC_MODIF_DOSSIER(
-                      id,
-                      INTERVENANT_ID,
-                      ATTR_NAME,
-                      ATTR_OLD_SOURCE_NAME,
-                      ATTR_OLD_VALUE,
-                      ATTR_NEW_SOURCE_NAME,
-                      ATTR_NEW_VALUE,
-                      EST_CREATION_DOSSIER, -- témoin indiquant s'il s'agit d'une création ou d'une modification de dossier
-                      HISTO_CREATION,       -- NB: date de modification du dossier
-                      HISTO_CREATEUR_ID,    -- NB: auteur de la modification du dossier
-                      HISTO_MODIFICATION,
-                      HISTO_MODIFICATEUR_ID
-                      )
-                  values (
-                             indic_modif_dossier_id_seq.nextval,
-                             intervenantId,
-                             attrNames(i),
-                             oldSourceName,
-                             to_char(attrOldVals(i)),
-                             newSourceName,
-                             to_char(attrNewVals(i)),
-                             estCreationDossier,
-                             :NEW.HISTO_MODIFICATION,
-                             :NEW.HISTO_MODIFICATEUR_ID,
-                             :NEW.HISTO_MODIFICATION,
-                             :NEW.HISTO_MODIFICATEUR_ID
-                             );
-            end loop;
+  AFTER INSERT OR UPDATE OF NOM_USUEL, NOM_PATRONYMIQUE, PRENOM, CIVILITE_ID, ADRESSE, RIB, DATE_NAISSANCE ON "DOSSIER"
+
+  FOR EACH ROW
+/**
+ * But : mettre à jour la liste des PJ attendues.
+ */
+DECLARE
+  i integer := 1;
+  intervenantId NUMERIC;
+  found integer;
+  estCreationDossier integer;
+  type array_t is table of varchar2(1024);
+
+  attrNames     array_t := array_t();
+  attrOldVals   array_t := array_t();
+  attrNewVals   array_t := array_t();
+
+  -- valeurs importées (format texte) :
+  impSourceName source.libelle%type;
+  impNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
+  impRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
+  -- anciennes valeurs dans le dossier (format texte) :
+  oldSourceName source.libelle%type;
+  oldNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
+  oldRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
+  -- nouvelles valeurs dans le dossier (format texte) :
+  newSourceName source.libelle%type;
+  newNomUsuel   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newNomPatro   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newPrenom     indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newCivilite   indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newDateNaiss  indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newAdresse    indic_modif_dossier.ATTR_NEW_VALUE%type;
+  newRib        indic_modif_dossier.ATTR_NEW_VALUE%type;
+BEGIN
+  --
+  -- Témoin indiquant s'il s'agit d'une création de dossier (insert).
+  --
+  estCreationDossier := case when inserting then 1 else 0 end;
+
+  --
+  -- Fetch source OSE.
+  --
+  select s.libelle into newSourceName from source s where s.code = 'OSE';
+
+  --
+  -- Fetch et formattage texte des valeurs importées.
+  --
+  select
+      i.id,
+      s.libelle,
+      nvl(i.NOM_USUEL, '(Aucun)'),
+      nvl(i.NOM_PATRONYMIQUE, '(Aucun)'),
+      nvl(i.PRENOM, '(Aucun)'),
+      nvl(c.libelle_court, '(Aucune)'),
+      nvl(to_char(i.DATE_NAISSANCE, 'DD/MM/YYYY'), '(Aucune)'),
+      nvl(ose_divers.formatted_rib(i.bic, i.iban), '(Aucun)'),
+      case when a.id is not null
+        then ose_divers.formatted_adresse(a.NO_VOIE, a.NOM_VOIE, a.BATIMENT, a.MENTION_COMPLEMENTAIRE, a.LOCALITE, a.CODE_POSTAL, a.VILLE, a.PAYS_LIBELLE)
+        else '(Aucune)'
+      end
+    into
+      intervenantId,
+      oldSourceName,
+      impNomUsuel,
+      impNomPatro,
+      impPrenom,
+      impCivilite,
+      impDateNaiss,
+      impRib,
+      impAdresse
+    from intervenant i
+    join source s on s.id = i.source_id
+    left join civilite c on c.id = i.civilite_id
+    left join adresse_intervenant a on a.intervenant_id = i.id AND a.histo_destruction IS NULL
+    where i.id = :NEW.intervenant_id;
+
+  --
+  -- Anciennes valeurs dans le cas d'une création de dossier : ce sont les valeurs importées.
+  --
+  if (1 = estCreationDossier) then
+    --dbms_output.put_line('inserting');
+    oldNomUsuel  := impNomUsuel;
+    oldNomPatro  := impNomPatro;
+    oldPrenom    := impPrenom;
+    oldCivilite  := impCivilite;
+    oldDateNaiss := impDateNaiss;
+    oldAdresse   := impAdresse;
+    oldRib       := impRib;
+  --
+  -- Anciennes valeurs dans le cas d'une mise à jour du dossier.
+  --
+  else
+    --dbms_output.put_line('updating');
+    oldNomUsuel     := trim(:OLD.NOM_USUEL);
+    oldNomPatro     := trim(:OLD.NOM_PATRONYMIQUE);
+    oldPrenom       := trim(:OLD.PRENOM);
+    oldDateNaiss    := case when :OLD.DATE_NAISSANCE is null then '(Aucune)' else to_char(:OLD.DATE_NAISSANCE, 'DD/MM/YYYY') end;
+    oldAdresse      := trim(:OLD.ADRESSE);
+    oldRib          := trim(:OLD.RIB);
+    if :OLD.CIVILITE_ID is not null then
+      select c.libelle_court into oldCivilite from civilite c where c.id = :OLD.CIVILITE_ID;
+    else
+      oldCivilite := '(Aucune)';
+    end if;
+    select s.libelle into oldSourceName from source s where s.code = 'OSE';
+  end if;
 
-      END;
+  --
+  -- Nouvelles valeurs saisies.
+  --
+  newNomUsuel   := trim(:NEW.NOM_USUEL);
+  newNomPatro   := trim(:NEW.NOM_PATRONYMIQUE);
+  newPrenom     := trim(:NEW.PRENOM);
+  newDateNaiss  := case when :NEW.DATE_NAISSANCE is null then '(Aucune)' else to_char(:NEW.DATE_NAISSANCE, 'DD/MM/YYYY') end;
+  newAdresse    := trim(:NEW.ADRESSE);
+  newRib        := trim(:NEW.RIB);
+  if :NEW.CIVILITE_ID is not null then
+    select c.libelle_court into newCivilite from civilite c where c.id = :NEW.CIVILITE_ID;
+  else
+    newCivilite := '(Aucune)';
+  end if;
+
+  --
+  -- Détection des différences.
+  --
+  if newNomUsuel <> oldNomUsuel then
+    --dbms_output.put_line('NOM_USUEL ' || sourceLib || ' = ' || oldNomUsuel || ' --> NOM_USUEL OSE = ' || :NEW.NOM_USUEL);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Nom usuel';
+    attrOldVals(i) := oldNomUsuel;
+    attrNewVals(i) := newNomUsuel;
+    i := i + 1;
+  end if;
+  if newNomPatro <> oldNomPatro then
+    --dbms_output.put_line('NOM_PATRONYMIQUE ' || sourceLib || ' = ' || oldNomPatro || ' --> NOM_PATRONYMIQUE OSE = ' || :NEW.NOM_PATRONYMIQUE);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Nom de naissance';
+    attrOldVals(i) := oldNomPatro;
+    attrNewVals(i) := newNomPatro;
+    i := i + 1;
+  end if;
+  if newPrenom <> oldPrenom then
+    --dbms_output.put_line('PRENOM ' || sourceLib || ' = ' || oldPrenom || ' --> PRENOM OSE = ' || :NEW.PRENOM);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Prénom';
+    attrOldVals(i) := oldPrenom;
+    attrNewVals(i) := newPrenom;
+    i := i + 1;
+  end if;
+  if newCivilite <> oldCivilite then
+    --dbms_output.put_line('CIVILITE_ID ' || sourceLib || ' = ' || oldCivilite || ' --> CIVILITE_ID OSE = ' || :NEW.CIVILITE_ID);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Civilité';
+    attrOldVals(i) := oldCivilite;
+    attrNewVals(i) := newCivilite;
+    i := i + 1;
+  end if;
+  if newDateNaiss <> oldDateNaiss then
+    --dbms_output.put_line('DATE_NAISSANCE ' || sourceLib || ' = ' || oldDateNaiss || ' --> DATE_NAISSANCE OSE = ' || :NEW.DATE_NAISSANCE);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Date de naissance';
+    attrOldVals(i) := oldDateNaiss;
+    attrNewVals(i) := newDateNaiss;
+    i := i + 1;
+  end if;
+  if newAdresse <> oldAdresse then
+    --dbms_output.put_line('ADRESSE ' || sourceLib || ' = ' || oldAdresse || ' --> ADRESSE OSE = ' || :NEW.ADRESSE);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'Adresse postale';
+    attrOldVals(i) := oldAdresse;
+    attrNewVals(i) := newAdresse;
+    i := i + 1;
+  end if;
+  if oldRib is null or newRib <> oldRib then
+    --dbms_output.put_line('RIB ' || sourceLib || ' = ' || oldRib || ' --> RIB OSE = ' || :NEW.RIB);
+    attrNames.extend(1);
+    attrOldVals.extend(1);
+    attrNewVals.extend(1);
+    attrNames(i)   := 'RIB';
+    attrOldVals(i) := oldRib;
+    attrNewVals(i) := newRib;
+    i := i + 1;
+  end if;
+
+  --
+  -- Enregistrement des différences.
+  --
+  for i in 1 .. attrNames.count loop
+    --dbms_output.put_line(attrNames(i) || ' ' || oldSourceName || ' = ' || attrOldVals(i) || ' --> ' || attrNames(i) || ' ' || newSourceName || ' = ' || attrNewVals(i));
+
+    -- vérification que la même modif n'est pas déjà consignée
+    select count(*) into found from indic_modif_dossier
+      where INTERVENANT_ID = intervenantId
+      and ATTR_NAME = attrNames(i)
+      and ATTR_OLD_VALUE = to_char(attrOldVals(i))
+      and ATTR_NEW_VALUE = to_char(attrNewVals(i));
+    if found > 0 then
+      continue;
+    end if;
+
+    insert into INDIC_MODIF_DOSSIER(
+      id,
+      INTERVENANT_ID,
+      ATTR_NAME,
+      ATTR_OLD_SOURCE_NAME,
+      ATTR_OLD_VALUE,
+      ATTR_NEW_SOURCE_NAME,
+      ATTR_NEW_VALUE,
+      EST_CREATION_DOSSIER, -- témoin indiquant s'il s'agit d'une création ou d'une modification de dossier
+      HISTO_CREATION,       -- NB: date de modification du dossier
+      HISTO_CREATEUR_ID,    -- NB: auteur de la modification du dossier
+      HISTO_MODIFICATION,
+      HISTO_MODIFICATEUR_ID
+    )
+    values (
+      indic_modif_dossier_id_seq.nextval,
+      intervenantId,
+      attrNames(i),
+      oldSourceName,
+      to_char(attrOldVals(i)),
+      newSourceName,
+      to_char(attrNewVals(i)),
+      estCreationDossier,
+      :NEW.HISTO_MODIFICATION,
+      :NEW.HISTO_MODIFICATEUR_ID,
+      :NEW.HISTO_MODIFICATION,
+      :NEW.HISTO_MODIFICATEUR_ID
+    );
+  end loop;
+
+END;
 
 
 /
@@ -18489,6 +19195,10 @@ INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIV
 INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[mise-en-paiement]', q'[Mises en paiement]', 120);
 INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[intervenant]', q'[Intervenant]', 30);
 INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[odf]', q'[Gestion de l'offre de formation]', 10);
+INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[domaines-fonctionnels]', q'[Domaines fonctionnels]', NULL);
+INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[motifs-modification-service-du]', q'[Motifs de modification de service dû]', NULL);
+INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[structures]', q'[Structures]', NULL);
+INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[formule]', q'[Formule de calcul]', NULL);
 INSERT INTO CATEGORIE_PRIVILEGE(ID, CODE, LIBELLE, ORDRE) VALUES (CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL, q'[etat-sortie]', q'[États de sortie]', NULL);
 
 
@@ -18861,6 +19571,17 @@ INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_
 INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[gestion-visualisation]', q'[Gestion (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[modif-service-du]'), 6);
 INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[administration-visualisation]', q'[Administration (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[etat-sortie]'), 1);
 INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[administration-edition]', q'[Administration (édition)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[etat-sortie]'), 2);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[grands-types-diplome-visualisation]', q'[Grands types de diplômes (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[odf]'), 12);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[grands-types-diplome-edition]', q'[Grands types de diplômes (édition)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[odf]'), 13);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[types-diplome-visualisation]', q'[Types de diplômes (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[odf]'), 14);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[types-diplome-edition]', q'[Types de diplômes (édition)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[odf]'), 15);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[visualisation]', q'[Administration (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[motifs-modification-service-du]'), 5);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[edition]', q'[Administration (édition)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[motifs-modification-service-du]'), 6);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[administration-visualisation]', q'[Administration (visualisation)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[structures]'), 7);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[administration-edition]', q'[Administration (édition)]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[structures]'), 8);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[types-ressources-visualisation]', q'[Types de ressources - Visualisation]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[budget]'), 15);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[types-ressources-edition]', q'[Types de ressources - Édition]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[budget]'), 16);
+INSERT INTO PRIVILEGE(ID, CODE, LIBELLE, CATEGORIE_ID, ORDRE) VALUES (PRIVILEGE_ID_SEQ.NEXTVAL, q'[tests]', q'[Tests]', (SELECT ID FROM CATEGORIE_PRIVILEGE WHERE ROWNUM = 1 AND CODE = q'[formule]'), 11);
 
 
 -- Table TBL
@@ -19112,8 +19833,6 @@ INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[TECHNICIEN DE RECHERCHE & FORMATION(NES)]', q'[TECH RF]', (SELECT id FROM source WHERE code = 'OSE'), q'[830]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[BIB. ASSISTANT SPE (NES)]', q'[BIBAS]', (SELECT id FROM source WHERE code = 'OSE'), q'[8HH]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[INFIRMIERS ENES CATEGORIE A]', q'[INF ENES]', (SELECT id FROM source WHERE code = 'OSE'), q'[986]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[ASSIST SERV SOCIAL ADMIN DE L'ETAT 2012]', q'[ASSAE (2012)]', (SELECT id FROM source WHERE code = 'OSE'), q'[987]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[CONS TECH DE SERV SOC AE (2012)]', q'[CTSSAE (2012)]', (SELECT id FROM source WHERE code = 'OSE'), q'[988]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[ADJOINT ADMINISTRATIF ADM.CENTR.(N.S)]', q'[ADJ AC (NS]', (SELECT id FROM source WHERE code = 'OSE'), q'[212]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[AGENT ADMINISTRATIF ADM.CENTR.]', q'[AG.A.C]', (SELECT id FROM source WHERE code = 'OSE'), q'[213]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[SECRETAIRE ADMINISTRATIF (SAC) NV.STATUT]', q'[SAC NV.ST]', (SELECT id FROM source WHERE code = 'OSE'), q'[217]', SYSDATE, 1, SYSDATE, 1);
@@ -19227,6 +19946,9 @@ INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[SOUS-DIRECTEUR DE LABO. DU CNAM]', q'[SOUS-DIR.LABO.CNAM]', (SELECT id FROM source WHERE code = 'OSE'), q'[361]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[PROFESSEUR DE L'ENSAM]', q'[PROF. DE L'ENSAM]', (SELECT id FROM source WHERE code = 'OSE'), q'[364]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[PROF. TECH. ADJOINT ET CHEF DE TP ENSAM]', q'[PROF.T.A.CH.TP ENSAM]', (SELECT id FROM source WHERE code = 'OSE'), q'[365]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[INSPECTEUR TECH D'ACTION SOCIALE AE]', q'[INSP TECH A SOC AE]', (SELECT id FROM source WHERE code = 'OSE'), q'[H11]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[ASSISTANT SERVICE SOCIAL ADMIN DE L'ETAT]', q'[ASSAE]', (SELECT id FROM source WHERE code = 'OSE'), q'[H09]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO CORPS(ID, LIBELLE_LONG, LIBELLE_COURT, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (CORPS_ID_SEQ.NEXTVAL, q'[CONS TECH DE SERV SOC AE]', q'[CTSSAE]', (SELECT id FROM source WHERE code = 'OSE'), q'[H10]', SYSDATE, 1, SYSDATE, 1);
 
 
 -- Table DEPARTEMENT
@@ -29277,9 +29999,6 @@ INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID,
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[INFIRMIERS ENES HC CATEG A]', q'[INFENES HC]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[986]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9864]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[INFIRMIERS ENES CS INT PJJ]', q'[INF PJJ CS]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[986]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9863]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[ADMINISTRATEUR CIVIL GENERAL]', q'[ADM. CIV. GENERAL]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[203]'), (SELECT id FROM source WHERE code = 'OSE'), q'[2035]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[ASSISTANT DE SERVICE SOCIAL AE]', q'[ASSAE]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[987]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9871]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[ASSISTANT PRINCIPAL DE SERVICE SOCIAL AE]', q'[ASSPAE]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[987]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9872]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[CONS TECH DE SERV SOC AE]', q'[CTSSAE]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[988]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9881]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[MEDECIN E.N.-CONSEILLER TECHNIQUE G1]', q'[MCT GR1]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[998]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9981]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[MEDECIN E.N.-CONSEILLER TECHNIQUE G2]', q'[MCT GR2]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[998]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9982]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO GRADE(ID, LIBELLE_LONG, LIBELLE_COURT, ECHELLE, CORPS_ID, SOURCE_ID, SOURCE_CODE, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (GRADE_ID_SEQ.NEXTVAL, q'[MEDECIN E.N.-CONSEILLER TECHNIQUE G3]', q'[MCT GR3]', NULL, (SELECT ID FROM CORPS WHERE ROWNUM = 1 AND SOURCE_CODE = q'[998]'), (SELECT id FROM source WHERE code = 'OSE'), q'[9983]', SYSDATE, 1, SYSDATE, 1);
@@ -29709,8 +30428,6 @@ INSERT INTO MOTIF_NON_PAIEMENT(ID, CODE, LIBELLE_COURT, LIBELLE_LONG, HISTO_CREA
 
 
 -- Table PARAMETRE
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[formule_package_name]', q'[UNICAEN_OSE_FORMULE]', q'[Nom du package contenant la formule de calcul]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[formule_function_name]', q'[CALCUL_RESULTAT_V3]', q'[Nom de la procédure permettant d'exécuter la formule de calcul]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[contrat_etablissement]', q'[L'université de Caen]', q'[Contrat : établissement]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[contrat_etablissement_represente]', q'[représentée par son Président, Pierre DENISE]', q'[Contrat : représentant]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[contrat_civilite_president]', q'[le Président]', q'[Contrat : civilité du président (avec article)]', SYSDATE, 1, SYSDATE, 1);
@@ -29739,10 +30456,11 @@ INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATE
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[discipline_codes_corresp_2_libelle]', q'[Section(s) CNU Harpège]', q'[Libellé de la liste 2 des correspondances de codes des disciplines]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[discipline_codes_corresp_3_libelle]', q'[Spécialité Harpège]', q'[Libellé de la liste 3 des correspondances de codes des disciplines]', SYSDATE, 1, SYSDATE, 1);
 INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[discipline_codes_corresp_4_libelle]', q'[Discipline du 2nd degré]', q'[Libellé de la liste 4 des correspondances de codes des disciplines]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[structure_univ]', NULL, q'[Composante représentant l'université (utile éventuellement pour la forpule de calcul)]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_winpaie]', NULL, q'[État de sortie pour l'extraction Winpaie]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_services_pdf]', NULL, q'[État de sortie pour l'édition PDF des services]', SYSDATE, 1, SYSDATE, 1);
-INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_etat_paiement]', NULL, q'[État de sortie pour les états de paiement]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[structure_univ]', null, q'[Composante représentant l'université (utile éventuellement pour la forpule de calcul)]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_winpaie]', null, q'[État de sortie pour l'extraction Winpaie]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_services_pdf]', null, q'[État de sortie pour l'édition PDF des services]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[es_etat_paiement]', null, q'[État de sortie pour les états de paiement]', SYSDATE, 1, SYSDATE, 1);
+INSERT INTO PARAMETRE(ID, NOM, VALEUR, DESCRIPTION, HISTO_CREATION, HISTO_CREATEUR_ID, HISTO_MODIFICATION, HISTO_MODIFICATEUR_ID) VALUES (PARAMETRE_ID_SEQ.NEXTVAL, q'[formule]', q'[1]', q'[Formule de calcul]', SYSDATE, 1, SYSDATE, 1);
 
 
 -- Table PAYS
@@ -30339,6 +31057,17 @@ INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE W
 INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'centres-couts-administration-edition'));
 INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'etat-sortie-administration-visualisation'));
 INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'etat-sortie-administration-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'odf-grands-types-diplome-visualisation'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'odf-grands-types-diplome-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'odf-types-diplome-visualisation'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'odf-types-diplome-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'motifs-modification-service-du-visualisation'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'motifs-modification-service-du-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'structures-administration-visualisation'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'structures-administration-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'budget-types-ressources-visualisation'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'budget-types-ressources-edition'));
+INSERT INTO ROLE_PRIVILEGE(ROLE_ID, PRIVILEGE_ID) VALUES ((SELECT ID FROM ROLE WHERE ROWNUM = 1 AND CODE = q'[administrateur]'), (SELECT p.id FROM privilege p JOIN categorie_privilege cp ON p.categorie_id = cp.id WHERE cp.code || '-' || p.code = 'formule-tests'));
 
 
 -- Table TAUX_HORAIRE_HETD
diff --git "a/data/Mises \303\240 jour/08.1.sql" "b/data/Mises \303\240 jour/08.1.sql"
index fd7ea86504d42f1a7dad32f665407c19d6113bc2..4bc70a6a1b654b582aab081e3d48fed8ce2b4b44 100644
--- "a/data/Mises \303\240 jour/08.1.sql"	
+++ "b/data/Mises \303\240 jour/08.1.sql"	
@@ -497,7 +497,7 @@ DROP PACKAGE UNICAEN_OSE_FORMULE
 
 -- DdlSequence.create.
 
-CREATE SEQUENCE FORMULE__ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE
+CREATE SEQUENCE FORMULE_ID_SEQ INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE
 
 /
 
@@ -4692,6 +4692,9 @@ FROM (
    UNION ALL SELECT 'budget' c, 'types-ressources-visualisation' p, 'Types de ressources - Visualisation' l FROM dual
    UNION ALL SELECT 'budget' c, 'types-ressources-edition' p, 'Types de ressources - Édition' l FROM dual
 
+   UNION ALL SELECT 'domaines-fonctionnels' c, 'administration-visualisation' p, 'Administration (visualisation)' l FROM dual
+   UNION ALL SELECT 'domaines-fonctionnels' c, 'administration-edition' p,	'Administration (édition)' l FROM dual
+
    UNION ALL SELECT 'formule' c, 'tests' p, 'Tests' l FROM dual
 
 ) t1;