diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py
index 425ff1923ca2682e8a942d08b188293f9104df5c..84dd2e1ca105fe79fca870ecc932434c9c4e87fc 100644
--- a/app/models/but_refcomp.py
+++ b/app/models/but_refcomp.py
@@ -53,7 +53,9 @@ class XMLModel:
 class ApcReferentielCompetences(db.Model, XMLModel):
     "Référentiel de compétence d'une spécialité"
     id = db.Column(db.Integer, primary_key=True)
-    dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
+    dept_id = db.Column(
+        db.Integer, db.ForeignKey("departement.id", ondelete="CASCADE"), index=True
+    )
     annexe = db.Column(db.Text())  # '1', '22', ...
     specialite = db.Column(db.Text())  # 'CJ', 'RT', 'INFO', ...
     specialite_long = db.Column(
@@ -207,7 +209,9 @@ class ApcCompetence(db.Model, XMLModel):
     "Compétence"
     id = db.Column(db.Integer, primary_key=True)
     referentiel_id = db.Column(
-        db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False
+        db.Integer,
+        db.ForeignKey("apc_referentiel_competences.id", ondelete="CASCADE"),
+        nullable=False,
     )
     # les compétences dans Orébut sont identifiées par leur id unique
     # (mais id_orebut n'est pas unique car le même ref. pourra être chargé dans plusieurs depts)
@@ -276,7 +280,9 @@ class ApcSituationPro(db.Model, XMLModel):
     "Situation professionnelle"
     id = db.Column(db.Integer, primary_key=True)
     competence_id = db.Column(
-        db.Integer, db.ForeignKey("apc_competence.id"), nullable=False
+        db.Integer,
+        db.ForeignKey("apc_competence.id", ondelete="CASCADE"),
+        nullable=False,
     )
     libelle = db.Column(db.Text(), nullable=False)
     # aucun attribut (le text devient le libellé)
@@ -288,7 +294,9 @@ class ApcComposanteEssentielle(db.Model, XMLModel):
     "Composante essentielle"
     id = db.Column(db.Integer, primary_key=True)
     competence_id = db.Column(
-        db.Integer, db.ForeignKey("apc_competence.id"), nullable=False
+        db.Integer,
+        db.ForeignKey("apc_competence.id", ondelete="CASCADE"),
+        nullable=False,
     )
     libelle = db.Column(db.Text(), nullable=False)
 
@@ -306,7 +314,9 @@ class ApcNiveau(db.Model, XMLModel):
 
     id = db.Column(db.Integer, primary_key=True)
     competence_id = db.Column(
-        db.Integer, db.ForeignKey("apc_competence.id"), nullable=False
+        db.Integer,
+        db.ForeignKey("apc_competence.id", ondelete="CASCADE"),
+        nullable=False,
     )
     libelle = db.Column(db.Text(), nullable=False)
     annee = db.Column(db.Text(), nullable=False)  # "BUT1", "BUT2", "BUT3"
@@ -387,7 +397,7 @@ app_critiques_modules = db.Table(
     ),
     db.Column(
         "app_crit_id",
-        db.ForeignKey("apc_app_critique.id"),
+        db.ForeignKey("apc_app_critique.id", ondelete="CASCADE"),
         primary_key=True,
     ),
 )
