# Architecture Finale du Système Coopératif - V2

Ce document consolide l'architecture logicielle et le modèle de données du système de gestion de la coopérative, intégrant toutes les analyses et recommandations discutées, ainsi que la logique métier implémentée.

## 1. Structure des Applications (Identique à V1)

La structure modulaire est conservée.

```
mon-projet/
├── authentication/      # Gère UNIQUEMENT le modèle User et son manager.
├── customers/           # App pour la logique métier des Clients.
├── members/             # App pour la logique métier des Membres.
├── banking/             # App pour les comptes financiers et transactions.
├── shares/              # App pour les parts sociales.
├── credits/             # App pour la gestion des crédits et prêts.
└── core/                # Fournit les modèles de base et la logique transverse.
```

## 2. Principes Fondamentaux (Nouveau)

- **Héritage de `core.models.BaseModel`**: Tous les modèles de données métier héritent de `BaseModel`, fournissant un ID UUID, l'horodatage (`created_at`, `updated_at`), le suivi de l'utilisateur (`created_by`, `updated_by`) et la suppression douce (`is_active`).
- **Logique métier dans les modèles**: Les opérations critiques (approbation, décaissement, etc.) sont implémentées comme des méthodes sur les modèles correspondants pour garantir la cohérence et la réutilisabilité.
- **Intégration de `core`**: La logique métier utilise systématiquement les modèles de `core` pour l'audit (`AuditLog`) et les alertes (`Notification`).

## 3. Modèle de Données V2

```mermaid
erDiagram
    USER {
        uuid id PK
        string email
    }

    CUSTOMER {
        uuid id PK
        uuid user_id FK
        string address
        string id_card_number
        string phone_number
        date birth_date
    }

    MEMBER {
        uuid customer_ptr_id PK "FK to CUSTOMER"
        date membership_start_date
        date membership_end_date
        string role_in_cooperative
        decimal membership_fee_paid
    }

    DOCUMENT {
        uuid id PK
        string title
        string file_url
    }

    ACCOUNT {
        uuid id PK
        uuid owner_id FK
        string name
        string type
        decimal balance
        string iban
        string account_number
    }

    TRANSACTION {
        uuid id PK
        uuid from_account_id FK
        uuid to_account_id FK
        decimal amount
        string description
        datetime transaction_date
        string category
    }

    SHARE_TYPE {
        uuid id PK
        string name
        decimal nominal_value
    }

    SHARE_SUBSCRIPTION {
        uuid id PK
        uuid member_id FK
        uuid share_type_id FK
        int subscribed_quantity
        date subscription_date
    }

    SHARE_PAYMENT {
        uuid id PK
        uuid subscription_id FK
        uuid financial_transaction_id FK
        int paid_quantity
        date payment_date
    }

    CREDIT_PRODUCT {
        uuid id PK
        string name
        string description
        decimal min_amount
        decimal max_amount
        decimal interest_rate
        int max_duration_months
    }

    LOAN_APPLICATION {
        uuid id PK
        uuid member_id FK
        uuid credit_product_id FK
        decimal requested_amount
        int requested_duration_months
        datetime application_date
        string status
        decimal approved_amount
        int approved_duration_months
        datetime approval_date
        uuid approved_by_id FK
        string rejection_reason
    }

    LOAN {
        uuid id PK
        uuid loan_application_id FK
        uuid member_id FK
        uuid credit_product_id FK
        decimal disbursed_amount
        datetime disbursement_date
        decimal interest_rate
        int duration_months
        string status
    }

    REPAYMENT_SCHEDULE {
        uuid id PK
        uuid loan_id FK
        date due_date
        decimal principal_due
        decimal interest_due
    }

    REPAYMENT {
        uuid id PK
        uuid loan_id FK
        uuid repayment_schedule_entry_id FK
        decimal amount_paid
        datetime payment_date
        uuid financial_transaction_id FK
    }

    USER ||--o| CUSTOMER : "a un profil"
    CUSTOMER ||--|| MEMBER : "est un (héritage)"
    CUSTOMER }o--|{ DOCUMENT : "a des documents d'identité"
    LOAN_APPLICATION }o--|{ DOCUMENT : "a des pièces justificatives"
    
    MEMBER ||--o{ LOAN_APPLICATION : "demande"
    MEMBER ||--o{ SHARE_SUBSCRIPTION : "souscrit à"

    LOAN_APPLICATION ||--|| LOAN : "devient"
    LOAN ||--o{ REPAYMENT : "reçoit"

    SHARE_SUBSCRIPTION ||--o{ SHARE_PAYMENT : "est payée par"

    ACCOUNT }o--|| TRANSACTION : "depuis"
    ACCOUNT }o--|| TRANSACTION : "vers"
    TRANSACTION ||--|| SHARE_PAYMENT : "concerne"
    TRANSACTION ||--|| REPAYMENT : "enregistre"
```

### Détail des Modèles et Logique Métier

#### `customers.models.Customer`
```python
class Customer(BaseModel):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    address = models.TextField(blank=True)
    id_card_number = models.CharField(max_length=50, blank=True)
    phone_number = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    identity_documents = models.ManyToManyField('core.Document', blank=True)

    @property
    def is_member(self):
        """Retourne True si le client est aussi un membre."""
        return hasattr(self, 'member')

    @transaction.atomic
    def promote_to_member(self, by_user, membership_start_date=None):
        """Promeut un client au statut de membre.
        - Crée une instance de Member.
        - Crée une entrée AuditLog.
        - Envoie une Notification de bienvenue.
        """
        pass # Implémentation détaillée dans le code source
```

