# Cohérence des données — UnionFlow Mobile

Ce document décrit les conventions et alignements API ↔ app pour éviter les incohérences.

## 1. Configuration

- **API** : `AppConfig.apiBaseUrl` (initialisé dans `main()` via `AppConfig.initialize()`). Utilisé par `ApiClient` (Dio `baseUrl`).
- **Keycloak** : `AppConfig.keycloakBaseUrl`, `keycloakRealmUrl`, `keycloakTokenUrl`.
- Toutes les requêtes passent par le même `ApiClient` (token, refresh, timeouts).

## 2. Membres (Annuaire)

| Backend (MembreSummaryResponse / PagedResponse) | Mobile (MembreCompletModel / repository) |
|-------------------------------------------------|------------------------------------------|
| `data` (liste), `total`, `page`, `size`, `totalPages` | `_parseMembreSearchResult` lit `data`, `total` (num→int), `page`, `size`, `totalPages` |
| `associationNom` | Normalisé → `organisationNom` dans `_normalizeAndParseMembre` |
| `statutCompte` ("ACTIF", etc.) | Normalisé → `statut` (enum StatutMembre) |
| `photoUrl` (MembreResponse détail) | Normalisé → `photo` si absent |
| `id`, `organisationId` (UUID) | Convertis en `String` avant `fromJson` |
| `nom`, `prenom`, `email` requis | Modèle : champs requis ; summary backend les envoie toujours |

- **Liste paginée** : GET `/api/membres?page=&size=` → réponse `PagedResponse` avec `data`, `total`, `page`, `size`, `totalPages`.
- **Recherche** : GET `/api/membres/recherche?q=&page=&size=` → liste ou même structure paginée selon backend.
- **Affichage annuaire** : `members_page_wrapper` convertit `MembreCompletModel` en `Map` avec `status` = libellé français (Actif, En attente, etc.) via `_mapStatutToString(statut)`.

## 3. Cotisations (Contributions)

- **Mes cotisations** : GET `/api/cotisations/mes-cotisations?page=&size=` → backend renvoie une **liste** (pas un objet paginé). Le repository gère `data is List`.
- **En attente** : GET `/api/cotisations/mes-cotisations/en-attente` → liste. Le repository accepte aussi `data['data']` ou `data['content']` si le format change.
- Modèle : `ContributionModel` avec `id`, `statut`, `montantDu`, `montantPaye`, `dateEcheance`, `nomMembre`, etc. alignés sur les champs backend. Côté mobile, `membreNom` utilise `nomMembre` avec fallback sur `nomCompletMembre` (Summary vs Response).

## 4. Épargne

- **Comptes** : GET `/api/v1/epargne/comptes/mes-comptes` → liste de comptes. `CompteEpargneModel` : `id`, `membreId`, `organisationId` en `String` (backend UUID sérialisé).
- **Transactions** : GET `/api/v1/epargne/transactions/compte/{compteId}` → liste. `TransactionEpargneModel.fromJson` avec `_toDouble` pour montants.
- Tous les IDs (compte, membre, org) sont traités en `String` côté mobile (`toString()` si besoin).

## 5. Organisations

- **Mes organisations** : GET `/api/organisations/mes` → liste. `OrganizationModel` avec `id`, `nom`, `nomCourt`, etc.
- **Liste (admin)** : GET `/api/organisations?page=&size=` → liste ou paginée selon endpoint. Repository parse `response.data as List` ou structure paginée.

## 6. Admin utilisateurs (SUPER_ADMIN)

- **Liste** : GET `/api/admin/users?page=&size=&search=` → UnionFlow renvoie `UserSearchResultDTO` (proxy lions-user-manager). Structure vérifiée dans `lions-user-manager-server-api` :
  - **UserSearchResultDTO** : `users` (List\<UserDTO\>), `totalCount` (Long), `currentPage` (Integer), `pageSize` (Integer), `totalPages` (Integer), plus optionnels (`hasNextPage`, `criteria`, `executionTimeMs`, etc.).
  - **UserDTO** (BaseDTO + champs) : `id`, `username`, `email`, `prenom`, `nom`, `enabled`, `realmRoles` (List\<String\>), `statut`, `dateCreation`, etc.
  - Le repository mobile lit `data['users']`, `totalCount`, `currentPage`, `pageSize`, `totalPages` (avec cast `num` → int) et parse chaque élément avec `AdminUserModel.fromJson`. Alignement confirmé.
- **Associer organisation** : POST `/api/admin/associer-organisation` avec body `{ "email", "organisationId" }`.

## 7. Dashboard

- **Avec organisation** : appel avec `organizationId` et `userId` (chaînes). `DashboardEntity` / `DashboardStatsModel` alignés sur les réponses backend.
- **Membre sans org** : GET `/api/dashboard/membre/me` → `MembreDashboardSyntheseModel`, mappé vers la même `DashboardEntity` pour réutilisation UI.

## 8. Bonnes pratiques

- **IDs** : toujours normaliser en `String` côté mobile (`.toString()`) pour UUID backend.
- **Pagination** : préférer `(data['total'] as num?)?.toInt()` pour accepter `int` ou `double` selon la sérialisation JSON.
- **Statut / libellé** : backend envoie souvent `statutCompte` + `statutCompteLibelle` ; le mobile peut normaliser `statutCompte` → `statut` (enum) et utiliser les libellés pour l’affichage.
- **Noms de champs** : garder une seule normalisation dans le repository (ex. `_normalizeAndParseMembre`) pour éviter les doublons (associationNom, photoUrl, statutCompte, etc.).

## 9. Vérifications effectuées

- Membres : PagedResponse `data`/`total`/`page`/`size`/`totalPages` alignés ; normalisation associationNom, statutCompte, photoUrl, id/organisationId.
- Cotisations : liste directe pour mes-cotisations et en-attente.
- Épargne : IDs en string, montants avec _toDouble.
- Config : une seule base URL et un seul ApiClient.
