> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cel-eleague.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Clubs effectifs

# Clubs et effectifs

Cette page décrit les règles vérifiées côté backend (Convex) concernant les clubs et la gestion d’effectif.

Sources principales :

* `convex/social/_shared.ts`
* `convex/social/clubs.ts`
* `convex/social/memberships.ts`
* `convex/competition/match_lineups.ts`
* `convex/social/announcements.ts`

## Modèle club (données de base)

La table `clubs` contient notamment :
`gmUserId`, `name`, `isPremium`, `eaClubId`, `eaPlatform`, `logoUrl`, `premiumLogoUrl`, `bannerUrl`, `primaryColor`, `secondaryColor`, `description`, `abbreviation`, `isActive`, `ownerId`, `country`, `searchText`, `createdAt`, `updatedAt`.

Le module normalise les couleurs et nettoie les URLs optionnelles : une chaîne vide est transformée en valeur absente.

## Rôles d’effectif

Les rôles valides côté membership sont :

* `MANAGER`
* `CO_MANAGER`
* `COACH`
* `MEMBER`

Un membership actif est défini par `status = IN_CLUB` et `leftAt` absent (`isMembershipActive`).

La création/mise à jour d’un membership passe par des helpers qui :

* empêchent qu’un même club ait déjà un `MANAGER`, `CO_MANAGER` ou `COACH` actif (`assertSingletonRoleAvailable`),
* vérifient les autorisations d’acteur pour les actions de management.

## Matrice d’autorisation (club / effectif)

`assertActorCanManageClub` et `canActorCanManageClub` encapsulent les règles :

* **GM du club** : toujours autorisé.
* **Rôles de gestion** (`MANAGER`, `CO_MANAGER`) : autorisés pour les actions management par défaut.
* **`COACH`** : autorisé uniquement quand le call backend passe `includeCoach: true`.
* **`ADMIN`** : autorisé par défaut.
* **Anonyme** : refusé, sauf si l’appel explicite `allowAnonymous: true` (rare, dépend du flux).

Matrice (résumé d’usage) :

* Gestion club classique (`clubs` / recrutement / annonces) : `MANAGER`, `CO_MANAGER`, GM, `ADMIN`.
* Actions demandant explicitement recrutement élargi : `COACH` inclus via `includeCoach: true`.
* Consultations non sensibles selon flux : peuvent être plus permissives (selon les fonctions appelantes), non couvertes ici.

## Règles de cap effectif (roster)

Le calcul de cap effectif est centralisé dans `_shared.ts` :

* `resolveLeagueRosterLimit(league, club)` :
  * cap base = `league.maxActiveRoster`,
  * +10 si `club.isPremium === true`.

`assertClubRosterCapacityAllowsJoin` vérifie que, avant invitation ou acceptation, le nombre de membres actifs existants (hors joueur en cours d’acceptation, via exclusion sur `playerProfileId`) reste en dessous du cap.

Quand la limite est atteinte :

* code : `CLUB_ROSTER_LIMIT_REACHED`
* message avec `(activeRosterCount/rosterLimit)`
* surcharge premium exposée dans `premiumBonus` (`10` pour club premium, `0` sinon).

## Cas non vérifiés / à confirmer (`non vérifié`)

* Le cycle exact de mise à jour côté UI de `rosterLimit` dans toutes les vues de club n’est pas décrit ici (verrouillé par le code métier existant, non audité dans un plan produit).
* Les effets visuels d’un changement de `logoUrl`/`premiumLogoUrl` dans l’ensemble des composants front ne sont pas documentés par une spécification produit centralisée (`non vérifié`).

## Effets connexes (effet de recrutement)

Dans le flux de transfert premium/open-to-offers :

* un joueur actif dans un autre club peut encore être traité via invitation si le joueur est premium + `openToOffers`,
* l’arrivée déclenche la fermeture de ses memberships actifs précédents et la création d’un membership `IN_CLUB` dans le club cible après acceptation.

Ces règles sont détaillées côté transferts dans `docs/transferts-mercato-premium.md`.

## Invariants d’ops

* Un membership actif doit rester cohérent (`status=IN_CLUB` + `leftAt` absent).
* Les autorisations sensibles passent par les helpers `_shared`.
* Les modifications de membership à fort impact déclenchent audits, événements et/ou annonces selon le flux.