#### `members.models.Member`
```python
class Member(Customer):
    role_in_cooperative = models.CharField(max_length=100, default="Membre")
    membership_start_date = models.DateField()
    membership_end_date = models.DateField(null=True, blank=True)
    membership_fee_paid = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)

    @property
    def total_shares_subscribed(self):
        """Calcule le total des parts souscrites par le membre."""
        pass

    @property
    def total_shares_paid(self):
        """Calcule le total des parts libérées (payées) par le membre."""
        pass
```

#### `credits.models.LoanApplication`
```python
class LoanApplication(BaseModel):
    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="loan_applications")
    credit_product = models.ForeignKey(CreditProduct, on_delete=models.PROTECT)
    requested_amount = models.DecimalField(max_digits=15, decimal_places=2)
    requested_duration_months = models.PositiveIntegerField()
    application_date = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=20, choices=ApplicationStatus.choices, default=ApplicationStatus.PENDING)
    approved_amount = models.DecimalField(max_digits=15, decimal_places=2, null=True, blank=True)
    approved_duration_months = models.PositiveIntegerField(null=True, blank=True)
    approval_date = models.DateTimeField(null=True, blank=True)
    approved_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_loan_applications', limit_choices_to={'is_staff': True})
    rejection_reason = models.TextField(blank=True)
    supporting_documents = models.ManyToManyField('core.Document', blank=True)

    def approve(self, by_user, approved_amount=None, approved_duration=None):
        """Approuve la demande de prêt.
        - Change le statut à APPROVED.
        - Crée une entrée AuditLog.
        - Envoie une Notification au membre.
        """
        pass # Implémentation détaillée dans le code source

    def reject(self, by_user, reason):
        """Rejette la demande de prêt.
        - Change le statut à REJECTED.
        - Crée une entrée AuditLog.
        - Envoie une Notification au membre.
        """
        pass # Implémentation détaillée dans le code source
```

#### `credits.models.Loan`
```python
class Loan(BaseModel):
    loan_application = models.OneToOneField(LoanApplication, on_delete=models.PROTECT, related_name="loan_instance")
    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="loans")
    credit_product = models.ForeignKey(CreditProduct, on_delete=models.PROTECT)
    disbursed_amount = models.DecimalField(max_digits=15, decimal_places=2)
    disbursement_date = models.DateTimeField(null=True, blank=True)
    interest_rate = models.DecimalField(max_digits=5, decimal_places=2)
    duration_months = models.PositiveIntegerField()
    status = models.CharField(max_length=20, choices=LoanStatus.choices, default=LoanStatus.ACTIVE)

    @property
    def total_repaid(self):
        """Calcule le montant total déjà remboursé."""
        pass

    @transaction.atomic
    def disburse(self, by_user, from_account):
        """Débloque les fonds pour le prêt.
        - Crée la Transaction financière.
        - Met à jour la date de décaissement.
        - Crée une entrée AuditLog.
        - Envoie une Notification au membre.
        """
        pass # Implémentation détaillée dans le code source

    @transaction.atomic
    def make_repayment(self, amount, by_user, from_account, to_account):
        """Enregistre un remboursement pour le prêt.
        - Crée un objet Repayment.
        - Crée la Transaction financière.
        - Met à jour le statut du prêt si soldé.
        - Crée une entrée AuditLog.
        - Envoie une Notification au membre.
        """
        pass # Implémentation détaillée dans le code source
```

#### `shares.models.ShareSubscription`
```python
class ShareSubscription(BaseModel):
    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="subscriptions")
    share_type = models.ForeignKey(ShareType, on_delete=models.PROTECT)
    subscribed_quantity = models.PositiveIntegerField()
    subscription_date = models.DateField(auto_now_add=True)

    @property
    def paid_quantity(self):
        """Calcule le nombre de parts déjà libérées pour cette souscription."""
        pass

    @property
    def is_fully_paid(self):
        """Vérifie si la souscription est entièrement libérée."""
        pass

    @transaction.atomic
    def make_payment(self, quantity, by_user, from_account):
        """Enregistre un paiement pour des parts.
        - Crée un objet SharePayment.
        - Crée la Transaction financière.
        - Crée une entrée AuditLog.
        - Envoie une Notification au membre.
        """
        pass # Implémentation détaillée dans le code source
```

## 4. Interface d'Administration (Nouveau)

L'interface d'administration de Django est configurée pour être le principal outil de back-office. Elle expose la logique métier via des actions personnalisées pour une gestion simple et sécurisée.

- **`CustomerAdmin`**: 
    - **Action**: `promote_to_member` pour promouvoir un ou plusieurs clients en membres.

- **`LoanApplicationAdmin`**: 
    - **Action**: `approve_applications` pour approuver une ou plusieurs demandes de prêt en attente.

- **`LoanAdmin`**: 
    - **Action**: `disburse_loans` pour décaisser un ou plusieurs prêts approuvés.

- **`ShareSubscriptionAdmin`**: 
    - **Action**: `mark_as_fully_paid` pour enregistrer le paiement complet d'une ou plusieurs souscriptions.