@@ -396,7 +406,9 @@ app_critiques_modules = db.Table(
 class ApcAppCritique(db.Model, XMLModel):
     "Apprentissage Critique BUT"
     id = db.Column(db.Integer, primary_key=True)
-    niveau_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id"), nullable=False)
+    niveau_id = db.Column(
+        db.Integer, db.ForeignKey("apc_niveau.id", ondelete="CASCADE"), nullable=False
+    )
     code = db.Column(db.Text(), nullable=False, index=True)
     libelle = db.Column(db.Text())
 
@@ -445,7 +457,10 @@ class ApcAppCritique(db.Model, XMLModel):
 parcours_modules = db.Table(
     "parcours_modules",
     db.Column(
-        "parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True
+        "parcours_id",
+        db.Integer,
+        db.ForeignKey("apc_parcours.id", ondelete="CASCADE"),
+        primary_key=True,
     ),
     db.Column(
         "module_id",
@@ -459,7 +474,10 @@ parcours_modules = db.Table(
 parcours_formsemestre = db.Table(
     "parcours_formsemestre",
     db.Column(
-        "parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True
+        "parcours_id",
+        db.Integer,
+        db.ForeignKey("apc_parcours.id", ondelete="CASCADE"),
+        primary_key=True,
     ),
     db.Column(
         "formsemestre_id",
@@ -475,7 +493,9 @@ class ApcParcours(db.Model, XMLModel):
     "Un parcours BUT"
     id = db.Column(db.Integer, primary_key=True)
     referentiel_id = db.Column(
-        db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False
+        db.Integer,
+        db.ForeignKey("apc_referentiel_competences.id", ondelete="CASCADE"),
+        nullable=False,
     )
     numero = db.Column(db.Integer)  # ordre de présentation
     code = db.Column(db.Text(), nullable=False)
@@ -516,7 +536,7 @@ class ApcParcours(db.Model, XMLModel):
 class ApcAnneeParcours(db.Model, XMLModel):
     id = db.Column(db.Integer, primary_key=True)
     parcours_id = db.Column(
-        db.Integer, db.ForeignKey("apc_parcours.id"), nullable=False
+        db.Integer, db.ForeignKey("apc_parcours.id", ondelete="CASCADE"), nullable=False
     )
     ordre = db.Column(db.Integer)
     "numéro de l'année: 1, 2, 3"
diff --git a/app/models/but_validations.py b/app/models/but_validations.py
index ccef89cd4d5d63eb9aad4d5ea8d7d5e62a93283b..9625d1e46918581cf5611c57269a68559ad41195 100644
--- a/app/models/but_validations.py
+++ b/app/models/but_validations.py
@@ -46,7 +46,9 @@ class ApcValidationRCUE(db.Model):
     ue1_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False)
     ue2_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False)
     # optionnel, le parcours dans lequel se trouve la compétence:
-    parcours_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), nullable=True)
+    parcours_id = db.Column(
+        db.Integer, db.ForeignKey("apc_parcours.id", ondelete="set null"), nullable=True
+    )
     date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
     code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True)
 
diff --git a/app/models/formations.py b/app/models/formations.py
index d4aac9ad29273a93c92fd90eeab505b140445c50..8264fa81d3bab31beaa0fcbf9efa3b648cca06c3 100644
--- a/app/models/formations.py
+++ b/app/models/formations.py
@@ -48,7 +48,7 @@ class Formation(db.Model):
 
     # Optionnel, pour les formations type BUT
     referentiel_competence_id = db.Column(
-        db.Integer, db.ForeignKey("apc_referentiel_competences.id")
+        db.Integer, db.ForeignKey("apc_referentiel_competences.id", ondelete="SET NULL")
     )
     ues = db.relationship("UniteEns", backref="formation", lazy="dynamic")
     formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index bafad116e7f1810a58df2cc623800ab078802bcc..f58a38b7215db8d3664f035180b49d39b47655b7 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -1037,7 +1037,9 @@ class FormSemestreInscription(db.Model):
     # Etape Apogée d'inscription (ajout 2020)
     etape = db.Column(db.String(APO_CODE_STR_LEN))
     # Parcours (pour les BUT)
-    parcour_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), index=True)
+    parcour_id = db.Column(
+        db.Integer, db.ForeignKey("apc_parcours.id", ondelete="SET NULL"), index=True
+    )
     parcour = db.relationship(ApcParcours)
 
     def __repr__(self):
diff --git a/app/models/ues.py b/app/models/ues.py
index 596e0bef6e378775670ae4193df0e8373a8f2bba..faa5df208acd9a69d1da02319f61c7efbeffe559 100644
--- a/app/models/ues.py
+++ b/app/models/ues.py
@@ -51,11 +51,15 @@ class UniteEns(db.Model):
     color = db.Column(db.Text())
 
     # BUT
-    niveau_competence_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id"))
+    niveau_competence_id = db.Column(
+        db.Integer, db.ForeignKey("apc_niveau.id", ondelete="SET NULL")
+    )
     niveau_competence = db.relationship("ApcNiveau", back_populates="ues")
 
     # Une ue appartient soit à tous les parcours (tronc commun), soit à un seul:
-    parcour_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), index=True)
+    parcour_id = db.Column(
+        db.Integer, db.ForeignKey("apc_parcours.id", ondelete="SET NULL"), index=True
+    )
     parcour = db.relationship("ApcParcours", back_populates="ues")
 
     # relations
