Skip to content

Règles métier ​

Les règles de calcul des heures sont dans src/utils/history.ts. Ce sont les mêmes que sur le web : un employé doit voir les mêmes chiffres sur son ordinateur et sur son téléphone.

Les constantes ​

ts
export const DAILY_TARGET_MINUTES = 7 * 60;                  // 420 minutes = 7 h
export const WEEKLY_TARGET_MINUTES = DAILY_TARGET_MINUTES * 5; // 2100 minutes = 35 h
const MAX_SHIFT_MINUTES = 14 * 60;                            // 840 minutes = 14 h

Tout est compté en minutes, pour éviter les arrondis. On ne convertit en heures que pour l'affichage.

Une période de travail ​

L'API renvoie des périodes : un début, une fin (ou null si elle est en cours), et des pauses à l'intérieur.

ts
type WorkingTime = {
  id: number;
  userId: number;
  start: Date;
  end: Date | null;
  breaks: { id: number; start: Date; end: Date | null }[];
};
08:30 ─────── 12:00 ░░░░░ 12:45 ──────────── 17:15
      travail          pause        travail

Les fonctions ​

isForgotten(période, maintenant) ​

Une période qui dure plus de 14 h est un pointage oublié : la personne a oublié de pointer son départ.

ts
export function isForgotten(period, now) {
  return minutesBetween(period.start, period.end ?? now) > MAX_SHIFT_MINUTES;
}

Pour une période encore ouverte, on compte jusqu'à maintenant. Une journée commencée hier à 8 h et jamais fermée devient « oubliée » à 22 h.

Une période oubliée :

  • n'est pas comptée dans les totaux ;
  • ne s'affiche pas comme « en service » sur l'onglet Clock ;
  • apparaît comme « Départ manquant » dans l'historique.

breakMinutesOf(période, maintenant) ​

La somme des pauses. Une pause en cours compte jusqu'à maintenant.

workedMinutesOf(période, maintenant) ​

ts
export function workedMinutesOf(period, now) {
  return minutesBetween(period.start, period.end ?? now) - breakMinutesOf(period, now);
}

Temps travaillé = durée de la période moins ses pauses. Pour l'exemple ci-dessus : 8 h 45 de période moins 45 min de pause = 8 h.

minutesByDay(périodes, maintenant) ​

Le temps travaillé par jour, pour les graphiques. Les périodes oubliées sont ignorées. Chaque période est rangée au jour où elle commence.

Le jour d'une période ​

Une période compte pour le jour où elle commence, même si elle se termine après minuit.

Lundi 22:00 ──────────────── Mardi 06:00
     → 8 h comptées le lundi

C'est la règle BR-11 des spécifications, pour le travail de nuit.

Le statut d'un jour : describeHistory ​

Cette fonction transforme une liste de périodes et de corrections en une ligne par jour, avec un statut. C'est elle qui alimente l'écran Times.

ts
type HistoryDay = {
  workDate: string;            // "2026-10-13"
  start: Date;
  end: Date | null;
  breakMinutes: number;
  workedMinutes: number | null;
  status: StatusKind;
};

Pour chaque jour (sauf aujourd'hui, qui est sur l'onglet Clock), le statut est choisi dans cet ordre :

StatutQuandCe qu'on affiche
Départ manquantUne période de plus de 14 h ce jour-là, ou une demande de correction « arrivée manquante » ou « départ manquant » encore en attentePas de fin ni de durée : –
Heures supplémentairesLe total de la semaine, en comptant ce jour, dépasse 35 hDébut, fin, durée
CorrigéUne demande de correction a été approuvée pour ce jourDébut, fin, durée
CompletTout le resteDébut, fin, durée

Les heures supplémentaires jour par jour ​

Les jours sont parcourus dans l'ordre, du plus ancien au plus récent, en additionnant le total de leur semaine :

JourTravailléTotal de la semaineStatut
Lundi8 h8 hComplet
Mardi9 h17 hComplet
Mercredi9 h26 hComplet
Jeudi8 h34 hComplet
Vendredi7 h41 hHeures supplémentaires

Seul le vendredi est marqué : c'est ce jour-là que la semaine dépasse 35 h (règle BR-02).

Le résultat est ensuite inversé : l'écran affiche le jour le plus récent en premier.

Les absences : src/utils/absences.ts ​

L'API stocke une absence par jour. Pour l'affichage, groupAbsences regroupe les jours ouvrés consécutifs du même type en une période. Le week-end ne coupe pas une période : un congé du vendredi et un du lundi suivant forment une seule période.

lun. 13, mar. 14, mer. 15 (congé)  →  « lun. 13 oct. – mer. 15 oct. »
ConstanteValeur
MAX_ABSENCE_DAYS31 jours au maximum pour une saisie

Une absence ne porte jamais de motif, seulement un type : congé, maladie ou autre (règle BR-12).

Les formats : src/utils/time.ts ​

FonctionExemple
formatClockTime(date)08:30
formatMinutes(minutes)7 h 30
formatTimer(minutes)7h 30m 05s (le chrono sous les boutons)
formatDay(date)« lun. 13 oct. » dans la langue choisie
isSameDay(a, b)Les deux dates sont-elles le même jour ?
isValidTime(texte)"08:30" est-il une heure valide ?

Ce qui n'est pas calculé par l'app ​

Certaines règles sont uniquement appliquées par l'API. L'app affiche simplement son refus avec un message clair :

  • deux périodes d'une même personne ne se chevauchent jamais (BR-18) ;
  • une semaine validée ne peut plus être modifiée (BR-13) ;
  • personne ne modifie ses propres heures (BR-01) ;
  • la fin d'une période ne peut pas être dans le futur.