# Architecture Finale du Système Coopératif

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.

## 1. Structure des Applications (Architecture des Dossiers)

Nous adoptons une architecture modulaire basée sur le principe de la responsabilité unique pour chaque application Django.

```
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.
├── credit/              # App pour la gestion des crédits et prêts.
└── core/                # Configuration générale du projet.
```

### Détail des Applications :

*   **`authentication/`** :
    *   **Rôle :** Gère uniquement le modèle `User` (compte de connexion) et son `UserManager`. C'est la couche la plus basse et la plus réutilisable pour l'authentification.
    *   **Contenu :** `models.py` (modèle `User`), `managers.py` (manager `UserManager`).

*   **`customers/`** :
    *   **Rôle :** Gère la logique métier de base des clients de la coopérative. Un `Customer` est un profil de base lié à un `User`.
    *   **Contenu :** `models.py` (modèle `Customer`), `serializers.py`, `views.py`, `urls.py`.

*   **`members/`** :
    *   **Rôle :** Gère la logique métier spécifique aux membres de la coopérative. Un `Member` est une spécialisation d'un `Customer` (héritage multi-tables).
    *   **Contenu :** `models.py` (modèle `Member`), `serializers.py`, `views.py`, `urls.py`.

*   **`banking/`** :
    *   **Rôle :** Gère les comptes financiers (courants, épargne, capital social) et toutes les transactions associées. C'est le cœur du grand livre comptable.
    *   **Contenu :** `models.py` (modèles `Account`, `Transaction`), `serializers.py`, `views.py`, `urls.py`.

*   **`shares/`** :
    *   **Rôle :** Gère la logique métier des parts sociales (types de parts, souscriptions, paiements).
    *   **Contenu :** `models.py` (modèles `ShareType`, `ShareSubscription`, `SharePayment`), `serializers.py`, `views.py`, `urls.py`.

*   **`credit/`** :
    *   **Rôle :** Gère la logique métier des crédits et prêts (produits de crédit, demandes, approbations, décaissements, remboursements).
    *   **Contenu :** `models.py` (modèles `CreditProduct`, `LoanApplication`, `Loan`, `RepaymentSchedule`, `Repayment`), `serializers.py`, `views.py`, `urls.py`.

*   **`core/`** :
    *   **Rôle :** Application de configuration générale du projet. Elle agrège les URLs des autres applications.
    *   **Contenu :** `settings.py`, `urls.py` (racine), `wsgi.py`, `asgi.py`.

## 2. Modèle de Données Final

Voici une représentation consolidée des principaux modèles et de leurs relations.

```mermaid
erDiagram
    USER {
        uuid id PK
        string email
        string username
        string password
        boolean is_staff
        boolean is_superuser
        datetime date_joined
    }

    CUSTOMER {
        uuid user_id PK "FK to USER"
        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
    }

    ACCOUNT {
        uuid id PK
        uuid owner_id FK "FK to USER (nullable)"
        uuid account_manager_id FK "FK to USER (nullable, is_staff=True)"
        string name
        string type "CURRENT, SAVINGS, SHARE_CAPITAL, CASH_DESK, MOBILE_MONEY"
        decimal balance
        string iban "Unique, nullable"
        string account_number "Unique, human-readable"
    }

    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 "PENDING, APPROVED, REJECTED, CANCELLED"
        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 "ACTIVE, PAID_OFF, DEFAULTED, CANCELLED"
    }

    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)"
    USER ||--o{ ACCOUNT : "possède"
    USER ||--o{ ACCOUNT : "gère"
    ACCOUNT }o--|| TRANSACTION : "depuis"
    ACCOUNT }o--|| TRANSACTION : "vers"
    MEMBER ||--o{ ACCOUNT : "possède"
    MEMBER ||--o{ SHARE_SUBSCRIPTION : "souscrit à"
    SHARE_TYPE ||--o{ SHARE_SUBSCRIPTION : "est de type"
    SHARE_SUBSCRIPTION ||--o{ SHARE_PAYMENT : "est libérée par"
    TRANSACTION ||--|| SHARE_PAYMENT : "concerne"
    MEMBER ||--o{ LOAN_APPLICATION : "demande"
    CREDIT_PRODUCT ||--o{ LOAN_APPLICATION : "pour produit"
    USER ||--o{ LOAN_APPLICATION : "approuve"
    LOAN_APPLICATION ||--|| LOAN : "devient"
    MEMBER ||--o{ LOAN : "emprunte"
    CREDIT_PRODUCT ||--o{ LOAN : "type de"
    LOAN ||--o{ REPAYMENT_SCHEDULE : "a échéancier"
    LOAN ||--o{ REPAYMENT : "reçoit remboursement"
    REPAYMENT_SCHEDULE ||--o{ REPAYMENT : "correspond à"
    TRANSACTION ||--|| REPAYMENT : "enregistre"
```

