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:
387
unionflow/FINANCE_WORKFLOW_API_TESTS.json
Normal file
387
unionflow/FINANCE_WORKFLOW_API_TESTS.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user