diff --git a/app/api/jury.py b/app/api/jury.py
index 7de6cdef08fd42f240125b43eabc85804ecf86a1..6103d11649a7130373c4a11ea207eea842aaa57e 100644
--- a/app/api/jury.py
+++ b/app/api/jury.py
@@ -10,7 +10,7 @@
 
 from flask import g, url_for
 from flask_json import as_json
-from flask_login import login_required
+from flask_login import current_user, login_required
 
 import app
 from app import db, log
@@ -29,6 +29,7 @@ from app.models import (
 )
 from app.scodoc import sco_cache
 from app.scodoc.sco_permissions import Permission
+from app.scodoc.sco_utils import json_error
 
 
 @bp.route("/formsemestre/<int:formsemestre_id>/decisions_jury")
@@ -73,7 +74,7 @@ def _news_delete_jury_etud(etud: Identite):
 )
 @login_required
 @scodoc
-@permission_required(Permission.ScoEtudInscrit)
+@permission_required(Permission.ScoView)
 @as_json
 def validation_ue_delete(etudid: int, validation_id: int):
     "Efface cette validation"
@@ -90,7 +91,7 @@ def validation_ue_delete(etudid: int, validation_id: int):
 )
 @login_required
 @scodoc
-@permission_required(Permission.ScoEtudInscrit)
+@permission_required(Permission.ScoView)
 @as_json
 def validation_formsemestre_delete(etudid: int, validation_id: int):
     "Efface cette validation"
@@ -106,6 +107,24 @@ def _validation_ue_delete(etudid: int, validation_id: int):
     validation = ScolarFormSemestreValidation.query.filter_by(
         id=validation_id, etudid=etudid
     ).first_or_404()
+    # Vérification de la permission:
+    # A le droit de supprimer cette validation: le chef de dept ou quelqu'un ayant
+    # le droit de saisir des décisions de jury dans le formsemestre concerné s'il y en a un
+    # (c'est le cas pour les validations de jury, mais pas pour les "antérieures" non
+    # rattachées à un formsemestre)
+    if not g.scodoc_dept:  # accès API
+        if not current_user.has_permission(Permission.ScoEtudInscrit):
+            return json_error(403, "validation_delete: non autorise")
+    else:
+        if validation.formsemestre:
+            if (
+                validation.formsemestre.dept_id != g.scodoc_dept_id
+            ) or not validation.formsemestre.can_edit_jury():
+                return json_error(403, "validation_delete: non autorise")
+        elif not current_user.has_permission(Permission.ScoEtudInscrit):
+            # Validation non rattachée à un semestre: on doit être chef
+            return json_error(403, "validation_delete: non autorise")
+
     log(f"validation_ue_delete: etuid={etudid} {validation}")
     db.session.delete(validation)
     sco_cache.invalidate_formsemestre_etud(etud)
diff --git a/app/models/validations.py b/app/models/validations.py
index 7686d78976a85f3d7b197f5a42ae396fb89b3019..10791758a0d68e525f7694e29537abdf7fea8f22 100644
--- a/app/models/validations.py
+++ b/app/models/validations.py
@@ -8,6 +8,8 @@ from app import log
 from app.models import SHORT_STR_LEN
 from app.models import CODE_STR_LEN
 from app.models.events import Scolog
+from app.scodoc import sco_cache
+from app.scodoc import sco_utils as scu
 
 
 class ScolarFormSemestreValidation(db.Model):
@@ -70,6 +72,14 @@ class ScolarFormSemestreValidation(db.Model):
         return f"""décision sur semestre {self.formsemestre.titre_mois()} du {
                 self.event_date.strftime("%d/%m/%Y")}"""
 
+    def delete(self):
+        "Efface cette validation"
+        log(f"{self.__class__.__name__}.delete({self})")
+        etud = self.etud
+        db.session.delete(self)
+        db.session.commit()
+        sco_cache.invalidate_formsemestre_etud(etud)
+
     def to_dict(self) -> dict:
         "as a dict"
         d = dict(self.__dict__)
@@ -79,15 +89,22 @@ class ScolarFormSemestreValidation(db.Model):
     def html(self, detail=False) -> str:
         "Affichage html"
         if self.ue_id is not None:
