# Backend Finance Workflow - Implémentation Complète

**Date:** 2026-03-14
**Module:** unionflow-server-impl-quarkus
**Version:** 1.0.0
**Status:** ✅ COMPLET - Compilation réussie

## Vue d'ensemble

Implémentation complète du système de workflow financier (approbations multi-niveaux et budgets) pour UnionFlow. Cette implémentation backend complète la feature mobile Finance Workflow et débloque la production.

## Architecture

### Pattern d'architecture
- **Multi-module Maven:** Séparation API (DTOs) / Implementation (Quarkus)
- **Clean Architecture:** Entities → Repositories → Services → Resources
- **DDD:** Logique métier dans les entités et services
- **Panache Repository:** BaseRepository<T> pattern pour les repositories

### Stack technique
- Quarkus 3.15.1
- Java 17
- Hibernate Panache
- PostgreSQL 15
- JAX-RS (REST)
- Jakarta Bean Validation
- Flyway (migrations)
- Lombok
- OpenAPI/Swagger

## Composants implémentés

### 1. Entités JPA (4 fichiers)

#### TransactionApproval.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/entity/finance/`

**Responsabilité:** Entité principale du workflow d'approbation de transactions

**Champs clés:**
```java
@Entity
@Table(name = "transaction_approvals")
public class TransactionApproval extends BaseEntity {
    @NotNull private UUID transactionId;
    @NotBlank private String transactionType; // CONTRIBUTION, DEPOSIT, WITHDRAWAL, etc.
    @NotNull private BigDecimal amount;
    @NotBlank private String currency;
    @NotNull private UUID requesterId;
    @NotBlank private String requesterName;
    private UUID organizationId;
    @NotBlank private String requiredLevel; // NONE, LEVEL1, LEVEL2, LEVEL3
    @NotBlank private String status; // PENDING, APPROVED, VALIDATED, REJECTED, EXPIRED, CANCELLED
    @OneToMany(mappedBy = "approval", cascade = CascadeType.ALL)
    private List<ApproverAction> approvers = new ArrayList<>();
    private String rejectionReason;
    private LocalDateTime expiresAt;
    private LocalDateTime completedAt;
    private String metadata;
}
```

**Méthodes métier:**
- `hasAllApprovals()`: Vérifie si toutes les approbations requises sont obtenues
- `isExpired()`: Vérifie si l'approbation a expiré
- `countApprovals()`: Compte le nombre d'approbations accordées
- `getRequiredApprovals()`: Retourne le nombre d'approbations requises selon le niveau

**Indexes:**
- `idx_approval_transaction` sur transaction_id
- `idx_approval_status` sur status
- `idx_approval_org_status` sur (organization_id, status)
- `idx_approval_expires` sur expires_at

#### ApproverAction.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/entity/finance/`

**Responsabilité:** Action individuelle d'un approbateur

**Champs clés:**
```java
@Entity
@Table(name = "approver_actions")
public class ApproverAction extends BaseEntity {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "approval_id", nullable = false)
    private TransactionApproval approval;
    @NotNull private UUID approverId;
    @NotBlank private String approverName;
    @NotBlank private String approverRole;
    @NotBlank private String decision; // PENDING, APPROVED, REJECTED
    private String comment;
    private LocalDateTime decidedAt;
}
```

**Méthodes métier:**
- `approve(String comment)`: Approuve avec commentaire optionnel
- `reject(String reason)`: Rejette avec raison obligatoire

#### Budget.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/entity/finance/`

**Responsabilité:** Budget périodique d'une organisation

**Champs clés:**
```java
@Entity
@Table(name = "budgets")
public class Budget extends BaseEntity {
    @NotBlank private String name;
    private String description;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "organisation_id", nullable = false)
    private Organisation organisation;
    @NotBlank private String period; // MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL
    @NotNull private Integer year;
    private Integer month; // Pour les budgets MONTHLY
    @NotBlank private String status; // DRAFT, ACTIVE, CLOSED, CANCELLED
    @OneToMany(mappedBy = "budget", cascade = CascadeType.ALL)
    private List<BudgetLine> lines = new ArrayList<>();
    @NotNull private BigDecimal totalPlanned;
    @NotNull private BigDecimal totalRealized;
    @NotBlank private String currency;
    @NotNull private UUID createdById;
    private LocalDateTime approvedAt;
    private UUID approvedById;
    @NotNull private LocalDate startDate;
    @NotNull private LocalDate endDate;
    private String metadata;
}
```

