koprogo_api/domain/copropriete/
conflit_dinterets.rs1use super::vote::Vote;
28use uuid::Uuid;
29
30#[derive(Debug, Clone, PartialEq)]
32pub struct ConflitDinterets {
33 pub interesse: Uuid,
35 pub bulletins: usize,
37 pub par_procuration: bool,
39}
40
41impl std::fmt::Display for ConflitDinterets {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 let voie = if self.par_procuration {
44 " (dont par procuration, ce qui ne contourne rien)"
45 } else {
46 ""
47 };
48 write!(
49 f,
50 "Art. 3.87 § 9 : {} est le prestataire de la mission délibérée et a pris part \
51 au vote pour {} bulletin(s){voie}. La délibération est à reprendre sans lui.",
52 self.interesse, self.bulletins
53 )
54 }
55}
56
57pub fn verifier_conflit_dinterets(
71 votes: &[Vote],
72 prestataire_de_la_mission: Option<Uuid>,
73) -> Result<(), ConflitDinterets> {
74 let Some(interesse) = prestataire_de_la_mission else {
75 return Ok(());
76 };
77
78 let bulletins: Vec<&Vote> = votes
79 .iter()
80 .filter(|v| v.owner_id == interesse || v.effective_voter_id() == interesse)
81 .collect();
82
83 if bulletins.is_empty() {
84 return Ok(());
85 }
86
87 Err(ConflitDinterets {
88 interesse,
89 bulletins: bulletins.len(),
90 par_procuration: bulletins.iter().any(|v| v.is_proxy_vote()),
91 })
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::domain::copropriete::vote::VoteChoice;
98 use rust_decimal::Decimal;
99 use rust_decimal_macros::dec;
100
101 fn vote(proprietaire: Uuid, voix: Decimal, mandataire: Option<Uuid>) -> Vote {
102 Vote::new(
103 Uuid::new_v4(),
104 proprietaire,
105 Uuid::new_v4(),
106 VoteChoice::Pour,
107 voix,
108 mandataire,
109 )
110 .expect("vote valide")
111 }
112
113 #[test]
114 fn happy_une_resolution_sans_prestataire_ne_declenche_rien() {
115 let votes = vec![
116 vote(Uuid::new_v4(), dec!(300), None),
117 vote(Uuid::new_v4(), dec!(300), None),
118 ];
119 assert!(verifier_conflit_dinterets(&votes, None).is_ok());
120 }
121
122 #[test]
123 fn happy_le_prestataire_absent_du_vote_ne_pose_pas_de_probleme() {
124 let entrepreneur = Uuid::new_v4();
125 let votes = vec![
126 vote(Uuid::new_v4(), dec!(300), None),
127 vote(Uuid::new_v4(), dec!(300), None),
128 ];
129 assert!(verifier_conflit_dinterets(&votes, Some(entrepreneur)).is_ok());
130 }
131
132 #[test]
133 fn negative_le_prestataire_ne_vote_pas_sur_sa_propre_mission() {
134 let entrepreneur = Uuid::new_v4();
135 let votes = vec![
136 vote(entrepreneur, dec!(150), None),
137 vote(Uuid::new_v4(), dec!(300), None),
138 ];
139
140 let conflit =
141 verifier_conflit_dinterets(&votes, Some(entrepreneur)).expect_err("doit refuser");
142 assert_eq!(conflit.interesse, entrepreneur);
143 assert_eq!(conflit.bulletins, 1);
144 assert!(!conflit.par_procuration);
145 }
146
147 #[test]
148 fn security_donner_procuration_ne_contourne_pas_la_regle() {
149 let entrepreneur = Uuid::new_v4();
153 let complaisant = Uuid::new_v4();
154 let votes = vec![
155 vote(entrepreneur, dec!(150), Some(complaisant)),
156 vote(Uuid::new_v4(), dec!(300), None),
157 ];
158
159 let conflit =
160 verifier_conflit_dinterets(&votes, Some(entrepreneur)).expect_err("doit refuser");
161 assert_eq!(conflit.bulletins, 1);
162 assert!(conflit.par_procuration);
163 }
164
165 #[test]
166 fn security_recevoir_procuration_ne_contourne_pas_davantage() {
167 let entrepreneur = Uuid::new_v4();
170 let votes = vec![
171 vote(Uuid::new_v4(), dec!(150), Some(entrepreneur)),
172 vote(Uuid::new_v4(), dec!(300), None),
173 ];
174
175 let conflit =
176 verifier_conflit_dinterets(&votes, Some(entrepreneur)).expect_err("doit refuser");
177 assert_eq!(conflit.bulletins, 1);
178 assert!(conflit.par_procuration);
179 }
180
181 #[test]
182 fn edge_les_deux_voies_cumulees_comptent_chaque_bulletin() {
183 let entrepreneur = Uuid::new_v4();
184 let votes = vec![
185 vote(entrepreneur, dec!(150), None),
186 vote(Uuid::new_v4(), dec!(100), Some(entrepreneur)),
187 vote(Uuid::new_v4(), dec!(300), None),
188 ];
189
190 let conflit =
191 verifier_conflit_dinterets(&votes, Some(entrepreneur)).expect_err("doit refuser");
192 assert_eq!(conflit.bulletins, 2);
193 }
194}