Skip to main content

koprogo_api/domain/copropriete/
personnalite_juridique.rs

1//! L'acquisition de la personnalité juridique par l'ACP.
2//!
3//! Art. 3.86 § 1er :
4//!
5//! > « L'association des copropriétaires acquiert la personnalité juridique au
6//! > moment où sont réunies les **deux conditions suivantes** :
7//! > 1° la **naissance de l'indivision** par la cession ou l'attribution d'un
8//! > lot au moins ;
9//! > 2° la **transcription** de l'acte de base et du règlement de copropriété
10//! > dans les registres du bureau compétent de l'Administration générale de la
11//! > Documentation patrimoniale. »
12//!
13//! Et § 2, dont l'asymétrie est le cœur de cette modélisation :
14//!
15//! > « En cas d'omission ou de retard dans la transcription des statuts,
16//! > l'association des copropriétaires **ne pourra se prévaloir de la
17//! > personnalité juridique à l'égard des tiers**, lesquels auront **néanmoins
18//! > la faculté d'en faire état contre elle**. »
19//!
20//! Autrement dit : une ACP dont les statuts ne sont pas transcrits ne peut pas
21//! opposer sa personnalité à un fournisseur, mais ce fournisseur peut la lui
22//! opposer. La protection joue **dans un seul sens**, contre l'association
23//! négligente et en faveur du tiers. Modéliser cela par un simple booléen
24//! ferait perdre exactement ce qui compte.
25//!
26//! Conséquence pratique pour le logiciel : tant que la transcription manque,
27//! l'ACP ne devrait pas pouvoir *engager* — signer un contrat, ouvrir un
28//! compte, ester en justice — alors qu'elle reste tenue de ce qu'on lui
29//! réclame.
30//!
31//! Hors périmètre ici : les associations partielles, qui « ne peuvent disposer
32//! de la personnalité juridique qu'à partir du moment où l'association
33//! principale dont elles dépendent dispose elle-même » (§ 2, dernière phrase).
34//! Elles sont reportées en v0.2.0 par l'ADR-0010.
35//!
36//! Voir issue #740.
37
38use chrono::NaiveDate;
39
40/// L'état de la personnalité juridique d'une ACP.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum PersonnaliteJuridique {
43    /// Aucun lot n'a été cédé ni attribué : pas d'indivision, donc pas
44    /// d'association. Un immeuble entier encore aux mains du promoteur.
45    Inexistante,
46    /// L'indivision est née mais les statuts ne sont pas transcrits.
47    ///
48    /// L'ACP ne peut pas s'en prévaloir envers un tiers ; un tiers, lui, peut
49    /// la lui opposer (Art. 3.86 § 2).
50    NonOpposableAuxTiers { depuis: NaiveDate },
51    /// Les deux conditions sont réunies.
52    Acquise { depuis: NaiveDate },
53}
54
55impl PersonnaliteJuridique {
56    /// L'ACP peut-elle **invoquer** sa personnalité contre un tiers ?
57    ///
58    /// C'est ce qui conditionne sa capacité à engager : signer un contrat,
59    /// ouvrir un compte à son nom, agir en justice.
60    pub fn opposable_par_lacp(&self) -> bool {
61        matches!(self, Self::Acquise { .. })
62    }
63
64    /// Un tiers peut-il l'**opposer** à l'ACP ?
65    ///
66    /// Oui dès que l'indivision est née, transcription ou non. C'est la
67    /// dissymétrie voulue par l'article : le retard de transcription ne
68    /// profite pas à l'association qui l'a laissé traîner.
69    pub fn opposable_par_un_tiers(&self) -> bool {
70        matches!(
71            self,
72            Self::Acquise { .. } | Self::NonOpposableAuxTiers { .. }
73        )
74    }
75
76    /// Depuis quand, le cas échéant.
77    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
108/// L'état de la personnalité, dérivé des deux conditions.
109///
110/// Elle s'acquiert au moment où la **seconde** condition est remplie : c'est
111/// la plus tardive des deux dates qui compte, pas la première.
112pub 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        // Sans indivision, la transcription seule ne fait rien naître.
118        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    /// C'est la plus tardive des deux dates qui fait naître la personnalité :
150    /// « au moment où sont réunies les deux conditions ».
151    #[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        // Un immeuble entier encore aux mains du promoteur : les statuts ont
163        // beau être transcrits, l'indivision n'est pas née.
164        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    /// Le cœur de l'article : l'asymétrie du § 2.
171    #[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}