> ## 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.

# Matchs reports resultats

# Matchs, reports, résultats et compositions

Cette page documente uniquement les règles vérifiées dans le backend Convex de CEL.

Sources principales :

* `convex/schema.ts`
* `convex/competition/match_result_mutations.ts`
* `convex/competition/match_postponement_mutations.ts`
* `convex/competition/match_postponement_shared.ts`
* `convex/competition/match_admin_mutations.ts`
* `convex/admin/disputes.ts`
* `convex/competition/match_lineups.ts`
* `convex/competition/match_scheduling_mutations.ts`

## `matches.status` vs `match_reports.status`

`matches.status` est une chaîne métier utilisée par les flux Convex de match : planification, paris, validation, etc.

`match_reports.status` est un enum optionnel (`PENDING`, `ACCEPTED`, `COUNTER_PROPOSED`, `CANCELLED`, `REJECTED`) ; `undefined` reste possible en legacy.

Conséquence vérifiée : une ligne de report legacy bloque la création d’un nouveau report tant que `assertNoLegacyMatchReportsForMatch` n’est pas passée.

## `matches` et données liées

`matches` contient notamment : `leagueId`, `homeClubId`, `awayClubId`, `matchday`, `scheduledAt`, `originalScheduledAt`, `postponedAt`, `postponedById`, `status`, `isActive`.

Des index techniques existent par ligue, statut, clubs, journée, date planifiée et états de synchronisation EA.

## Cycle de vie des résultats de match

### Statuts observés

* `scheduled`
* `pending_validation`
* `disputed`
* `validated`
* `completed` (présent dans certains filtres ; le passage exact vers/depuis ce statut n’est pas confirmé dans les modules lus) **\[non vérifié]**

### Soumission GM (`submitMatchResult`)

* En premier passage (match en `scheduled`), une soumission côté club crée `homeScoreBy*` / `awayScoreBy*` et met `match.status = pending_validation`.
* En second passage, si les deux scores concordent :
  * scores finaux posés,
  * `validatedAt`,
  * `match.status = validated`,
  * settlement paris (vague courante) + mise à jour classement.
* Si les soumissions divergent :
  * `match_results.isDisputed = true`,
  * `match.status = disputed`.

### Actions admin avancées

* `adminSetMatchResult` (admin) :
  * écrit un score explicite,
  * cible `validated` (défaut) ou `pending_validation`,
  * inverse la précédente contribution au classement si nécessaire,
  * peut nettoyer les sous-soumissions,
  * relance settlement/standings en `validated`,
  * réouvre paris si retour en `pending_validation`.
* `adminResetMatchResult` (admin) :
  * supprime le résultat,
  * inverse classement si nécessaire,
  * remet en `scheduled` ou `pending_validation`,
  * réouvre les paris.
* `contestResult` suit le flux de gestion de litige côté module (admin ou staff selon règles d’accès applicables), puis réévalue statut/paris selon la phase de résolution.

## Report de match (postponement)

Les `match_reports` peuvent avoir les statuts vérifiés : `PENDING`, `ACCEPTED`, `COUNTER_PROPOSED`, `CANCELLED`, `REJECTED`.

`isReportQuotaConsumed` compte uniquement les `ACCEPTED`.

Le quota métier de base utilise `league.reportsPerClubTotal` avec un bonus premium `PREMIUM_REPORTS_BONUS = 2` côté mutation.

### `requestPostponement`

Conditions confirmées :

* match existant en `scheduled` ou `provisional`,
* acteur côté l’un des clubs,
* quota disponible,
* `scheduledAt` conforme, pas de conflit de calendrier,
* pas de report legacy,
* pas de `PENDING` / `COUNTER_PROPOSED`,
* pas d’`ACCEPTED` existant.

Effet : création `PENDING` + audit.

### `acceptPostponement`

Entrées : report `PENDING` ou `COUNTER_PROPOSED`.

* Côté adverse si `PENDING`, côté demandeur si `COUNTER_PROPOSED`.
* revalidation date + conflit + quota.

Effets :

* status report `ACCEPTED`,
* `matches.scheduledAt` déplacé,
* conservation de `originalScheduledAt` si déjà présent,
* `postponedAt`, `postponedById` mis à jour,
* `match.status = scheduled`.

### `counterProposePostponement`

Sur `PENDING`, côté adverse, avec nouvelle date valide.
Statut -> `COUNTER_PROPOSED`.

### `cancelPostponement`

Sur `PENDING` ou `COUNTER_PROPOSED`, acteur du club demandeur selon la logique du module.

### `rejectPostponement`

Exclusif admin sur `PENDING` ou `COUNTER_PROPOSED`.

### `adminDirectPostponement`

Parcours admin qui valide quotas/date/conflits et crée directement un report `ACCEPTED` en impactant le match.

## Snapshots calendrier

Le modèle conserve au moins `originalScheduledAt` avant report.

`acceptPostponement` met à jour `scheduledAt` du match.

La mécanique exacte de restauration inverse d’un report annulé/invalidé après acceptation n’est pas pleinement détaillée dans les modules consultés. **\[non vérifié]**

## Litiges

`reportDispute` (module résultat) :

* crée une entrée `disputes` en `OPEN` si aucun litige ouvert,
* met `match.status = disputed`.

Le contrôle d’accès côté invocation n’est pas exclusivement admin-only : côté club actif et admin peuvent initier la déclaration selon helpers.

`resolveDispute` (admin) :

* `APPROVED` : validation du match,
* `REJECTED` : `match.status = pending_validation`,
* `CUSTOM` + score : enregistrement score, sortie litige, `validatedAt`.

`convex/admin/disputes.ts` expose la même palette de statuts de décision.

## Compositions d’avant-match

`match_lineups` couvre les statuts `draft` et `submitted`.

Règles vérifiées :

* deadline : `LINEUP_DEADLINE_MS = 20 * 60 * 1000`,
* soumission seulement si match `scheduled` et avant deadline,
* minimum 7 joueurs à la soumission,
* `ADMIN` et `MODERATOR` peuvent bypasser statut/deadline (auditées).

## Limites / précautions

* La provenance exacte de certains statuts historiques (ex. `completed`) reste non vérifiée dans ce périmètre de code. **\[non vérifié]**
* La restitution complète d’un report sur annulation/rejet post-acceptation dépend du chainage d’appels externes au module lu. **\[non vérifié]**
