diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0868ce029ddba1452c62f0013ee170bd8b244a16..a4d3b4812a2a32b41d5ebd6bb7a5f5278215f04b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,14 @@
 CHANGELOG
 =========
 
+6.1.0 (22/08/2023)
+------------------
+
+- Refonte du dispositif, plus aucun package utilisé
+
 6.0.2 (19/06/2023)
 ------------------
-- Pb de caractère "NUL" enlevé grâce à un TRIM
+- Pb de caractère "NULL" enlevé grâce à un TRIM
 
 6.0.1 (23/03/2023)
 ------------------
diff --git a/autoload_classmap.php b/autoload_classmap.php
index e830f47538453c66a819339751ce425d00694452..21df9cc03c51114bcafbfff1be21d8994141a7ed 100755
--- a/autoload_classmap.php
+++ b/autoload_classmap.php
@@ -1,29 +1,5 @@
 <?php
 // Generated by UnicaenCode
 return [
-    'UnicaenTbl\Controller\AdminController'                     => __DIR__ . '/src/Controller/AdminController.php',
-    'UnicaenTbl\Controller\Factory\AdminControllerFactory'      => __DIR__ . '/src/Controller/Factory/AdminControllerFactory.php',
-    'UnicaenTbl\Entity\Column'                                  => __DIR__ . '/src/Entity/Column.php',
-    'UnicaenTbl\Entity\TableauBord'                             => __DIR__ . '/src/Entity/TableauBord.php',
-    'UnicaenTbl\Entity\Traits\TableauBordAwareTrait'            => __DIR__ . '/src/Entity/Traits/TableauBordAwareTrait.php',
-    'UnicaenTbl\Form\ActualisationForm'                         => __DIR__ . '/src/Form/ActualisationForm.php',
-    'UnicaenTbl\Form\Factory\ActualisationFormFactory'          => __DIR__ . '/src/Form/Factory/ActualisationFormFactory.php',
-    'UnicaenTbl\Form\Traits\ActualisationFormAwareTrait'        => __DIR__ . '/src/Form/Traits/ActualisationFormAwareTrait.php',
-    'UnicaenTbl\Module'                                         => __DIR__ . '/Module.php',
-    'UnicaenTbl\Options\Factory\ModuleOptionsFactory'           => __DIR__ . '/src/Options/Factory/ModuleOptionsFactory.php',
-    'UnicaenTbl\Options\ModuleOptions'                          => __DIR__ . '/src/Options/ModuleOptions.php',
-    'UnicaenTbl\Options\Traits\ModuleOptionsAwareTrait'         => __DIR__ . '/src/Options/Traits/ModuleOptionsAwareTrait.php',
-    'UnicaenTbl\Provider\Privilege\Privileges'                  => __DIR__ . '/src/Provider/Privilege/Privileges.php',
-    'UnicaenTbl\Service\AbstractService'                        => __DIR__ . '/src/Service/AbstractService.php',
-    'UnicaenTbl\Service\CollectorService'                       => __DIR__ . '/src/Service/CollectorService.php',
-    'UnicaenTbl\Service\Factory\CollectorServiceFactory'        => __DIR__ . '/src/Service/Factory/CollectorServiceFactory.php',
-    'UnicaenTbl\Service\Factory\QueryGeneratorServiceFactory'   => __DIR__ . '/src/Service/Factory/QueryGeneratorServiceFactory.php',
-    'UnicaenTbl\Service\Factory\SchemaServiceFactory'           => __DIR__ . '/src/Service/Factory/SchemaServiceFactory.php',
-    'UnicaenTbl\Service\Factory\TableauBordServiceFactory'      => __DIR__ . '/src/Service/Factory/TableauBordServiceFactory.php',
-    'UnicaenTbl\Service\QueryGeneratorService'                  => __DIR__ . '/src/Service/QueryGeneratorService.php',
-    'UnicaenTbl\Service\SchemaService'                          => __DIR__ . '/src/Service/SchemaService.php',
-    'UnicaenTbl\Service\TableauBordService'                     => __DIR__ . '/src/Service/TableauBordService.php',
-    'UnicaenTbl\Service\Traits\QueryGeneratorServiceAwareTrait' => __DIR__ . '/src/Service/Traits/QueryGeneratorServiceAwareTrait.php',
-    'UnicaenTbl\Service\Traits\SchemaServiceAwareTrait'         => __DIR__ . '/src/Service/Traits/SchemaServiceAwareTrait.php',
-    'UnicaenTbl\Service\Traits\TableauBordServiceAwareTrait'    => __DIR__ . '/src/Service/Traits/TableauBordServiceAwareTrait.php',
+
 ];
\ No newline at end of file
diff --git a/config/module.config.php b/config/module.config.php
index 9b1ef6f4cdaafb1f99976d4982a9f9100be482b1..1c6210e27e4c6639d89f61849a956ed09fc04f13 100644
--- a/config/module.config.php
+++ b/config/module.config.php
@@ -2,131 +2,36 @@
 
 namespace UnicaenTbl;
 
-use UnicaenTbl\Controller\AdminController;
-use UnicaenTbl\Form\ActualisationForm;
 use UnicaenTbl\Options\ModuleOptions;
-use UnicaenTbl\Provider\Privilege\Privileges;
-use UnicaenTbl\Service\CollectorService;
-use UnicaenTbl\Service\QueryGeneratorService;
-use UnicaenTbl\Service\SchemaService;
+use UnicaenTbl\Process\DbDiffProcess;
+use UnicaenTbl\Process\DbDiffProcessFactory;
+use UnicaenTbl\Process\PlsqlProcess;
+use UnicaenTbl\Process\PlsqlProcessFactory;
+use UnicaenTbl\Service\BddService;
+use UnicaenTbl\Service\BddServiceFactory;
 use UnicaenTbl\Service\TableauBordService;
 
