diff --git a/app/but/jury_but.py b/app/but/jury_but.py index fb255f0d2218b6e8b23422b54bfe1cab3fda0d66..840933c0871e6d225b51b4c364d4ddcb47f7fc72 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -571,6 +571,32 @@ class DecisionsProposeesAnnee(DecisionsProposees): # s'il n'y a pas de codee, efface dec.record(dec.codes[0]) + def erase(self): + """Efface les décisions de jury de cet étudiant + pour cette année: décisions d'UE, de RCUE, d'année, + et autorisations d'inscription émises. + """ + for dec_ue in self.decisions_ues.values(): + dec_ue.erase() + for dec_rcue in self.decisions_rcue_by_niveau.values(): + dec_rcue.erase() + if self.formsemestre_impair: + ScolarAutorisationInscription.delete_autorisation_etud( + self.etud.id, self.formsemestre_impair.id + ) + if self.formsemestre_pair: + ScolarAutorisationInscription.delete_autorisation_etud( + self.etud.id, self.formsemestre_pair.id + ) + validations = ApcValidationAnnee.query.filter_by( + etudid=self.etud.id, + formsemestre_id=self.formsemestre_impair.id, + ordre=self.annee_but, + ) + for validation in validations: + db.session.delete(validation) + db.session.flush() + class DecisionsProposeesRCUE(DecisionsProposees): """Liste des codes de décisions que l'on peut proposer pour @@ -637,6 +663,14 @@ class DecisionsProposeesRCUE(DecisionsProposees): db.session.add(self.validation) self.recorded = True + def erase(self): + """Efface la décision de jury de cet étudiant pour cet RCUE""" + # par prudence, on requete toutes les validations, en cas de doublons + validations = self.rcue.query_validations() + for validation in validations: + db.session.delete(validation) + db.session.flush() + class DecisionsProposeesUE(DecisionsProposees): """Décisions de jury sur une UE du BUT @@ -743,6 +777,16 @@ class DecisionsProposeesUE(DecisionsProposees): db.session.add(self.validation) self.recorded = True + def erase(self): + """Efface la décision de jury de cet étudiant pour cette UE""" + # par prudence, on requete toutes les validations, en cas de doublons + validations = ScolarFormSemestreValidation.query.filter_by( + etudid=self.etud.id, formsemestre_id=self.formsemestre.id, ue_id=self.ue.id + ) + for validation in validations: + db.session.delete(validation) + db.session.flush() + class BUTCursusEtud: # WIP TODO """Validation du cursus d'un étudiant""" diff --git a/app/scodoc/sco_pvjury.py b/app/scodoc/sco_pvjury.py index cfa7bae61528ec728d2e35c2ac0f49da7e68baaf..7ddc0aa1d9e0422637b04ec7713bc08c470bffc9 100644 --- a/app/scodoc/sco_pvjury.py +++ b/app/scodoc/sco_pvjury.py @@ -492,9 +492,7 @@ def pvjury_table( def formsemestre_pvjury(formsemestre_id, format="html", publish=True): - """Page récapitulant les décisions de jury - dpv: result of dict_pvjury - """ + """Page récapitulant les décisions de jury""" footer = html_sco_header.sco_footer() dpv = dict_pvjury(formsemestre_id, with_prev=True) diff --git a/app/templates/confirm_dialog.html b/app/templates/confirm_dialog.html new file mode 100644 index 0000000000000000000000000000000000000000..8067f6c74bfb3848dfb69d372fd4a79dc6464b28 --- /dev/null +++ b/app/templates/confirm_dialog.html @@ -0,0 +1,22 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} + +<h2>{{ title }}</h2> + +<div style="margin-top: 16px;"> + {{ explanation }} +</div> +<div style="margin-top: 16px;"> + <form method="post"> + <input type="submit" value="OK" /> + {% if cancel_url %} + <input type="button" value="Annuler" style="margin-left: 16px;" + onClick="document.location='{{ cancel_url }}';" /> + {% endif %} + </form> +</div> + +{% endblock %} \ No newline at end of file diff --git a/app/views/notes.py b/app/views/notes.py index 97aa43bafe7f5905a76e74eef0a4b30e19285dc8..005e7963a6ed515cfbdd6649e36fac6977ac002b 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -2262,6 +2262,13 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int): etudid=etudid, ) ) + if deca.code_valide: + erase_span = f"""<a href="{ + url_for("notes.formsemestre_jury_but_erase", + scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, + etudid=etudid)}" class="stdlink">effacer décisions</a>""" + else: + erase_span = "" H.append( f""" <div> @@ -2279,6 +2286,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int): disabled=True, klass="manual") } <span>({'non ' if deca.code_valide is None else ''}enregistrée)</span> + <span>{erase_span}</span> </div> <span class="but_explanation">{deca.explanation}</span> </div> @@ -2630,6 +2638,41 @@ def formsemestre_jury_but_recap(formsemestre_id: int, selected_etudid: int = Non ) +@bp.route( + "/formsemestre_jury_but_erase/<int:formsemestre_id>/<int:etudid>", + methods=["GET", "POST"], +) +@scodoc +@permission_required(Permission.ScoView) +def formsemestre_jury_but_erase(formsemestre_id: int, etudid: int = None): + """Supprime la décision de jury BUT pour cette année""" + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) + if not formsemestre.formation.is_apc(): + raise ScoValueError("semestre non BUT") + etud: Identite = Identite.query.get_or_404(etudid) + if not sco_permissions_check.can_validate_sem(formsemestre_id): + raise ScoValueError("opération non autorisée") + dest_url = url_for( + "notes.formsemestre_validation_but", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre_id, + etudid=etudid, + ) + if request.method == "POST": + deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre) + deca.erase() + db.session.commit() + flash("décisions de jury effacées") + return redirect(dest_url) + + return render_template( + "confirm_dialog.html", + title=f"Effacer les validations de jury de {etud.nomprenom} ?", + explanation="""Les validations de toutes les UE, RCUE (compétences) et année seront effacées.""", + cancel_url=dest_url, + ) + + sco_publish( "/formsemestre_lettres_individuelles", sco_pvjury.formsemestre_lettres_individuelles,