diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py
index c7bbe5a9769bf18d5cab625d9f1f60b4e941c353..e88fa7968273e4cfe59c500cc762c30af2f45436 100644
--- a/app/scodoc/sco_inscr_passage.py
+++ b/app/scodoc/sco_inscr_passage.py
@@ -625,104 +625,147 @@ def etuds_select_boxes(
     for src_cat in auth_etuds_by_cat.keys():
         infos = auth_etuds_by_cat[src_cat]["infos"]
         infos["comment"] = infos.get("comment", "")  # commentaire dans sous-titre boite
-        help_txt = infos.get("help", "")
         etuds = auth_etuds_by_cat[src_cat]["etuds"]
         etuds.sort(key=itemgetter("nom"))
         with_checkbox = (not read_only) and auth_etuds_by_cat[src_cat]["infos"].get(
             "with_checkbox", True
         )
-        checkbox_name = auth_etuds_by_cat[src_cat]["infos"].get(
-            "checkbox_name", "etuds"
-        )
-        etud_key = auth_etuds_by_cat[src_cat]["infos"].get("etud_key", "etudid")
         if etuds or show_empty_boxes:
             infos["nbetuds"] = len(etuds)
-            H.append(
-                """<div class="pas_sembox" id="%(id)s">
-                <div class="pas_sembox_title"><a href="%(title_target)s" """
-                % infos
+            xls_url = (
+                scu.build_url_query(base_url, export_cat_xls=src_cat)
+                if base_url and etuds
+                else ""
             )
-            if help_txt:  # bubble
-                H.append('title="%s"' % help_txt)
             H.append(
-                """>%(title)s</a></div>
-                <div class="pas_sembox_subtitle">(%(nbetuds)d étudiants%(comment)s)"""
-                % infos
-            )
-            if with_checkbox:
-                H.append(
-                    """ (Select.
-                <a href="#" class="stdlink" onclick="sem_select('%(id)s', true);">tous</a>
-                <a href="#" class="stdlink" onclick="sem_select('%(id)s', false );">aucun</a>"""  # "
-                    % infos
+                etuds_select_box(
+                    etuds,
+                    infos,
+                    with_checkbox=with_checkbox,
+                    sel_inscrits=sel_inscrits,
+                    xls_url=xls_url,
+                    inscrits_ailleurs=inscrits_ailleurs,
                 )
-            if sel_inscrits:
-                H.append(
-                    """<a href="#" class="stdlink" onclick="sem_select_inscrits('%(id)s');">inscrits</a>"""
-                    % infos
-                )
-            if with_checkbox or sel_inscrits:
-                H.append(")")
-            if base_url and etuds:
-                url = scu.build_url_query(base_url, export_cat_xls=src_cat)
-                H.append(f'<a href="{url}">{scu.ICON_XLS}</a>&nbsp;')
-            H.append("</div>")
-            for etud in etuds:
-                if etud.get("inscrit", False):
-                    c = " deja-inscrit"
-                    checked = 'checked="checked"'
-                else:
-                    checked = ""
-                    if etud["etudid"] in inscrits_ailleurs:
-                        c = " inscrit-ailleurs"
-                    else:
-                        c = ""
-                sco_etud.format_etud_ident(etud)
-                if etud["etudid"]:
-                    elink = f"""<a id="{etud['etudid']}" class="discretelink etudinfo {c}"
-                        href="{ url_for(
-                            'scolar.fiche_etud',
-                            scodoc_dept=g.scodoc_dept,
-                            etudid=etud['etudid'],
-                        )
-                        }">{etud['nomprenom']}</a>
-                    """
-                else:
-                    # ce n'est pas un etudiant ScoDoc
-                    elink = etud["nomprenom"]
-
-                if etud.get("datefinalisationinscription", None):
-                    elink += (
-                        '<span class="finalisationinscription">'
-                        + " : inscription finalisée le "
-                        + etud["datefinalisationinscription"].strftime(scu.DATE_FMT)
-                        + "</span>"
-                    )
-
-                if not etud.get("paiementinscription", True):
-                    elink += '<span class="paspaye"> (non paiement)</span>'
-
-                H.append("""<div class="pas_etud%s">""" % c)
-                if "etape" in etud:
-                    etape_str = etud["etape"] or ""
-                else:
-                    etape_str = ""
-                H.append("""<span class="sp_etape">%s</span>""" % etape_str)
-                if with_checkbox:
-                    H.append(
-                        """<input type="checkbox" name="%s:list" value="%s" %s>"""
-                        % (checkbox_name, etud[etud_key], checked)
-                    )
-                H.append(elink)
-                if with_checkbox:
-                    H.append("""</input>""")
-                H.append("</div>")
-            H.append("</div>")
+            )
 
     H.append("</div>")
     return "\n".join(H)
 
 
+def etuds_select_box(
+    etuds: list[dict],
+    infos: dict,
+    with_checkbox: bool = True,
+    sel_inscrits: bool = True,
+    xls_url: str = "",
+    inscrits_ailleurs: set = None,
+) -> str:
+    """HTML pour une "boite" avec une liste d'étudiants à sélectionner"""
+    inscrits_ailleurs = inscrits_ailleurs or {}
+    box_id = infos["id"]
+    H = []
+    H.append(
+        f"""<div class="pas_sembox" id="{box_id}">
+        <div class="pas_sembox_title"><a href="{infos['title_target']}" """
+    )
+    help_txt = infos.get("help")
+    if help_txt:  # bubble
+        H.append(f'title="{help_txt}"')
+    H.append(
+        """>%(title)s</a></div>
+        <div class="pas_sembox_subtitle">(%(nbetuds)d étudiants%(comment)s)"""
+        % infos
+    )
+    if with_checkbox:
+        H.append(
+            f""" (Select.
+        <a href="#" class="stdlink" onclick="sem_select('{box_id}', true);">tous</a>
+        <a href="#" class="stdlink" onclick="sem_select('{box_id}', false );">aucun</a>"""
+        )
+    if sel_inscrits:
+        H.append(
+            f"""<a href="#" class="stdlink" onclick="sem_select_inscrits('{box_id}');">inscrits</a>"""
+        )
+    if with_checkbox or sel_inscrits:
+        H.append(")")
+    if xls_url:
+        H.append(f'<a href="{xls_url}">{scu.ICON_XLS}</a>&nbsp;')
+    H.append("</div>")
+    for etud in etuds:
+        is_inscrit = etud.get("inscrit", False)
+        extra_class = (
+            "deja-inscrit"
+            if is_inscrit
+            else ("inscrit-ailleurs" if etud["etudid"] in inscrits_ailleurs else "")
+        )
+        H.append(
+            _etud_row(
+                etud,
+                with_checkbox=with_checkbox,
+                checkbox_name=infos.get("checkbox_name", "etuds"),
+                etud_key=infos.get("etud_key", "etudid"),
+                is_inscrit=is_inscrit,
+                extra_class=extra_class,
+            )
+        )
+
+    H.append("</div>")
+    return "\n".join(H)
+
+
+def _etud_row(
+    etud: dict,
+    with_checkbox: bool = True,
+    checkbox_name: str = "etuds",
+    etud_key: str = "",
+    is_inscrit: bool = False,
+    extra_class: str = "",
+) -> str:
+    """HTML 'row' for this etud"""
+    H = []
+    nomprenom = scu.format_nomprenom(etud)
+    if etud["etudid"]:
+        elink = f"""<a id="{etud['etudid']}" class="discretelink etudinfo {extra_class}"
+            href="{ url_for(
+                'scolar.fiche_etud',
+                scodoc_dept=g.scodoc_dept,
+                etudid=etud['etudid'],
+            )
+            }">{nomprenom}</a>
+        """
+    else:
+        # ce n'est pas un etudiant ScoDoc
+        elink = nomprenom
+
+    if etud.get("datefinalisationinscription", None):
+        elink += (
+            '<span class="finalisationinscription">'
+            + " : inscription finalisée le "
+            + etud["datefinalisationinscription"].strftime(scu.DATE_FMT)
+            + "</span>"
+        )
+
+    if not etud.get("paiementinscription", True):
+        elink += '<span class="paspaye"> (non paiement)</span>'
+
+    H.append(f"""<div class="pas_etud {extra_class}">""")
+    if "etape" in etud:
+        etape_str = etud["etape"] or ""
+    else:
+        etape_str = ""
+    H.append(f"""<span class="sp_etape">{etape_str}</span>""")
+    if with_checkbox:
+        H.append(
+            f"""<input type="checkbox" name="{checkbox_name}:list"
+                value="{etud[etud_key]}" {'checked="checked"' if is_inscrit else ''}>"""
+        )
+    H.append(elink)
+    if with_checkbox:
+        H.append("""</input>""")
+    H.append("</div>")
+    return "\n".join(H)
+
+
 def etuds_select_box_xls(src_cat):
     "export a box to excel"
     etuds = src_cat["etuds"]