-            return f"""Validation de l'UE <b>{self.ue.acronyme}</b>
+            moyenne = (
+                f", moyenne {scu.fmt_note(self.moy_ue)}/20 "
+                if self.moy_ue is not None
+                else ""
+            )
+            return f"""Validation
+                {'<span class="redboldtext">externe</span>' if self.is_external else ""}
+                de l'UE <b>{self.ue.acronyme}</b>
                 {('parcours <span class="parcours">'
                   + ", ".join([p.code for p in self.ue.parcours]))
                   + "</span>"
                   if self.ue.parcours else ""}
                 de {self.ue.formation.acronyme}
                 {("émise par " + self.formsemestre.html_link_status()) 
-                    if self.formsemestre else ""}
-                : <b>{self.code}</b>
+                    if self.formsemestre else "externe/antérieure"}
+                : <b>{self.code}</b>{moyenne}
                 le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
             """
         else:
diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py
index c827207b5518e780b8d5824ebd57a5c0f42aac5e..d8f62b983e7a1c5c7746d8ad2ab71f09f19786c1 100644
--- a/app/scodoc/sco_bulletins.py
+++ b/app/scodoc/sco_bulletins.py
@@ -1238,7 +1238,7 @@ def make_menu_autres_operations(
             "enabled": current_user.has_permission(Permission.ScoImplement),
         },
         {
-            "title": "Enregistrer une validation d'UE antérieure",
+            "title": "Gérer les validations d'UEs antérieures",
             "endpoint": "notes.formsemestre_validate_previous_ue",
             "args": {
                 "formsemestre_id": formsemestre.id,
diff --git a/app/scodoc/sco_cursus_dut.py b/app/scodoc/sco_cursus_dut.py
index 3cf2897fa3bd0d5f4f3f5e3de1d1fddf9ba7b781..9eab3da6b76b5b58150ca953f6c622d89414373b 100644
--- a/app/scodoc/sco_cursus_dut.py
+++ b/app/scodoc/sco_cursus_dut.py
@@ -972,7 +972,7 @@ def do_formsemestre_validate_ue(
                 moy_ue = ue_status["moy"] if ue_status else ""
             args["moy_ue"] = moy_ue
         log("formsemestre_validate_ue: create %s" % args)
-        if code != None:
+        if code is not None:
             scolar_formsemestre_validation_create(cnx, args)
         else:
             log("formsemestre_validate_ue: code is None, not recording validation")
diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py
index fbe8e2be9611c664182b8f0dcf8d072444d39e06..90f01f04eeadfab7df9431e67286a5204089c685 100644
--- a/app/scodoc/sco_edit_ue.py
+++ b/app/scodoc/sco_edit_ue.py
@@ -502,7 +502,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
         else:
             clone_form = ""
         bonus_div = """<div id="bonus_description"></div>"""
-        ue_div = """<div id="ue_list_code"></div>"""
+        ue_div = """<div id="ue_list_code" class="sco_box sco_green_bg"></div>"""
         return (
             "\n".join(H)
             + tf[1]
@@ -1375,13 +1375,12 @@ def _ue_table_modules(
     return "\n".join(H)
 
 
-def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
+def ue_sharing_code(ue_code: str = "", ue_id: int = None, hide_ue_id: int = None):
     """HTML list of UE sharing this code
     Either ue_code or ue_id may be specified.
     hide_ue_id spécifie un id à retirer de la liste.
     """
-    ue_code = str(ue_code)
-    if ue_id:
+    if ue_id is not None:
         ue = UniteEns.query.get_or_404(ue_id)
         if not ue_code:
             ue_code = ue.ue_code
@@ -1400,29 +1399,36 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
             .filter_by(dept_id=g.scodoc_dept_id)
         )
 
-    if hide_ue_id:  # enlève l'ue de depart
+    if hide_ue_id is not None:  # enlève l'ue de depart
         q_ues = q_ues.filter(UniteEns.id != hide_ue_id)
 
     ues = q_ues.all()
+    msg = " dans les formations du département "
     if not ues:
-        if ue_id:
-            return (
-                f"""<span class="ue_share">Seule UE avec code {ue_code or '-'}</span>"""
-            )
+        if ue_id is not None:
+            return f"""<span class="ue_share">Seule UE avec code {
+                ue_code if ue_code is not None else '-'}{msg}</span>"""
         else:
-            return f"""<span class="ue_share">Aucune UE avec code {ue_code or '-'}</span>"""
+            return f"""<span class="ue_share">Aucune UE avec code {
+                ue_code if ue_code is not None else '-'}{msg}</span>"""
     H = []
     if ue_id:
         H.append(
-            f"""<span class="ue_share">Autres UE avec le code {ue_code or '-'}:</span>"""
+            f"""<span class="ue_share">Pour information, autres UEs avec le code {
+                ue_code if ue_code is not None else '-'}{msg}:</span>"""
         )
     else:
-        H.append(f"""<span class="ue_share">UE avec le code {ue_code or '-'}:</span>""")
+        H.append(
+            f"""<span class="ue_share">UE avec le code {
+                ue_code if ue_code is not None else '-'}{msg}:</span>"""
+        )
     H.append("<ul>")
     for ue in ues:
         H.append(
-            f"""<li>{ue.acronyme} ({ue.titre}) dans <a class="stdlink"
-            href="{url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}"
+            f"""<li>{ue.acronyme} ({ue.titre}) dans
+            <a class="stdlink" href="{
+                url_for("notes.ue_table",
+                scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}"
             >{ue.formation.acronyme} ({ue.formation.titre})</a>, version {ue.formation.version}
             </li>
             """
diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py
index 67f05d625a6fb95a7c452912711bdaf039715fa2..de32912f3e2ca505011958cbc6a50b6646ba8577 100644
--- a/app/scodoc/sco_formsemestre_exterieurs.py
+++ b/app/scodoc/sco_formsemestre_exterieurs.py
@@ -517,7 +517,7 @@ def _record_ue_validations_and_coefs(
         )
         assert code is None or (note)  # si code validant, il faut une note
         sco_formsemestre_validation.do_formsemestre_validate_previous_ue(
-            formsemestre.id,
+            formsemestre,
             etud.id,
             ue.id,
             note,
diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py
index c087ff2b79667a6ff13431fb6de1aa0145d921c9..f00542f7bfdfb6e9f1587513e3688c381e869f22 100644
--- a/app/scodoc/sco_formsemestre_validation.py
+++ b/app/scodoc/sco_formsemestre_validation.py
@@ -31,8 +31,9 @@ import time
 
 import flask
 from flask import url_for, flash, g, request
-from app.models.etudiants import Identite
+import sqlalchemy as sa
 
+from app.models.etudiants import Identite
 import app.scodoc.notesdb as ndb
 import app.scodoc.sco_utils as scu
 from app import db, log
@@ -1081,62 +1082,44 @@ def formsemestre_validation_suppress_etud(formsemestre_id, etudid):
     )  # > suppr. decision jury (peut affecter de plusieurs semestres utilisant UE capitalisée)
 
 
-def formsemestre_validate_previous_ue(formsemestre_id, etudid):
+def formsemestre_validate_previous_ue(formsemestre: FormSemestre, etud: Identite):
     """Form. saisie UE validée hors ScoDoc
     (pour étudiants arrivant avec un UE antérieurement validée).
     """
-    etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
-    sem = sco_formsemestre.get_formsemestre(formsemestre_id)
-    formation: Formation = Formation.query.get_or_404(sem["formation_id"])
-    H = [
-        html_sco_header.sco_header(
-            page_title="Validation UE",
-            javascripts=["js/validate_previous_ue.js"],
+    formation: Formation = formsemestre.formation
+
+    # Toutes les UEs non bonus de cette formation sont présentées
+    # avec indice de semestre <= semestre courant ou NULL
+    ues = formation.ues.filter(
+        UniteEns.type != UE_SPORT,
+        db.or_(
+            UniteEns.semestre_idx == None,
+            UniteEns.semestre_idx <= formsemestre.semestre_id,
         ),
-        '<table style="width: 100%"><tr><td>',
-        """<h2 class="formsemestre">%s: validation d'une UE antérieure</h2>"""
-        % etud["nomprenom"],
-        (
-            '</td><td style="text-align: right;"><a href="%s">%s</a></td></tr></table>'
-            % (
-                url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
-                sco_photos.etud_photo_html(etud, title="fiche de %s" % etud["nom"]),
-            )
-        ),
-        f"""<p class="help">Utiliser cette page pour enregistrer une UE validée antérieurement,
-    <em>dans un semestre hors ScoDoc</em>.</p>
-    <p><b>Les UE validées dans ScoDoc sont déjà
-    automatiquement prises en compte</b>. Cette page n'est utile que pour les étudiants ayant
-    suivi un début de cursus dans <b>un autre établissement</b>, ou bien dans un semestre géré 
-    <b>sans ScoDoc</b> et qui <b>redouble</b> ce semestre
-    (<em>ne pas utiliser pour les semestres précédents !</em>).
-    </p>
-    <p>Notez que l'UE est validée, avec enregistrement immédiat de la décision et
-    l'attribution des ECTS.</p>
-    <p>On ne peut prendre en compte ici que les UE du cursus <b>{formation.titre}</b></p>
-    """,
-    ]
+    ).order_by(UniteEns.semestre_idx, UniteEns.numero)
 
-    # Toutes les UE de cette formation sont présentées (même celles des autres semestres)
-    ues = formation.ues.order_by(UniteEns.numero)
-    ue_names = ["Choisir..."] + [f"{ue.acronyme} {ue.titre}" for ue in ues]
+    ue_names = ["Choisir..."] + [
+        f"""{('S'+str(ue.semestre_idx)+' : ') if ue.semestre_idx is not None else ''
+           }{ue.acronyme} {ue.titre} ({ue.ue_code or ""})"""
+        for ue in ues
+    ]
     ue_ids = [""] + [ue.id for ue in ues]
-    tf = TrivialFormulator(
-        request.base_url,
-        scu.get_request_args(),
+    form_descr = [
+        ("etudid", {"input_type": "hidden"}),
+        ("formsemestre_id", {"input_type": "hidden"}),
         (
-            ("etudid", {"input_type": "hidden"}),
-            ("formsemestre_id", {"input_type": "hidden"}),
-            (
-                "ue_id",
-                {
-                    "input_type": "menu",
-                    "title": "Unité d'Enseignement (UE)",
-                    "allow_null": False,
-                    "allowed_values": ue_ids,
-                    "labels": ue_names,
-                },
-            ),
+            "ue_id",
+            {
+                "input_type": "menu",
+                "title": "Unité d'Enseignement (UE)",
+                "allow_null": False,
+                "allowed_values": ue_ids,
+                "labels": ue_names,
+            },
+        ),
+    ]
+    if not formation.is_apc():
+        form_descr.append(
             (
                 "semestre_id",
                 {
@@ -1147,69 +1130,159 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid):
                     "allowed_values": [""] + [x for x in range(11)],
                     "labels": ["-"] + list(range(11)),
                 },
-            ),
-            (
-                "date",
-                {
-                    "input_type": "date",
-                    "size": 9,
-                    "explanation": "j/m/a",
-                    "default": time.strftime("%d/%m/%Y"),
-                },
-            ),
-            (
-                "moy_ue",
-                {
-                    "type": "float",
-                    "allow_null": False,
-                    "min_value": 0,
-                    "max_value": 20,
-                    "title": "Moyenne (/20) obtenue dans cette UE:",
-                },
-            ),
+            )
+        )
+    form_descr += [
+        (
+            "date",
+            {
+                "input_type": "date",
+                "size": 9,
+                "explanation": "j/m/a",
+                "default": time.strftime("%d/%m/%Y"),
+            },
+        ),
+        (
+            "moy_ue",
+            {
+                "type": "float",
+                "allow_null": False,
+                "min_value": 0,
+                "max_value": 20,
+                "title": "Moyenne (/20) obtenue dans cette UE:",
+            },
         ),
-        cancelbutton="Annuler",
+    ]
+    tf = TrivialFormulator(
+        request.base_url,
+        scu.get_request_args(),
+        form_descr,
+        cancelbutton="Revenir au bulletin",
         submitlabel="Enregistrer validation d'UE",
     )
     if tf[0] == 0:
