koprogo_api/domain/copropriete/
personnalite_juridique.rs1use chrono::NaiveDate;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum PersonnaliteJuridique {
43 Inexistante,
46 NonOpposableAuxTiers { depuis: NaiveDate },
51 Acquise { depuis: NaiveDate },
53}
54
55impl PersonnaliteJuridique {
56 pub fn opposable_par_lacp(&self) -> bool {
61 matches!(self, Self::Acquise { .. })
62 }
63
64 pub fn opposable_par_un_tiers(&self) -> bool {
70 matches!(
71 self,
72 Self::Acquise { .. } | Self::NonOpposableAuxTiers { .. }
73 )
74 }
75
76 pub fn depuis(&self) -> Option<NaiveDate> {
78 match self {
79 Self::Inexistante => None,
80 Self::NonOpposableAuxTiers { depuis } | Self::Acquise { depuis } => Some(*depuis),
81 }
82 }
83}
84
85impl std::fmt::Display for PersonnaliteJuridique {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 Self::Inexistante => write!(
89 f,
90 "Aucun lot cédé ni attribué : l'indivision n'est pas née (Art. 3.86 § 1er, 1°)"
91 ),
92 Self::NonOpposableAuxTiers { depuis } => write!(
93 f,
94 "Indivision née le {depuis} mais statuts non transcrits : l'ACP ne peut pas se \
95 prévaloir de sa personnalité envers les tiers, qui peuvent la lui opposer \
96 (Art. 3.86 § 2)"
97 ),
98 Self::Acquise { depuis } => {
99 write!(
100 f,
101 "Personnalité juridique acquise le {depuis} (Art. 3.86 § 1er)"
102 )
103 }
104 }
105 }
106}
107
108pub fn personnalite(
113 premiere_cession_de_lot: Option<NaiveDate>,
114 transcription_statuts: Option<NaiveDate>,
115) -> PersonnaliteJuridique {
116 let Some(cession) = premiere_cession_de_lot else {
117 return PersonnaliteJuridique::Inexistante;
119 };
120 match transcription_statuts {
121 Some(transcription) => PersonnaliteJuridique::Acquise {
122 depuis: cession.max(transcription),
123 },
124 None => PersonnaliteJuridique::NonOpposableAuxTiers { depuis: cession },
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 fn le(annee: i32, mois: u32, jour: u32) -> NaiveDate {
133 NaiveDate::from_ymd_opt(annee, mois, jour).expect("date valide")
134 }
135
136 #[test]
137 fn happy_les_deux_conditions_reunies_donnent_la_personnalite() {
138 let p = personnalite(Some(le(2026, 3, 1)), Some(le(2026, 2, 1)));
139 assert_eq!(
140 p,
141 PersonnaliteJuridique::Acquise {
142 depuis: le(2026, 3, 1)
143 }
144 );
145 assert!(p.opposable_par_lacp());
146 assert!(p.opposable_par_un_tiers());
147 }
148
149 #[test]
152 fn edge_la_personnalite_nait_a_la_seconde_condition_remplie() {
153 let transcription_apres = personnalite(Some(le(2026, 1, 10)), Some(le(2026, 5, 20)));
154 assert_eq!(transcription_apres.depuis(), Some(le(2026, 5, 20)));
155
156 let cession_apres = personnalite(Some(le(2026, 5, 20)), Some(le(2026, 1, 10)));
157 assert_eq!(cession_apres.depuis(), Some(le(2026, 5, 20)));
158 }
159
160 #[test]
161 fn negative_sans_lot_cede_il_ny_a_pas_dassociation() {
162 let p = personnalite(None, Some(le(2026, 2, 1)));
165 assert_eq!(p, PersonnaliteJuridique::Inexistante);
166 assert!(!p.opposable_par_lacp());
167 assert!(!p.opposable_par_un_tiers());
168 }
169
170 #[test]
172 fn security_sans_transcription_la_protection_ne_joue_que_dans_un_sens() {
173 let p = personnalite(Some(le(2026, 3, 1)), None);
174
175 assert!(
176 !p.opposable_par_lacp(),
177 "l'ACP ne peut pas se prévaloir de sa personnalité envers un tiers"
178 );
179 assert!(
180 p.opposable_par_un_tiers(),
181 "mais un tiers peut la lui opposer : le retard de transcription ne \
182 profite pas à l'association qui l'a laissé traîner"
183 );
184 }
185
186 #[test]
187 fn happy_lasymetrie_disparait_une_fois_les_statuts_transcrits() {
188 let avant = personnalite(Some(le(2026, 3, 1)), None);
189 let apres = personnalite(Some(le(2026, 3, 1)), Some(le(2026, 4, 1)));
190
191 assert_ne!(avant.opposable_par_lacp(), apres.opposable_par_lacp());
192 assert_eq!(
193 avant.opposable_par_un_tiers(),
194 apres.opposable_par_un_tiers(),
195 "de ce côté-là, rien ne change : le tiers pouvait déjà"
196 );
197 }
198
199 #[test]
200 fn edge_les_deux_conditions_le_meme_jour() {
201 let p = personnalite(Some(le(2026, 3, 1)), Some(le(2026, 3, 1)));
202 assert_eq!(p.depuis(), Some(le(2026, 3, 1)));
203 assert!(p.opposable_par_lacp());
204 }
205
206 #[test]
207 fn negative_une_acp_vide_nest_opposable_par_personne() {
208 let p = personnalite(None, None);
209 assert_eq!(p, PersonnaliteJuridique::Inexistante);
210 assert_eq!(p.depuis(), None);
211 }
212}