Skip to content

Commit 5497424

Browse files
authored
Merge pull request #3078 from uclouvain/hotfix/OS-1741
2 parents 270ea52 + 5612167 commit 5497424

File tree

11 files changed

+165
-47
lines changed

11 files changed

+165
-47
lines changed

ddd/admission/doctorat/preparation/domain/model/groupe_de_supervision.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -120,8 +120,8 @@ def get_membre_CA(self, signataire_id: str) -> 'MembreCAIdentity':
120120
return membre_CA
121121

122122
def inviter_a_signer(self) -> None:
123-
"""Inviter à signer tous les promoteurs et membres CA non invités ou refusés"""
124-
etats_initiaux = [ChoixEtatSignature.NOT_INVITED, ChoixEtatSignature.DECLINED]
123+
"""Inviter à signer tous les promoteurs et membres CA invités, non invités ou refusés"""
124+
etats_initiaux = [ChoixEtatSignature.INVITED, ChoixEtatSignature.NOT_INVITED, ChoixEtatSignature.DECLINED]
125125
for promoteur in filter(lambda s: s.etat in etats_initiaux, self.signatures_promoteurs):
126126
InviterASignerValidatorList(groupe_de_supervision=self, signataire_id=promoteur.promoteur_id).validate()
127127
self.signatures_promoteurs = [s for s in self.signatures_promoteurs if s != promoteur]