-        X = """
-           <div id="ue_list_etud_validations"><!-- filled by get_etud_ue_cap_html --></div>
-           <div id="ue_list_code"><!-- filled by ue_sharing_code --></div>
-        """
-        warn, ue_multiples = check_formation_ues(formation.id)
-        return "\n".join(H) + tf[1] + X + warn + html_sco_header.sco_footer()
-    elif tf[0] == -1:
-        return flask.redirect(
-            scu.NotesURL()
-            + "/formsemestre_status?formsemestre_id="
-            + str(formsemestre_id)
-        )
-    else:
-        if tf[2]["semestre_id"]:
-            semestre_id = int(tf[2]["semestre_id"])
-        else:
-            semestre_id = None
-        do_formsemestre_validate_previous_ue(
-            formsemestre_id,
-            etudid,
-            tf[2]["ue_id"],
-            tf[2]["moy_ue"],
-            tf[2]["date"],
-            semestre_id=semestre_id,
-        )
-        flash("Validation d'UE enregistrée")
+        return f"""
+    {html_sco_header.sco_header(
+        page_title="Validation UE antérieure",
+        javascripts=["js/validate_previous_ue.js"],
+        cssstyles=["css/jury_delete_manual.css"],
+        etudid=etud.id,
+    )}
+    <h2 class="formsemestre">Gestion des validations d'UEs antérieures
+    de {etud.html_link_fiche()}
+    </h2>
+
+    <p class="help">Utiliser cette page pour enregistrer une UE validée antérieurement,
+    <em>dans un semestre hors ScoDoc</em>.</p>
+    <p class="expl"><b>Les UE validées dans ScoDoc sont déjà
+    automatiquement prises en compte</b>. Cette page n'est utile que pour les étudiants ayant
+    suivi un début de cursus dans <b>un autre établissement</b>, ou bien dans un semestre géré
+    <b>sans ScoDoc</b> et qui <b>redouble</b> ce semestre
+    (<em>pour les semestres précédents gérés avec ScoDoc,
+    passer par la page jury normale)</em>).
+    </p>
+    <p>Notez que l'UE est validée (ADM), avec enregistrement immédiat de la décision et
+    l'attribution des ECTS.</p>
+    <p>On ne peut valider ici que les UEs du cursus <b>{formation.titre}</b></p>
+
+    {_get_etud_ue_cap_html(etud, formsemestre)}
+
+    <div class="sco_box">
+        <div class="sco_box_title">
+        Enregistrer une UE antérieure
+        </div>
+        {tf[1]}
+    </div>
+    <div id="ue_list_code" class="sco_box sco_green_bg">
+        <!-- filled by ue_sharing_code -->
+    </div>
+    {check_formation_ues(formation.id)[0]}
+    {html_sco_header.sco_footer()}
+    """
+
+    dest_url = url_for(
+        "notes.formsemestre_validate_previous_ue",
+        scodoc_dept=g.scodoc_dept,
+        formsemestre_id=formsemestre.id,
+        etudid=etud.id,
+    )
+    if tf[0] == -1:
         return flask.redirect(
             url_for(
                 "notes.formsemestre_bulletinetud",
                 scodoc_dept=g.scodoc_dept,
-                formsemestre_id=formsemestre_id,
-                etudid=etudid,
+                formsemestre_id=formsemestre.id,
+                etudid=etud.id,
             )
         )
