koprogo_api/application/ports/unit_owner_repository.rs
1use crate::domain::entities::{LotHolder, UnitOwner};
2use async_trait::async_trait;
3use rust_decimal::Decimal;
4use uuid::Uuid;
5
6#[async_trait]
7pub trait UnitOwnerRepository: Send + Sync {
8 /// Create a new unit-owner relationship
9 async fn create(&self, unit_owner: &UnitOwner) -> Result<UnitOwner, String>;
10
11 /// Find a unit-owner relationship by ID
12 async fn find_by_id(&self, id: Uuid) -> Result<Option<UnitOwner>, String>;
13
14 /// Get all current owners of a unit (end_date IS NULL)
15 async fn find_current_owners_by_unit(&self, unit_id: Uuid) -> Result<Vec<UnitOwner>, String>;
16
17 /// Get all current units of an owner (end_date IS NULL)
18 async fn find_current_units_by_owner(&self, owner_id: Uuid) -> Result<Vec<UnitOwner>, String>;
19
20 /// Get ownership history of a unit (including past owners)
21 async fn find_all_owners_by_unit(&self, unit_id: Uuid) -> Result<Vec<UnitOwner>, String>;
22
23 /// Get ownership history of an owner (including past units)
24 async fn find_all_units_by_owner(&self, owner_id: Uuid) -> Result<Vec<UnitOwner>, String>;
25
26 /// Update a unit-owner relationship
27 async fn update(&self, unit_owner: &UnitOwner) -> Result<UnitOwner, String>;
28
29 /// Delete a unit-owner relationship
30 async fn delete(&self, id: Uuid) -> Result<(), String>;
31
32 /// Check if a unit has any active owners
33 async fn has_active_owners(&self, unit_id: Uuid) -> Result<bool, String>;
34
35 /// Get the total ownership percentage for a unit (should be <= 1.0)
36 async fn get_total_ownership_percentage(&self, unit_id: Uuid) -> Result<Decimal, String>;
37
38 /// Find active unit-owner relationship by unit and owner IDs
39 async fn find_active_by_unit_and_owner(
40 &self,
41 unit_id: Uuid,
42 owner_id: Uuid,
43 ) -> Result<Option<UnitOwner>, String>;
44
45 /// Get all active unit-owner relationships for a building
46 /// Returns tuples of (unit_id, owner_id, ownership_percentage)
47 /// Useful for calculating charge distributions
48 /// Détentions actives d'un immeuble, avec le pourcentage BRUT de détention
49 /// dans le lot (`unit_owners.ownership_percentage`).
50 ///
51 /// ⚠️ Ce pourcentage n'est PAS une quote-part de charge. Un propriétaire
52 /// unique d'un lot vaut 1.0 quel que soit le poids de son lot dans
53 /// l'immeuble. Ne l'utiliser que pour identifier QUI détient quoi
54 /// (autorisations, éligibilité au vote), jamais pour répartir un montant :
55 /// voir `find_active_quota_shares_by_building`.
56 async fn find_active_by_building(
57 &self,
58 building_id: Uuid,
59 ) -> Result<Vec<(Uuid, Uuid, Decimal)>, String>;
60
61 /// Quotes-parts de CHARGE des détenteurs actifs d'un immeuble.
62 ///
63 /// Renvoie `(unit_id, owner_id, part)` où `part` est la fraction `[0, 1]` du
64 /// montant total qui incombe à ce copropriétaire :
65 ///
66 /// ```text
67 /// part = (unit.quota / building.total_tantiemes) × ownership_percentage
68 /// ```
69 ///
70 /// C'est la formule de l'Art. 3.84 CC, déjà implémentée et testée par
71 /// `ChargeDistribution::resolve_owner_quota`. La somme des parts d'un
72 /// immeuble conforme vaut 1.
73 async fn find_active_quota_shares_by_building(
74 &self,
75 building_id: Uuid,
76 ) -> Result<Vec<(Uuid, Uuid, Decimal)>, String>;
77
78 /// Story H17 (Art. 3.87 §1 CC) — titularités actives d'un lot réduites aux
79 /// attributs pertinents pour le calcul du droit de vote (`ownership_type`
80 /// + `is_voting_representative`). Consommé par le gate vote (`cast_vote`)
81 /// pour rejeter les lots démembrés/indivis sans représentant unique désigné
82 /// (`VOTING_RIGHT_SUSPENDED`).
83 async fn find_voting_holders_by_unit(&self, unit_id: Uuid) -> Result<Vec<LotHolder>, String>;
84
85 /// Story #848 (Art. 3.87 §1 CC) — la ligne `unit_owners` identifiée
86 /// porte-t-elle déjà la désignation de représentant de vote ? Sert à
87 /// rendre la désignation idempotente : redésigner le représentant déjà en
88 /// place ne doit pas se heurter à la règle du représentant unique contre
89 /// elle-même (`assert_single_voting_representative`).
90 async fn is_voting_representative(&self, unit_owner_id: Uuid) -> Result<bool, String>;
91
92 /// Story #848 (Art. 3.87 §1 CC) — écrit la désignation du représentant de
93 /// vote pour une ligne `unit_owners` précise. L'appelant DOIT avoir validé
94 /// `assert_single_voting_representative` sur l'état prospectif AVANT
95 /// d'appeler cette méthode : elle écrit, elle ne contrôle rien (cf.
96 /// `UnitOwnerUseCases::designate_voting_representative`).
97 async fn set_voting_representative(&self, unit_owner_id: Uuid) -> Result<(), String>;
98}