**Méthodes métier:**
- `recalculateTotals()`: Recalcule totalPlanned et totalRealized depuis les lignes
- `getRealizationRate()`: Calcule le taux de réalisation
- `getVariance()`: Calcule l'écart (realized - planned)
- `isOverBudget()`: Vérifie si le budget est dépassé

#### BudgetLine.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/entity/finance/`

**Responsabilité:** Ligne budgétaire individuelle par catégorie

**Champs clés:**
```java
@Entity
@Table(name = "budget_lines")
public class BudgetLine extends BaseEntity {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "budget_id", nullable = false)
    private Budget budget;
    @NotBlank private String category; // CONTRIBUTIONS, SAVINGS, SOLIDARITY, etc.
    @NotBlank private String name;
    private String description;
    @NotNull private BigDecimal amountPlanned;
    @NotNull private BigDecimal amountRealized;
    private String notes;
}
```

**Catégories supportées:**
- CONTRIBUTIONS
- SAVINGS
- SOLIDARITY
- EVENTS
- OPERATIONAL
- INVESTMENTS
- OTHER

### 2. Repositories (2 fichiers)

#### TransactionApprovalRepository.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/repository/finance/`

**Méthodes:**
```java
@ApplicationScoped
@Unremovable
public class TransactionApprovalRepository extends BaseRepository<TransactionApproval> {
    // Recherche toutes les approbations en attente pour une organisation
    public List<TransactionApproval> findPendingByOrganisation(UUID organisationId);

    // Trouve une approbation par ID de transaction
    public Optional<TransactionApproval> findByTransactionId(UUID transactionId);

    // Trouve toutes les approbations expirées
    public List<TransactionApproval> findExpired();

    // Compte les approbations en attente pour une organisation
    public long countPendingByOrganisation(UUID organisationId);

    // Historique avec filtres
    public List<TransactionApproval> findHistory(
        UUID organizationId,
        LocalDateTime startDate,
        LocalDateTime endDate,
        String status
    );

    // Toutes les approbations en attente pour un utilisateur
    public List<TransactionApproval> findPendingForApprover(UUID approverId);

    // Approbations par demandeur
    public List<TransactionApproval> findByRequester(UUID requesterId);
}
```

#### BudgetRepository.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/domain/repository/finance/`

**Méthodes:**
```java
@ApplicationScoped
@Unremovable
public class BudgetRepository extends BaseRepository<Budget> {
    // Tous les budgets d'une organisation
    public List<Budget> findByOrganisation(UUID organisationId);

    // Budgets avec filtres optionnels
    public List<Budget> findByOrganisationAndFilters(
        UUID organisationId,
        String status,
        Integer year
    );

    // Budget actif courant
    public Optional<Budget> findActiveBudgetForCurrentPeriod(UUID organisationId);

    // Budgets par année
    public List<Budget> findByYear(UUID organisationId, Integer year);

    // Budgets par période
    public List<Budget> findByPeriod(UUID organisationId, String period);

    // Compte les budgets actifs
    public long countActiveBudgets(UUID organisationId);
}
```

### 3. DTOs (10 fichiers dans server-api)

#### DTOs Response (6)

**TransactionApprovalResponse.java**
- Données complètes d'une approbation
- Champs calculés: approvalCount, requiredApprovals, hasAllApprovals, isExpired, isPending, isCompleted

**ApproverActionResponse.java**
- Détails d'une action d'approbateur
- Champs: approverId, approverName, approverRole, decision, comment, decidedAt

**BudgetResponse.java**
- Données complètes d'un budget
- Champs calculés: realizationRate, variance, varianceRate, isOverBudget, isActive, isCurrentPeriod