diff --git a/app/models/validations.py b/app/models/validations.py
index f0ec9749ef994c5a257258688de9ca5f17ef7f66..20a4bb5871ebc8c041355a198ba5b8e20636ea64 100644
--- a/app/models/validations.py
+++ b/app/models/validations.py
@@ -160,11 +160,11 @@ class ScolarEvent(db.Model):
     event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
     formsemestre_id = db.Column(
         db.Integer,
-        db.ForeignKey("notes_formsemestre.id"),
+        db.ForeignKey("notes_formsemestre.id", ondelete="SET NULL"),
     )
     ue_id = db.Column(
         db.Integer,
-        db.ForeignKey("notes_ue.id"),
+        db.ForeignKey("notes_ue.id", ondelete="SET NULL"),
     )
     # 'CREATION', 'INSCRIPTION', 'DEMISSION',
     # 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py
index 6fdf92e4ae99b216a382ab47e421b40b32c19bfd..110a764ccbb42c40e44dc020ef93e816c73c9961 100644
--- a/app/scodoc/sco_dept.py
+++ b/app/scodoc/sco_dept.py
@@ -317,7 +317,7 @@ def _style_sems(sems):
         ] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """
 
 
-def delete_dept(dept_id: int):
+def delete_dept(dept_id: int) -> str:
     """Suppression irréversible d'un département et de tous les objets rattachés"""
     assert isinstance(dept_id, int)
 
@@ -361,6 +361,7 @@ def delete_dept(dept_id: int):
             )
 
         reqs = [
+            "delete from apc_referentiel_competences where dept_id = %(dept_id)s",
             "delete from identite where dept_id = %(dept_id)s",
             "delete from sco_prefs where dept_id = %(dept_id)s",
             "delete from notes_semset_formsemestre where formsemestre_id in (select id from formsemestres_temp)",
@@ -392,8 +393,10 @@ def delete_dept(dept_id: int):
         ]
         for r in reqs:
             cursor.execute(r, {"dept_id": dept_id})
-    except:
+    except Exception as e:
         cnx.rollback()
+        return str(e)
     finally:
         cnx.commit()
         app.clear_scodoc_cache()
