koprogo_api/domain/copropriete/
signatures_pv.rs1use uuid::Uuid;
39
40#[derive(Debug, Clone, PartialEq)]
42pub struct SeanceCloturee {
43 pub president: Uuid,
45 pub president_est_coproprietaire: bool,
46 pub secretaire: Option<Uuid>,
48 pub presents_a_la_cloture: Vec<Uuid>,
51}
52
53#[derive(Debug, Clone, PartialEq)]
55pub enum SignatureManquante {
56 President { president: Uuid },
58 SecretaireNonDesigne,
60 Secretaire { secretaire: Uuid },
62 CoproprietairesPresents { manquants: Vec<Uuid> },
64 PresidentNonCoproprietaire { president: Uuid },
66}
67
68impl std::fmt::Display for SignatureManquante {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::President { president } => {
72 write!(
73 f,
74 "Art. 3.87 § 10 : le président {president} n'a pas signé."
75 )
76 }
77 Self::SecretaireNonDesigne => write!(
78 f,
79 "Art. 3.87 § 10 : aucun secrétaire n'a été désigné à l'ouverture de la séance."
80 ),
81 Self::Secretaire { secretaire } => {
82 write!(
83 f,
84 "Art. 3.87 § 10 : le secrétaire {secretaire} n'a pas signé."
85 )
86 }
87 Self::CoproprietairesPresents { manquants } => write!(
88 f,
89 "Art. 3.87 § 10 : {} copropriétaire(s) encore présent(s) à la clôture n'ont \
90 pas signé.",
91 manquants.len()
92 ),
93 Self::PresidentNonCoproprietaire { president } => write!(
94 f,
95 "Art. 3.87 § 5 : {president} a présidé sans être copropriétaire. \
96 « L'assemblée générale est présidée par un copropriétaire » — un syndic \
97 qui préside vicie le procès-verbal."
98 ),
99 }
100 }
101}
102
103pub fn verifier_signatures(
110 seance: &SeanceCloturee,
111 signataires: &[Uuid],
112) -> Vec<SignatureManquante> {
113 let mut manquements = Vec::new();
114
115 if !seance.president_est_coproprietaire {
116 manquements.push(SignatureManquante::PresidentNonCoproprietaire {
117 president: seance.president,
118 });
119 }
120 if !signataires.contains(&seance.president) {
121 manquements.push(SignatureManquante::President {
122 president: seance.president,
123 });
124 }
125
126 match seance.secretaire {
127 None => manquements.push(SignatureManquante::SecretaireNonDesigne),
128 Some(secretaire) if !signataires.contains(&secretaire) => {
129 manquements.push(SignatureManquante::Secretaire { secretaire })
130 }
131 Some(_) => {}
132 }
133
134 let manquants: Vec<Uuid> = seance
135 .presents_a_la_cloture
136 .iter()
137 .filter(|p| !signataires.contains(p))
138 .copied()
139 .collect();
140 if !manquants.is_empty() {
141 manquements.push(SignatureManquante::CoproprietairesPresents { manquants });
142 }
143
144 manquements
145}
146
147pub fn pv_valablement_signe(seance: &SeanceCloturee, signataires: &[Uuid]) -> bool {
149 verifier_signatures(seance, signataires).is_empty()
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 fn seance(president: Uuid, secretaire: Uuid, presents: Vec<Uuid>) -> SeanceCloturee {
157 SeanceCloturee {
158 president,
159 president_est_coproprietaire: true,
160 secretaire: Some(secretaire),
161 presents_a_la_cloture: presents,
162 }
163 }
164
165 #[test]
166 fn happy_un_pv_signe_par_tous_est_valable() {
167 let president = Uuid::new_v4();
168 let secretaire = Uuid::new_v4();
169 let present = Uuid::new_v4();
170 let s = seance(president, secretaire, vec![present]);
171
172 assert!(pv_valablement_signe(&s, &[president, secretaire, present]));
173 }
174
175 #[test]
176 fn negative_un_pv_que_le_president_na_pas_signe_est_incomplet() {
177 let president = Uuid::new_v4();
178 let secretaire = Uuid::new_v4();
179 let s = seance(president, secretaire, vec![]);
180
181 assert_eq!(
182 verifier_signatures(&s, &[secretaire]),
183 vec![SignatureManquante::President { president }]
184 );
185 }
186
187 #[test]
192 fn security_un_president_non_coproprietaire_vicie_le_pv() {
193 let syndic = Uuid::new_v4();
194 let secretaire = Uuid::new_v4();
195 let s = SeanceCloturee {
196 president: syndic,
197 president_est_coproprietaire: false,
198 secretaire: Some(secretaire),
199 presents_a_la_cloture: vec![],
200 };
201
202 let manquements = verifier_signatures(&s, &[syndic, secretaire]);
203 assert_eq!(
204 manquements,
205 vec![SignatureManquante::PresidentNonCoproprietaire { president: syndic }],
206 "il a bien signé, mais il n'aurait pas dû présider"
207 );
208 }
209
210 #[test]
213 fn negative_sans_secretaire_designe_le_pv_est_incomplet() {
214 let president = Uuid::new_v4();
215 let s = SeanceCloturee {
216 president,
217 president_est_coproprietaire: true,
218 secretaire: None,
219 presents_a_la_cloture: vec![],
220 };
221
222 assert_eq!(
223 verifier_signatures(&s, &[president]),
224 vec![SignatureManquante::SecretaireNonDesigne]
225 );
226 }
227
228 #[test]
233 fn happy_un_coproprietaire_parti_en_cours_de_seance_na_pas_a_signer() {
234 let president = Uuid::new_v4();
235 let secretaire = Uuid::new_v4();
236 let reste = Uuid::new_v4();
237 let _parti_plus_tot = Uuid::new_v4();
238
239 let s = seance(president, secretaire, vec![reste]);
241
242 assert!(pv_valablement_signe(&s, &[president, secretaire, reste]));
243 }
244
245 #[test]
247 fn security_un_present_a_la_cloture_qui_ne_signe_pas_est_releve() {
248 let president = Uuid::new_v4();
249 let secretaire = Uuid::new_v4();
250 let recalcitrant = Uuid::new_v4();
251 let s = seance(president, secretaire, vec![recalcitrant]);
252
253 assert_eq!(
254 verifier_signatures(&s, &[president, secretaire]),
255 vec![SignatureManquante::CoproprietairesPresents {
256 manquants: vec![recalcitrant]
257 }]
258 );
259 }
260
261 #[test]
266 fn negative_tous_les_manquements_remontent_ensemble() {
267 let syndic = Uuid::new_v4();
268 let present = Uuid::new_v4();
269 let s = SeanceCloturee {
270 president: syndic,
271 president_est_coproprietaire: false,
272 secretaire: None,
273 presents_a_la_cloture: vec![present],
274 };
275
276 let manquements = verifier_signatures(&s, &[]);
277 assert_eq!(
278 manquements.len(),
279 4,
280 "président non copropriétaire, président non signataire, \
281 secrétaire non désigné, présent non signataire"
282 );
283 }
284
285 #[test]
286 fn happy_une_seance_sans_personne_a_la_cloture_ne_demande_que_deux_signatures() {
287 let president = Uuid::new_v4();
289 let secretaire = Uuid::new_v4();
290 let s = seance(president, secretaire, vec![]);
291
292 assert!(pv_valablement_signe(&s, &[president, secretaire]));
293 }
294}