**BudgetLineResponse.java**
- Détails d'une ligne budgétaire
- Champs calculés: realizationRate, variance, isOverBudget

#### DTOs Request (4)

**ApproveTransactionRequest.java**
```java
@Data
public class ApproveTransactionRequest {
    @Size(max = 1000, message = "Le commentaire ne peut dépasser 1000 caractères")
    private String comment;
}
```

**RejectTransactionRequest.java**
```java
@Data
public class RejectTransactionRequest {
    @NotBlank(message = "La raison du rejet est requise")
    @Size(min = 10, max = 1000)
    private String reason;
}
```

**CreateBudgetRequest.java**
```java
@Data
public class CreateBudgetRequest {
    @NotBlank private String name;
    private String description;
    @NotNull private UUID organizationId;
    @NotBlank private String period;
    @NotNull private Integer year;
    private Integer month;
    @NotBlank private String currency;
    @Valid @NotEmpty private List<CreateBudgetLineRequest> lines;
    private String metadata;
}
```

**CreateBudgetLineRequest.java**
```java
@Data
public class CreateBudgetLineRequest {
    @NotBlank private String category;
    @NotBlank private String name;
    private String description;
    @NotNull @DecimalMin("0.0") private BigDecimal amountPlanned;
    private String notes;
}
```

### 4. Services (2 fichiers)

#### ApprovalService.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/service/finance/`

**Méthodes principales:**
```java
@ApplicationScoped
public class ApprovalService {
    // Liste des approbations en attente
    public List<TransactionApprovalResponse> getPendingApprovals(UUID organizationId);

    // Détails d'une approbation
    public TransactionApprovalResponse getApprovalById(UUID approvalId);

    // Approuver une transaction
    @Transactional
    public TransactionApprovalResponse approveTransaction(
        UUID approvalId,
        ApproveTransactionRequest request,
        UUID approverId,
        String approverName,
        String approverRole
    );

    // Rejeter une transaction
    @Transactional
    public TransactionApprovalResponse rejectTransaction(
        UUID approvalId,
        RejectTransactionRequest request
    );

    // Historique avec filtres
    public List<TransactionApprovalResponse> getApprovalsHistory(
        UUID organizationId,
        LocalDateTime startDate,
        LocalDateTime endDate,
        String status
    );

    // Comptage
    public long countPendingApprovals(UUID organizationId);
}
```

**Logique métier implémentée:**
- Validation: transaction non expirée, approbateur différent du demandeur
- Transition automatique: PENDING → APPROVED → VALIDATED (quand toutes les approbations sont obtenues)
- Gestion des expirations
- Enregistrement de l'historique des actions

#### BudgetService.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/service/finance/`

**Méthodes principales:**
```java
@ApplicationScoped
public class BudgetService {
    // Liste des budgets avec filtres optionnels
    public List<BudgetResponse> getBudgets(
        UUID organizationId,
        String status,
        Integer year
    );

    // Détails d'un budget
    public BudgetResponse getBudgetById(UUID budgetId);

    // Créer un budget
    @Transactional
    public BudgetResponse createBudget(
        CreateBudgetRequest request,
        UUID createdById
    );

    // Suivi budgétaire (tracking)
    public Map<String, Object> getBudgetTracking(UUID budgetId);
}
```

**Logique métier implémentée:**
- Calcul automatique des dates selon la période (MONTHLY, QUARTERLY, etc.)
- Calcul des totaux à partir des lignes
- Métriques: taux de réalisation, variance, dépassement
- Suivi par catégorie avec top 5 des écarts

### 5. REST Resources (2 fichiers)

#### ApprovalResource.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/resource/finance/`

**Endpoints (6):**