ddd/admission/doctorat/preparation/domain/validator/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -105,11 +105,11 @@
105105
from ._should_proposition_statut_etre_correct_pour_soumission_ca import (
106106
ShouldPropositionStatutEtreCorrectPourSoumissionCA,
107107
)
108+
from ._should_signataire_a_pas_deja_approuve import ShouldSignataireAPasDejaApprouve
108109
from ._should_signataire_etre_dans_groupe_de_supervision import (
109110
ShouldSignataireEtreDansGroupeDeSupervision,
110111
)
111112
from ._should_signataire_etre_invite import ShouldSignataireEtreInvite
112-
from ._should_signataire_pas_invite import ShouldSignatairePasDejaInvite
113113
from ._should_signatures_pas_etre_envoyees import ShouldSignaturesPasEtreEnvoyees
114114
from ._should_type_contrat_travail_dependre_type_financement import (
115115
ShouldTypeContratTravailDependreTypeFinancement,
@@ -123,7 +123,7 @@
123123
"ShouldPromoteurEtreDansGroupeDeSupervision",
124124
"ShouldSignataireEtreDansGroupeDeSupervision",
125125
"ShouldSignataireEtreInvite",
126-
"ShouldSignatairePasDejaInvite",
126+
"ShouldSignataireAPasDejaApprouve",
127127
"ShouldTypeContratTravailDependreTypeFinancement",
128128
"ShouldGroupeDeSupervisionNonCompletPourMembresCA",
129129
"ShouldGroupeDeSupervisionNonCompletPourPromoteurs",

ddd/admission/doctorat/preparation/domain/validator/_should_signataire_pas_invite.py renamed to ddd/admission/doctorat/preparation/domain/validator/_should_signataire_a_pas_deja_approuve.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2021 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -30,18 +30,18 @@
3030
from admission.ddd.admission.doctorat.preparation.business_types import *
3131
from admission.ddd.admission.doctorat.preparation.domain.model.enums import ChoixEtatSignature
3232
from admission.ddd.admission.doctorat.preparation.domain.validator.exceptions import (
33-
SignataireDejaInviteException,
33+
SignataireADejaApprouveException,
3434
)
3535
from base.ddd.utils.business_validator import BusinessValidator
3636

3737

3838
@attr.dataclass(frozen=True, slots=True)
39-
class ShouldSignatairePasDejaInvite(BusinessValidator):
39+
class ShouldSignataireAPasDejaApprouve(BusinessValidator):
4040
groupe_de_supervision: 'GroupeDeSupervision'
4141
signataire_id: Union['PromoteurIdentity', 'MembreCAIdentity']
4242

4343
def validate(self, *args, **kwargs): # pragma: no cover
44-
etats_initiaux = [ChoixEtatSignature.NOT_INVITED, ChoixEtatSignature.DECLINED]
44+
etats_initiaux = [ChoixEtatSignature.INVITED, ChoixEtatSignature.NOT_INVITED, ChoixEtatSignature.DECLINED]
4545
if any(
4646
signature
4747
for signature in self.groupe_de_supervision.signatures_promoteurs
@@ -51,4 +51,4 @@ def validate(self, *args, **kwargs): # pragma: no cover
5151
for signature in self.groupe_de_supervision.signatures_membres_CA
5252
if signature.membre_CA_id == self.signataire_id and signature.etat not in etats_initiaux
5353
):
54-
raise SignataireDejaInviteException
54+
raise SignataireADejaApprouveException

ddd/admission/doctorat/preparation/domain/validator/exceptions.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -697,6 +697,14 @@ def __init__(self, **kwargs):
697697
super().__init__(message, **kwargs)
698698

699699

700+
class SignataireADejaApprouveException(BusinessException):
701+
status_code = "PROPOSITION-77"
702+
703+
def __init__(self, **kwargs): # pragma: no cover
704+
message = _("The member of the supervision group can't be invited because he has already agreed.")
705+
super().__init__(message, **kwargs)
706+
707+
700708
class AbsenceDeDetteNonCompleteeDoctoratException(BusinessException):
701709
status_code = "DOCTORAT-1"
702710

ddd/admission/doctorat/preparation/domain/validator/validator_by_business_action.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -285,7 +285,7 @@ def get_data_contract_validators(self) -> List[BusinessValidator]:
285285
def get_invariants_validators(self) -> List[BusinessValidator]:
286286
return [
287287
ShouldSignataireEtreDansGroupeDeSupervision(self.groupe_de_supervision, self.signataire_id),
288-
ShouldSignatairePasDejaInvite(self.groupe_de_supervision, self.signataire_id),
288+
ShouldSignataireAPasDejaApprouve(self.groupe_de_supervision, self.signataire_id),
289289
]
290290

291291

ddd/admission/doctorat/preparation/test/factory/groupe_de_supervision.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2024 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
2323
# see http://www.gnu.org/licenses/.
2424
#
2525
# ##############################################################################
26+
import datetime
2627
import uuid
2728
from typing import List, Optional
2829

@@ -147,14 +148,22 @@ class GroupeDeSupervisionSC3DPAvecPromoteurEtMembreEtCotutelleFactory(_GroupeDeS
147148
proposition_id = factory.SubFactory(_PropositionIdentityFactory, uuid='uuid-SC3DP-promoteur-membre-cotutelle')
148149
signatures_promoteurs = factory.LazyFunction(
149150
lambda: [
150-
_SignaturePromoteurFactory(promoteur_id__uuid='promoteur-SC3DP-externe'),
151-
_SignaturePromoteurFactory(promoteur_id__uuid='promoteur-SC3DP'),
151+
_SignaturePromoteurFactory(promoteur_id__uuid='promoteur-SC3DP-externe', date=datetime.datetime.now()),
152+
_SignaturePromoteurFactory(promoteur_id__uuid='promoteur-SC3DP', date=datetime.datetime.now()),
152153
]
153154
)
154155
signatures_membres_CA = factory.LazyFunction(
155156
lambda: [
156-
_SignatureMembreCAFactory(membre_CA_id__uuid='membre-ca-SC3DP', etat=ChoixEtatSignature.INVITED),
157-
_SignatureMembreCAFactory(membre_CA_id__uuid='membre-ca-SC3DP2', etat=ChoixEtatSignature.INVITED),
157+
_SignatureMembreCAFactory(
158+
membre_CA_id__uuid='membre-ca-SC3DP',
159+
etat=ChoixEtatSignature.INVITED,
160+
date=datetime.datetime.now(),
161+
),
162+
_SignatureMembreCAFactory(
163+
membre_CA_id__uuid='membre-ca-SC3DP2',
164+
etat=ChoixEtatSignature.INVITED,
165+
date=datetime.datetime.now(),
166+
),
158167
]
159168
)
160169
cotutelle = factory.SubFactory(

ddd/admission/doctorat/preparation/test/use_case/write/test_demander_signatures_service.py

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@
4242
ChoixEtatSignature,
4343
ChoixStatutPropositionDoctorale,
4444
)
45+
from admission.ddd.admission.doctorat.preparation.domain.model.proposition import PropositionIdentity
4546
from admission.ddd.admission.doctorat.preparation.domain.validator.exceptions import (
4647
CotutelleDoitAvoirAuMoinsUnPromoteurExterneException,
4748
CotutelleNonCompleteException,
@@ -133,17 +134,65 @@ def test_should_demander_signatures(self):
133134
self.assertEqual(signatures[0].etat, ChoixEtatSignature.INVITED)
134135

135136
def test_should_reinviter_si_membre_refuse(self):
136-
cmd = attr.evolve(self.cmd, uuid_proposition="uuid-SC3DP-promoteur-refus-membre-deja-approuve")
137+
proposition_identity = PropositionIdentity(uuid='uuid-SC3DP-promoteur-refus-membre-deja-approuve')
138+
139+
groupe_original = self.groupe_de_supervision_repository.get_by_proposition_id(proposition_identity)
140+
141+
signatures_originales_promoteurs = groupe_original.signatures_promoteurs
142+
signatures_originales_membres_ca = groupe_original.signatures_membres_CA
143+
144+
cmd = attr.evolve(self.cmd, uuid_proposition=proposition_identity.uuid)
145+
137146
proposition_id = self.message_bus.invoke(cmd)
138-
self.assertEqual(proposition_id.uuid, "uuid-SC3DP-promoteur-refus-membre-deja-approuve")
139-
groupe = self.groupe_de_supervision_repository.get_by_proposition_id(proposition_id)
147+
self.assertEqual(proposition_id, proposition_identity)
148+
149+
groupe_a_jour = self.groupe_de_supervision_repository.get_by_proposition_id(proposition_id)
140150
proposition = self.proposition_repository.get(proposition_id)
151+
141152
self.assertEqual(proposition.statut, ChoixStatutPropositionDoctorale.EN_ATTENTE_DE_SIGNATURE)
142153
self.assertTrue(proposition.est_verrouillee_pour_signature)
143-
self.assertEqual(len(groupe.signatures_promoteurs), 1)
144-
self.assertEqual(len(groupe.signatures_membres_CA), 2)
145-
self.assertEqual(groupe.signatures_promoteurs[0].etat, ChoixEtatSignature.INVITED)
146-
self.assertEqual(groupe.signatures_membres_CA[0].etat, ChoixEtatSignature.APPROVED)
154+
155+
self.assertEqual(len(groupe_a_jour.signatures_promoteurs), 1)
156+
self.assertEqual(len(groupe_a_jour.signatures_membres_CA), 2)
157+
158+
self.assertEqual(groupe_a_jour.signatures_promoteurs[0].etat, ChoixEtatSignature.INVITED)
159+
self.assertEqual(groupe_a_jour.signatures_membres_CA[0].etat, ChoixEtatSignature.APPROVED)
160+
self.assertEqual(groupe_a_jour.signatures_membres_CA[1].etat, ChoixEtatSignature.APPROVED)
161+
162+
self.assertNotEqual(signatures_originales_promoteurs[0], groupe_a_jour.signatures_promoteurs[0])
163+
self.assertEqual(signatures_originales_membres_ca[0], groupe_a_jour.signatures_membres_CA[0])
164+
self.assertEqual(signatures_originales_membres_ca[1], groupe_a_jour.signatures_membres_CA[1])
165+
166+
def test_should_reinviter_si_membre_deja_invite(self):
167+
proposition_identity = PropositionIdentity(uuid=self.uuid_proposition)
168+
169+
groupe_original = self.groupe_de_supervision_repository.get_by_proposition_id(proposition_identity)
170+
171+
signatures_originales_promoteurs = groupe_original.signatures_promoteurs
172+
signatures_originales_membres_ca = groupe_original.signatures_membres_CA
173+
174+
proposition_id = self.message_bus.invoke(self.cmd)
175+
176+
self.assertEqual(proposition_id, proposition_identity)
177+
178+
groupe_a_jour = self.groupe_de_supervision_repository.get_by_proposition_id(proposition_id)
179+
proposition = self.proposition_repository.get(proposition_identity)
180+
181+
self.assertEqual(proposition.statut, ChoixStatutPropositionDoctorale.EN_ATTENTE_DE_SIGNATURE)
182+
self.assertTrue(proposition.est_verrouillee_pour_signature)
183+
184+
self.assertEqual(len(groupe_a_jour.signatures_promoteurs), 2)
185+
self.assertEqual(len(groupe_a_jour.signatures_membres_CA), 2)
186+
187+
self.assertEqual(groupe_a_jour.signatures_promoteurs[0].etat, ChoixEtatSignature.INVITED)
188+
self.assertEqual(groupe_a_jour.signatures_promoteurs[1].etat, ChoixEtatSignature.INVITED)
189+
self.assertEqual(groupe_a_jour.signatures_membres_CA[0].etat, ChoixEtatSignature.INVITED)
190+
self.assertEqual(groupe_a_jour.signatures_membres_CA[1].etat, ChoixEtatSignature.INVITED)
191+
192+
self.assertNotEqual(signatures_originales_promoteurs[0], groupe_a_jour.signatures_promoteurs[0])
193+
self.assertNotEqual(signatures_originales_promoteurs[1], groupe_a_jour.signatures_promoteurs[1])
194+
self.assertNotEqual(signatures_originales_membres_ca[0], groupe_a_jour.signatures_membres_CA[0])
195+
self.assertNotEqual(signatures_originales_membres_ca[1], groupe_a_jour.signatures_membres_CA[1])
147196

148197
def test_should_pas_demander_si_detail_projet_pas_complete_pour_admission(self):
149198
cmd = attr.evolve(self.cmd, uuid_proposition='uuid-SC3DP-no-project')

infrastructure/admission/doctorat/preparation/repository/groupe_de_supervision.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The core business involves the administration of students, teachers,
77
# courses, programs and so on.
88
#
9-
# Copyright (C) 2015-2025 Université catholique de Louvain (http://www.uclouvain.be)
9+
# Copyright (C) 2015-2026 Université catholique de Louvain (http://www.uclouvain.be)
1010
#
1111
# This program is free software: you can redistribute it and/or modify
1212
# it under the terms of the GNU General Public License as published by
@@ -264,8 +264,9 @@ def _update_members(
264264
):
265265
for actor in member_list:
266266
membre = cls._get_member(signature_list, str(actor.uuid))
267-
if actor.state != membre.etat.name:
267+
if actor.state != membre.etat.name or actor.last_state_date != membre.date:
268268
StateHistory.objects.create(state=membre.etat.name, actor_id=actor.id)
269+
if actor.state != membre.etat.name:
269270
if membre.etat.name in [ChoixEtatSignature.APPROVED.name, ChoixEtatSignature.DECLINED.name]:
270271
actor.comment = membre.commentaire_externe
271272
actor.supervisionactor.pdf_from_candidate = membre.pdf
@@ -331,10 +332,10 @@ def add_member(
331332
language=language,
332333
)
333334
if type == ActorType.PROMOTER:
334-
group_name, model = 'promoters', Promoter
335+
_group_name, model = 'promoters', Promoter
335336
signataire_id = PromoteurIdentity(str(new_actor.uuid))
336337
else:
337-
group_name, model = 'committee_members', CommitteeMember
338+
_group_name, model = 'committee_members', CommitteeMember
338339
signataire_id = MembreCAIdentity(str(new_actor.uuid))
339340
if invited_by_default:
340341
new_actor.switch_state(SignatureState.INVITED)

locale/en/LC_MESSAGES/django.po

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6671,6 +6671,11 @@ msgstr ""
66716671
msgid "The member is not external."
66726672
msgstr ""
66736673

6674+
msgid ""
6675+
"The member of the supervision group can't be invited because he has already "
6676+
"agreed."
6677+
msgstr ""
6678+
66746679
msgid "The mimetypes must be a list with at least one mimetype."
66756680
msgstr ""
66766681

locale/fr_BE/LC_MESSAGES/django.po

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7013,8 +7013,9 @@ msgid ""
70137013
"text\">supervisor</span> and <span class=\"bold-text\">supervisory "
70147014
"committee</span>, composed as follows:"
70157015
msgstr ""
7016-
"La Commission doctorale de domaine a approuvé votre proposition de <span class=\"bold-"
7017-
"text\">promoteur·trice</span> et de <span class=\"bold-text\">comité d'accompagnement</span>, constitué de la manière suivante :"
7016+
"La Commission doctorale de domaine a approuvé votre proposition de <span "
7017+
"class=\"bold-text\">promoteur·trice</span> et de <span class=\"bold-"
7018+
"text\">comité d'accompagnement</span>, constitué de la manière suivante :"
70187019

70197020
msgid ""
70207021
"The Previous experience must be in the \"Sufficient\" status in order to do "
@@ -7391,6 +7392,11 @@ msgstr "Le nombre maximum de documents doit être un nombre positif."
73917392
msgid "The member is not external."
73927393
msgstr "Le membre n'est pas externe."
73937394

7395+
msgid ""
7396+
"The member of the supervision group can't be invited because he has already "
7397+
"agreed."
7398+
msgstr "Le membre du groupe de supervision ne peut pas être invité car il a déjà approuvé."
7399+
73947400
msgid "The mimetypes must be a list with at least one mimetype."
73957401
msgstr ""
73967402
"Les types MIME doivent être spécifiés avec une liste contenant au-moins un "
@@ -7613,7 +7619,9 @@ msgstr ""
76137619
msgid ""
76147620
"The tuition fee communicated by the Enrolment Office is based on your status."
76157621
msgstr ""
7616-
"Le montant des droits d'inscription communiqué par le Service des inscriptions est déterminé en fonction de votre statut."
7622+
"Le montant des droits d'inscription communiqué par le Service des "
7623+
"inscriptions est déterminé en fonction de votre statut."
7624+
76177625
#, python-format
76187626
msgid "The type of the admission form item is unknown (%(type)s)."
76197627
msgstr "Le type de l'élément du formulaire d'admission est inconnu (%(type)s)."
@@ -8220,8 +8228,9 @@ msgid ""
82208228
"We would like to draw your attention to the need to meet the deadline set "
82218229
"out in the PhD regulations for the submission of your mid-term evaluation."
82228230
msgstr ""
8223-
"Nous attirons votre attention sur la nécessite de respecter le délai prévu par le règlement doctoral pour la "
8224-
"présentation de votre épreuve de confirmation."
8231+
"Nous attirons votre attention sur la nécessite de respecter le délai prévu "
8232+
"par le règlement doctoral pour la présentation de votre épreuve de "
8233+
"confirmation."
82258234

82268235
msgid "Weight"
82278236
msgstr "Poids"
@@ -8496,10 +8505,12 @@ msgid ""
84968505
"partner university, submission of application form) must be completed within "
84978506
"the first 12 months of starting your thesis."
84988507
msgstr ""
8499-
"Vous faites votre thèse dans le cadre d'une <span class=\"bold-text\">cotutelle"
8500-
"</span>. Veuillez noter que les procédures liées à l'établissement d'une cotutelle "
8501-
"(accord de la CDD sur le principe de la cotutelle, contacts avec l'université partenaire, envoi de la fiche d'ouverture) doivent "
8502-
"être réalisées dans les douze premiers mois suivant le démarrage de la thèse."
8508+
"Vous faites votre thèse dans le cadre d'une <span class=\"bold-"
8509+
"text\">cotutelle</span>. Veuillez noter que les procédures liées à "
8510+
"l'établissement d'une cotutelle (accord de la CDD sur le principe de la "
8511+
"cotutelle, contacts avec l'université partenaire, envoi de la fiche "
8512+
"d'ouverture) doivent être réalisées dans les douze premiers mois suivant le "
8513+
"démarrage de la thèse."
85038514

85048515
msgid ""
85058516
"You can add any document you feel is relevant to your application "
@@ -8802,8 +8813,9 @@ msgid ""
88028813
"stages of the PhD programme and more helpful information on the website of "
88038814
"your PhD Field Committee."
88048815
msgstr ""
8805-
"Vous trouverez sur le site de votre Commission doctorale de domaine le règlement doctoral, les dispositions "
8806-
"particulières de votre domaine, les étapes du parcours doctoral ainsi que toute autre information utile."
8816+
"Vous trouverez sur le site de votre Commission doctorale de domaine le "
8817+
"règlement doctoral, les dispositions particulières de votre domaine, les "
8818+
"étapes du parcours doctoral ainsi que toute autre information utile."
88078819

88088820
msgid ""
88098821
"You will receive a monthly newsletter explaining all the benefits and "

0 commit comments

Comments
 (0)