### Détail des Modèles :

```python
# authentication/models.py
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
# from .managers import UserManager # Assuming UserManager is defined elsewhere

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=150, unique=True)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']
    # objects = UserManager() # Assuming UserManager is set up

    def __str__(self):
        return self.email

# customers/models.py
from django.conf import settings
from django.db import models

class Customer(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
    address = models.TextField(blank=True)
    id_card_number = models.CharField(max_length=50, blank=True)
    # ... autres champs spécifiques au client

    def __str__(self):
        return self.user.email

# members/models.py
from django.db import models
from customers.models import Customer

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)
    # ... autres champs spécifiques au membre

    @property
    def total_shares_subscribed(self):
        # Logique pour calculer le total des parts souscrites
        pass

    @property
    def total_shares_paid(self):
        # Logique pour calculer le total des parts libérées
        pass

    def __str__(self):
        return f"Membre: {self.user.email}"

# banking/models.py
import uuid
from django.db import models
from django.conf import settings

class Account(models.Model):
    class AccountType(models.TextChoices):
        CURRENT = 'CURRENT', 'Compte Courant'
        SAVINGS = 'SAVINGS', 'Compte Épargne'
        SHARE_CAPITAL = 'SHARE_CAPITAL', 'Compte de Capital Social'
        CASH_DESK = 'CASH_DESK', 'Caisse de Guichet'
        MOBILE_MONEY = 'MOBILE_MONEY', 'Compte Mobile Money'
        LOAN_FUND = 'LOAN_FUND', 'Fonds de Prêt'
        INTEREST_INCOME = 'INTEREST_INCOME', 'Revenus d\'Intérêts'
        LOAN_LOSS_RESERVE = 'LOAN_LOSS_RESERVE', 'Réserve pour Créances Douteuses'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name="accounts")
    account_manager = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="managed_accounts",
        limit_choices_to={'is_staff': True},
        verbose_name="Gestionnaire de Compte"
    )
    name = models.CharField(max_length=255)
    type = models.CharField(max_length=20, choices=AccountType.choices)
    balance = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    iban = models.CharField(max_length=34, unique=True, blank=True, null=True, verbose_name="IBAN")
    account_number = models.CharField(max_length=50, unique=True, blank=True, null=True, verbose_name="Numéro de Compte") # Added

    def __str__(self):
        return f"{self.name} ({self.get_type_display()})"

class TransactionCategory(models.TextChoices):
    # Catégories bancaires standards
    DEPOSIT = "deposit", "Dépôt"
    WITHDRAWAL = "withdrawal", "Retrait"
    TRANSFER = "transfer", "Virement"
    FEE = "fee", "Frais"
    INTEREST = "interest", "Intérêts"
    PENALTY = "penalty", "Pénalité"
    CORRECTION = "correction", "Correction"
    OPENING = "opening", "Ouverture"
    CLOSING = "closing", "Fermeture"

    # Catégories pour la solidarité et les fonds spéciaux
    SOLIDARITY = "solidarity", "Solidarité"
    SOLIDARITY_FUND = "solidarity_fund", "Fonds de Solidarité"

    # Catégories pour les crédits et débits génériques
    CREDIT = "credit", "Crédit"
    DEBIT = "debit", "Débit"

    # Catégories pour les parts sociales
    SHARE_SUBSCRIPTION = "share_subscription", "Souscription de Parts"
    SHARE_PAYMENT = "share_payment", "Paiement de Parts"
    SHARE_TRANSFER = "share_transfer", "Transfert de Parts"
    SHARE_REDEMPTION = "share_redemption", "Rachat de Parts"
    DIVIDEND_PAYMENT = "dividend_payment", "Paiement de Dividende"
    MEMBERSHIP_FEE = "membership_fee", "Frais d'Adhésion"

    # Catégories spécifiques pour fondateurs
    FOUNDER_PAYMENT = "founder_payment", "Paiement Fondateur"
    FOUNDER_DIVIDEND = "founder_dividend", "Dividende Fondateur"

    # Catégories pour les crédits et prêts
    LOAN_DISBURSEMENT = "loan_disbursement", "Décaissement de Prêt"
    LOAN_REPAYMENT = "loan_repayment", "Remboursement de Prêt"
    LOAN_INTEREST = "loan_interest", "Intérêts sur Prêt"
    LOAN_PENALTY = "loan_penalty", "Pénalité de Prêt"
    LOAN_FEE = "loan_fee", "Frais de Prêt"

    # Catégories administratives
    SYSTEM = "system", "Système"
    ADMINISTRATIVE = "administrative", "Administration"


class Transaction(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    from_account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="outgoing_transactions")
    to_account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="incoming_transactions")
    amount = models.DecimalField(max_digits=15, decimal_places=2)
    description = models.CharField(max_length=255)
    transaction_date = models.DateTimeField(auto_now_add=True)
    category = models.CharField(max_length=50, choices=TransactionCategory.choices, verbose_name="Catégorie de Transaction") # Added category field

    def __str__(self):
        return f"{self.transaction_date}: {self.amount} de {self.from_account} à {self.to_account} ({self.get_category_display()})"

# shares/models.py
import uuid
from django.db import models
from members.models import Member
from banking.models import Transaction

class ShareType(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, unique=True)
    nominal_value = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.name

class ShareSubscription(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    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
        return self.payments.aggregate(total=models.Sum('paid_quantity'))['total'] or 0

    @property
    def is_fully_paid(self):
        return self.paid_quantity >= self.subscribed_quantity

    def __str__(self):
        return f"Souscription {self.subscribed_quantity} parts de {self.share_type} par {self.member}"

class SharePayment(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    subscription = models.ForeignKey(ShareSubscription, on_delete=models.CASCADE, related_name="payments")
    paid_quantity = models.PositiveIntegerField()
    payment_date = models.DateField(auto_now_add=True)
    financial_transaction = models.OneToOneField(Transaction, on_delete=models.PROTECT, related_name="loan_repayment")

    @property
    def amount_paid(self):
        return self.financial_transaction.amount

    def __str__(self):
        return f"Paiement de {self.paid_quantity} parts pour {self.subscription}"
```