```java
@Path("/api/finance/approvals")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ApprovalResource {

    // GET /api/finance/approvals/pending?organizationId={uuid}
    @GET
    @Path("/pending")
    @RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})
    public Response getPendingApprovals(@QueryParam("organizationId") UUID organizationId);

    // GET /api/finance/approvals/{approvalId}
    @GET
    @Path("/{approvalId}")
    public Response getApprovalById(@PathParam("approvalId") UUID approvalId);

    // POST /api/finance/approvals/{approvalId}/approve
    @POST
    @Path("/{approvalId}/approve")
    public Response approveTransaction(
        @PathParam("approvalId") UUID approvalId,
        @Valid ApproveTransactionRequest request
    );

    // POST /api/finance/approvals/{approvalId}/reject
    @POST
    @Path("/{approvalId}/reject")
    public Response rejectTransaction(
        @PathParam("approvalId") UUID approvalId,
        @Valid RejectTransactionRequest request
    );

    // GET /api/finance/approvals/history?organizationId={uuid}&startDate=...&endDate=...&status=...
    @GET
    @Path("/history")
    @RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})
    public Response getApprovalsHistory(
        @QueryParam("organizationId") UUID organizationId,
        @QueryParam("startDate") String startDate,
        @QueryParam("endDate") String endDate,
        @QueryParam("status") String status
    );

    // GET /api/finance/approvals/count/pending?organizationId={uuid}
    @GET
    @Path("/count/pending")
    @RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})
    public Response countPendingApprovals(@QueryParam("organizationId") UUID organizationId);
}
```

**Sécurité:**
- Extraction JWT via `@Inject JsonWebToken jwt`
- Validation des rôles avec `@RolesAllowed`
- Vérification que l'approbateur != demandeur

**Gestion d'erreurs:**
- 400 Bad Request pour données invalides
- 404 Not Found pour ressources inexistantes
- 403 Forbidden pour tentatives d'auto-approbation
- 410 Gone pour approbations expirées
- 500 Internal Server Error avec logging

#### BudgetResource.java
**Localisation:** `src/main/java/dev/lions/unionflow/server/impl/quarkus/resource/finance/`

**Endpoints (4):**

```java
@Path("/api/finance/budgets")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class BudgetResource {

    // GET /api/finance/budgets?organizationId={uuid}&status=...&year=...
    @GET
    @RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})
    public Response getBudgets(
        @QueryParam("organizationId") UUID organizationId,
        @QueryParam("status") String status,
        @QueryParam("year") Integer year
    );

    // GET /api/finance/budgets/{budgetId}
    @GET
    @Path("/{budgetId}")
    public Response getBudgetById(@PathParam("budgetId") UUID budgetId);

    // POST /api/finance/budgets
    @POST
    @RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})
    public Response createBudget(@Valid CreateBudgetRequest request);

    // GET /api/finance/budgets/{budgetId}/tracking
    @GET
    @Path("/{budgetId}/tracking")
    public Response getBudgetTracking(@PathParam("budgetId") UUID budgetId);
}
```

### 6. Migration Flyway (1 fichier)

#### V6__Create_Finance_Workflow_Tables.sql
**Localisation:** `src/main/resources/db/migration/`

