Skip to main content

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.
Last modified on June 24, 2026