Répartit les préconisations de rattrapage sur les demi-journées restantes de la semaine
Au lieu de reporter tout le manque horaire sur la sortie du jour courant, le calcul distribue désormais le retard à parts égales sur chaque demi-journée non traitée (aujourd'hui et jours à venir), avec un plafond de sécurité si le rattrapage dépasse minuit. La table affiche une cible par demi-journée (matin/après-midi) et le bandeau de soldes indique le reste à faire et le nombre de demi-journées restantes.
This commit is contained in:
@ -95,12 +95,14 @@ def test_compute_week_cumul_visible_seulement_si_chaine_ininterrompue():
|
||||
assert jours[4]["cumul_visible"] is False
|
||||
|
||||
|
||||
def test_compute_week_sortie_cible_journee_en_cours(freeze_today):
|
||||
def test_compute_week_cible_repartie_sur_seule_demi_journee_restante(freeze_today):
|
||||
"""Régression: capture exactement les seuils 9h/12h/13h30/17h codés en dur.
|
||||
|
||||
Lundi-jeudi complets à 468 min. Vendredi (aujourd'hui, frozen) pointé
|
||||
seulement le matin 09:00-12:00 (180 min). Le calcul doit produire une
|
||||
cible de sortie et d'entrée dérivées des seuils métier actuels.
|
||||
seulement le matin 09:00-12:00 (180 min, sous les 234 min nominales de la
|
||||
demi-journée). Comme il ne reste qu'une seule demi-journée non traitée
|
||||
(l'après-midi de vendredi), tout le manque de la semaine s'y retrouve —
|
||||
même résultat que l'ancien algorithme "cliff-edge" dans ce cas précis.
|
||||
"""
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"]
|
||||
pointages = _semaine_complete(dates_completes)
|
||||
@ -109,32 +111,50 @@ def test_compute_week_sortie_cible_journee_en_cours(freeze_today):
|
||||
result = compute_week(pointages, [], HEURES_JOUR_MIN)
|
||||
vendredi = result["jours"][4]
|
||||
|
||||
assert vendredi["sortie_cible"] == "18:18"
|
||||
assert vendredi["entree_cible"] == "12:12"
|
||||
assert vendredi["cible_matin"] is None # matin déjà pointé (entrée+sortie), plus rien à y faire
|
||||
assert vendredi["cible_aprem"] == "18:18"
|
||||
|
||||
|
||||
def test_compute_week_jour_futur_non_entame_pas_de_cible(freeze_today):
|
||||
"""Un jour strictement après "aujourd'hui" et pas encore entamé n'a pas de cible."""
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16", "2026-07-17"]
|
||||
def test_compute_week_jour_futur_non_entame_recoit_une_cible_repartie(freeze_today):
|
||||
"""Un jour futur non entamé fait maintenant partie de la répartition (spread sur la semaine)."""
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"]
|
||||
pointages = _semaine_complete(dates_completes)
|
||||
pointages.append(_pointage("2026-07-17", matin_entree="09:00", matin_sortie="12:00"))
|
||||
pointages.append(_pointage("2026-07-20")) # lundi suivant, rien pointé
|
||||
|
||||
result = compute_week(pointages, [], HEURES_JOUR_MIN)
|
||||
# Le manque (288 min) est maintenant réparti entre l'après-midi de vendredi et
|
||||
# les deux demi-journées de lundi, au lieu d'être entièrement dumpé sur vendredi soir.
|
||||
vendredi = result["jours"][4]
|
||||
lundi_suivant = result["jours"][5]
|
||||
assert lundi_suivant["entree_cible"] is None
|
||||
assert lundi_suivant["sortie_cible"] is None
|
||||
assert vendredi["cible_aprem"] is not None
|
||||
assert lundi_suivant["cible_matin"] is not None or lundi_suivant["cible_aprem"] is not None
|
||||
|
||||
|
||||
def test_compute_week_jour_courant_non_entame_calcule_quand_meme_une_cible(freeze_today):
|
||||
""""Aujourd'hui" sans rien pointé n'est PAS traité comme futur (date > today est faux à égalité)."""
|
||||
def test_compute_week_jour_futur_a_egalite_pas_de_cible(freeze_today):
|
||||
"""Quand le rythme normal des demi-journées restantes suffit déjà, aucune cible n'est affichée."""
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16", "2026-07-17"]
|
||||
pointages = _semaine_complete(dates_completes)
|
||||
pointages.append(_pointage("2026-07-20")) # lundi suivant, rien pointé — mais la semaine est déjà à l'équilibre
|
||||
|
||||
result = compute_week(pointages, [], HEURES_JOUR_MIN)
|
||||
lundi_suivant = result["jours"][5]
|
||||
assert lundi_suivant["cible_matin"] is None
|
||||
assert lundi_suivant["cible_aprem"] is None
|
||||
|
||||
|
||||
def test_compute_week_jour_courant_non_entame_rythme_normal_suffit(freeze_today):
|
||||
""""Aujourd'hui" sans rien pointé n'est pas traité comme un jour futur figé : ses deux
|
||||
demi-journées entrent dans la répartition. Ici le manque (468 min) correspond exactement
|
||||
au nominal des deux demi-journées restantes : le rythme normal suffit, pas de cible."""
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"]
|
||||
pointages = _semaine_complete(dates_completes)
|
||||
pointages.append(_pointage("2026-07-17"))
|
||||
|
||||
result = compute_week(pointages, [], HEURES_JOUR_MIN)
|
||||
vendredi = result["jours"][4]
|
||||
assert vendredi["sortie_cible"] == "21:18"
|
||||
assert vendredi["entree_cible"] is None
|
||||
assert vendredi["cible_matin"] is None
|
||||
assert vendredi["cible_aprem"] is None
|
||||
|
||||
|
||||
def test_compute_week_plages_personnalisees(freeze_today):
|
||||
@ -147,9 +167,11 @@ def test_compute_week_plages_personnalisees(freeze_today):
|
||||
heures_jour = (13 * 60 - 8 * 60) + (18 * 60 - 14 * 60) # 540 = journée complète sous ce schéma
|
||||
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"]
|
||||
pointages = [_pointage(d, "08:00", "13:00", "14:00", "18:00") for d in dates_completes]
|
||||
pointages.append(_pointage("2026-07-17", "08:00", "13:00")) # matin complet, aprem pas commencé
|
||||
# Matin écourté (240 min au lieu des 300 nominales) : le manque doit être
|
||||
# rattrapé sur l'unique demi-journée restante, avec les seuils personnalisés.
|
||||
pointages.append(_pointage("2026-07-17", "08:00", "12:00"))
|
||||
|
||||
result = compute_week(pointages, [], heures_jour, plages)
|
||||
vendredi = result["jours"][4]
|
||||
assert vendredi["sortie_cible"] == "18:00"
|
||||
assert vendredi["entree_cible"] is None
|
||||
assert vendredi["cible_matin"] is None
|
||||
assert vendredi["cible_aprem"] == "19:00"
|
||||
|
||||
Reference in New Issue
Block a user