## 3. Analyse : Faut-il séparer les applications `transactions` et `banking` ?

Dans notre architecture actuelle, les modèles `Account` et `Transaction` résident tous deux dans l'application `banking`. Cette approche est généralement recommandée pour les raisons suivantes :

*   **Forte Cohésion :** Les transactions sont intrinsèquement liées aux comptes. Une transaction n'a de sens que dans le contexte d'un mouvement entre comptes. Il est difficile d'imaginer une logique de transaction sans accès direct aux comptes qu'elle affecte.
*   **Principe de Responsabilité Unique (SRP) :** Bien que `Account` gère l'état d'un compte et `Transaction` le mouvement de fonds, ils partagent une responsabilité commune : la gestion du grand livre comptable. Regrouper ces modèles dans une seule application `banking` permet de centraliser toute la logique liée à la tenue des comptes et aux mouvements financiers.
*   **Simplicité et Maintenance :** Avoir moins d'applications réduit la complexité de la configuration (settings, URLs) et simplifie la navigation dans le codebase. Les développeurs travaillant sur la logique financière trouveront toutes les entités pertinentes au même endroit.
*   **Atomicité des Opérations :** Les opérations qui impliquent la mise à jour des soldes de comptes suite à une transaction nécessitent souvent des transactions de base de données atomiques. Avoir les modèles `Account` et `Transaction` dans la même application facilite la gestion de ces opérations critiques.

**Recommandation :**

Pour le système de la coopérative, il est **fortement recommandé de maintenir les modèles `Account` et `Transaction` au sein de la même application `banking`**. Cette approche est conforme aux meilleures pratiques pour les systèmes financiers de taille moyenne, où la cohésion entre comptes et transactions est primordiale.

Une séparation ne serait justifiée que dans des systèmes à très grande échelle, où des équipes distinctes travailleraient sur des microservices dédiés à la gestion des comptes et à la gestion des transactions, ou si la logique de transaction devenait d'une complexité extrême (ex: moteur de trading haute fréquence, détection de fraude en temps réel) nécessitant une isolation complète. Ce n'est pas le cas pour le système actuel de la coopérative.

## 4. Recommandations Générales et Bonnes Pratiques

Cette architecture est conçue pour être robuste, modulaire et évolutive. Voici un résumé des bonnes pratiques à suivre pour son implémentation et sa maintenance :

*   **Principe de Responsabilité Unique (SRP) :** Chaque application (et chaque modèle, chaque fonction) doit avoir une seule raison de changer. Cela rend le code plus facile à comprendre, à tester et à maintenir.
*   **Séparation des Préoccupations :**
    *   **Authentification vs. Rôles Métier :** Le modèle `User` est pur pour l'authentification. Les rôles métier (`Customer`, `Member`) sont gérés par des modèles distincts liés à `User`.
    *   **API par Application :** Chaque application expose sa propre API (serializers, vues, URLs) plutôt qu'une application `api` monolithique.
