feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -0,0 +1,387 @@
{
"description": "Finance Workflow - Exemples de requêtes API pour tests",
"baseUrl": "http://localhost:8085",
"note": "Ces exemples peuvent être utilisés dans Swagger UI, Postman, ou curl",
"authentication": {
"note": "Si JWT requis, utilisez Keycloak pour obtenir un token",
"keycloakUrl": "http://localhost:8180/realms/unionflow/protocol/openid-connect/token",
"exampleToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"tests": [
{
"id": "TEST-01",
"name": "Lister les approbations en attente",
"method": "GET",
"endpoint": "/api/finance/approvals/pending",
"description": "Récupère toutes les approbations en attente pour une organisation",
"queryParams": {
"organizationId": "00000000-0000-0000-0000-000000000001"
},
"expectedStatus": [200, 401, 403],
"expectedResponse": {
"success_empty": [],
"success_with_data": [
{
"id": "uuid",
"transactionId": "uuid",
"transactionType": "CONTRIBUTION",
"amount": 50000.00,
"currency": "XOF",
"requesterId": "uuid",
"requesterName": "Jean Dupont",
"organizationId": "uuid",
"requiredLevel": "LEVEL1",
"status": "PENDING",
"approvers": [],
"createdAt": "2026-03-14T00:00:00",
"expiresAt": "2026-03-21T00:00:00",
"approvalCount": 0,
"requiredApprovals": 1,
"hasAllApprovals": false,
"isExpired": false,
"isPending": true,
"isCompleted": false
}
]
}
},
{
"id": "TEST-02",
"name": "Récupérer une approbation par ID",
"method": "GET",
"endpoint": "/api/finance/approvals/{approvalId}",
"description": "Récupère les détails d'une approbation spécifique",
"pathParams": {
"approvalId": "00000000-0000-0000-0000-000000000001"
},
"expectedStatus": [200, 404],
"expectedResponse": {
"success": {
"id": "uuid",
"transactionId": "uuid",
"transactionType": "CONTRIBUTION",
"amount": 50000.00,
"status": "PENDING"
},
"error_404": {
"message": "Approbation non trouvée: {approvalId}"
}
}
},
{
"id": "TEST-03",
"name": "Approuver une transaction",
"method": "POST",
"endpoint": "/api/finance/approvals/{approvalId}/approve",
"description": "Approuve une transaction en attente",
"authRequired": true,
"pathParams": {
"approvalId": "00000000-0000-0000-0000-000000000001"
},
"requestBody": {
"comment": "Approuvé après vérification des documents"
},
"expectedStatus": [200, 400, 403, 404],
"expectedResponse": {
"success": {
"id": "uuid",
"status": "VALIDATED",
"approvalCount": 1,
"hasAllApprovals": true,
"completedAt": "2026-03-14T10:30:00"
},
"error_403_self_approval": {
"message": "Un utilisateur ne peut pas approuver sa propre demande"
},
"error_400_expired": {
"message": "Cette approbation est expirée"
}
}
},
{
"id": "TEST-04",
"name": "Rejeter une transaction",
"method": "POST",
"endpoint": "/api/finance/approvals/{approvalId}/reject",
"description": "Rejette une transaction avec une raison obligatoire",
"authRequired": true,
"pathParams": {
"approvalId": "00000000-0000-0000-0000-000000000001"
},
"requestBody": {
"reason": "Montant trop élevé, nécessite révision du budget avant approbation"
},
"expectedStatus": [200, 400, 404],
"expectedResponse": {
"success": {
"id": "uuid",
"status": "REJECTED",
"rejectionReason": "Montant trop élevé...",
"completedAt": "2026-03-14T10:30:00"
},
"error_400_short_reason": {
"message": "La raison doit contenir entre 10 et 1000 caractères"
}
}
},
{
"id": "TEST-05",
"name": "Historique des approbations",
"method": "GET",
"endpoint": "/api/finance/approvals/history",
"description": "Récupère l'historique avec filtres optionnels",
"queryParams": {
"organizationId": "00000000-0000-0000-0000-000000000001",
"startDate": "2026-03-01T00:00:00",
"endDate": "2026-03-14T23:59:59",
"status": "APPROVED"
},
"expectedStatus": [200],
"expectedResponse": {
"success": [
{
"id": "uuid",
"status": "APPROVED",
"completedAt": "2026-03-10T14:30:00"
}
]
}
},
{
"id": "TEST-06",
"name": "Compter les approbations en attente",
"method": "GET",
"endpoint": "/api/finance/approvals/count/pending",
"description": "Retourne le nombre d'approbations en attente",
"queryParams": {
"organizationId": "00000000-0000-0000-0000-000000000001"
},
"expectedStatus": [200],
"expectedResponse": {
"success": 5
}
},
{
"id": "TEST-07",
"name": "Lister les budgets",
"method": "GET",
"endpoint": "/api/finance/budgets",
"description": "Liste tous les budgets avec filtres optionnels",
"queryParams": {
"organizationId": "00000000-0000-0000-0000-000000000001",
"status": "ACTIVE",
"year": 2026
},
"expectedStatus": [200],
"expectedResponse": {
"success": [
{
"id": "uuid",
"name": "Budget Q1 2026",
"period": "QUARTERLY",
"year": 2026,
"status": "ACTIVE",
"totalPlanned": 10000000.00,
"totalRealized": 8500000.00,
"currency": "XOF",
"realizationRate": 85.0,
"variance": -1500000.00,
"isOverBudget": false
}
]
}
},
{
"id": "TEST-08",
"name": "Créer un budget mensuel",
"method": "POST",
"endpoint": "/api/finance/budgets",
"description": "Crée un nouveau budget avec lignes budgétaires",
"authRequired": true,
"requestBody": {
"name": "Budget Mars 2026",
"description": "Budget mensuel pour le mois de mars",
"organizationId": "00000000-0000-0000-0000-000000000001",
"period": "MONTHLY",
"year": 2026,
"month": 3,
"currency": "XOF",
"lines": [
{
"category": "CONTRIBUTIONS",
"name": "Cotisations mensuelles",
"description": "Cotisations des membres actifs",
"amountPlanned": 2000000.00,
"notes": "Basé sur 40 membres à 50000 XOF"
},
{
"category": "SAVINGS",
"name": "Épargne collective",
"description": "Épargne pour projets futurs",
"amountPlanned": 1000000.00
},
{
"category": "OPERATIONAL",
"name": "Frais opérationnels",
"description": "Loyer, électricité, etc.",
"amountPlanned": 500000.00
}
]
},
"expectedStatus": [200, 400, 404],
"expectedResponse": {
"success": {
"id": "uuid",
"name": "Budget Mars 2026",
"period": "MONTHLY",
"year": 2026,
"month": 3,
"startDate": "2026-03-01",
"endDate": "2026-03-31",
"totalPlanned": 3500000.00,
"totalRealized": 0.00,
"lines": [
{
"category": "CONTRIBUTIONS",
"amountPlanned": 2000000.00
}
]
},
"error_400_monthly_without_month": {
"message": "Le mois est requis pour un budget MONTHLY"
}
}
},
{
"id": "TEST-09",
"name": "Créer un budget trimestriel",
"method": "POST",
"endpoint": "/api/finance/budgets",
"description": "Budget pour un trimestre (Q1, Q2, Q3, Q4)",
"authRequired": true,
"requestBody": {
"name": "Budget Q2 2026",
"description": "Budget deuxième trimestre 2026",
"organizationId": "00000000-0000-0000-0000-000000000001",
"period": "QUARTERLY",
"year": 2026,
"month": 4,
"currency": "XOF",
"lines": [
{
"category": "CONTRIBUTIONS",
"name": "Cotisations trimestrielles",
"amountPlanned": 6000000.00
},
{
"category": "EVENTS",
"name": "Événements du trimestre",
"amountPlanned": 2000000.00
}
]
},
"expectedStatus": [200],
"expectedResponse": {
"success": {
"period": "QUARTERLY",
"startDate": "2026-04-01",
"endDate": "2026-06-30"
}
}
},
{
"id": "TEST-10",
"name": "Suivi budgétaire (tracking)",
"method": "GET",
"endpoint": "/api/finance/budgets/{budgetId}/tracking",
"description": "Retourne les métriques de suivi d'un budget",
"pathParams": {
"budgetId": "00000000-0000-0000-0000-000000000001"
},
"expectedStatus": [200, 404],
"expectedResponse": {
"success": {
"budgetId": "uuid",
"budgetName": "Budget Q1 2026",
"trackingByCategory": [
{
"category": "CONTRIBUTIONS",
"planned": 5000000.00,
"realized": 4500000.00,
"realizationRate": 90.0,
"variance": -500000.00,
"isOverBudget": false
},
{
"category": "EVENTS",
"planned": 3000000.00,
"realized": 3200000.00,
"realizationRate": 106.67,
"variance": 200000.00,
"isOverBudget": true
}
],
"topVariances": [
{
"category": "CONTRIBUTIONS",
"variance": -500000.00
},
{
"category": "EVENTS",
"variance": 200000.00
}
],
"overallRealizationRate": 95.0
}
}
}
],
"validationTests": [
{
"id": "VAL-01",
"name": "Validation - Raison de rejet trop courte",
"method": "POST",
"endpoint": "/api/finance/approvals/{approvalId}/reject",
"requestBody": {
"reason": "Court"
},
"expectedStatus": [400],
"expectedError": {
"field": "reason",
"message": "La raison doit contenir entre 10 et 1000 caractères"
}
},
{
"id": "VAL-02",
"name": "Validation - Budget sans lignes",
"method": "POST",
"endpoint": "/api/finance/budgets",
"requestBody": {
"name": "Budget vide",
"organizationId": "uuid",
"period": "MONTHLY",
"year": 2026,
"month": 3,
"currency": "XOF",
"lines": []
},
"expectedStatus": [400],
"expectedError": {
"field": "lines",
"message": "ne doit pas être vide"
}
}
]
}