+    if tf[2].get("semestre_id"):
+        semestre_id = int(tf[2]["semestre_id"])
+    else:
+        semestre_id = None
+
+    do_formsemestre_validate_previous_ue(
+        formsemestre,
+        etud.id,
+        tf[2]["ue_id"],
+        tf[2]["moy_ue"],
+        tf[2]["date"],
+        semestre_id=semestre_id,
+    )
+    flash("Validation d'UE enregistrée")
+    return flask.redirect(dest_url)
+
+
+def _get_etud_ue_cap_html(etud: Identite, formsemestre: FormSemestre) -> str:
+    """HTML listant les validations d'UEs pour cet étudiant dans des formations de même
+    code que celle du formsemestre indiqué.
+    """
+    validations: list[ScolarFormSemestreValidation] = (
+        ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
+        .join(UniteEns)
+        .join(Formation)
+        .filter_by(formation_code=formsemestre.formation.formation_code)
+        .order_by(
+            sa.desc(UniteEns.semestre_idx),
+            UniteEns.acronyme,
+            sa.desc(ScolarFormSemestreValidation.event_date),
+        )
+        .all()
+    )
+
+    if not validations:
+        return ""
+    H = [
+        f"""<div class="sco_box sco_lightgreen_bg ue_list_etud_validations">
+        <div class="sco_box_title">Validations d'UEs dans cette formation</div>
+        <div class="help">Liste de toutes les UEs validées par {etud.html_link_fiche()}, 
+        sur des semestres ou déclarées comme "antérieures" (externes).
+        </div>
+        <ul>"""
+    ]
+    for validation in validations:
+        if validation.formsemestre_id is None:
+            origine = " enregistrée d'un parcours antérieur (hors ScoDoc)"
+        else:
+            origine = f", du semestre {formsemestre.html_link_status()}"
+        if validation.semestre_id is not None:
+            origine += f" (<b>S{validation.semestre_id}</b>)"
+        H.append(
+            f"""
+            <li>{validation.html()}
+                <form class="inline-form">
+                    <button 
+                    data-v_id="{validation.id}" data-type="validation_ue" data-etudid="{etud.id}"
+                    >effacer</button>
+                </form>
+            </li>
+            """,
+        )
+    H.append("</ul></div>")
+    return "\n".join(H)
 
 
 def do_formsemestre_validate_previous_ue(
-    formsemestre_id,
+    formsemestre: FormSemestre,
     etudid,
     ue_id,
     moy_ue,
@@ -1222,21 +1295,20 @@ def do_formsemestre_validate_previous_ue(
     Si le coefficient est spécifié, modifie le coefficient de
     cette UE (utile seulement pour les semestres extérieurs).
     """
-    formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
     nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
     ue: UniteEns = UniteEns.query.get_or_404(ue_id)
 
     cnx = ndb.GetDBConnexion()
     if ue_coefficient != None:
         sco_formsemestre.do_formsemestre_uecoef_edit_or_create(
-            cnx, formsemestre_id, ue_id, ue_coefficient
+            cnx, formsemestre.id, ue_id, ue_coefficient
         )
     else:
-        sco_formsemestre.do_formsemestre_uecoef_delete(cnx, formsemestre_id, ue_id)
+        sco_formsemestre.do_formsemestre_uecoef_delete(cnx, formsemestre.id, ue_id)
     sco_cursus_dut.do_formsemestre_validate_ue(
         cnx,
         nt,
-        formsemestre_id,  # "importe" cette UE dans le semestre (new 3/2015)
+        formsemestre.id,  # "importe" cette UE dans le semestre (new 3/2015)
         etudid,
         ue_id,
         code,
@@ -1274,62 +1346,6 @@ def _invalidate_etud_formation_caches(etudid, formation_id):
         )  # > modif decision UE (inval tous semestres avec cet etudiant, ok mais conservatif)
 
 
-def get_etud_ue_cap_html(etudid, formsemestre_id, ue_id):
-    """Ramene bout de HTML pour pouvoir supprimer une validation de cette UE"""
-    valids = ndb.SimpleDictFetch(
-        """SELECT SFV.* 
-        FROM scolar_formsemestre_validation SFV
-        WHERE ue_id=%(ue_id)s 
-        AND etudid=%(etudid)s""",
-        {"etudid": etudid, "ue_id": ue_id},
-    )
-    if not valids:
-        return ""
-    H = [
-        '<div class="existing_valids"><span>Validations existantes pour cette UE:</span><ul>'
-    ]
-    for valid in valids:
-        valid["event_date"] = ndb.DateISOtoDMY(valid["event_date"])
-        if valid["moy_ue"] != None:
-            valid["m"] = ", moyenne %(moy_ue)g/20" % valid
-        else:
-            valid["m"] = ""
-        if valid["formsemestre_id"]:
-            sem = sco_formsemestre.get_formsemestre(valid["formsemestre_id"])
-            valid["s"] = ", du semestre %s" % sem["titreannee"]
-        else:
-            valid["s"] = " enregistrée d'un parcours antérieur (hors ScoDoc)"
-        if valid["semestre_id"]:
-            valid["s"] += " (<b>S%d</b>)" % valid["semestre_id"]
-        valid["ds"] = formsemestre_id
-        H.append(
-            '<li>%(code)s%(m)s%(s)s, le %(event_date)s  <a class="stdlink" href="etud_ue_suppress_validation?etudid=%(etudid)s&ue_id=%(ue_id)s&formsemestre_id=%(ds)s" title="supprime cette validation">effacer</a></li>'
-            % valid
-        )
-    H.append("</ul></div>")
-    return "\n".join(H)
-
-
-def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id):
-    """Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
-    log("etud_ue_suppress_validation( %s, %s, %s)" % (etudid, formsemestre_id, ue_id))
-    cnx = ndb.GetDBConnexion()
-    cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
-    cursor.execute(
-        "DELETE FROM scolar_formsemestre_validation WHERE etudid=%(etudid)s and ue_id=%(ue_id)s",
-        {"etudid": etudid, "ue_id": ue_id},
-    )
-
-    sem = sco_formsemestre.get_formsemestre(formsemestre_id)
-    _invalidate_etud_formation_caches(etudid, sem["formation_id"])
-
-    return flask.redirect(
-        scu.NotesURL()
-        + "/formsemestre_validate_previous_ue?etudid=%s&formsemestre_id=%s"
-        % (etudid, formsemestre_id)
-    )
-
-
 def check_formation_ues(formation_id):
     """Verifie que les UE d'une formation sont chacune utilisée dans un seul semestre_id
     Si ce n'est pas le cas, c'est probablement (mais pas forcément) une erreur de
diff --git a/app/static/css/jury_delete_manual.css b/app/static/css/jury_delete_manual.css
index 6fa14f9ea009b0ea8e46fe11c13c091c4cdcfc3b..d41e4f802b16655c24fcf231c7c6ebc5ffbde8ac 100644
--- a/app/static/css/jury_delete_manual.css
+++ b/app/static/css/jury_delete_manual.css
@@ -4,10 +4,6 @@ div.jury_decisions_list div {
     font-weight: bold;
 }
 
-div.jury_decisions_list form {
-    display: inline-block;
-}
-
 span.parcours {
     color:blueviolet;
 }
diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css
index ca1b1f12907004167d1f78b0a2d8a22c235c1277..443f47bc8a2ac7741896e4b5969dda4f16f29f50 100644
--- a/app/static/css/scodoc.css
+++ b/app/static/css/scodoc.css
@@ -76,16 +76,16 @@ div.flashes {
 }
 
 div.alert {
-  /*   
-  position: absolute;
-  top: 10px;
-  right: 10px; */
+  padding: 16px;
+  border-radius: 12px;
+  font-size: 200%;
+  opacity: 0.8;
 }
 
 div.alert-info {
-  color: #0019d7;
-  background-color: #68f36d;
-  border-color: #0a8d0c;
+  color: #208d3b;
+  background-color: #fffd97;
+  border-color: #208d3b;
 }
 
 div.alert-error {
@@ -94,6 +94,9 @@ div.alert-error {
   border-color: #8d0a17;
 }
 
+form.inline-form {
+  display: inline-block;
+}
 
 div.tab-content {
   margin-top: 10px;
@@ -1112,9 +1115,11 @@ a.discretelink:hover {
   text-align: center;
 }
 
+.expl, .help {
+  max-width: var(--sco-content-max-width);
+}
 .help {
   font-style: italic;
-  max-width: 800px;
 }
 
 .help_important {
@@ -1122,13 +1127,28 @@ a.discretelink:hover {
   color: red;
 }
 
-div.sco_help {
+div.sco_box, div.sco_help {
   margin-top: 12px;
   margin-bottom: 4px;
+  margin-left: 0px;
   padding: 8px;
   border-radius: 4px;
+  border: 1px solid grey;
+  max-width: var(--sco-content-max-width);
+}
+div.sco_help {
   font-style: italic;
-  max-width: 800px;
+  background-color: rgb(209, 255, 214);
+}
+div.sco_box_title {
+  font-size: 120%;
+  font-weight: bold;
+  margin-bottom: 8px;
+}
+.sco_green_bg {
+  background-color: rgb(155, 218, 155);
+}
+.sco_lightgreen_bg {
   background-color: rgb(209, 255, 214);
 }
 
@@ -2504,13 +2524,7 @@ input.sco_tag_checkbox {
 }
 
 div#ue_list_code {
-  background-color: rgb(155, 218, 155);
-  padding: 10px;
   border: 1px solid blue;
-  border-radius: 10px;
-  padding: 10px;
-  margin-top: 10px;
-  margin-right: 15px;
 }
 
 ul.notes_module_list {
@@ -2596,16 +2610,6 @@ div#ue_list_modules {
   margin-right: 15px;
 }
 
-div#ue_list_etud_validations {
-  background-color: rgb(220, 250, 220);
-  padding-left: 4px;
-  padding-bottom: 1px;
-  margin: 3ex;
-}
-
-div#ue_list_etud_validations span {
-  font-weight: bold;
-}
 
 span.ue_share {
   font-weight: bold;
diff --git a/app/static/js/validate_previous_ue.js b/app/static/js/validate_previous_ue.js
index a741380b31d3041fcbe3efd4b2efb0371e4597de..22655f498e23016a74dc2ec745e0359b145e731c 100644
--- a/app/static/js/validate_previous_ue.js
+++ b/app/static/js/validate_previous_ue.js
@@ -1,31 +1,43 @@
 // Affiche et met a jour la liste des UE partageant le meme code
 
-$().ready(function () {
-    update_ue_validations();
-    update_ue_list();
-    $("#tf_ue_id").bind("change", update_ue_list);
-    $("#tf_ue_id").bind("change", update_ue_validations);
-});
+document.addEventListener("DOMContentLoaded", () => {
+  update_ue_list();
+  $("#tf_ue_id").bind("change", update_ue_list);
 
+  const buttons = document.querySelectorAll(".ue_list_etud_validations button");
 
-function update_ue_list() {
-    var ue_id = $("#tf_ue_id")[0].value;
-    if (ue_id) {
-        var query = "ue_sharing_code?ue_id=" + ue_id;
-        $.get(query, '', function (data) {
-            $("#ue_list_code").html(data);
+  buttons.forEach((button) => {
+    button.addEventListener("click", (event) => {
+      // Handle button click event here
+      event.preventDefault();
+      const etudid = event.target.dataset.etudid;
+      const v_id = event.target.dataset.v_id;
+      const validation_type = event.target.dataset.type;
+      if (confirm("Supprimer cette validation ?")) {
+        fetch(
+          `${SCO_URL}/../api/etudiant/${etudid}/jury/${validation_type}/${v_id}/delete`,
+          {
+            method: "POST",
+          }
+        ).then((response) => {
+          // Handle the response
+          if (response.ok) {
+            location.reload();
+          } else {
+            throw new Error("Request failed");
+          }
         });
-    }
-}
+      }
+    });
+  });
+});
 
-function update_ue_validations() {
-    var etudid = $("#tf_etudid")[0].value;
-    var ue_id = $("#tf_ue_id")[0].value;
-    var formsemestre_id = $("#tf_formsemestre_id")[0].value;
-    if (ue_id) {
-        var query = SCO_URL + "/Notes/get_etud_ue_cap_html?ue_id=" + ue_id + "&etudid=" + etudid + "&formsemestre_id=" + formsemestre_id;
-        $.get(query, '', function (data) {
-            $("#ue_list_etud_validations").html(data);
-        });
-    }
-}
\ No newline at end of file
+function update_ue_list() {
+  var ue_id = $("#tf_ue_id")[0].value;
+  if (ue_id) {
+    var query = SCO_URL + "/Notes/ue_sharing_code?ue_id=" + ue_id;
+    $.get(query, "", function (data) {
+      $("#ue_list_code").html(data);
+    });
+  }
+}
diff --git a/app/templates/jury/jury_delete_manual.j2 b/app/templates/jury/jury_delete_manual.j2
index e423501fbfcfa760a0a4f5fc93be54ee8225666d..f1b750a885d598c10d9833054f576f0418ce37b4 100644
--- a/app/templates/jury/jury_delete_manual.j2
+++ b/app/templates/jury/jury_delete_manual.j2
@@ -26,7 +26,10 @@ pages de saisie de jury habituelles).
     <ul>
         {% for v in sem_vals %}
             <li>{{v.html()|safe}}
-            <form><button data-v_id="{{v.id}}" data-type="validation_formsemestre">effacer</button></form>
+            <form>
+            <button 
+                data-v_id="{{v.id}}" data-type="validation_formsemestre" data-etudid="{{etud.id}}"
+            >effacer</button></form>
             </li>
         {% endfor %}
     </ul>
@@ -39,7 +42,10 @@ pages de saisie de jury habituelles).
     <ul>
         {% for v in ue_vals %}
             <li>{{v.html(detail=True)|safe}}
-            <form><button data-v_id="{{v.id}}" data-type="validation_ue">effacer</button></form>
+                <form class="inline-form">
+                    <button data-v_id="{{v.id}}" data-type="validation_ue" data-etudid="{{etud.id"}}
+                    >effacer</button>
+                </form>
             </li>
         {% endfor %}
     </ul>
@@ -52,7 +58,10 @@ pages de saisie de jury habituelles).
     <ul>
         {% for v in rcue_vals %}
             <li>{{v.html()|safe}}
-            <form><button data-v_id="{{v.id}}" data-type="validation_rcue">effacer</button></form>
+            <form>
+            <button data-v_id="{{v.id}}" data-type="validation_rcue" data-etudid="{{etud.id}}"
+            >effacer</button>
+            </form>
             </li>
         {% endfor %}
     </ul>
@@ -65,7 +74,10 @@ pages de saisie de jury habituelles).
     <ul>
         {% for v in annee_but_vals %}
             <li>{{v.html()|safe}}
-            <form><button data-v_id="{{v.id}}" data-type="validation_annee_but">effacer</button></form>
+            <form>
+            <button data-v_id="{{v.id}}" data-type="validation_annee_but" data-etudid="{{etud.id}}"
+            >effacer</button>
+            </form>
             </li>
         {% endfor %}
     </ul>
@@ -78,7 +90,10 @@ pages de saisie de jury habituelles).
     <ul>
         {% for v in autorisations %}
             <li>{{v.html()|safe}}
-            <form><button data-v_id="{{v.id}}" data-type="autorisation_inscription">effacer</button></form>
+            <form>
+            <button data-v_id="{{v.id}}" data-type="autorisation_inscription" data-etudid="{{etud.id}}"
+            >effacer</button>
+            </form>
             </li>
         {% endfor %}
     </ul>
@@ -113,10 +128,11 @@ document.addEventListener('DOMContentLoaded', () => {
     button.addEventListener('click', (event) => {
         // Handle button click event here
         event.preventDefault();
+        const etudid = event.target.dataset.etudid;
         const v_id = event.target.dataset.v_id;
         const validation_type = event.target.dataset.type;
         if (confirm("Supprimer cette validation ?")) {
-            fetch(`${SCO_URL}/../api/etudiant/{{etud.id}}/jury/${validation_type}/${v_id}/delete`,
+            fetch(`${SCO_URL}/../api/etudiant/${etudid}/jury/${validation_type}/${v_id}/delete`,
                 {
                     method: "POST",
                 }).then(response => {                    
diff --git a/app/views/notes.py b/app/views/notes.py
index abdfdfaf1d824e252d867f443c0fc636f04981fd..3b1772982de5b56e833ad63d0ed3fce058aac10e 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -56,17 +56,22 @@ from app.but.forms import jury_but_forms
 
 from app.comp import jury, res_sem
 from app.comp.res_compat import NotesTableCompat
-from app.models import Formation, ScolarAutorisationInscription, ScolarNews, Scolog
+from app.models import (
+    Formation,
+    ScolarFormSemestreValidation,
+    ScolarAutorisationInscription,
+    ScolarNews,
+    Scolog,
+)
 from app.models.but_refcomp import ApcNiveau
 from app.models.config import ScoDocSiteConfig
 from app.models.etudiants import Identite
-from app.models.formsemestre import FormSemestre
+from app.models.formsemestre import FormSemestre, FormSemestreInscription
 from app.models.formsemestre import FormSemestreUEComputationExpr
 from app.models.moduleimpls import ModuleImpl
 from app.models.modules import Module
 from app.models.ues import DispenseUE, UniteEns
 from app.scodoc.sco_exceptions import ScoFormationConflict, ScoPermissionDenied
-from app.tables import jury_recap
 from app.views import notes_bp as bp
 
 from app.decorators import (
@@ -483,7 +488,21 @@ def ue_set_internal(ue_id):
     )
 
 
-sco_publish("/ue_sharing_code", sco_edit_ue.ue_sharing_code, Permission.ScoView)
+@bp.route("/ue_sharing_code")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def ue_sharing_code():
+    ue_code = request.args.get("ue_code")
+    ue_id = request.args.get("ue_id")
+    hide_ue_id = request.args.get("hide_ue_id")
+    return sco_edit_ue.ue_sharing_code(
+        ue_code=ue_code,
+        ue_id=None if ue_id is None else int(ue_id),
+        hide_ue_id=None if hide_ue_id is None else int(hide_ue_id),
+    )
+
+
 sco_publish(
     "/edit_ue_set_code_apogee",
     sco_edit_ue.edit_ue_set_code_apogee,
@@ -2621,10 +2640,12 @@ def formsemestre_validation_auto_but(formsemestre_id: int = None):
     )
 
 
-@bp.route("/formsemestre_validate_previous_ue", methods=["GET", "POST"])
+@bp.route(
+    "/formsemestre_validate_previous_ue/<int:formsemestre_id>/<int:etudid>",
+    methods=["GET", "POST"],
+)
 @scodoc
 @permission_required(Permission.ScoView)
-@scodoc7func
 def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
     "Form. saisie UE validée hors ScoDoc"
     formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
@@ -2636,9 +2657,15 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
                 formsemestre_id=formsemestre_id,
             )
         )
+    etud: Identite = (
+        Identite.query.filter_by(id=etudid)
+        .join(FormSemestreInscription)
+        .filter_by(formsemestre_id=formsemestre_id)
+        .first_or_404()
+    )
 
     return sco_formsemestre_validation.formsemestre_validate_previous_ue(
-        formsemestre_id, etudid
+        formsemestre, etud
     )
 
 
@@ -2671,34 +2698,6 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid=None):
     )
 
 
-sco_publish(
-    "/get_etud_ue_cap_html",
-    sco_formsemestre_validation.get_etud_ue_cap_html,
-    Permission.ScoView,
-)
-
-
-@bp.route("/etud_ue_suppress_validation")
-@scodoc
-@permission_required(Permission.ScoView)
-@scodoc7func
-def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id):
-    """Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
-    formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
-    if not formsemestre.can_edit_jury():
-        raise ScoPermissionDenied(
-            dest_url=url_for(
-                "notes.formsemestre_status",
-                scodoc_dept=g.scodoc_dept,
-                formsemestre_id=formsemestre_id,
-            )
-        )
-
-    return sco_formsemestre_validation.etud_ue_suppress_validation(
-        etudid, formsemestre_id, ue_id
-    )
-
-
 @bp.route("/formsemestre_validation_auto")
 @scodoc
 @permission_required(Permission.ScoView)