*   **Utilisation Stratégique des Identifiants :**
    *   **UUIDs pour l'Interne :** Utiliser des UUIDs pour les identifiants primaires techniques (`id`) garantit l'unicité globale.
    *   **Numéros Lisibles par l'Humain :** Introduire des champs distincts (`account_number`) pour les identifiants destinés aux utilisateurs, avec une stratégie de génération adaptée (ex: séquentielle avec préfixe).
    *   **IBANs pour l'Externe :** Collecter et valider les IBANs fournis par les banques externes, sans tenter de les générer.
*   **Validation Robuste :** Implémenter des validations strictes au niveau des modèles, des formulaires et des serializers, en particulier pour les données financières (montants positifs, IBANs valides, etc.).
*   **Gestion des Permissions :** Utiliser le système de groupes et permissions de Django pour gérer finement les accès du personnel aux différentes fonctionnalités et données. Ne pas coder en dur les rôles.
*   **Architecture en Couches :**
    *   **Modèles :** Définissent la structure des données et la logique métier de base.
    *   **Managers :** Gèrent les opérations de création et de requêtage complexes.
    *   **Serializers :** Gèrent la validation et la transformation des données pour l'API.
    *   **Vues (ViewSet) :** Gèrent le contexte HTTP (requête, réponse, permissions) et délèguent la logique métier aux serializers ou services.
    *   **Services (Couche Métier) :** Pour la logique métier complexe qui ne rentre pas naturellement dans les modèles, managers, serializers ou vues. Cela favorise la réutilisabilité et la testabilité.
*   **Tests :** Écrire des tests unitaires, d'intégration et fonctionnels pour chaque composant critique du système afin de garantir sa fiabilité et de faciliter les évolutions.
*   **Documentation :** Maintenir une documentation claire et à jour de l'architecture, du modèle de données et des décisions clés.

En adhérant à ces principes, le système de la coopérative sera non seulement fonctionnel, mais aussi durable, performant et facile à faire évoluer face aux besoins futurs.

---

## 5. Application Crédit : Modèles et Logique Métier

L'application `credit` gère toutes les fonctionnalités liées à l'octroi et au cycle de vie du crédit dans la coopérative.

### 5.1. Justification d'une Application Dédiée

*   **Principe de Responsabilité Unique (SRP) :** La gestion du crédit est un domaine métier à part entière, avec ses propres règles, son cycle de vie (demande, approbation, décaissement, remboursement, recouvrement) et ses modèles de données spécifiques.
*   **Modularité et Organisation :** Une application dédiée maintient le code organisé et évite que l'application `banking` ne devienne monolithique.
*   **Évolution Indépendante :** Les fonctionnalités de crédit peuvent devenir très complexes (modèles de risque, conformité réglementaire, différents types de prêts).

### 5.2. Interaction avec les Applications Existantes

La nouvelle application `credit` interagit avec les applications existantes via des interfaces bien définies :

*   **Avec `banking` :**
    *   **Décaissement du Prêt :** Création de `Transaction`s (compte de décaissement → compte courant du membre)
    *   **Remboursements :** Création de `Transaction`s (compte courant du membre → compte de remboursement)
    *   **Intérêts et Frais :** Calcul et création des `Transaction`s correspondantes

*   **Avec `members` :**
    *   Accès aux informations des membres pour évaluer les demandes de prêt
    *   Liaison des prêts aux membres appropriés

### 5.3. Modèles Détaillés pour l'Application `credit`