-return [
-
-    'router' => [
-        'routes' => [
-            'unicaen-tbl' => [
-                'type'          => 'Literal',
-                'options'       => [
-                    'route'    => '/unicaen-tbl',
-                    'defaults' => [
-                        'controller' => AdminController::class,
-                        'action'     => 'index',
-                    ],
-                ],
-                'may_terminate' => true,
-                'child_routes'  => [
-                    'build' => [
-                        'type'          => 'Literal',
-                        'options'       => [
-                            'route'    => '/build',
-                            'defaults' => [
-                                'action' => 'build-procedures',
-                            ],
-                        ],
-                        'may_terminate' => true,
-                    ],
-                    'actualisation'   => [
-                        'type'          => 'Segment',
-                        'options'       => [
-                            'route'    => '/actualisation/:tableauBord',
-                            'defaults' => [
-                                'action' => 'actualisation',
-                            ],
-                        ],
-                        'may_terminate' => true,
-                    ],
-                ],
-            ],
-        ],
-    ],
 
-    'console' => [
-        'router' => [
-            'routes' => [
-                'unicaen-tbl' => [
-                    'options' => [
-                        'type'     => 'catchall',
-                        'route'    => 'UnicaenTbl build-procedures',
-                        'defaults' => [
-                            'controller' => AdminController::class,
-                            'action'     => 'build-procedures',
-                        ],
-                    ],
-                ],
-            ],
-        ],
-    ],
+return [
 
-    'doctrine' => [
-        'driver' => [
-            'unicaen_tbl_driver' => [
-                'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
-                'cache' => 'array',
-                'paths' => [
-                    __DIR__ . '/../src/Entity',
-                ],
-            ],
-            'orm_default'        => [
-                'drivers' => [
-                    'UnicaenTbl\Entity\Db' => 'unicaen_tbl_driver',
-                ],
-            ],
-        ],
-    ],
+    'unicaen-tbl' => [
+        'tableaux_bord' => [],
 
-    'view_manager' => [
-        'template_path_stack' => [
-            'unicaen-tbl' => __DIR__ . '/../view',
-        ],
-    ],
-
-    'controllers' => [
-        'factories' => [
-            AdminController::class => Controller\Factory\AdminControllerFactory::class,
-        ],
+        'entity_manager_name' => 'doctrine.entitymanager.orm_default',
     ],
 
     'service_manager' => [
-        'factories' => [
-            CollectorService::class      => Service\Factory\CollectorServiceFactory::class,
-            ModuleOptions::class         => Options\Factory\ModuleOptionsFactory::class,
-            SchemaService::class         => Service\Factory\SchemaServiceFactory::class,
-            QueryGeneratorService::class => Service\Factory\QueryGeneratorServiceFactory::class,
-            TableauBordService::class    => Service\Factory\TableauBordServiceFactory::class,
+        'aliases'   => [
+            'unicaen-tbl.process.DbDiff' => DbDiffProcess::class,
+            'unicaen-tbl.process.Plsql'  => PlsqlProcess::class,
         ],
-    ],
-
-    'form_elements' => [
         'factories' => [
-            ActualisationForm::class => Form\Factory\ActualisationFormFactory::class,
-        ],
-    ],
+            ModuleOptions::class         => Options\Factory\ModuleOptionsFactory::class,
+            TableauBordService::class    => Service\TableauBordServiceFactory::class,
+            BddService::class            => BddServiceFactory::class,
 
-    'bjyauthorize' => [
-        'guards' => [
-            'UnicaenPrivilege\Guard\PrivilegeController' => [
-                [
-                    'controller' => AdminController::class,
-                    'action'     => ['index'],
-                    'privileges' => [Privileges::UNICAEN_TBL_ADMIN],
-                ],
-                [
-                    'controller' => AdminController::class,
-                    'action'     => ['actualisation'],
-                    'privileges' => [Privileges::UNICAEN_TBL_ACTUALISATION],
-                ],
-            ],
+            DbDiffProcess::class => DbDiffProcessFactory::class,
+            PlsqlProcess::class  => PlsqlProcessFactory::class,
         ],
     ],
 ];
\ No newline at end of file
diff --git a/data/module.sql b/data/module.sql
deleted file mode 100644
index 3912c68da75e4af01a7fa4d019f1fa5fcd1369b1..0000000000000000000000000000000000000000
--- a/data/module.sql
+++ /dev/null
@@ -1,22 +0,0 @@
-/*********************************************************************************************
- *                                          Privilèges
- *********************************************************************************************/
-
-/
-
-insert into CATEGORIE_PRIVILEGE (ID, CODE, LIBELLE, ORDRE) values (
-  CATEGORIE_PRIVILEGE_ID_SEQ.NEXTVAL,
-  'unicaen-tbl',
-  'Tableaux de bord',
-  999
-);
-
-/
-
-insert into PRIVILEGE (ID, CATEGORIE_ID, CODE, LIBELLE, ORDRE) select
-   PRIVILEGE_ID_SEQ.NEXTVAL,
-   cat.id,
-   'admin',
-   'Gestion des tableaux de bord',
-   1
-from CATEGORIE_PRIVILEGE cat where code = 'unicaen-tbl';
diff --git a/data/package.sql b/data/package.sql
deleted file mode 100644
index 1bad8368b7300f690e9c73288d5f6ef3fe64dfb7..0000000000000000000000000000000000000000
--- a/data/package.sql
+++ /dev/null
@@ -1,335 +0,0 @@
-CREATE OR REPLACE PACKAGE "UNICAEN_TBL" AS
-
-  ACTIV_TRIGGERS BOOLEAN DEFAULT TRUE;
-  ACTIV_CALCULS  BOOLEAN DEFAULT TRUE;
-
-  TYPE t_params IS RECORD (
-    p1 VARCHAR2(30), v1 VARCHAR2(80),
-    p2 VARCHAR2(30), v2 VARCHAR2(80),
-    p3 VARCHAR2(30), v3 VARCHAR2(80),
-    p4 VARCHAR2(30), v4 VARCHAR2(80),
-    p5 VARCHAR2(30), v5 VARCHAR2(80)
-  );
-
-  CALCUL_PROC_PARAMS t_params;
-
-  FUNCTION QUERY_APPLY_PARAM( sqlQuery VARCHAR2, param VARCHAR2, value VARCHAR2) RETURN CLOB;
-  FUNCTION MAKE_WHERE(param VARCHAR2 DEFAULT NULL, value VARCHAR2 DEFAULT NULL, alias VARCHAR2 DEFAULT NULL) RETURN VARCHAR2;
-
-  PROCEDURE CALCULER( TBL_NAME VARCHAR2 );
-  PROCEDURE CALCULER( TBL_NAME VARCHAR2, param VARCHAR2, value VARCHAR2 );
-  PROCEDURE CALCULER(TBL_NAME VARCHAR2, params t_params);
-  PROCEDURE DEMANDE_CALCUL( TBL_NAME VARCHAR2, param VARCHAR2, value VARCHAR2 );
-
-  PROCEDURE ANNULER_DEMANDES;
-  PROCEDURE ANNULER_DEMANDES( TBL_NAME VARCHAR2 );
-  PROCEDURE CALCULER_DEMANDES;
-
-  -- AUTOMATIC GENERATION --
-
-  -- END OF AUTOMATIC GENERATION --
-
-END UNICAEN_TBL;
-
-/
-
-CREATE OR REPLACE PACKAGE BODY "UNICAEN_TBL" AS
-  TYPE t_dems_values IS TABLE OF BOOLEAN INDEX BY VARCHAR2(80);
-  TYPE t_dems_params IS TABLE OF t_dems_values INDEX BY VARCHAR2(30);
-  TYPE t_dems IS TABLE OF t_dems_params INDEX BY VARCHAR2(30);
-
-  dems t_dems;
-
-
-
-  FUNCTION MAKE_WHERE(param VARCHAR2 DEFAULT NULL, value VARCHAR2 DEFAULT NULL,
-                      alias VARCHAR2 DEFAULT NULL) RETURN VARCHAR2 IS
-    res VARCHAR2(120) DEFAULT '';
-  BEGIN
-    IF param IS NULL THEN
-      RETURN '1=1';
-    END IF;
-    IF alias IS NOT NULL THEN
-      res := alias || '.';
-    END IF;
-    IF value IS NULL THEN
-      RETURN res || param || ' IS NULL';
-    END IF;
-   RETURN res || param || ' = q''[' || value || ']''';
-  END;
-
-
-
-  FUNCTION QUERY_APPLY_PARAM(sqlQuery VARCHAR2, param VARCHAR2, value VARCHAR2) RETURN CLOB IS
-    pos       NUMERIC;
-    paramLen  NUMERIC;
-    paramComm VARCHAR2(200);
-    debComm   NUMERIC;
-    endComm   NUMERIC;
-    debReal   NUMERIC;
-    realParam VARCHAR2(80);
-    realValue VARCHAR2(120);
-    q         CLOB;
-  BEGIN
-    q := sqlQuery;
-    IF param IS NULL THEN
-      RETURN q;
-    END IF;
-
-    paramlen := length(param);
-    IF value IS NULL THEN
-      realValue := ' IS NULL';
-    ELSE
-      BEGIN
-        realValue := TO_NUMBER(value);
-      EXCEPTION
-        WHEN VALUE_ERROR THEN
-          realValue := 'q''[' || value || ']''';
-      END;
-     realValue := '=' || realValue;
-    END IF;
-
-    LOOP
-      pos := instr(q, '/*@' || param, 1, 1);
-      EXIT WHEN pos = 0;
-     debComm := pos - 1;
-      endComm := instr(q, '*/', pos, 1);
-      paramComm := substr(q, debComm, endComm - debComm);
-     debReal := instr(paramComm, '=', 1, 1);
-     realParam := trim(substr(paramComm, debReal + 1));
-     --realParam := 'AND ' || substr(q,pos + paramLen + 4,endComm-pos - paramLen - 4);
-      realParam := 'AND ' || realParam || realValue;
-     q := substr(q, 1, debComm) || realParam || substr(q, endComm + 2);
-    END LOOP;
-
-    RETURN q;
-  END;
-
-
-  FUNCTION QUERY_APPLY_PARAMS(sqlQuery VARCHAR2, useParams BOOLEAN DEFAULT FALSE) RETURN CLOB IS
-    q CLOB;
-  BEGIN
-    q := sqlQuery;
-
-    IF NOT useParams THEN
-      RETURN q;
-    END IF;
-
-    IF UNICAEN_TBL.CALCUL_PROC_PARAMS.p1 IS NOT NULL THEN
-      q := QUERY_APPLY_PARAM(q, UNICAEN_TBL.CALCUL_PROC_PARAMS.p1, UNICAEN_TBL.CALCUL_PROC_PARAMS.v1);
-    END IF;
-
-    IF UNICAEN_TBL.CALCUL_PROC_PARAMS.p2 IS NOT NULL THEN
-      q := QUERY_APPLY_PARAM(q, UNICAEN_TBL.CALCUL_PROC_PARAMS.p2, UNICAEN_TBL.CALCUL_PROC_PARAMS.v2);
-    END IF;
-
-    IF UNICAEN_TBL.CALCUL_PROC_PARAMS.p3 IS NOT NULL THEN
-      q := QUERY_APPLY_PARAM(q, UNICAEN_TBL.CALCUL_PROC_PARAMS.p3, UNICAEN_TBL.CALCUL_PROC_PARAMS.v3);
-    END IF;
-
-    IF UNICAEN_TBL.CALCUL_PROC_PARAMS.p4 IS NOT NULL THEN
-      q := QUERY_APPLY_PARAM(q, UNICAEN_TBL.CALCUL_PROC_PARAMS.p4, UNICAEN_TBL.CALCUL_PROC_PARAMS.v4);
-    END IF;
-
-    IF UNICAEN_TBL.CALCUL_PROC_PARAMS.p5 IS NOT NULL THEN
-      q := QUERY_APPLY_PARAM(q, UNICAEN_TBL.CALCUL_PROC_PARAMS.p5, UNICAEN_TBL.CALCUL_PROC_PARAMS.v5);
-    END IF;
-
-    RETURN q;
-  END;
-
-
-  FUNCTION PARAMS_MAKE_FILTER(useParams BOOLEAN DEFAULT FALSE) RETURN VARCHAR2 IS
-    filter VARCHAR2(4000) DEFAULT '';
-  BEGIN
-    IF NOT useParams THEN
-      RETURN '1=1';
-    END IF;
-
-    IF unicaen_tbl.calcul_proc_params.p1 IS NOT NULL THEN
-      IF filter IS NOT NULL THEN
-        filter := filter || ' AND ';
-      END IF;
-      filter := filter || 'COALESCE(v.' || unicaen_tbl.calcul_proc_params.p1 || ', t.' || unicaen_tbl.calcul_proc_params.p1 || ') ';
-      IF unicaen_tbl.calcul_proc_params.v1 IS NULL THEN
-        filter := filter || 'IS NULL';
-      ELSE
-        filter := filter || '= q''[' || unicaen_tbl.calcul_proc_params.v1 || ']''';
-      END IF;
-    END IF;
-
-    IF unicaen_tbl.calcul_proc_params.p2 IS NOT NULL THEN
-      IF filter IS NOT NULL THEN
-        filter := filter || ' AND ';
-      END IF;
-      filter := filter || 'COALESCE(v.' || unicaen_tbl.calcul_proc_params.p2 || ', t.' || unicaen_tbl.calcul_proc_params.p2 || ') ';
-      IF unicaen_tbl.calcul_proc_params.v2 IS NULL THEN
-        filter := filter || 'IS NULL';
-      ELSE
-        filter := filter || '= q''[' || unicaen_tbl.calcul_proc_params.v2 || ']''';
-      END IF;
-    END IF;
-
-    IF unicaen_tbl.calcul_proc_params.p3 IS NOT NULL THEN
-      IF filter IS NOT NULL THEN
-        filter := filter || ' AND ';
-      END IF;
-      filter := filter || 'COALESCE(v.' || unicaen_tbl.calcul_proc_params.p3 || ', t.' || unicaen_tbl.calcul_proc_params.p3 || ') ';
-      IF unicaen_tbl.calcul_proc_params.v3 IS NULL THEN
-        filter := filter || 'IS NULL';
-      ELSE
-        filter := filter || '= q''[' || unicaen_tbl.calcul_proc_params.v3 || ']''';
-      END IF;
-    END IF;
-
-    IF unicaen_tbl.calcul_proc_params.p4 IS NOT NULL THEN
-      IF filter IS NOT NULL THEN
-        filter := filter || ' AND ';
-      END IF;
-      filter := filter || 'COALESCE(v.' || unicaen_tbl.calcul_proc_params.p4 || ', t.' || unicaen_tbl.calcul_proc_params.p4 || ') ';
-      IF unicaen_tbl.calcul_proc_params.v4 IS NULL THEN
-        filter := filter || 'IS NULL';
-      ELSE
-        filter := filter || '= q''[' || unicaen_tbl.calcul_proc_params.v4 || ']''';
-      END IF;
-    END IF;
-
-    IF unicaen_tbl.calcul_proc_params.p5 IS NOT NULL THEN
-      IF filter IS NOT NULL THEN
-        filter := filter || ' AND ';
-      END IF;
-      filter := filter || 'COALESCE(v.' || unicaen_tbl.calcul_proc_params.p5 || ', t.' || unicaen_tbl.calcul_proc_params.p5 || ') ';
-      IF unicaen_tbl.calcul_proc_params.v5 IS NULL THEN
-        filter := filter || 'IS NULL';
-      ELSE
-        filter := filter || '= q''[' || unicaen_tbl.calcul_proc_params.v5 || ']''';
-      END IF;
-    END IF;
-
-    IF filter = '' THEN
-      RETURN '1=1';
-    END IF;
-
-    RETURN filter;
-  END;
-
-
-
-  PROCEDURE CALCULER(TBL_NAME VARCHAR2) IS
-    params t_params;
-  BEGIN
-    ANNULER_DEMANDES(TBL_NAME);
-    CALCULER(TBL_NAME, params);
-  END;
-
-
-
-  PROCEDURE CALCULER(TBL_NAME VARCHAR2, param VARCHAR2, value VARCHAR2) IS
-    calcul_proc varchar2(30);
-    params t_params;
-  BEGIN
-    IF NOT UNICAEN_TBL.ACTIV_CALCULS THEN RETURN; END IF;
-
-    SELECT custom_calcul_proc INTO calcul_proc FROM tbl WHERE tbl_name = CALCULER.TBL_NAME;
-
-    params.p1 := param;
-    params.v1 := value;
-
-    unicaen_tbl.calcul_proc_params := params;
-
-    IF calcul_proc IS NOT NULL THEN
-      EXECUTE IMMEDIATE
-        'BEGIN ' || calcul_proc || '(UNICAEN_TBL.CALCUL_PROC_PARAMS.p1, UNICAEN_TBL.CALCUL_PROC_PARAMS.v1); END;';
-    ELSE
-      EXECUTE IMMEDIATE
-        'BEGIN UNICAEN_TBL.C_' || TBL_NAME || '(TRUE); END;';
-    END IF;
-  END;
-
-
-
-  PROCEDURE CALCULER(TBL_NAME VARCHAR2, params t_params) IS
-    calcul_proc varchar2(30);
-  BEGIN
-    IF NOT UNICAEN_TBL.ACTIV_CALCULS THEN RETURN; END IF;
-
-    SELECT custom_calcul_proc INTO calcul_proc FROM tbl WHERE tbl_name = CALCULER.TBL_NAME;
-
-    unicaen_tbl.calcul_proc_params := params;
-
-    IF calcul_proc IS NOT NULL THEN
-      EXECUTE IMMEDIATE
-              'BEGIN ' || calcul_proc || '(UNICAEN_TBL.CALCUL_PROC_PARAMS.p1, UNICAEN_TBL.CALCUL_PROC_PARAMS.v1); END;';
-    ELSE
-      EXECUTE IMMEDIATE
-              'BEGIN UNICAEN_TBL.C_' || TBL_NAME || '(TRUE); END;';
-    END IF;
-  END;
-
-
-
-  PROCEDURE DEMANDE_CALCUL(TBL_NAME VARCHAR2, param VARCHAR2, value VARCHAR2) IS
-  BEGIN
-    dems(TBL_NAME)(param)(value) := TRUE;
-  END;
-
-
-
-  PROCEDURE ANNULER_DEMANDES IS
-  BEGIN
-    dems.delete;
-  END;
-
-
-
-  PROCEDURE ANNULER_DEMANDES(TBL_NAME VARCHAR2) IS
-  BEGIN
-    IF dems.exists(tbl_name) THEN
-      dems(tbl_name).delete;
-    END IF;
-  END;
-
-
-
-  FUNCTION HAS_DEMANDES RETURN BOOLEAN IS
-  BEGIN
-    RETURN dems.count > 0;
-  END;
-
-
-
-  PROCEDURE CALCULER_DEMANDES IS
-    d t_dems;
-    tbl_name VARCHAR2(30);
-    param VARCHAR2(30);
-    value VARCHAR2(80);
-  BEGIN
-    d := dems;
-    dems.delete;
-
-    tbl_name := d.FIRST;
-    LOOP EXIT WHEN tbl_name IS NULL;
-      param := d(tbl_name).FIRST;
-      LOOP EXIT WHEN param IS NULL;
-        value := d(tbl_name)(param).FIRST;
-        LOOP EXIT WHEN value IS NULL;
-          calculer(tbl_name, param, value);
-          value := d(tbl_name)(param).NEXT(value);
-        END LOOP;
-        param := d(tbl_name).NEXT(param);
-      END LOOP;
-      tbl_name := d.NEXT(tbl_name);
-    END LOOP;
-
-    IF HAS_DEMANDES THEN -- pour les boucles !!
-      CALCULER_DEMANDES;
-    END IF;
-  END;
-
-
-
-  -- AUTOMATIC GENERATION --
-
-  -- END OF AUTOMATIC GENERATION --
-
-END UNICAEN_TBL;
diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php
deleted file mode 100644
index b08b6f23aebda68ce14bc24b0738f536d6d3b58a..0000000000000000000000000000000000000000
--- a/src/Controller/AdminController.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Controller;
-
-use UnicaenTbl\Form\Traits\ActualisationFormAwareTrait;
-use UnicaenTbl\Service\Traits\QueryGeneratorServiceAwareTrait;
-use UnicaenTbl\Service\Traits\SchemaServiceAwareTrait;
-use UnicaenTbl\Service\Traits\TableauBordServiceAwareTrait;
-use Laminas\Http\Request;
-use Laminas\Mvc\Controller\AbstractActionController;
-use Laminas\View\Model\JsonModel;
-
-/**
- *
- *
- * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
- */
-class AdminController extends AbstractActionController
-{
-    use SchemaServiceAwareTrait;
-    use QueryGeneratorServiceAwareTrait;
-    use ActualisationFormAwareTrait;
-    use TableauBordServiceAwareTrait;
-
-
-
-    public function indexAction()
-    {
-        $tbls = $this->getServiceSchema()->getTableauBords();
-
-        return compact('tbls');
-    }
-
-
-
-    public function buildProceduresAction()
-    {
-        $inConsole = !$this->getRequest() instanceof Request;
-
-        if ($inConsole) {
-            echo 'Construction des procédures de mise à jour des tableaux de bord...' . "\n";
-        }
-        try {
-            $this->getServiceQueryGenerator()->updateProcedures();
-            $msg = 'Procédures de mise à jour des tableaux de bord construites'."\n";
-            if ($inConsole){
-                echo $msg;
-            }else{
-                $this->flashMessenger()->addSuccessMessage($msg);
-            }
-        } catch (\Exception $e) {
-            $msg = 'Une erreur a été rencontrée : '.$e->getMessage().'.'."\n";
-            if ($inConsole){
-                echo $msg;
-            }else{
-                $this->flashMessenger()->addErrorMessage($msg);
-            }
-        }
-
-        if (!$inConsole) {
-            $this->redirect()->toRoute('unicaen-tbl');
-        }
-    }
-
-
-
-    public function actualisationAction()
-    {
-        $tableauBord = $this->params()->fromRoute('tableauBord');
-
-        if (!$tableauBord) {
-            throw new \LogicException('Le tableau de bord n\'a pas été spécifié');
-        }
-
-        $form = $this->getFormActualisation();
-        $options = $this->getServiceSchema()->getTableauBords()[$tableauBord]->getColumns();
-        $options = array_keys($options);
-        $options = array_combine($options,$options);
-        $form->get('param')->setValueOptions($options);
-        $form->setAttribute('action', $this->url()->fromRoute('unicaen-tbl/actualisation', ['tableauBord' => $tableauBord]));
-
-        if ($this->getRequest()->isPost()) {
-            $form->setData($this->getRequest()->getPost());
-            if ($form->isValid()) {
-                try{
-                    $param = $form->get('param')->getValue();
-                    $value = $form->get('value')->getValue();
-                    $this->getServiceTableauBord()->calculer($tableauBord, $param, $value);
-                    $this->flashMessenger()->addSuccessMessage('Actualisation réussie');
-                }catch(\Exception $e){
-                    $this->flashMessenger()->addErrorMessage($e->getMessage());
-                }
-            }
-        }
-
-        return compact('form');
-    }
-}
\ No newline at end of file
diff --git a/src/Controller/Factory/AdminControllerFactory.php b/src/Controller/Factory/AdminControllerFactory.php
deleted file mode 100644
index 4a4e00b96cc609204c0bba9ead45f72f4ee02efa..0000000000000000000000000000000000000000
--- a/src/Controller/Factory/AdminControllerFactory.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Controller\Factory;
-
-use Psr\Container\ContainerInterface;
-use UnicaenTbl\Controller\AdminController;
-use UnicaenTbl\Form\ActualisationForm;
-use UnicaenTbl\Service\QueryGeneratorService;
-use UnicaenTbl\Service\SchemaService;
-use UnicaenTbl\Service\TableauBordService;
-
-
-class AdminControllerFactory
-{
-    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
-    {
-        $controller = new AdminController();
-
-        $controller->setServiceSchema(
-            $container->get(SchemaService::class)
-        );
-        $controller->setServiceQueryGenerator(
-            $container->get(QueryGeneratorService::class)
-        );
-        $controller->setServiceTableauBord(
-            $container->get(TableauBordService::class)
-        );
-        $controller->setFormActualisation(
-            $container->get('FormElementManager')->get(ActualisationForm::class)
-        );
-
-        return $controller;
-    }
-}
\ No newline at end of file
diff --git a/src/Entity/Column.php b/src/Entity/Column.php
deleted file mode 100644
index ceb6cd8d0e7cca124f39a183f525cbca4414d0a4..0000000000000000000000000000000000000000
--- a/src/Entity/Column.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Entity;
-
-class Column
-{
-
-    /**
-     * Nom de la colonne du tableau de bord
-     *
-     * @var string
-     */
-    protected $name;
-
-    /**
-     * @var bool
-     */
-    protected $nullable;
-
-    /**
-     * @var bool
-     */
-    protected $key;
-
-    /**
-     * @var bool
-     */
-    protected $inView = true;
-
-
-
-    /**
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-
-
-    /**
-     * @param string $name
-     *
-     * @return Column
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return bool
-     */
-    public function isNullable()
-    {
-        return $this->nullable;
-    }
-
-
-
-    /**
-     * @param bool $nullable
-     *
-     * @return Column
-     */
-    public function setNullable($nullable)
-    {
-        $this->nullable = $nullable;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return bool
-     */
-    public function isKey()
-    {
-        return $this->key;
-    }
-
-
-
-    /**
-     * @param bool $key
-     *
-     * @return Column
-     */
-    public function setKey($key)
-    {
-        $this->key = $key;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return bool
-     */
-    public function isInView(): bool
-    {
-        return $this->inView;
-    }
-
-
-
-    /**
-     * @param bool $inView
-     *
-     * @return Column
-     */
-    public function setInView(bool $inView): Column
-    {
-        $this->inView = $inView;
-
-        return $this;
-    }
-
-
-
-    /**
-     * The __toString method allows a class to decide how it will react when it is converted to a string.
-     *
-     * @return string
-     * @link http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
-     */
-    function __toString()
-    {
-        return $this->getName();
-    }
-
-}
\ No newline at end of file
diff --git a/src/Entity/TableauBord.php b/src/Entity/TableauBord.php
deleted file mode 100644
index eeef19d4c11b16a8b70fbf85070666ea1e528871..0000000000000000000000000000000000000000
--- a/src/Entity/TableauBord.php
+++ /dev/null
@@ -1,276 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Entity;
-
-/**
- * Class TableauBord
- *
- * @package UnicaenTbl\Entity
- */
-class TableauBord
-{
-
-    /**
-     * Nom du tableau de bord
-     *
-     * @var string
-     */
-    protected $name;
-
-    /**
-     * @var string
-     */
-    protected $tableName;
-
-    /**
-     * @var string
-     */
-    protected $viewName;
-
-    /**
-     * @var string
-     */
-    protected $constraintName;
-
-    /**
-     * @var string
-     */
-    protected $sequenceName;
-
-    /**
-     * @var string
-     */
-    protected $customCalculProc;
-
-    /**
-     * @var Column[]
-     */
-    protected $columns = [];
-
-
-
-    /**
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-
-
-    /**
-     * @param string $name
-     *
-     * @return TableauBord
-     */
-    public function setName($name)
-    {
-        $this->name = $name;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return string
-     */
-    public function getTableName($byTblNameIfNull = false)
-    {
-        if (!$this->tableName && $byTblNameIfNull) {
-            $tableName = strtoupper($this->name);
-
-            return $tableName;
-        }
-
-        return $this->tableName;
-    }
-
-
-
-    /**
-     * @param string $tableName
-     *
-     * @return TableauBord
-     */
-    public function setTableName($tableName)
-    {
-        $this->tableName = $tableName;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return string
-     */
-    public function getViewName($byTblNameIfNull = false)
-    {
-        if (!$this->viewName && $byTblNameIfNull) {
-            $viewName = strtoupper('V_TBL_' . substr($this->name, 0, 24));
-
-            return $viewName;
-        }
-
-        return $this->viewName;
-    }
-
-
-
-    /**
-     * @param string $viewName
-     *
-     * @return TableauBord
-     */
-    public function setViewName($viewName)
-    {
-        $this->viewName = $viewName;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return string
-     */
-    public function getConstraintName($byTblNameIfNull = false)
-    {
-        if (!$this->constraintName && $byTblNameIfNull) {
-            $constraintName = strtoupper(substr($this->name, 0, 26) . '__UN');
-
-            return $constraintName;
-        }
-
-        return $this->constraintName;
-    }
-
-
-
-    /**
-     * @param string $constraintName
-     *
-     * @return TableauBord
-     */
-    public function setConstraintName($constraintName)
-    {
-        $this->constraintName = $constraintName;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return string
-     */
-    public function getSequenceName($byTblNameIfNull = false)
-    {
-        if (!$this->sequenceName && $byTblNameIfNull) {
-            $sequenceName = strtoupper(substr($this->tableName, 0, 23) . '_ID_SEQ');
-
-            return $sequenceName;
-        }
-
-        return $this->sequenceName;
-    }
-
-
-
-    /**
-     * @param string $sequenceName
-     *
-     * @return TableauBord
-     */
-    public function setSequenceName($sequenceName)
-    {
-        $this->sequenceName = $sequenceName;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return string
-     */
-    public function getCustomCalculProc()
-    {
-        return $this->customCalculProc;
-    }
-
-
-
-    /**
-     * @param string $customCalculProc
-     *
-     * @return TableauBord
-     */
-    public function setCustomCalculProc($customCalculProc)
-    {
-        $this->customCalculProc = $customCalculProc;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return Column[]
-     */
-    public function getKeyColumns()
-    {
-        $key = [];
-        foreach ($this->columns as $column) {
-            if ($column->isKey()) {
-                $key[$column->getName()] = $column;
-            }
-        }
-
-        return $key;
-    }
-
-
-
-    /**
-     * @return Column[]
-     */
-    public function getColumns()
-    {
-        return $this->columns;
-    }
-
-
-
-    /**
-     * @param Column $column
-     *
-     * @return $this
-     */
-    public function addColumn(Column $column)
-    {
-        $this->columns[$column->getName()] = $column;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @param $column
-     *
-     * @return $this
-     */
-    public function removeColumn($column)
-    {
-        if ($column instanceof Column) {
-            $column = $column->getName();
-        }
-        unset($this->columns[$column]);
-
-        return $this;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Entity/Traits/TableauBordAwareTrait.php b/src/Entity/Traits/TableauBordAwareTrait.php
deleted file mode 100644
index 3937ab5b705fc1e62a5aa0d74f551ebfd6a0ebaa..0000000000000000000000000000000000000000
--- a/src/Entity/Traits/TableauBordAwareTrait.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Entity\Traits;
-use UnicaenTbl\Entity\TableauBord;
-
-trait TableauBordAwareTrait
-{
-
-    /**
-     * @var TableauBord
-     */
-    private $tableauBord;
-
-
-
-    /**
-     * @return TableauBord
-     */
-    public function getTableauBord()
-    {
-        return $this->tableauBord;
-    }
-
-
-
-    /**
-     * @param TableauBord $tableauBord
-     *
-     * @return TableauBordAwareTrait
-     */
-    public function setTableauBord($tableauBord)
-    {
-        $this->tableauBord = $tableauBord;
-
-        return $this;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Form/ActualisationForm.php b/src/Form/ActualisationForm.php
deleted file mode 100644
index 55109ab207f21d740ebe4ba2c915252af8d3d78f..0000000000000000000000000000000000000000
--- a/src/Form/ActualisationForm.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Form;
-
-use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilterProviderInterface;
-use Laminas\Stdlib\Hydrator\HydratorInterface;
-
-
-/**
- * Description of ActualisationForm
- *
- * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
- */
-class ActualisationForm extends Form implements InputFilterProviderInterface
-{
-
-    public function init()
-    {
-        $this->add([
-            'name'    => 'param',
-            'type'    => 'Select',
-            'options' => [
-                'label'        => 'Nom du champ éventuel',
-                'empty_option' => "- Tout sans filtre -",
-            ],
-        ]);
-
-        $this->add([
-            'name'    => 'value',
-            'type'    => 'Text',
-            'options' => [
-                'label' => 'Valeur',
-            ],
-        ]);
-
-        $this->add([
-            'name'       => 'submit',
-            'type'       => 'Submit',
-            'attributes' => [
-                'value' => 'Actualiser le tableau de bord',
-                'class' => 'btn btn-primary',
-            ],
-        ]);
-    }
-
-
-
-    /**
-     * Should return an array specification compatible with
-     * {@link Laminas\InputFilter\Factory::createInputFilter()}.
-     *
-     * @return array
-     */
-    public function getInputFilterSpecification()
-    {
-        return [
-            'param' => ['required' => false],
-            'value' => ['required' => false],
-        ];
-    }
-
-}
\ No newline at end of file
diff --git a/src/Form/Factory/ActualisationFormFactory.php b/src/Form/Factory/ActualisationFormFactory.php
deleted file mode 100644
index af6f6b0469520d37e9a0b5e535888f4519d096c5..0000000000000000000000000000000000000000
--- a/src/Form/Factory/ActualisationFormFactory.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Form\Factory;
-
-use Psr\Container\ContainerInterface;
-use UnicaenTbl\Form\ActualisationForm;
-
-
-/**
- * Description of ActualisationFormFactory
- *
- * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
- */
-class ActualisationFormFactory
-{
-    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
-    {
-        $form = new ActualisationForm();
-
-        return $form;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Form/Traits/ActualisationFormAwareTrait.php b/src/Form/Traits/ActualisationFormAwareTrait.php
deleted file mode 100644
index 5e967ddd458f7605dc3baf9e250688e4b50bc611..0000000000000000000000000000000000000000
--- a/src/Form/Traits/ActualisationFormAwareTrait.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Form\Traits;
-
-use UnicaenTbl\Form\ActualisationForm;
-use Application\Module;
-use RuntimeException;
-
-/**
- * Description of ActualisationFormAwareTrait
- *
- * @author UnicaenCode
- */
-trait ActualisationFormAwareTrait
-{
-    /**
-     * @var ActualisationForm
-     */
-    private $formActualisation;
-
-
-
-    /**
-     * @param ActualisationForm $formActualisation
-     *
-     * @return self
-     */
-    public function setFormActualisation(ActualisationForm $formActualisation)
-    {
-        $this->formActualisation = $formActualisation;
-
-        return $this;
-    }
-
-
-
-    /**
-     * Retourne un nouveau formulaire ou fieldset systématiquement, sauf si ce dernier a été fourni manuellement.
-     *
-     * @return ActualisationForm
-     */
-    public function getFormActualisation()
-    {
-        return $this->formActualisation;
-    }
-}
-
diff --git a/src/Options/ModuleOptions.php b/src/Options/ModuleOptions.php
index 12f68e36deb88069d9229668ab3ff3ad1cbd1cb8..ee782ed8debab3fb7e972d36e0debed55a626831 100755
--- a/src/Options/ModuleOptions.php
+++ b/src/Options/ModuleOptions.php
@@ -3,6 +3,8 @@
 namespace UnicaenTbl\Options;
 
 use Laminas\Stdlib\AbstractOptions;
+use Psr\Container\ContainerInterface;
+use UnicaenTbl\TableauBord;
 
 
 /**
@@ -10,22 +12,14 @@ use Laminas\Stdlib\AbstractOptions;
  *
  * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
  */
-class ModuleOptions extends AbstractOptions {
+class ModuleOptions extends AbstractOptions
+{
 
     /**
      * Turn off strict options mode
      */
     protected $__strictMode__ = false;
 
-    /**
-     * @var string
-     */
-    protected $package = 'UNICAEN_TBL';
-
-    /**
-     * @var string
-     */
-    protected $entityManagerName = 'doctrine.entitymanager.orm_default';
 
 
     /**
@@ -74,4 +68,41 @@ class ModuleOptions extends AbstractOptions {
         return $this;
     }
 
+
+
+    /**
+     * @param ContainerInterface $container
+     * @return array|TableauBord[]
+     * @throws \Exception
+     */
+    public function loadTableauxBord(ContainerInterface $container): array
+    {
+        $options = $this->getTableauxBord() ?? [];
+
+        $tableauxBord = [];
+        foreach ($options as $tblName => $tblOptions) {
+            $tableauBord = new TableauBord();
+            $tableauBord->setName($tblName);
+            $tableauBord->loadOptions($tblOptions, $container);
+            $tableauxBord[$tableauBord->getName()] = $tableauBord;
+        }
+
+        return $tableauxBord;
+    }
+
+
+
+    public function getTableauxBord(): ?array
+    {
+        return $this->tableauxBord;
+    }
+
+
+
+    public function setTableauxBord(array $tableauxBord): self
+    {
+        $this->tableauxBord = $tableauxBord;
+
+        return $this;
+    }
 }
\ No newline at end of file
diff --git a/src/Process/DbDiffProcess.php b/src/Process/DbDiffProcess.php
new file mode 100644
index 0000000000000000000000000000000000000000..45205fdd3488fbf83e3ca56f0cb07852126cb5bd
--- /dev/null
+++ b/src/Process/DbDiffProcess.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+use UnicaenTbl\Service\BddServiceAwareTrait;
+use UnicaenTbl\TableauBord;
+
+/**
+ * Description of DbDiffProcess
+ *
+ * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
+ */
+class DbDiffProcess implements ProcessInterface
+{
+    use BddServiceAwareTrait;
+
+    protected TableauBord $tableauBord;
+
+    /**
+     * Paramètres de filtres en entrée
+     */
+    protected array $params;
+
+    /**
+     * Nom de la vue source
+     */
+    protected string $view;
+
+    /**
+     * Nom de la table de destination
+     */
+    protected string $table;
+
+    /**
+     * Liste des colonnes à transférer
+     */
+    protected array $cols;
+
+    /**
+     * Clé pour identifier une ligne (contrainte d'unicité obligatoire)
+     */
+    protected array $key;
+
+    /**
+     * Liste des valeurs par défaut pour remplacer les NULL au cas où
+     *
+     * @var array
+     */
+    protected array $keyValuesIfNull = [];
+
+
+
+    public function run(TableauBord $tableauBord, array $params = [])
+    {
+        $this->tableauBord = $tableauBord;
+        $this->params = $params;
+
+        $defaultView = 'V_TBL_' . strtoupper($tableauBord->getName());
+        $defaultTable = 'TBL_' . strtoupper($tableauBord->getName());
+
+        $this->view = $tableauBord->getOption('view', $defaultView, 'Une vue doit être fournie comme source de données');
+        $this->table = $tableauBord->getOption('table', $defaultTable, 'Une table doit être fournie comme destination de données');
+
+        $this->cols = $tableauBord->getOption('cols', [], 'Les colonnes doivent être listées');
+        $this->key = $tableauBord->getOption('key', [], 'La clé d\'unicité doit être définie');
+        $this->keyValuesIfNull = $tableauBord->getOption('key_values_if_null', []);
+
+        $sql = $this->diffQuery($params);
+//sqlDump($sql);return;
+        $conn = $this->getServiceBdd()->getEntityManager()->getConnection();
+        $conn->beginTransaction();
+
+        $stmt = $conn->executeQuery($sql);
+        while ($data = $stmt->fetchAssociative()) {
+            $srcId = (int)$data['UNICAEN_TBL_SRC_ID'];
+            $destId = (int)$data['UNICAEN_TBL_DEST_ID'];
+            unset($data['UNICAEN_TBL_SRC_ID']);
+            unset($data['UNICAEN_TBL_DEST_ID']);
+
+            if ($srcId != 0 && $destId != 0) {
+                $conn->update($this->table, $data, ['ID' => $destId]);
+            }
+            if ($srcId == 0) {
+                $conn->delete($this->table, ['ID' => $destId]);
+            }
+            if ($destId == 0) {
+                $conn->insert($this->table, $data);
+            }
+        }
+
+        $conn->commit();
+    }
+
+
+
+    protected function sourceQuery(): string
+    {
+        $query = $this->getServiceBdd()->getViewDefinition($this->view);
+
+        return $query;
+    }
+
+
+
+    protected function diffQuery(array $params): string
+    {
+        $bdd = $this->getServiceBdd();
+
+        $viewSql = $this->sourceQuery();
+        $viewSql = $bdd->injectKey($viewSql, $params);
+
+        $cols = implode(',', $this->cols);
+        $where = $bdd->makeWhere($params);
+
+        $joinTest = '';
+        foreach ($this->key as $keycol) {
+            if ($joinTest != '') $joinTest .= ' AND ';
+
+            if (array_key_exists($keycol, $this->keyValuesIfNull)){
+                $valueIfNull = $bdd->escape($this->keyValuesIfNull[$keycol]);
+                $joinTest .= "COALESCE(src.$keycol,$valueIfNull) = COALESCE(dest.$keycol,$valueIfNull)";
+            }else{
+                $joinTest .= "src.$keycol = dest.$keycol";
+            }
+        }
+
+
+        $sql = "
+        SELECT
+          dest.ID UNICAEN_TBL_DEST_ID,
+          src.*
+        FROM
+            (SELECT ID, $cols FROM $this->table $where) dest
+            FULL JOIN (SELECT rownum UNICAEN_TBL_SRC_ID, $cols FROM ($viewSql) $where) src ON $joinTest
+        WHERE
+            dest.id IS NULL OR src.UNICAEN_TBL_SRC_ID IS NULL
+        ";
+
+        foreach ($this->cols as $col) {
+            $sql .= "\nOR dest.$col <> src.$col OR (dest.$col IS NULL AND src.$col IS NOT NULL) OR (dest.$col IS NOT NULL AND src.$col IS NULL)";
+        }
+
+        return $sql;
+    }
+}
\ No newline at end of file
diff --git a/src/Process/DbDiffProcessFactory.php b/src/Process/DbDiffProcessFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..9b585c7592fa53d04fbb6c8f2ca6d16e157c7bcc
--- /dev/null
+++ b/src/Process/DbDiffProcessFactory.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+use Psr\Container\ContainerInterface;
+use UnicaenTbl\Service\BddService;
+
+
+/**
+ * Description of DbDiffProcessFactory
+ *
+ * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
+ */
+class DbDiffProcessFactory
+{
+    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
+    {
+        $service = new DbDiffProcess();
+
+        $service->setServiceBdd($container->get(BddService::class));
+
+        return $service;
+    }
+}
\ No newline at end of file
diff --git a/src/Process/PlsqlProcess.php b/src/Process/PlsqlProcess.php
new file mode 100755
index 0000000000000000000000000000000000000000..63d471cbbe77a884e06d74b453a4aeae8028eccc
--- /dev/null
+++ b/src/Process/PlsqlProcess.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+use UnicaenTbl\Service\BddServiceAwareTrait;
+use UnicaenTbl\TableauBord;
+
+/**
+ * Description of PlsqlProcess
+ *
+ * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
+ */
+class PlsqlProcess implements ProcessInterface
+{
+    use BddServiceAwareTrait;
+
+    protected TableauBord $tableauBord;
+
+    /**
+     * Paramètres de filtres en entrée
+     */
+    protected array $params;
+
+    /**
+     * Commande à exécuter
+     */
+    protected string $command;
+
+
+
+
+    public function run(TableauBord $tableauBord, array $params = [])
+    {
+        $this->tableauBord = $tableauBord;
+        $this->params = $params;
+
+        $this->command = $tableauBord->getOption('command', null, 'Une commande PL/SQL doit être spécifiée');
+
+        $conn = $this->getServiceBdd()->getEntityManager()->getConnection();
+        $conn->beginTransaction();
+
+        if (!empty($params)) {
+
+            foreach ($params as $n => $v) {
+
+                $plsql = "BEGIN $this->command(:n, :v); END;";
+                $conn->executeStatement($plsql, compact('n', 'v'));
+
+                // Seul le premier paramètre est pris en compte. Pour les autres c'est impossible
+                break;
+            }
+
+        }else{
+            $plsql = "BEGIN $this->command(); END;";
+            $conn->executeStatement($plsql);
+        }
+
+        $conn->commit();
+    }
+
+}
\ No newline at end of file
diff --git a/src/Process/PlsqlProcessAwareTrait.php b/src/Process/PlsqlProcessAwareTrait.php
new file mode 100755
index 0000000000000000000000000000000000000000..0a7d01f5d0cd2d6650d4c10dac125c751d443def
--- /dev/null
+++ b/src/Process/PlsqlProcessAwareTrait.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+
+/**
+ * Description of PlsqlProcessAwareTrait
+ *
+ * @author UnicaenCode
+ */
+trait PlsqlProcessAwareTrait
+{
+    protected ?PlsqlProcess $processPlsql = null;
+
+
+
+    /**
+     * @param PlsqlProcess $processPlsql
+     *
+     * @return self
+     */
+    public function setProcessPlsql( ?PlsqlProcess $processPlsql )
+    {
+        $this->processPlsql = $processPlsql;
+
+        return $this;
+    }
+
+
+
+    public function getProcessPlsql(): ?PlsqlProcess
+    {
+        return $this->processPlsql;
+    }
+}
\ No newline at end of file
diff --git a/src/Process/PlsqlProcessFactory.php b/src/Process/PlsqlProcessFactory.php
new file mode 100755
index 0000000000000000000000000000000000000000..eef3f8d8e1df08d27f4c546b4e0c5edc3e61c2ff
--- /dev/null
+++ b/src/Process/PlsqlProcessFactory.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+use Psr\Container\ContainerInterface;
+use UnicaenTbl\Service\BddService;
+
+
+/**
+ * Description of PlsqlProcessFactory
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class PlsqlProcessFactory
+{
+
+    /**
+     * @param ContainerInterface $container
+     * @param string             $requestedName
+     * @param array|null         $options
+     *
+     * @return PlsqlProcess
+     */
+    public function __invoke(ContainerInterface $container, $requestedName, $options = null): PlsqlProcess
+    {
+        $service = new PlsqlProcess;
+
+        $service->setServiceBdd($container->get(BddService::class));
+
+        return $service;
+    }
+}
\ No newline at end of file
diff --git a/src/Process/ProcessInterface.php b/src/Process/ProcessInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba7c5e192b34234e6fc3e99ddf69b5ca71576cde
--- /dev/null
+++ b/src/Process/ProcessInterface.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace UnicaenTbl\Process;
+
+use UnicaenTbl\TableauBord;
+
+/**
+ * Description of ProcessInterface
+ *
+ * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
+ */
+interface ProcessInterface
+{
+    public function run(TableauBord $tableauBord, array $params = []);
+}
\ No newline at end of file
diff --git a/src/Provider/Privilege/Privileges.php b/src/Provider/Privilege/Privileges.php
deleted file mode 100644
index 7b50583064078715f20b8e52532295302b23af6a..0000000000000000000000000000000000000000
--- a/src/Provider/Privilege/Privileges.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Provider\Privilege;
-
-/**
- * Description of Privileges
- *
- * Liste des privilèges utilisables dans votre application
- *
- * @author UnicaenCode
- */
-class Privileges extends \UnicaenPrivilege\Provider\Privilege\Privileges
-{
-
-    const UNICAEN_TBL_ADMIN           = 'unicaen-tbl-admin';
-    const UNICAEN_TBL_ACTUALISATION   = 'unicaen-tbl-actualisation';
-
-}
\ No newline at end of file
diff --git a/src/Service/AbstractService.php b/src/Service/AbstractService.php
deleted file mode 100644
index 64a17b127a0e99d27caae803e965a370e4ff4284..0000000000000000000000000000000000000000
--- a/src/Service/AbstractService.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service;
-
-use Doctrine\ORM\EntityManager;
-use UnicaenApp\Service\EntityManagerAwareTrait;
-
-/**
- * Classe mère des services
- *
- * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
- */
-class AbstractService
-{
-    use EntityManagerAwareTrait;
-
-
-
-    /**
-     * Echappe une chaîne de caractères pour convertir en SQL
-     *
-     * @param string $string
-     *
-     * @return string
-     */
-    public static function escapeKW($string)
-    {
-        return '"' . str_replace('"', '""', strtoupper($string)) . '"';
-    }
-
-
-
-    /**
-     * Echappe une valeur pour convertir en SQL
-     *
-     * @param mixed $value
-     *
-     * @return string
-     */
-    public static function escape($value)
-    {
-        if (null === $value) return 'NULL';
-        switch (gettype($value)) {
-            case 'string':
-                return "'" . str_replace("'", "''", $value) . "'";
-            case 'integer':
-                return (string)$value;
-            case 'boolean':
-                return $value ? '1' : '0';
-            case 'double':
-                return (string)$value;
-            case 'array':
-                return '(' . implode(',', array_map(__class__ . '::escape', $value)) . ')';
-        }
-        throw new \Exception('La valeur transmise ne peut pas être convertie en SQL');
-    }
-
-
-
-    /**
-     * Retourne le code SQL correspondant à la valeur transmise, précédé de "=", "IS" ou "IN" suivant le contexte.
-     *
-     * @param mixed $value
-     *
-     * @return string
-     */
-    public static function equals($value)
-    {
-        if (null === $value) {
-            $eq = ' IS ';
-        } elseif (is_array($value)) $eq = ' IN ';
-        else                        $eq = ' = ';
-
-        return $eq . self::escape($value);
-    }
-
-
-
-    /**
-     * Retourne une tableau des résultats de la requête transmise.
-     *
-     *
-     * @param string $sql
-     * @param array  $params
-     * @param string $colRes
-     *
-     * @return array
-     */
-    protected function query($sql, $params = null, $colRes = null)
-    {
-        $stmt   = $this->getEntityManager()->getConnection()->executeQuery($sql, $params);
-        $result = [];
-        while ($r = $stmt->fetch()) {
-            if (empty($colRes)) $result[] = $r; else $result[] = $r[$colRes];
-        }
-
-        return $result;
-    }
-
-
-
-    /**
-     * exécute un ordre SQL
-     *
-     * @param string $sql
-     *
-     * @return integer
-     */
-    protected function exec($sql)
-    {
-        return $this->getEntityManager()->getConnection()->exec($sql);
-    }
-
-}
\ No newline at end of file
diff --git a/src/Service/BddService.php b/src/Service/BddService.php
new file mode 100755
index 0000000000000000000000000000000000000000..c66b474853be687655cd4dd1e0420c913b736446
--- /dev/null
+++ b/src/Service/BddService.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace UnicaenTbl\Service;
+
+use UnicaenApp\Service\EntityManagerAwareTrait;
+
+/**
+ * Description of BddService
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class BddService
+{
+    use EntityManagerAwareTrait;
+
+    protected array $viewCache = [];
+
+
+    /**
+     * Echappe une chaîne de caractères pour convertir en SQL
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    public function escapeKW($string)
+    {
+        return '"' . str_replace('"', '""', strtoupper($string)) . '"';
+    }
+
+
+
+    /**
+     * Echappe une valeur pour convertir en SQL
+     *
+     * @param mixed $value
+     *
+     * @return string
+     */
+    public function escape($value): string
+    {
+        if (null === $value) return 'NULL';
+        switch (gettype($value)) {
+            case 'string':
+                return "'" . str_replace("'", "''", $value) . "'";
+            case 'integer':
+                return (string)$value;
+            case 'boolean':
+                return $value ? '1' : '0';
+            case 'double':
+                return (string)$value;
+            case 'array':
+                return '(' . implode(',', array_map(__class__ . '::escape', $value)) . ')';
+        }
+        throw new \Exception('La valeur transmise ne peut pas être convertie en SQL');
+    }
+
+
+
+    /**
+     * Retourne le code SQL correspondant à la valeur transmise, précédé de "=", "IS" ou "IN" suivant le contexte.
+     *
+     * @param mixed $value
+     *
+     * @return string
+     */
+    public function equals($value): string
+    {
+        if (null === $value) {
+            $eq = ' IS ';
+        } elseif (is_array($value)) $eq = ' IN ';
+        else                        $eq = ' = ';
+
+        return $eq . $this->escape($value);
+    }
+
+
+
+    /**
+     * Retourne le code SQL "select" de la VUE
+     *
+     * @param string $viewName
+     * @return string
+     */
+    public function getViewDefinition(string $viewName): string
+    {
+        if (!array_key_exists($viewName, $this->viewCache)) {
+            $sql = "SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = '" . $viewName . "'";
+            $result = $this->query($sql, [], 'TEXT');
+
+            $definition = $result[0];
+
+            $definition = trim($definition);
+
+            $this->viewCache[$viewName] = $definition;
+        }
+
+        return $this->viewCache[$viewName];
+    }
+
+
+
+    /**
+     * Transforme en filtre les commentaires de variables d'optimisation placés dans une requête
+     *
+     * @param string $query
+     * @param array $key
+     * @return string
+     */
+    public function injectKey(string $query, array $key): string
+    {
+        foreach( $key as $k => $v ){
+            while(false !== ($start=strpos($query, '/*@'.$k))){
+                $end = strpos($query, '*/', $start) + 2;
+
+                $varStart = $start + strlen($k) + 4;
+
+                $nf = substr($query, $varStart, $end - $varStart - 2);
+                $nf = 'AND '.$nf.self::equals($v);
+
+                $query = substr_replace($query, $nf, $start, $end - $start);
+            }
+        }
+
+        return $query;
+    }
+
+
+
+    public function makeWhere(array $key = []): string
+    {
+        $where = '';
+        foreach( $key as $k => $v ){
+            if ($where != '') $where .= ' AND ';
+            $where .= $k.self::equals($v);
+        }
+
+        if ($where == ''){
+            return '';
+        }
+
+        return 'WHERE '.$where;
+    }
+
+
+
+    /**
+     * Retourne un tableau des résultats de la requête transmise.
+     *
+     *
+     * @param string $sql
+     * @param array $params
+     * @param string $colRes
+     *
+     * @return array
+     */
+    protected function query($sql, $params = null, $colRes = null)
+    {
+        $stmt = $this->getEntityManager()->getConnection()->executeQuery($sql, $params);
+        $result = [];
+        while ($r = $stmt->fetchAssociative()) {
+            if (empty($colRes)) $result[] = $r; else $result[] = $r[$colRes];
+        }
+
+        return $result;
+    }
+
+
+
+    public function sequenceNextVal(string $sequence): int
+    {
+        $dbConnection = $this->getEntityManager()->getConnection();
+        $nextvalQuery = $dbConnection->getDatabasePlatform()->getSequenceNextValSQL($sequence);
+
+        $stmt = $dbConnection->executeQuery($nextvalQuery);
+        $res = (int)$stmt->fetchFirstColumn()[0];
+
+        return $res;
+    }
+
+
+    /**
+     * exécute un ordre SQL
+     *
+     * @param string $sql
+     *
+     * @return integer
+     */
+    protected function exec($sql)
+    {
+        return $this->getEntityManager()->getConnection()->exec($sql);
+    }
+}
\ No newline at end of file
diff --git a/src/Service/BddServiceAwareTrait.php b/src/Service/BddServiceAwareTrait.php
new file mode 100755
index 0000000000000000000000000000000000000000..203ec1cb12378772c002a2beef65db45840e235e
--- /dev/null
+++ b/src/Service/BddServiceAwareTrait.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace UnicaenTbl\Service;
+
+
+/**
+ * Description of BddServiceAwareTrait
+ *
+ * @author UnicaenCode
+ */
+trait BddServiceAwareTrait
+{
+    protected ?BddService $serviceBdd = null;
+
+
+
+    /**
+     * @param BddService $serviceBdd
+     *
+     * @return self
+     */
+    public function setServiceBdd(?BddService $serviceBdd)
+    {
+        $this->serviceBdd = $serviceBdd;
+
+        return $this;
+    }
+
+
+
+    public function getServiceBdd(): ?BddService
+    {
+        if (empty($this->serviceBdd)) {
+            $this->serviceBdd = \Application::$container->get(BddService::class);
+        }
+
+        return $this->serviceBdd;
+    }
+}
\ No newline at end of file
diff --git a/src/Service/BddServiceFactory.php b/src/Service/BddServiceFactory.php
new file mode 100755
index 0000000000000000000000000000000000000000..5f99eb8f2ad0103ed0850ad34bea2c8bba8185a4
--- /dev/null
+++ b/src/Service/BddServiceFactory.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace UnicaenTbl\Service;
+
+use Psr\Container\ContainerInterface;
+use UnicaenTbl\Options\ModuleOptions;
+
+
+/**
+ * Description of BddServiceFactory
+ *
+ * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
+ */
+class BddServiceFactory
+{
+
+    /**
+     * @param ContainerInterface $container
+     * @param string             $requestedName
+     * @param array|null         $options
+     *
+     * @return BddService
+     */
+    public function __invoke(ContainerInterface $container, $requestedName, $options = null): BddService
+    {
+        $service = new BddService;
+
+        /** @var ModuleOptions $options */
+        $options = $container->get(ModuleOptions::class);
+
+        $em = $container->get($options->getEntityManagerName());
+        /* @var $em \Doctrine\ORM\EntityManager */
+
+        $service->setEntityManager($em);
+
+        return $service;
+    }
+}
\ No newline at end of file
diff --git a/src/Service/CollectorService.php b/src/Service/CollectorService.php
deleted file mode 100644
index f0d0a783d3d8d2af06ad81e6dba08c51949e21db..0000000000000000000000000000000000000000
--- a/src/Service/CollectorService.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service;
-
-use Serializable;
-use Laminas\Mvc\MvcEvent;
-use Laminas\DeveloperTools\Collector\CollectorInterface;
-
-/**
- * Collecteur de données UnicaenCode
- *
- * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
- */
-class CollectorService implements CollectorInterface, Serializable
-{
-
-    const NAME     = 'unicaen-tbl_collector';
-
-    const PRIORITY = 150;
-
-
-
-    public function getViews()
-    {
-        return ['build' => 'Construction des tableaux de bord'];
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getName()
-    {
-        return self::class;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getPriority()
-    {
-        return static::PRIORITY;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function collect(MvcEvent $mvcEvent)
-    {
-
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function serialize()
-    {
-        return [];
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function unserialize($serialized)
-    {
-
-    }
-}
diff --git a/src/Service/Factory/CollectorServiceFactory.php b/src/Service/Factory/CollectorServiceFactory.php
deleted file mode 100644
index 040b18501ff041240ab339f58c3b3a2c420ff477..0000000000000000000000000000000000000000
--- a/src/Service/Factory/CollectorServiceFactory.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service\Factory;
-
-use Psr\Container\ContainerInterface;
-use UnicaenTbl\Service\CollectorService;
-
-class CollectorServiceFactory
-{
-
-    public function __invoke(ContainerInterface $container, $requestedName, $options = null)
-    {
-        $service = new CollectorService();
-
-        return $service;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Service/Factory/QueryGeneratorServiceFactory.php b/src/Service/Factory/QueryGeneratorServiceFactory.php
deleted file mode 100644
index c4934dfe1f9f660c8a43bb41381300b5049271df..0000000000000000000000000000000000000000
--- a/src/Service/Factory/QueryGeneratorServiceFactory.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service\Factory;
-
-use Psr\Container\ContainerInterface;
-use UnicaenTbl\Options\ModuleOptions;
-use UnicaenTbl\Service\QueryGeneratorService;
-use UnicaenTbl\Service\SchemaService;
-
-
-/**
- * Description of SchemaServiceFactory
- *
- * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
- */
-class QueryGeneratorServiceFactory
-{
-    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
-    {
-        $queryGenerator = new QueryGeneratorService();
-
-        /** @var ModuleOptions $options */
-        $options = $container->get(ModuleOptions::class);
-
-        $em = $container->get($options->getEntityManagerName());
-        /* @var $em \Doctrine\ORM\EntityManager */
-
-        $queryGenerator->setOptionsModule($options);
-        $queryGenerator->setEntityManager($em);
-        $queryGenerator->setServiceSchema($container->get(SchemaService::class));
-
-        return $queryGenerator;
-    }
-}
\ No newline at end of file
diff --git a/src/Service/Factory/SchemaServiceFactory.php b/src/Service/Factory/SchemaServiceFactory.php
deleted file mode 100644
index 4f107546220e5bc6b77bc9a14c3f6950b49672b3..0000000000000000000000000000000000000000
--- a/src/Service/Factory/SchemaServiceFactory.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service\Factory;
-
-use Psr\Container\ContainerInterface;
-use UnicaenTbl\Options\ModuleOptions;
-use UnicaenTbl\Service\SchemaService;
-
-
-/**
- * Description of SchemaServiceFacvtory
- *
- * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
- */
-class SchemaServiceFactory
-{
-    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
-    {
-        $schema = new SchemaService();
-
-        /** @var ModuleOptions $options */
-        $options = $container->get(ModuleOptions::class);
-
-        $em = $container->get($options->getEntityManagerName());
-        /* @var $em \Doctrine\ORM\EntityManager */
-
-        $schema->setEntityManager($em);
-
-        return $schema;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Service/QueryGeneratorService.php b/src/Service/QueryGeneratorService.php
deleted file mode 100644
index f579c63c55bd9bed094eec05dd1ef09a04eb28c6..0000000000000000000000000000000000000000
--- a/src/Service/QueryGeneratorService.php
+++ /dev/null
@@ -1,329 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service;
-
-use UnicaenTbl\Entity\TableauBord;
-use UnicaenTbl\Options\Traits\ModuleOptionsAwareTrait;
-use UnicaenTbl\Service\Traits\SchemaServiceAwareTrait;
-
-/**
- *
- *
- * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
- */
-class QueryGeneratorService extends AbstractService
-{
-    use SchemaServiceAwareTrait;
-    use ModuleOptionsAwareTrait;
-
-    const AG_BEGIN = '-- AUTOMATIC GENERATION --';
-    const AG_END   = '-- END OF AUTOMATIC GENERATION --';
-
-    /**
-     * @var TableauBord[]
-     */
-    protected $tableauxBords = [];
-
-
-
-    /**
-     * Mettre à jour toutes les infos dans la BDD
-     *
-     * @return self
-     */
-    public function updateProcedures()
-    {
-        $this->tableauxBords = $this->getServiceSchema()->getTableauBords();
-
-        foreach ($this->tableauxBords as $i => $tbl) {
-            if ($tbl->getCustomCalculProc()) {
-                unset($this->tableauxBords[$i]);
-            }
-        }
-
-        $decl  = $this->makePackageDeclaration();
-        $body  = $this->makePackageBody();
-        $procs = $this->makeProcBodies();
-
-        $this->getEntityManager()->getConnection()->exec($decl);
-        $this->getEntityManager()->getConnection()->exec($body);
-
-        return $this;
-    }
-
-
-
-    /**
-     * Retourne le code source du package de gestion des tableaux de bord
-     *
-     * @return string
-     */
-    protected function getPackageDeclaration()
-    {
-        $sql    = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '" . $this->getOptionsModule()->getPackage() . "' AND type = 'PACKAGE'";
-        $result = $this->query($sql, [], 'TEXT');
-
-        return implode("", $result);
-    }
-
-
-
-    /**
-     * Retourne le code source du package de gestion des tableaux de bord
-     *
-     * @return string
-     */
-    protected function getPackageBody()
-    {
-        $sql    = "SELECT TEXT FROM USER_SOURCE WHERE NAME = '" . $this->getOptionsModule()->getPackage() . "' AND type = 'PACKAGE BODY'";
-        $result = $this->query($sql, [], 'TEXT');
-
-        return implode("", $result);
-    }
-
-
-
-    /**
-     * @param string $viewName
-     *
-     * @return string
-     */
-    protected function getViewDefinition($viewName)
-    {
-        $sql    = "SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = '" . $viewName . "'";
-        $result = $this->query($sql, [], 'TEXT');
-
-        $definition = $result[0];
-
-        $definition = trim($definition);
-
-        return $definition;
-    }
-
-
-
-    /**
-     * Construit toutes les déclarations de procédures
-     *
-     * @return array
-     */
-    protected function makeProcDeclarations()
-    {
-        $result = [];
-        foreach ($this->tableauxBords as $tbl) {
-            $result[$tbl->getName()] = $this->makeProcDeclaration($tbl);
-        }
-
-        return $result;
-    }
-
-
-
-    /**
-     * Construit tous les corps de procédures
-     *
-     * @return array
-     */
-    protected function makeProcBodies()
-    {
-        $result = [];
-        foreach ($this->tableauxBords as $tbl) {
-            $result[$tbl->getName()] = $this->makeProcBody($tbl);
-        }
-
-        return $result;
-    }
-
-
-
-    /**
-     * Constuit la nouvelle déclaration du package TBL
-     *
-     * @return string
-     */
-    protected function makePackageDeclaration()
-    {
-        $src  = $this->getPackageDeclaration();
-        $decl = implode("\n", $this->makeProcDeclarations());
-
-        return $this->updatePackageContent($src, $decl);
-    }
-
-
-
-    /**
-     * Constuit la nouvelle déclaration du package TBL
-     *
-     * @return string
-     */
-    protected function makePackageBody()
-    {
-        $src  = $this->getPackageBody();
-        $decl = implode("\n\n\n\n", $this->makeProcBodies());
-
-        return $this->updatePackageContent($src, $decl);
-    }
-
-
-
-    /**
-     * Mise à jour du contenu d'un package (déclaration ou corps)
-     *
-     * @param string $packageSource
-     * @param string $newContent
-     *
-     * @return string
-     */
-    protected function updatePackageContent($packageSource, $newContent)
-    {
-        $src = $packageSource;
-        if (null === $begin = strpos($packageSource, self::AG_BEGIN)) {
-            throw new Exception('Le tag indiquant le début de la zone automatique du package n\'a pas été trouvée');
-        }
-
-        if (null === $end = strpos($packageSource, self::AG_END)) {
-            throw new Exception('Le tag indiquant la fin de la zone automatique du package n\'a pas été trouvée');
-        }
-
-        $src = 'CREATE OR REPLACE '
-            . substr($packageSource, 0, $begin + strlen(self::AG_BEGIN))
-            . "\n\n" . $newContent . "\n\n  "
-            . substr($packageSource, $end);
-
-        return $src;
-    }
-
-
-
-    /**
-     * Génère une déclaration de procédure pour une table donnée
-     *
-     * @param TableauBord $tbl
-     *
-     * @return string
-     */
-    protected function makeProcDeclaration(TableauBord $tbl)
-    {
-        return "  PROCEDURE C_" . strtoupper($tbl->getName()) . "(useParams BOOLEAN DEFAULT FALSE);";
-    }
-
-
-
-    /**
-     * Génère un corps de procédure pour une table donnée
-     *
-     * @param TableauBord $tbl
-     *
-     * @return string
-     */
-    protected function makeProcBody(TableauBord $tbl)
-    {
-        $tableauBord = strtoupper($tbl->getName());
-        $table       = strtoupper($tbl->getTableName(true));
-        $view        = strtoupper($tbl->getViewName(true));
-        $sequence    = strtoupper($tbl->getSequenceName(true));
-
-        if (empty($tbl->getKeyColumns())) {
-            throw new \Exception('Le tableau de bord ' . $tbl->getName() . ' n\'a pas de contrainte d\'unicité.');
-        }
-
-        $maxKeyColLen = 0;
-        foreach ($tbl->getKeyColumns() as $column) {
-            if (strlen($column) > $maxKeyColLen) {
-                $maxKeyColLen = strlen($column);
-            }
-        }
-
-        $maxColLen = 0;
-        foreach ($tbl->getColumns() as $column) {
-            if (strlen($column) > $maxColLen) {
-                $maxColLen = strlen($column);
-            }
-        }
-
-        $join     = [];
-        $testNull = [];
-        foreach ($tbl->getKeyColumns() as $column) {
-            $colLen   = strlen($column);
-            $colAjust = str_pad('', $maxColLen - $colLen);
-
-            if ($column->isNullable()) {
-                $join[] = "COALESCE(t.$column,0)$colAjust = COALESCE(v.$column,0)\n";
-            } else {
-                $join[] = "t.$column$colAjust             = v.$column\n";
-            }
-            $testNull[] = "d.$column IS NULL\n";
-        }
-        $join     = trim(implode("        AND ", $join));
-        $testNull = trim(implode("        AND ", $testNull));
-
-        $testDiff = [];
-        $cols     = [];
-        foreach ($tbl->getColumns() as $column) {
-            $colLen   = strlen($column);
-            $colAjust = str_pad('', $maxColLen - $colLen);
-
-            if ($column->isInView()) {
-                if ($column->isNullable()) {
-                    $testDiff[] = "COALESCE(t.$column,0)$colAjust = COALESCE(v.$column,0)\n";
-                } else {
-                    $testDiff[] = "t.$column$colAjust             = v.$column\n";
-                }
-            }
-            if ($column == 'ID') {
-                $cols[] = 'ID';
-            } else {
-                $cols[] = "v.$column";
-            }
-        }
-        $testDiff = trim(implode("        AND ", $testDiff));
-        foreach ($cols as $c => $col) {
-            if ('ID' == $col) {
-                $cols[$c] = "CASE WHEN 
-            $testDiff
-      THEN -1 ELSE t.ID END ID";
-            }
-        }
-        $cols = trim(implode(",\n      ", $cols));
-
-        $view = $this->getViewDefinition($view);
-        $view = str_replace('\'', '\'\'', $view);
-        $view = str_replace("\n", "\n        ", $view);
-
-        $sql = "  PROCEDURE C_$tableauBord(useParams BOOLEAN DEFAULT FALSE) IS
-  TYPE r_cursor IS REF CURSOR;
-  c r_cursor;
-  d $table%rowtype;
-  viewQuery CLOB;
-  BEGIN
-    viewQuery := '$view';
-    
-    OPEN c FOR '
-    SELECT
-      $cols
-    FROM
-      (' || QUERY_APPLY_PARAMS(viewQuery, useParams) || ') v
-      FULL JOIN $table t ON 
-            $join
-    WHERE ' || PARAMS_MAKE_FILTER(useParams);
-    LOOP
-      FETCH c INTO d; EXIT WHEN c%NOTFOUND;
-
-      IF d.id IS NULL THEN
-        d.id := $sequence.NEXTVAL;
-        INSERT INTO $table values d;
-      ELSIF 
-            $testNull
-      THEN
-        DELETE FROM $table WHERE id = d.id;
-      ELSIF d.id <> -1 THEN
-        UPDATE $table SET row = d WHERE id = d.id;
-      END IF;
-    END LOOP;
-    CLOSE c;
-  END;
-";
-
-        return $sql;
-    }
-}
\ No newline at end of file
diff --git a/src/Service/SchemaService.php b/src/Service/SchemaService.php
deleted file mode 100755
index 395c7362b00a526105c97f0860b253ad2cc033ea..0000000000000000000000000000000000000000
--- a/src/Service/SchemaService.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service;
-
-use UnicaenTbl\Entity\Column;
-use UnicaenTbl\Entity\TableauBord;
-
-
-/**
- * Description of SchemaService
- *
- * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
- */
-class SchemaService extends AbstractService
-{
-
-    protected function getTableauxBordsData()
-    {
-        $data        = [];
-        $tables      = [];
-        $views       = [];
-        $constraints = [];
-
-        $sql = "SELECT * FROM tbl ORDER BY tbl_name";
-        /* Récupération des données de tableau de bord */
-        $d = $this->query($sql, []);
-        foreach ($d as $t) {
-            $name           = $t['TBL_NAME'];
-            $tableName      = $t['TABLE_NAME'];
-            $viewName       = $t['VIEW_NAME'];
-            $constraintName = $t['CONSTRAINT_NAME'];
-            $data[$name]    = [
-                'name'               => $name,
-                'table-name'         => $tableName,
-                'view-name'          => $viewName,
-                'constraint-name'    => $constraintName,
-                'sequence-name'      => $t['SEQUENCE_NAME'],
-                'custom-calcul-proc' => $t['CUSTOM_CALCUL_PROC'],
-                'columns'            => [],
-            ];
-
-
-            if ($tableName) {
-                $tables[$tableName] = $name;
-            }
-
-            if ($viewName) {
-                $views[$viewName] = $name;
-            }
-
-            if ($constraintName) {
-                $constraints[$constraintName] = $name;
-            }
-        }
-
-        /* Récupération des colonnes des tableaux de bord */
-        if ($tables) {
-            $sql = "SELECT TABLE_NAME, COLUMN_NAME, NULLABLE FROM USER_TAB_COLS WHERE "
-                . "TABLE_NAME IN ('" . implode("','", array_keys($tables)) . "') AND hidden_column = 'NO' ORDER BY COLUMN_ID";
-
-            $d = $this->query($sql, []);
-
-            foreach ($d as $c) {
-                $tableName  = $c['TABLE_NAME'];
-                $columnName = $c['COLUMN_NAME'];
-                $nullable   = $c['NULLABLE'] == 'Y';
-                $tblName    = $tables[$tableName];
-
-                $data[$tblName]['columns'][$columnName] = [
-                    'name'          => $columnName,
-                    'nullable'      => $nullable,
-                    'in-table'      => true,
-                    'in-view'       => false,
-                    'in-constraint' => false,
-                ];
-            }
-        }
-
-        if ($views) {
-            $sql = "SELECT TABLE_NAME, COLUMN_NAME FROM USER_TAB_COLS WHERE "
-                . "TABLE_NAME IN ('" . implode("','", array_keys($views)) . "')";
-
-            $d = $this->query($sql, []);
-
-            foreach ($d as $c) {
-                $viewName   = $c['TABLE_NAME'];
-                $columnName = $c['COLUMN_NAME'];
-
-                $tblName = $views[$viewName];
-
-                if (isset($data[$tblName]['columns'][$columnName])) {
-                    $data[$tblName]['columns'][$columnName]['in-view'] = true;
-                }
-            }
-        }
-
-        /* Récupération des colonnes liées aux contraintes */
-        if ($constraints) {
-            $sql      = "SELECT TABLE_NAME, COLUMN_NAME FROM USER_CONS_COLUMNS WHERE CONSTRAINT_NAME IN ('"
-                . implode('\',\'', array_keys($constraints)) . "')";
-            $d        = $this->query($sql, []);
-            $consCols = [];
-            foreach ($d as $c) {
-                $tableName                                                          = $c['TABLE_NAME'];
-                $columnName                                                         = $c['COLUMN_NAME'];
-                $data[$tables[$tableName]]['columns'][$columnName]['in-constraint'] = true;
-            }
-        }
-
-        return $data;
-    }
-
-
-
-    /**
-     * @return TableauBord[]
-     */
-    public function getTableauBords()
-    {
-        /* @var $tbls TableauBord[] */
-        $tbls = [];
-        $data = $this->getTableauxBordsData();
-
-        foreach ($data as $tbl) {
-            $t = new TableauBord();
-            $t->setName($tbl['name']);
-            $t->setTableName($tbl['table-name']);
-            $t->setViewName($tbl['view-name']);
-            $t->setConstraintName($tbl['constraint-name']);
-            $t->setSequenceName($tbl['sequence-name']);
-            $t->setCustomCalculProc($tbl['custom-calcul-proc']);
-
-            foreach ($tbl['columns'] as $col) {
-                if ($col['in-table']) {
-                    $c = new Column();
-                    $c->setName($col['name']);
-                    $c->setNullable($col['nullable']);
-                    $c->setKey($col['in-constraint']);
-                    $c->setInView($col['in-view']);
-                    $t->addColumn($c);
-                }
-            }
-            $tbls[$t->getName()] = $t;
-        }
-
-        return $tbls;
-    }
-
-}
\ No newline at end of file
diff --git a/src/Service/TableauBordService.php b/src/Service/TableauBordService.php
index ab2b3c1c94170398856ac4e24853057437be73d3..25faa4f8f631b1f6059af0ee8eb1e21f6e483406 100644
--- a/src/Service/TableauBordService.php
+++ b/src/Service/TableauBordService.php
@@ -2,148 +2,60 @@
 
 namespace UnicaenTbl\Service;
 
-use UnicaenTbl\Entity\TableauBord;
-use Laminas\ServiceManager\ServiceLocatorAwareInterface;
-use Laminas\ServiceManager\ServiceLocatorAwareTrait;
+use UnicaenTbl\TableauBord;
 
 /**
  * Description of TableauBordService
  *
  * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
  */
-class TableauBordService extends AbstractService
+class TableauBordService
 {
-
-    /**
-     * @return $this
-     */
-    public function activerTriggers()
-    {
-        $this->exec("BEGIN UNICAEN_TBL.ACTIV_TRIGGERS := TRUE; END;");
-
-        return $this;
-    }
+    protected array $tableauxBord = [];
 
 
 
     /**
-     * @return $this
+     * @return array|TableauBord[]
      */
-    public function desactiverTriggers()
+    public function getTableauxBord(): array
     {
-        $this->exec("BEGIN UNICAEN_TBL.ACTIV_TRIGGERS := FALSE; END;");
-
-        return $this;
+        return $this->tableauxBord;
     }
 
 
 
-    /**
-     * @return $this
-     */
-    public function activerCalculs()
+    public function hasTableauBord(string $name): bool
     {
-        $this->exec("BEGIN UNICAEN_TBL.ACTIV_CALCULS := TRUE; END;");
-
-        return $this;
+        return array_key_exists($name, $this->tableauxBord);
     }
 
 
-
-    /**
-     * @return $this
-     */
-    public function desactiverCalculs()
+    public function getTableauBord(string $name): TableauBord
     {
-        $this->exec("BEGIN UNICAEN_TBL.ACTIV_CALCULS := FALSE; END;");
-
-        return $this;
+        return $this->tableauxBord[$name];
     }
 
 
 
-    /**
-     * @param string|TableauBord $tableauBord
-     * @param array|string|null  $param
-     * @param string|null        $value
-     *
-     * @return $this
-     */
-    public function calculer($tableauBord, $param = null, ?string $value = null)
+    public function setTableauxBord(array $tableauxBord): TableauBordService
     {
-        if (is_array($param)) {
-            $this->execArrayFunction('CALCULER', $tableauBord, $param);
-        } else {
-            $this->execFunction('CALCULER', $tableauBord, $param, $value);
-        }
-
+        uasort($tableauxBord, function($a,$b){
+           return $a->getOrder() - $b->getOrder();
+        });
+        $this->tableauxBord = $tableauxBord;
         return $this;
     }
 
 
 
-    /**
-     * @param string|TableauBord $tableauBord
-     * @param array|string       $params
-     *
-     * @return $this
-     */
-    public function demandeCalcul($tableauBord, ?string $param = null, ?string $value = null)
+    public function calculer(string|TableauBord $tableauBord, array $params = []): self
     {
-        return $this->execFunction('DEMANDE_CALCUL', $tableauBord, $param, $value);
-    }
-
-
-
-    /**
-     * @param string|TableauBord $tableauBord
-     *
-     * @return $this
-     */
-    public function annulerDemandes($tableauBord = null)
-    {
-        return $this->execFunction('ANNULER_DEMANDES', $tableauBord);
-    }
-
-
-
-    /**
-     *
-     * @return $this
-     */
-    public function calculerDemandes()
-    {
-        /* Construction et exécution de la requête, puis retour */
-        $sql = "BEGIN UNICAEN_TBL.CALCULER_DEMANDES; END;";
-        $this->exec($sql);
-
-        return $this;
-    }
-
-
-
-    /**
-     * @param string             $funcName
-     * @param string|TableauBord $tableauBord
-     * @param array|string       $params
-     *
-     * @return $this
-     */
-    private function execFunction($funcName, $tableauBord, ?string $param = null, ?string $value = null)
-    {
-        /* Formatage du nom du tableau de bord */
-        if ($tableauBord instanceof TableauBord) {
-            $tableauBord = $tableauBord->getName();
+        if (is_string($tableauBord)){
+            $tableauBord = $this->getTableauBord($tableauBord);
         }
-        $sqltn = self::escape($tableauBord);
 
-        /* Formatage du passage des paramètres */
-        if ($param) {
-            $sql = 'BEGIN UNICAEN_TBL.' . $funcName . '(' . $sqltn . ', ' . self::escape($param) . ', ' . self::escape($value) . '); END;';
-        } else {
-            $sql = 'BEGIN UNICAEN_TBL.' . $funcName . '(' . $sqltn . '); END;';
-        }
-        $this->exec($sql);
+        $tableauBord->calculer($params);
 
         return $this;
     }
@@ -151,35 +63,48 @@ class TableauBordService extends AbstractService
 
 
     /**
-     * @param string             $funcName
-     * @param string|TableauBord $tableauBord
-     * @param array              $params
-     *
-     * @return $this
+     * @return int
+     * @throws \Doctrine\DBAL\DBALException
      */
-    private function execArrayFunction($funcName, $tableauBord, array $params)
+    public function calculerTout(array $excludes = [], $beforeTrigger = null, $afterTrigger = null)
     {
-        /* Formatage du nom du tableau de bord */
-        if ($tableauBord instanceof TableauBord) {
-            $tableauBord = $tableauBord->getName();
+        $tableauxBord = $this->getTableauxBord();
+
+        $result = true;
+        foreach ($tableauxBord as $tableauBord) {
+            if (in_array($tableauBord->getName(), $excludes)){
+                continue;
+            }
+            $begin = microtime(true);
+
+
+            if ($beforeTrigger instanceof \Closure) {
+                $beforeTrigger([
+                    'tableau-bord' => $tableauBord->getName(),
+                ]);
+            }
+            try {
+                $tableauBord->calculer([]);
+                if ($afterTrigger instanceof \Closure) {
+                    $afterTrigger([
+                        'tableau-bord' => $tableauBord->getName(),
+                        'result'       => true,
+                        'duree'        => microtime(true) - $begin,
+                    ]);
+                }
+            } catch (\Exception $e) {
+                if ($afterTrigger instanceof \Closure) {
+                    $afterTrigger([
+                        'tableau-bord' => $tableauBord->getName(),
+                        'result'       => false,
+                        'exception'    => $e,
+                        'duree'        => microtime(true) - $begin,
+                    ]);
+                }
+                $result = false;
+            }
         }
-        $sqltn = self::escape($tableauBord);
-
-        /* Construction et exécution de la requête, puis retour */
-        $sql = "DECLARE\n";
-        $sql .= "  params UNICAEN_TBL.T_PARAMS;\n";
-        $sql .= "BEGIN\n";
-        $i   = 1;
-        foreach ($params as $p => $v) {
-            $sql .= "  params.p$i := " . self::escape($p) . ";\n";
-            $sql .= "  params.v$i := " . self::escape($v) . ";\n";
-            $i++;
-        }
-        $sql .= "  UNICAEN_TBL.$funcName($sqltn, params);\n";
-        $sql .= "END;";
-        $this->exec($sql);
 
-        return $this;
+        return $result;
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/Service/Traits/TableauBordServiceAwareTrait.php b/src/Service/TableauBordServiceAwareTrait.php
similarity index 86%
rename from src/Service/Traits/TableauBordServiceAwareTrait.php
rename to src/Service/TableauBordServiceAwareTrait.php
index 91e1fc68cb4c91067234b073db9c4274cfdaf54a..5bc3eda6f175faf19c8313b55bc03ecdde97be52 100644
--- a/src/Service/Traits/TableauBordServiceAwareTrait.php
+++ b/src/Service/TableauBordServiceAwareTrait.php
@@ -1,9 +1,7 @@
 <?php
 
-namespace UnicaenTbl\Service\Traits;
+namespace UnicaenTbl\Service;
 
-use UnicaenTbl\Service\TableauBordService;
-use Application\Module;
 use RuntimeException;
 
 /**
diff --git a/src/Service/Factory/TableauBordServiceFactory.php b/src/Service/TableauBordServiceFactory.php
similarity index 68%
rename from src/Service/Factory/TableauBordServiceFactory.php
rename to src/Service/TableauBordServiceFactory.php
index a32cb902e37a9be982db421c9359ab1aa66fdb5d..3a82d0a66702f81c4d8662d37aa99a40fcf01c14 100644
--- a/src/Service/Factory/TableauBordServiceFactory.php
+++ b/src/Service/TableauBordServiceFactory.php
@@ -1,10 +1,9 @@
 <?php
 
-namespace UnicaenTbl\Service\Factory;
+namespace UnicaenTbl\Service;
 
 use Psr\Container\ContainerInterface;
 use UnicaenTbl\Options\ModuleOptions;
-use UnicaenTbl\Service\TableauBordService;
 
 
 /**
@@ -21,10 +20,7 @@ class TableauBordServiceFactory
         /** @var ModuleOptions $options */
         $options = $container->get(ModuleOptions::class);
 
-        $em = $container->get($options->getEntityManagerName());
-        /* @var $em \Doctrine\ORM\EntityManager */
-
-        $tbl->setEntityManager($em);
+        $tbl->setTableauxBord($options->loadTableauxBord($container));
 
         return $tbl;
     }
diff --git a/src/Service/Traits/QueryGeneratorServiceAwareTrait.php b/src/Service/Traits/QueryGeneratorServiceAwareTrait.php
deleted file mode 100755
index a9b35e45f4b0ef101355da71b49fef250a7d291a..0000000000000000000000000000000000000000
--- a/src/Service/Traits/QueryGeneratorServiceAwareTrait.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service\Traits;
-
-use UnicaenTbl\Service\QueryGeneratorService;
-use Application\Module;
-use RuntimeException;
-
-/**
- * Description of QueryGeneratorServiceAwareTrait
- *
- * @author UnicaenCode
- */
-trait QueryGeneratorServiceAwareTrait
-{
-    /**
-     * @var QueryGeneratorService
-     */
-    private $serviceQueryGenerator;
-
-
-
-    /**
-     * @param QueryGeneratorService $serviceQueryGenerator
-     *
-     * @return self
-     */
-    public function setServiceQueryGenerator(QueryGeneratorService $serviceQueryGenerator)
-    {
-        $this->serviceQueryGenerator = $serviceQueryGenerator;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return QueryGeneratorService
-     * @throws RuntimeException
-     */
-    public function getServiceQueryGenerator()
-    {
-        return $this->serviceQueryGenerator;
-    }
-}
\ No newline at end of file
diff --git a/src/Service/Traits/SchemaServiceAwareTrait.php b/src/Service/Traits/SchemaServiceAwareTrait.php
deleted file mode 100755
index 5fdada0018d4ef2412f16aad218f14fbb49e253a..0000000000000000000000000000000000000000
--- a/src/Service/Traits/SchemaServiceAwareTrait.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-namespace UnicaenTbl\Service\Traits;
-
-use UnicaenTbl\Service\SchemaService;
-use Application\Module;
-use RuntimeException;
-
-/**
- * Description of SchemaServiceAwareTrait
- *
- * @author UnicaenCode
- */
-trait SchemaServiceAwareTrait
-{
-    /**
-     * @var SchemaService
-     */
-    private $serviceSchema;
-
-
-
-    /**
-     * @param SchemaService $serviceSchema
-     *
-     * @return self
-     */
-    public function setServiceSchema(SchemaService $serviceSchema)
-    {
-        $this->serviceSchema = $serviceSchema;
-
-        return $this;
-    }
-
-
-
-    /**
-     * @return SchemaService
-     * @throws RuntimeException
-     */
-    public function getServiceSchema()
-    {
-        return $this->serviceSchema;
-    }
-}
\ No newline at end of file
diff --git a/src/TableauBord.php b/src/TableauBord.php
new file mode 100644
index 0000000000000000000000000000000000000000..d89fe73740a97a1754e6fb68341b3ac7f93e5387
--- /dev/null
+++ b/src/TableauBord.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace UnicaenTbl;
+
+use Psr\Container\ContainerInterface;
+use UnicaenTbl\Process\ProcessInterface;
+
+/**
+ * Description of TableauBord
+ *
+ * @author LECLUSE Laurent <laurent.lecluse at unicaen.fr>
+ */
+class TableauBord
+{
+    protected ?string $name = null;
+
+    protected int $order = 0;
+
+    protected ?ProcessInterface $process = null;
+
+
+    /** @var string[] */
+    protected array $options = [];
+
+
+
+    public function getName(): ?string
+    {
+        return $this->name;
+    }
+
+
+
+    public function setName(?string $name): TableauBord
+    {
+        $this->name = $name;
+
+        return $this;
+    }
+
+
+
+    public function getOrder(): int
+    {
+        return $this->order;
+    }
+
+
+
+    public function setOrder(int $order): TableauBord
+    {
+        $this->order = $order;
+
+        return $this;
+    }
+
+
+
+    public function getProcess(): ?ProcessInterface
+    {
+        return $this->process;
+    }
+
+
+
+    public function setProcess(?ProcessInterface $process): TableauBord
+    {
+        $this->process = $process;
+
+        return $this;
+    }
+
+
+
+    public function hasOption(string $name)
+    {
+        return array_key_exists($name, $this->options);
+    }
+
+
+
+    public function getOption(string $name, $defaultValue = null, ?string $errorIfNull = null)
+    {
+        $option = $this->options[$name] ?? $defaultValue;
+
+        if (empty($option) && $errorIfNull) {
+            throw new \Exception('Option ' . $name . ' manquante. ' . $errorIfNull);
+        }
+
+        return $option;
+    }
+
+
+
+    public function setOption(string $name, $value)
+    {
+        $this->options[$name] = $value;
+    }
+
+
+
+    public function loadOptions(array $options, ContainerInterface $container): self
+    {
+        if (array_key_exists('name', $options)) {
+            $this->setName($options['name']);
+        }
+
+        if (array_key_exists('order', $options)) {
+            $this->setOrder($options['order']);
+        }
+
+        if (array_key_exists('process', $options)) {
+            $processName = $options['process'] ?? null;
+            if (!$container->has($processName)) {
+                $processName = 'unicaen-tbl.process.' . $processName;
+            }
+            if (!$container->has($processName)) {
+                throw new \Exception('Le process "' . $processName . '" n\'est pas un service reconnu ou accessible via le container');
+            }
+            /** @var ProcessInterface $process */
+            $process = $container->get($processName);
+            $this->setProcess($process);
+        }
+
+        $this->options = $options;
+
+        return $this;
+    }
+
+
+
+    public function calculer(array $params = [])
+    {
+        $this->getProcess()->run($this, $params);
+    }
+
+}
\ No newline at end of file
diff --git a/view/laminas-developer-tools/toolbar/unicaen-tbl.phtml b/view/laminas-developer-tools/toolbar/unicaen-tbl.phtml
deleted file mode 100644
index b1ca63afb0b6bbb7ef4bc324ec71ac1f1b7e8fb1..0000000000000000000000000000000000000000
--- a/view/laminas-developer-tools/toolbar/unicaen-tbl.phtml
+++ /dev/null
@@ -1,18 +0,0 @@
-<div class="laminas-toolbar-entry">
-    <div class="laminas-toolbar-preview">
-        <span class="laminas-toolbar-info">
-               <span class="laminas-toolbar-extra-info">
-                    <img style="padding-bottom: 4px;"
-                         src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACAElEQVR4AX1Sw8KcQRCsD+vYOgaPkH2G2LnHOMa2brFt56XiZBl8GP1Ta9dY1dXd49y6e22tkOqS5zgTHceBMQZsGhy1bYDWdm4bDEcNpRX3vthus2/Xz1csXZnOZrNIp9NokoAV7ZFbILEljWOBYqEw7f2nd699uwYvFX7/RiabQacKopOMc6oRcWzv5qCknOFrJfioq40Cj13XBSGlhB8EoRFSYOLEScjmsiTotD5AgUYYRiiVihCSBGGA9x/eYvac2Zg7bw4mTJiIIAjAACploJUCgyaFguMCP77/wP9//1GtVu2eJaD1tC1KKTx++BTJZJJW+lzhHhFFEfL5xW0XyEz4vo9FCxZix47dPKCf3Gu5EcUClH377g3u06EGge2acDwPR44eAlW1YsD9VgBdpFLJVlykFN0ESsY4feoseMGzZFTBORGEIX7bVD94cLfhHtPZoyCVTuP4yWMQIgafoUcBqCCZQOMIUvUQSKlw4dxF8EdSAaEUs6AR/P+PSqWC6zevdNxvZKEJpuzQkYNwrSXHaUfeoSt2LhhczwWIxvf2mcsmXGv17Olz9JH+t9LXVMAU3rx9HR2gC8KUy2Uw/55lP2yz4HtMk4ZWbQUwdQWzZs8G7xcKJWOFfnHWbVy3LJHwDlnr6YRVYBrfnU+5aMNwqTloJaG0KalY7x0DbYmBlWlA3zgAAAAASUVORK5CYII="/>
-                    UnicaenTbl
-            </span>
-        </span>
-    </div>
-    <div class="laminas-toolbar-detail">
-            <span class="laminas-toolbar-info">
-                <span class="laminas-detail-value">
-                    <a href="<?php echo $this->url('unicaen-tbl/build'); ?>">Construction des tableaux de bord</a>
-                </span>
-            </span>
-    </div>
-</div>
\ No newline at end of file
diff --git a/view/unicaen-tbl/admin/actualisation.phtml b/view/unicaen-tbl/admin/actualisation.phtml
deleted file mode 100644
index 41f07a064960c7e0f30ee6fb40a7bbd42fe68ec1..0000000000000000000000000000000000000000
--- a/view/unicaen-tbl/admin/actualisation.phtml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-/**
- * @var $form ActualisationForm
- */
-
-use UnicaenTbl\Form\ActualisationForm;
-
-echo '<h1 class="page-header">Actualisation de tableau de bord</h1>';
-echo $this->messenger()->addCurrentMessagesFromFlashMessenger();
-echo $this->form()->openTag($form->prepare());
-echo $this->formControlGroup($form->get('param'));
-echo $this->formControlGroup($form->get('value'));
-echo $this->formSubmit($form->get('submit'));
-
-echo $this->form()->closeTag();
\ No newline at end of file
diff --git a/view/unicaen-tbl/admin/index.phtml b/view/unicaen-tbl/admin/index.phtml
deleted file mode 100644
index d33be16b97f38eed10e90fc411e8222ef2b105fe..0000000000000000000000000000000000000000
--- a/view/unicaen-tbl/admin/index.phtml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-use UnicaenTbl\Entity\TableauBord;
-
-/* @var $tbls TableauBord[] */
-
-
-?>
-<style>
-
-    dt  {
-        color: red;
-    }
-
-</style>
-<h1 class="page-header">Tableaux de bord internes de calcul intermédiaire</h1>
-<?php echo $this->messenger()->addMessagesFromFlashMessenger(); ?>
-<?php foreach ($tbls as $tbl): ?>
-    <div class="panel panel-default">
-        <div class="panel-heading">
-            <?php echo $tbl->getName() ?>
-        </div>
-        <div class="panel-body">
-            <div class="col-md-6">
-                <h3>Propriétés</h3>
-                <dl>
-                    <dt>Vue</dt>
-                    <dd><?php echo $tbl->getViewName(true) ?></dd>
-
-                    <dt>Table</dt>
-                    <dd><?php echo $tbl->getTableName(true) ?></dd>
-
-                    <dt>Contrainte</dt>
-                    <dd><?php echo $tbl->getConstraintName(true) ?></dd>
-
-                    <dt>Séquence</dt>
-                    <dd><?php echo $tbl->getSequenceName(true) ?></dd>
-
-                    <dt>Procédure de calcul</dt>
-                    <dd><?php echo $tbl->getCustomCalculProc() ?></dd>
-                </dl>
-            </div>
-            <div class="col-md-6">
-                <h3>Colonnes</h3>
-                <table class="table table-bordered table-condensed">
-                    <tr>
-                        <th>Colonne</th>
-                        <th>Clé</th>
-                        <th>Nullable</th>
-                    </tr>
-                    <?php $cs = $tbl->getColumns();
-                    foreach ($cs as $column): ?>
-                        <tr>
-                            <td style="width:60%"><?php echo $column->getName() ?></td>
-                            <td style="width:20%" class="text-center"><?php if ($column->isKey()): ?><span
-                                        class="glyphicon glyphicon-ok text-success"></span><?php endif; ?></td>
-                            <td style="width:20%" class="text-center"><?php if ($column->isNullable()): ?><span
-                                        class="glyphicon glyphicon-ok text-success"></span><?php endif; ?></td>
-                        </tr>
-                    <?php endforeach; ?>
-                </table>
-                <button type="button" class="btn btn-default pop-ajax" data-url="<?php echo $this->url('unicaen-tbl/actualisation', ['tableauBord' => $tbl->getName()]) ?>">Actualiser le tableau de bord</button>
-            </div>
-        </div>
-
-    </div>
-<?php endforeach; ?>