**Contenu:**
```sql
-- Table des approbations de transactions
CREATE TABLE transaction_approvals (
    -- Clé primaire
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Informations de transaction
    transaction_id UUID NOT NULL,
    transaction_type VARCHAR(20) NOT NULL
        CHECK (transaction_type IN ('CONTRIBUTION', 'DEPOSIT', 'WITHDRAWAL', 'TRANSFER', 'SOLIDARITY', 'EVENT', 'OTHER')),
    amount NUMERIC(14, 2) NOT NULL CHECK (amount >= 0),
    currency VARCHAR(3) NOT NULL DEFAULT 'XOF',

    -- Demandeur
    requester_id UUID NOT NULL,
    requester_name VARCHAR(200) NOT NULL,

    -- Organisation (optionnel pour transactions personnelles)
    organisation_id UUID REFERENCES organisations(id) ON DELETE CASCADE,

    -- Niveau d'approbation requis
    required_level VARCHAR(10) NOT NULL DEFAULT 'NONE'
        CHECK (required_level IN ('NONE', 'LEVEL1', 'LEVEL2', 'LEVEL3')),

    -- Statut
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING'
        CHECK (status IN ('PENDING', 'APPROVED', 'VALIDATED', 'REJECTED', 'EXPIRED', 'CANCELLED')),

    -- Détails
    rejection_reason TEXT,
    expires_at TIMESTAMP,
    completed_at TIMESTAMP,
    metadata TEXT, -- JSON

    -- Champs BaseEntity
    date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    date_modification TIMESTAMP,
    utilisateur_creation VARCHAR(100),
    utilisateur_modification VARCHAR(100),
    version INTEGER NOT NULL DEFAULT 0,
    actif BOOLEAN NOT NULL DEFAULT TRUE
);

-- Indexes
CREATE INDEX idx_approval_transaction ON transaction_approvals(transaction_id);
CREATE INDEX idx_approval_status ON transaction_approvals(status);
CREATE INDEX idx_approval_org_status ON transaction_approvals(organisation_id, status)
    WHERE organisation_id IS NOT NULL;
CREATE INDEX idx_approval_expires ON transaction_approvals(expires_at)
    WHERE expires_at IS NOT NULL AND status = 'PENDING';

-- Table des actions d'approbateurs
CREATE TABLE approver_actions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    approval_id UUID NOT NULL REFERENCES transaction_approvals(id) ON DELETE CASCADE,
    approver_id UUID NOT NULL,
    approver_name VARCHAR(200) NOT NULL,
    approver_role VARCHAR(50) NOT NULL,
    decision VARCHAR(20) NOT NULL DEFAULT 'PENDING'
        CHECK (decision IN ('PENDING', 'APPROVED', 'REJECTED')),
    comment TEXT,
    decided_at TIMESTAMP,
    -- Champs BaseEntity...
);

CREATE INDEX idx_approver_approval ON approver_actions(approval_id);

-- Table des budgets
CREATE TABLE budgets (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(200) NOT NULL,
    description TEXT,
    organisation_id UUID NOT NULL REFERENCES organisations(id) ON DELETE CASCADE,
    period VARCHAR(20) NOT NULL
        CHECK (period IN ('MONTHLY', 'QUARTERLY', 'SEMIANNUAL', 'ANNUAL')),
    year INTEGER NOT NULL CHECK (year >= 2020 AND year <= 2100),
    month INTEGER CHECK (month >= 1 AND month <= 12),
    status VARCHAR(20) NOT NULL DEFAULT 'DRAFT'
        CHECK (status IN ('DRAFT', 'ACTIVE', 'CLOSED', 'CANCELLED')),
    total_planned NUMERIC(14, 2) NOT NULL DEFAULT 0,
    total_realized NUMERIC(14, 2) NOT NULL DEFAULT 0,
    currency VARCHAR(3) NOT NULL DEFAULT 'XOF',
    created_by_id UUID NOT NULL,
    approved_at TIMESTAMP,
    approved_by_id UUID,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    metadata TEXT, -- JSON
    -- Champs BaseEntity...
    CONSTRAINT check_end_after_start CHECK (end_date > start_date),
    CONSTRAINT check_month_for_monthly CHECK (period != 'MONTHLY' OR month IS NOT NULL)
);

CREATE INDEX idx_budget_org ON budgets(organisation_id);
CREATE INDEX idx_budget_period ON budgets(organisation_id, year, period);
CREATE INDEX idx_budget_status ON budgets(status);

-- Table des lignes budgétaires
CREATE TABLE budget_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    budget_id UUID NOT NULL REFERENCES budgets(id) ON DELETE CASCADE,
    category VARCHAR(50) NOT NULL
        CHECK (category IN ('CONTRIBUTIONS', 'SAVINGS', 'SOLIDARITY', 'EVENTS', 'OPERATIONAL', 'INVESTMENTS', 'OTHER')),
    name VARCHAR(200) NOT NULL,
    description TEXT,
    amount_planned NUMERIC(14, 2) NOT NULL CHECK (amount_planned >= 0),
    amount_realized NUMERIC(14, 2) NOT NULL DEFAULT 0 CHECK (amount_realized >= 0),
    notes TEXT,
    -- Champs BaseEntity...
);

CREATE INDEX idx_budgetline_budget ON budget_lines(budget_id);
CREATE INDEX idx_budgetline_category ON budget_lines(budget_id, category);

-- Commentaires
COMMENT ON TABLE transaction_approvals IS 'Approbations de transactions avec workflow multi-niveaux';
COMMENT ON TABLE approver_actions IS 'Actions individuelles des approbateurs';
COMMENT ON TABLE budgets IS 'Budgets organisationnels par période';
COMMENT ON TABLE budget_lines IS 'Lignes budgétaires par catégorie';
```