```python
# credit/models.py
import uuid
from django.db import models
from django.conf import settings
from members.models import Member # Ou Customer, selon la logique métier

class CreditProduct(models.Model):
    """Définit les différents types de produits de crédit offerts."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(blank=True)
    min_amount = models.DecimalField(max_digits=15, decimal_places=2)
    max_amount = models.DecimalField(max_digits=15, decimal_places=2)
    interest_rate = models.DecimalField(max_digits=5, decimal_places=2) # Taux annuel
    max_duration_months = models.PositiveIntegerField()
    # ... autres règles spécifiques au produit (garanties, frais, etc.)

    def __str__(self):
        return self.name

class LoanApplication(models.Model):
    """Représente une demande de prêt soumise par un membre."""
    class ApplicationStatus(models.TextChoices):
        PENDING = 'PENDING', 'En attente'
        APPROVED = 'APPROVED', 'Approuvée'
        REJECTED = 'REJECTED', 'Rejetée'
        CANCELLED = 'CANCELLED', 'Annulée'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    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, limit_choices_to={'is_staff': True})
    rejection_reason = models.TextField(blank=True)

    def __str__(self):
        return f"Demande de {self.requested_amount} par {self.member} ({self.status})"

class Loan(models.Model):
    """Représente un prêt approuvé et décaissé."""
    class LoanStatus(models.TextChoices):
        ACTIVE = 'ACTIVE', 'Actif'
        PAID_OFF = 'PAID_OFF', 'Soldé'
        DEFAULTED = 'DEFAULTED', 'En défaut'
        CANCELLED = 'CANCELLED', 'Annulé'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    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(auto_now_add=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)
    # ... champs pour le suivi des remboursements, pénalités, etc.

    def __str__(self):
        return f"Prêt {self.disbursed_amount} à {self.member} ({self.status})"

class RepaymentSchedule(models.Model):
    """Définit les échéances de remboursement pour un prêt."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE, related_name="repayment_schedule")
    due_date = models.DateField()
    principal_due = models.DecimalField(max_digits=15, decimal_places=2)
    interest_due = models.DecimalField(max_digits=15, decimal_places=2)
    # ... champs pour les frais, pénalités, etc.

    def __str__(self):
        return f"Échéance {self.due_date} pour Prêt {self.loan.id}"

class Repayment(models.Model):
    """Enregistre un remboursement effectué par le membre."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    loan = models.ForeignKey(Loan, on_delete=models.CASCADE, related_name="repayments")
    repayment_schedule_entry = models.ForeignKey(RepaymentSchedule, on_delete=models.SET_NULL, null=True, blank=True, related_name="actual_repayments")
    amount_paid = models.DecimalField(max_digits=15, decimal_places=2)
    payment_date = models.DateTimeField(auto_now_add=True)
    # Lien vers la transaction financière dans l'app banking
    financial_transaction = models.OneToOneField('banking.Transaction', on_delete=models.PROTECT, related_name="loan_repayment")

    def __str__(self):
        return f"Remboursement {self.amount_paid} pour Prêt {self.loan.id}"
```

### 5.4. Workflow du Crédit

Le cycle de vie d'un crédit dans la coopérative suit ces étapes principales :

1. **Demande de Crédit** : Un membre soumet une `LoanApplication`
2. **Évaluation** : L'équipe de crédit évalue la demande (capacité de remboursement, garanties, etc.)
3. **Approbation/Rejet** : Mise à jour du statut de la `LoanApplication`
4. **Décaissement** : Si approuvé, création d'un `Loan` et génération automatique d'une `Transaction` (Fonds de Prêt → Compte Courant du membre)
5. **Génération d'Échéancier** : Création automatique des `RepaymentSchedule` selon le type de crédit
6. **Remboursements** : Enregistrement des `Repayment` avec leurs `Transaction` associées
7. **Suivi et Recouvrement** : Gestion des retards, pénalités, et créances douteuses

```mermaid
sequenceDiagram
    participant Membre
    participant API Credit
    participant DB Credit
    participant API Banking
    participant DB Banking

    Membre->>+API Credit: POST /loan-applications/ (demande de prêt)
    API Credit->>+DB Credit: Create LoanApplication
    DB Credit-->>-API Credit: LoanApplication créée
    API Credit-->>-Membre: 201 Created

    Note over API Credit: Processus d'approbation (manuel ou automatique)

    API Credit->>+DB Credit: Update LoanApplication (APPROVED)
    API Credit->>+DB Credit: Create Loan
    API Credit->>+API Banking: Create Transaction (décaissement)
    API Banking->>+DB Banking: Create Transaction (Fonds Prêt → Compte Membre)
    DB Banking-->>-API Banking: Transaction créée
    API Banking-->>-API Credit: Transaction ID
    API Credit->>DB Credit: Create RepaymentSchedule entries
    DB Credit-->>-API Credit: Échéancier créé
    
    Note over Membre: Plus tard : remboursement

    Membre->>+API Credit: POST /repayments/ (remboursement)
    API Credit->>+API Banking: Create Transaction (Compte Membre → Revenus Intérêts)
    API Banking->>+DB Banking: Create Transaction
    DB Banking-->>-API Banking: Transaction créée
    API Banking-->>-API Credit: Transaction ID
    API Credit->>+DB Credit: Create Repayment (lié à Transaction)
    DB Credit-->>-API Credit: Repayment créé
    API Credit-->>-Membre: 200 OK
```