Mode hors ligne
L'application mobile doit fonctionner sans réseau : un employé qui pointe dans un sous-sol ne doit pas perdre son arrivée. Cette page explique ce que l'API garantit pour que ce soit possible. Le côté téléphone est décrit dans Mobile : mode hors ligne.
Le principe : une file d'attente
Sans réseau, l'application range chaque action dans une file d'attente sur le téléphone, puis la renvoie dès que le réseau revient.
Pour que ce soit fiable, l'API garantit trois choses.
1. Une intention, pas une bascule
Le site web dit « pointe » et l'API inverse l'état. Rejouée, la même requête inverserait l'état une seconde fois : un départ dont la réponse s'est perdue deviendrait une arrivée.
L'application dit donc ce que la personne a fait : "action": "in" ou "out" ("start" ou "end" pour une pause). Si l'action ne correspond plus à l'état, l'API répond 409 avec un code précis (already_clocked_in, not_clocked_in, already_on_break, not_on_break), et l'application sait qu'il est inutile de réessayer.
2. L'idempotence
Idempotent veut dire : envoyer une requête une fois ou dix fois donne le même résultat.
Chaque action reçoit, sur le téléphone, un identifiant unique (un UUID) : le client_id. Si l'API l'a déjà reçu pour cette personne, elle renvoie ce qu'elle a enregistré (200 au lieu de 201), et rien ne change. Un index unique de la base le garantit en dernier recours.
| Action | Champ |
|---|---|
| Pointage | clocks.client_id |
| Pause | breaks.start_client_id, breaks.end_client_id |
| Demande de correction | correction_requests.client_id |
Le rejeu est cherché avant toute autre règle : une demande renvoyée après la validation de sa semaine reste « la demande enregistrée » (200), et non un 409 qui ferait croire au téléphone qu'elle a été refusée.
3. L'heure réelle, dans des limites
L'action envoie l'heure où elle a eu lieu ("time"). L'API la garde, mais accepter une heure envoyée par le client ouvrirait la porte à la triche (« je suis arrivé à 7 h »). Elle est donc vérifiée :
| Règle | Erreur |
|---|---|
| pas dans le futur (1 minute de tolérance pour l'horloge du téléphone) | 422 in_future |
| au plus 72 heures (un week-end sans réseau) | 422 too_old |
| jamais avant le pointage ou la pause précédente | 422 out_of_order |
| jamais sur une journée qu'un manager a saisie entre-temps | 422 overlaps |
| jamais dans une semaine validée | 409 week_validated |
Et l'API note quand elle l'a reçue : start_received_at, end_received_at sur la journée et ses pauses, vides quand l'heure est celle du serveur. Le site web affiche « Offline · sent at 11:10 » quand une heure est arrivée plus de 5 minutes après l'action. Le manager sait quelles heures viennent du téléphone, et l'employé voit la même chose.
Et la session ?
Une session qui expire pendant une coupure ne doit pas faire perdre la file. Les sessions mobiles survivent à une réponse perdue lors du rafraîchissement des jetons, sans affaiblir la détection d'un jeton volé : voir Authentification.
Les fichiers
| Fichier | Rôle |
|---|---|
lib/timemanager/device_actions.ex | ce qu'un téléphone peut envoyer, et les règles sans la base (futur, 72 h) |
lib/timemanager/clocks.ex | intention, rejeu, ordre, semaine validée pour les pointages |
lib/timemanager/workingtime.ex | la même chose pour les pauses (clock_break/2) |
lib/timemanager/corrections.ex | rejeu des demandes (submit_request/2) |