## Compilation et Installation

### Compilation réussie

```bash
# Module server-api
cd unionflow/unionflow-server-api
mvn clean install -DskipTests
# BUILD SUCCESS - 249 source files compiled

# Module server-impl-quarkus
cd unionflow/unionflow-server-impl-quarkus
mvn compile -DskipTests
# BUILD SUCCESS - 254 source files compiled
```

### Installation locale
Les artifacts sont installés dans le repository Maven local:
- `~/.m2/repository/dev/lions/unionflow/unionflow-server-api/1.0.0/`

## Tests

### Tests unitaires à créer
- [ ] ApprovalServiceTest
- [ ] BudgetServiceTest
- [ ] TransactionApprovalTest (entité)
- [ ] BudgetTest (entité)

### Tests d'intégration à créer
- [ ] ApprovalResourceTest
- [ ] BudgetResourceTest
- [ ] Workflow complet: création → approbation → validation
- [ ] Gestion des expirations
- [ ] Calculs budgétaires

### Tests manuels via Swagger UI
Endpoints accessibles sur: `http://localhost:8085/q/swagger-ui`

## Workflow d'approbation

### Niveaux d'approbation
- **NONE:** Pas d'approbation requise (0)
- **LEVEL1:** 1 approbation requise
- **LEVEL2:** 2 approbations requises
- **LEVEL3:** 3 approbations requises

### États possibles
```
PENDING → APPROVED → VALIDATED
    ↓         ↓
  REJECTED  REJECTED
    ↓
  EXPIRED
```

### Flux nominal
1. Transaction créée → TransactionApproval créé avec status=PENDING
2. Approbateur 1 approuve → ApproverAction créée avec decision=APPROVED
3. Si hasAllApprovals() → status passe à VALIDATED
4. Transaction peut être exécutée

### Flux de rejet
1. Un approbateur rejette → status=REJECTED
2. rejectionReason enregistrée
3. Transaction ne peut pas être exécutée

### Gestion des expirations
- Job scheduled peut marquer les approbations expirées (expiresAt < now et status=PENDING)
- Status passe à EXPIRED
- Transaction doit être re-soumise

## Gestion des budgets

### Périodes supportées
- **MONTHLY:** Budget mensuel (year + month requis)
- **QUARTERLY:** Budget trimestriel (year requis)
- **SEMIANNUAL:** Budget semestriel (year requis)
- **ANNUAL:** Budget annuel (year requis)

### Calculs automatiques
```java
// Dates
startDate = calculé selon période
endDate = calculé selon période

// Totaux
totalPlanned = sum(lines.amountPlanned)
totalRealized = sum(lines.amountRealized)

// Métriques
realizationRate = (totalRealized / totalPlanned) * 100
variance = totalRealized - totalPlanned
varianceRate = (variance / totalPlanned) * 100
isOverBudget = totalRealized > totalPlanned
```

### Suivi (Tracking)
Le endpoint `/budgets/{id}/tracking` retourne:
```json
{
  "budgetId": "uuid",
  "budgetName": "Budget Q1 2026",
  "trackingByCategory": [
    {
      "category": "CONTRIBUTIONS",
      "planned": 5000000.00,
      "realized": 4750000.00,
      "realizationRate": 95.0,
      "variance": -250000.00,
      "isOverBudget": false
    }
  ],
  "topVariances": [
    {"category": "EVENTS", "variance": -500000.00},
    {"category": "OPERATIONAL", "variance": 200000.00}
  ],
  "overallRealizationRate": 92.5
}
```

## Sécurité