+    return ""
diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py
index 80a0b9c44a2d27ea95658f51a1f3ebeed049fb89..ad038ffe56097be6aeaa02c0867a2952bd61a0d7 100644
--- a/app/scodoc/sco_dump_db.py
+++ b/app/scodoc/sco_dump_db.py
@@ -28,8 +28,8 @@
 """Dump base de données pour debug et support technique
 
 Le principe est le suivant:
- 1- S'il existe une base en cours d'anonymisation, s'arrête et affiche un msg d'erreur l'utilisateur,
-    qui peut décider de la supprimer.
+ 1- S'il existe une base en cours d'anonymisation, s'arrête et affiche un msg
+ d'erreur à l'utilisateur, qui peut décider de la supprimer.
 
  2- ScoDoc lance un script qui duplique la base (la copie de SCORT devient ANORT)
      -  (si elle existe deja, s'arrête)
@@ -49,9 +49,9 @@ pg_dump SCORT | psql ANORT
 import base64
 import fcntl
 import os
-import requests
 import subprocess
-import traceback
+
+import requests
 
 from flask import g, request
 from flask_login import current_user
@@ -59,9 +59,8 @@ from flask_login import current_user
 import app.scodoc.notesdb as ndb
 import app.scodoc.sco_utils as scu
 from app import log
-from app.scodoc import sco_users
-import sco_version
 from app.scodoc.sco_exceptions import ScoValueError
+import sco_version
 
 SCO_DUMP_LOCK = "/tmp/scodump.lock"
 
@@ -81,12 +80,10 @@ def sco_dump_and_send_db(
     try:
         x = open(SCO_DUMP_LOCK, "w+")
         fcntl.flock(x, fcntl.LOCK_EX | fcntl.LOCK_NB)
-    except (IOError, OSError):
+    except (IOError, OSError) as e:
         raise ScoValueError(
-            "Un envoi de la base "
-            + db_name
-            + " est déjà en cours, re-essayer plus tard"
-        )
+            "Un envoi de la base {db_name} est déjà en cours, re-essayer plus tard"
+        ) from e
 
     try:
         # Drop if exists
@@ -96,7 +93,7 @@ def sco_dump_and_send_db(
         _duplicate_db(db_name, ano_db_name)
 
         # Anonymisation
-        _anonymize_db(ano_db_name)
+        anonymize_db(ano_db_name)
 
         # Send
         r = _send_db(ano_db_name, message, request_url, traceback_str=traceback_str)
@@ -116,44 +113,44 @@ def sco_dump_and_send_db(
 def _duplicate_db(db_name, ano_db_name):
     """Create new database, and copy old one into"""
     cmd = ["createdb", "-E", "UTF-8", ano_db_name]
-    log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd))
+    log(f"sco_dump_and_send_db/_duplicate_db: {cmd}")
     try:
         _ = subprocess.check_output(cmd)
     except subprocess.CalledProcessError as e:
-        log("sco_dump_and_send_db: exception createdb {}".format(e))
+        log(f"sco_dump_and_send_db: exception createdb {e}")
         raise ScoValueError(
-            "erreur lors de la creation de la base {}".format(ano_db_name)
-        )
+            f"erreur lors de la creation de la base {ano_db_name}"
+        ) from e
 
-    cmd = "pg_dump {} | psql {}".format(db_name, ano_db_name)
+    cmd = f"pg_dump {db_name} | psql {ano_db_name}"
     log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd))
     try:
         _ = subprocess.check_output(cmd, shell=1)
     except subprocess.CalledProcessError as e:
         log("sco_dump_and_send_db: exception {}".format(e))
         raise ScoValueError(
-            "erreur lors de la duplication de la base {} vers {}".format(
-                db_name, ano_db_name
-            )
-        )
+            f"erreur lors de la duplication de la base {db_name} vers {ano_db_name}"
+        ) from e
 
 
-def _anonymize_db(ano_db_name):
-    """Anonymize a departement database"""
+def anonymize_db(ano_db_name):
+    """Anonymize a ScoDoc database"""
     cmd = os.path.join(scu.SCO_TOOLS_DIR, "anonymize_db.py")
-    log("_anonymize_db: {}".format(cmd))
+    log(f"anonymize_db: {cmd}")
     try:
         _ = subprocess.check_output([cmd, ano_db_name])
     except subprocess.CalledProcessError as e:
-        log("sco_dump_and_send_db: exception in anonymisation: {}".format(e))
+        log(f"sco_dump_and_send_db: exception in anonymisation: {e}")
         raise ScoValueError(
-            "erreur lors de l'anonymisation de la base {}".format(ano_db_name)
-        )
+            f"erreur lors de l'anonymisation de la base {ano_db_name}"
+        ) from e
 
 
 def _get_scodoc_serial():
     try:
-        with open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")) as f:
+        with open(
+            os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn"), encoding=scu.SCO_ENCODING
+        ) as f:
             return int(f.read())
     except:
         return 0
diff --git a/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py b/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e8e15ff51650fd4456b4b7e6ec2b7f9f5aafa90
--- /dev/null
+++ b/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py
@@ -0,0 +1,415 @@
+"""cascades ref. comp.
+
+Revision ID: d8288b7f0a3e
+Revises: 5c7b208355df
+Create Date: 2023-02-09 11:25:28.879434
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "d8288b7f0a3e"
+down_revision = "5c7b208355df"
+branch_labels = ""
+depends_on = ""
+
+
+def upgrade():
+    # EVENTS
+    op.drop_constraint(
+        "scolar_events_formsemestre_id_fkey", "scolar_events", type_="foreignkey"
+    )
+    op.drop_constraint("scolar_events_ue_id_fkey", "scolar_events", type_="foreignkey")
+    op.create_foreign_key(
+        "scolar_events_ue_id_fkey",
+        "scolar_events",
+        "notes_ue",
+        ["ue_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    op.create_foreign_key(
+        "scolar_events_formsemestre_id_fkey",
+        "scolar_events",
+        "notes_formsemestre",
+        ["formsemestre_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    # REF COMP
+    op.drop_constraint(
+        "apc_annee_parcours_parcours_id_fkey", "apc_annee_parcours", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_annee_parcours_parcours_id_fkey",
+        "apc_annee_parcours",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_app_critique_niveau_id_fkey", "apc_app_critique", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_app_critique_niveau_id_fkey",
+        "apc_app_critique",
+        "apc_niveau",
+        ["niveau_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_competence_referentiel_id_fkey", "apc_competence", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_competence_referentiel_id_fkey",
+        "apc_competence",
+        "apc_referentiel_competences",
+        ["referentiel_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_composante_essentielle_competence_id_fkey",
+        "apc_composante_essentielle",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_composante_essentielle_competence_id_fkey",
+        "apc_composante_essentielle",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_modules_acs_app_crit_id_fkey", "apc_modules_acs", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_modules_acs_app_crit_id_fkey",
+        "apc_modules_acs",
+        "apc_app_critique",
+        ["app_crit_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_niveau_competence_id_fkey", "apc_niveau", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_niveau_competence_id_fkey",
+        "apc_niveau",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_parcours_referentiel_id_fkey", "apc_parcours", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_parcours_referentiel_id_fkey",
+        "apc_parcours",
+        "apc_referentiel_competences",
+        ["referentiel_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_referentiel_competences_dept_id_fkey",
+        "apc_referentiel_competences",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_referentiel_competences_dept_id_fkey",
+        "apc_referentiel_competences",
+        "departement",
+        ["dept_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_situation_pro_competence_id_fkey", "apc_situation_pro", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_situation_pro_competence_id_fkey",
+        "apc_situation_pro",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "apc_validation_rcue_parcours_id_fkey",
+        "apc_validation_rcue",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_validation_rcue_parcours_id_fkey",
+        "apc_validation_rcue",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+        ondelete="set null",
+    )
+    op.drop_constraint(
+        "notes_formations_referentiel_competence_id_fkey",
+        "notes_formations",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "notes_formations_referentiel_competence_id_fkey",
+        "notes_formations",
+        "apc_referentiel_competences",
+        ["referentiel_competence_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    op.drop_constraint(
+        "notes_formsemestre_inscription_parcour_id_fkey",
+        "notes_formsemestre_inscription",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "notes_formsemestre_inscription_parcour_id_fkey",
+        "notes_formsemestre_inscription",
+        "apc_parcours",
+        ["parcour_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    op.drop_constraint(
+        "notes_ue_niveau_competence_id_fkey", "notes_ue", type_="foreignkey"
+    )
+    op.drop_constraint("notes_ue_parcour_id_fkey", "notes_ue", type_="foreignkey")
+    op.create_foreign_key(
+        "notes_ue_niveau_competence_id_fkey",
+        "notes_ue",
+        "apc_niveau",
+        ["niveau_competence_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    op.create_foreign_key(
+        "notes_ue_parcour_id_fkey",
+        "notes_ue",
+        "apc_parcours",
+        ["parcour_id"],
+        ["id"],
+        ondelete="SET NULL",
+    )
+    op.drop_constraint(
+        "parcours_formsemestre_parcours_id_fkey",
+        "parcours_formsemestre",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "parcours_formsemestre_parcours_id_fkey",
+        "parcours_formsemestre",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    op.drop_constraint(
+        "parcours_modules_parcours_id_fkey", "parcours_modules", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "parcours_modules_parcours_id_fkey",
+        "parcours_modules",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_constraint(
+        "parcours_modules_parcours_id_fkey", "parcours_modules", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "parcours_modules_parcours_id_fkey",
+        "parcours_modules",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "parcours_formsemestre_parcours_id_fkey",
+        "parcours_formsemestre",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "parcours_formsemestre_parcours_id_fkey",
+        "parcours_formsemestre",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+    )
+    op.drop_constraint("notes_ue_parcour_id_fkey", "notes_ue", type_="foreignkey")
+    op.drop_constraint(
+        "notes_ue_niveau_competence_id_fkey", "notes_ue", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "notes_ue_parcour_id_fkey", "notes_ue", "apc_parcours", ["parcour_id"], ["id"]
+    )
+    op.create_foreign_key(
+        "notes_ue_niveau_competence_id_fkey",
+        "notes_ue",
+        "apc_niveau",
+        ["niveau_competence_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "notes_formsemestre_inscription_parcour_id_fkey",
+        "notes_formsemestre_inscription",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "notes_formsemestre_inscription_parcour_id_fkey",
+        "notes_formsemestre_inscription",
+        "apc_parcours",
+        ["parcour_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "notes_formations_referentiel_competence_id_fkey",
+        "notes_formations",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "notes_formations_referentiel_competence_id_fkey",
+        "notes_formations",
+        "apc_referentiel_competences",
+        ["referentiel_competence_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_validation_rcue_parcours_id_fkey",
+        "apc_validation_rcue",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_validation_rcue_parcours_id_fkey",
+        "apc_validation_rcue",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_situation_pro_competence_id_fkey", "apc_situation_pro", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_situation_pro_competence_id_fkey",
+        "apc_situation_pro",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_referentiel_competences_dept_id_fkey",
+        "apc_referentiel_competences",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_referentiel_competences_dept_id_fkey",
+        "apc_referentiel_competences",
+        "departement",
+        ["dept_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_parcours_referentiel_id_fkey", "apc_parcours", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_parcours_referentiel_id_fkey",
+        "apc_parcours",
+        "apc_referentiel_competences",
+        ["referentiel_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_niveau_competence_id_fkey", "apc_niveau", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_niveau_competence_id_fkey",
+        "apc_niveau",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_modules_acs_app_crit_id_fkey", "apc_modules_acs", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_modules_acs_app_crit_id_fkey",
+        "apc_modules_acs",
+        "apc_app_critique",
+        ["app_crit_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_composante_essentielle_competence_id_fkey",
+        "apc_composante_essentielle",
+        type_="foreignkey",
+    )
+    op.create_foreign_key(
+        "apc_composante_essentielle_competence_id_fkey",
+        "apc_composante_essentielle",
+        "apc_competence",
+        ["competence_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_competence_referentiel_id_fkey", "apc_competence", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_competence_referentiel_id_fkey",
+        "apc_competence",
+        "apc_referentiel_competences",
+        ["referentiel_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_app_critique_niveau_id_fkey", "apc_app_critique", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_app_critique_niveau_id_fkey",
+        "apc_app_critique",
+        "apc_niveau",
+        ["niveau_id"],
+        ["id"],
+    )
+    op.drop_constraint(
+        "apc_annee_parcours_parcours_id_fkey", "apc_annee_parcours", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "apc_annee_parcours_parcours_id_fkey",
+        "apc_annee_parcours",
+        "apc_parcours",
+        ["parcours_id"],
+        ["id"],
+    )
+    # EVENTS
+    op.drop_constraint("scolar_events_ue_id_fkey", "scolar_events", type_="foreignkey")
+    op.drop_constraint(
+        "scolar_events_formsemestre_id_fkey", "scolar_events", type_="foreignkey"
+    )
+    op.create_foreign_key(
+        "scolar_events_ue_id_fkey", "scolar_events", "notes_ue", ["ue_id"], ["id"]
+    )
+    op.create_foreign_key(
+        "scolar_events_formsemestre_id_fkey",
+        "scolar_events",
+        "notes_formsemestre",
+        ["formsemestre_id"],
+        ["id"],
+    )
+
+    # ### end Alembic commands ###
diff --git a/scodoc.py b/scodoc.py
index 12556c2c20c6a7659a273311b2c226437b711527..ec197aa73eedad8132c62c55d10ba4e521b87613 100755
--- a/scodoc.py
+++ b/scodoc.py
@@ -41,6 +41,7 @@ from app.models.but_refcomp import (
 )
 from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
 from app.models.evaluations import Evaluation
+from app.scodoc import sco_dump_db
 from app.scodoc.sco_logos import make_logo_local
 from app.scodoc.sco_permissions import Permission
 from app.views import notes, scolar
@@ -131,6 +132,18 @@ def sco_db_init(erase=False):  # sco-db-init
     initialize_scodoc_database(erase=erase)
 
 
+@app.cli.command()
+@click.argument("database")
+def anonymize_db(database):  # anonymize-db
+    """Anonymise la base de nom indiqué (et non pas la base courante!)"""
+    click.confirm(
+        f"L'anonymisation va affecter la base {database} et PERDRE beaucoup de données.\nContinuer ?",
+        abort=True,
+    )
+    sco_dump_db.anonymize_db(database)
+    click.echo(f"Base {database} pseudonymisée")
+
+
 @app.cli.command()
 def user_db_clear():
     """Erase all users and roles from the database !"""
@@ -397,9 +410,11 @@ def delete_dept(dept, force=False):  # delete-dept
         sys.stderr.write(f"Erreur: le departement {dept} n'existe pas !\n")
         return 2
     elif d:
-        sco_dept.delete_dept(d.id)
+        msg = sco_dept.delete_dept(d.id)
         db.session.commit()
-    return 0
+        if msg:
+            print(f"Erreur:\n {msg}")
+    return 0 if not msg else 1
 
 
 @app.cli.command()
diff --git a/tools/anonymize_db.py b/tools/anonymize_db.py
index a68250985f5a543ade20c5427601abcff8d11172..2e5040a255e66722b280279498f2336bd87cc6cd 100755
--- a/tools/anonymize_db.py
+++ b/tools/anonymize_db.py
@@ -26,8 +26,6 @@
 #
 ##############################################################################
 
-# TODO à tester avec ScoDoc9, devrait fonctionner sans problème majeur ?
-
 """Anonymize une base de données ScoDoc
 
 Runned as user "scodoc" with scodoc and postgresql up.