### Authentification
- JWT via Keycloak
- Token injecté avec `@Inject JsonWebToken jwt`
- Extraction: `UUID.fromString(jwt.getSubject())`

### Autorisation
- `@RolesAllowed({"ORG_ADMIN", "SUPER_ADMIN"})` sur endpoints administratifs
- Validation approbateur != demandeur dans ApprovalService

### Validation des données
- Bean Validation sur tous les DTOs
- Contraintes CHECK en base de données
- Validation métier dans les services

## Intégration avec le mobile

### Endpoints utilisés par Flutter
```dart
// Approbations
GET    /api/finance/approvals/pending?organizationId={id}
GET    /api/finance/approvals/{id}
POST   /api/finance/approvals/{id}/approve
POST   /api/finance/approvals/{id}/reject
GET    /api/finance/approvals/count/pending?organizationId={id}

// Budgets
GET    /api/finance/budgets?organizationId={id}&status={status}&year={year}
GET    /api/finance/budgets/{id}
POST   /api/finance/budgets
GET    /api/finance/budgets/{id}/tracking
```

### Format des réponses
- Toujours JSON
- Dates ISO 8601: `yyyy-MM-dd'T'HH:mm:ss`
- BigDecimal sérialisé en nombre
- Listes jamais null (toujours `[]` si vide)

## Prochaines étapes

### Priorité P0 (Production blockers)
- [x] Compilation backend réussie
- [ ] Tests unitaires des services
- [ ] Test d'intégration mobile-backend
- [ ] Migration Flyway testée en dev
- [ ] Documentation Swagger complétée

### Priorité P1 (Post-production)
- [ ] Job scheduled pour marquer les approbations expirées
- [ ] Notifications push lors d'une nouvelle demande d'approbation
- [ ] Export PDF des budgets
- [ ] Statistiques d'approbation (temps moyen, taux d'approbation, etc.)
- [ ] Audit log des actions d'approbation

### Priorité P2 (Améliorations futures)
- [ ] Délégation d'approbations
- [ ] Workflows d'approbation personnalisables par organisation
- [ ] Templates de budgets
- [ ] Comparaison budgets multi-périodes
- [ ] Alertes de dépassement budgétaire

## Fichiers créés

### Entities (4)
- `TransactionApproval.java` (142 lignes)
- `ApproverAction.java` (98 lignes)
- `Budget.java` (178 lignes)
- `BudgetLine.java` (92 lignes)

### Repositories (2)
- `TransactionApprovalRepository.java` (87 lignes)
- `BudgetRepository.java` (76 lignes)

### Services (2)
- `ApprovalService.java` (234 lignes)
- `BudgetService.java` (187 lignes)

### Resources (2)
- `ApprovalResource.java` (198 lignes)
- `BudgetResource.java` (132 lignes)

### DTOs Response (4)
- `TransactionApprovalResponse.java` (82 lignes)
- `ApproverActionResponse.java` (45 lignes)
- `BudgetResponse.java` (93 lignes)
- `BudgetLineResponse.java` (48 lignes)

### DTOs Request (4)
- `ApproveTransactionRequest.java` (27 lignes)
- `RejectTransactionRequest.java` (27 lignes)
- `CreateBudgetRequest.java` (58 lignes)
- `CreateBudgetLineRequest.java` (42 lignes)

### Migration (1)
- `V6__Create_Finance_Workflow_Tables.sql` (187 lignes)

**Total: 19 fichiers, ~2023 lignes de code**

## Conclusion

✅ **Implémentation backend Finance Workflow complétée avec succès**

L'implémentation suit rigoureusement les patterns établis dans UnionFlow:
- Architecture multi-module (API/Implementation)
- BaseEntity et BaseRepository
- Services transactionnels
- REST resources avec sécurité JWT
- Flyway pour la migration
- Validation complète (Bean Validation + DB constraints)

Le backend est maintenant prêt pour:
1. Tests unitaires et d'intégration
2. Déploiement en environnement de développement
3. Intégration avec l'app mobile Flutter
4. Tests end-to-end du workflow complet

**Date de complétion:** 2026-03-14
**Status:** ✅ READY FOR TESTING
