Retour décalé après astreinte : la présence obligatoire couverte compte comme effectuée

Quand le repos de 11h décale le retour au bureau (ex. 11:00), les heures de
présence obligatoire couvertes (ex. 09:00→11:00) sont créditées à la colonne
Travaillé au lieu d'être retirées des heures dues, qui restent complètes.
Le solde est inchangé, mais le calcul est explicite : badge « +2h00 repos »
sous le total (plages couvertes au survol) et rappel dans le bloc astreinte.
Rien n'est crédité sans heures dues (week-end, férié, congé), et le crédit
ne dépasse pas le nominal du jour.

Corrige aussi la perte des interventions postées sans description (le zip
s'arrêtait sur la liste vide des descriptions).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
Jacquin Antoine
2026-09-09 21:05:50 +02:00
parent 3d38abd853
commit 77ae115c4f
7 changed files with 143 additions and 24 deletions

View File

@ -108,8 +108,22 @@ def exclusion_retour(conges_date: set, retour_min: int | None, plages: dict, fer
return matin_ex, aprem_ex
def couvert_detail(plages: dict, retour_min: int | None, matin_ex: int, aprem_ex: int) -> str:
"""Description des plages de présence obligatoire couvertes par le repos de
11h (affichée avec le crédit d'heures effectuées). N'apparaît que couvert > 0,
donc retour_min défini."""
parts = []
if matin_ex:
fin = minutes_to_time(min(hhmm_to_minutes(plages["matin_fin"]), retour_min or 0))
parts.append(f"matin {plages['matin_debut']}→{fin}")
if aprem_ex:
fin = minutes_to_time(min(hhmm_to_minutes(plages["aprem_fin"]), retour_min or 0))
parts.append(f"après-midi {plages['aprem_debut']}→{fin}")
return ", ".join(parts)
def nominaux_du_jour(conges_date: set, du_full_min: int, matin_excl_min: int, aprem_excl_min: int,
ferie: bool = False) -> tuple[int, int]:
ferie: bool = False) -> tuple[int, int]:
"""Nominaux (attendus) matin et aprem en minutes, après congés, jour férié et
exclusion liée à l'heure de retour."""
if ferie or "jour" in conges_date:
@ -470,9 +484,18 @@ def compute_week(pointages: list[dict], conges: list[dict], heures_jour_min: int
forfait = forfait_as_min if p["date"] in astreintes else 0
# Le forfait d'astreinte ne compte jamais dans les heures travaillées :
# il est suivi à part dans le récapitulatif d'astreinte.
travaille = heures_travaillees(p, retour_min)
du = heures_dues(p["date"], conges, heures_jour_min, plages, retour_min, jour_ferie)
du_full = du + matin_ex + aprem_ex
#
# Les heures de présence obligatoire couvertes par le repos de 11h
# (matin_ex + aprem_ex) sont comptées comme effectuées : créditées au
# travaillé du jour, les heures dues restent complètes. Le solde est le
# même qu'en soustrayant couvert du dû, mais le calcul est explicite :
# travaillé = pointé + couvert, dû = nominal complet. Sans heures dues
# (week-end, férié, congé) il n'y a rien à créditer, et le crédit ne
# dépasse jamais le nominal du jour.
du_full = heures_dues(p["date"], conges, heures_jour_min, plages, None, jour_ferie)
couvert = min(matin_ex + aprem_ex, du_full)
travaille = heures_travaillees(p, retour_min) + couvert
du = du_full
matin_nom, aprem_nom = nominaux_du_jour(conges_date, du_full, matin_ex, aprem_ex, jour_ferie)
delta = travaille - du
total_travaille += travaille
@ -492,6 +515,9 @@ def compute_week(pointages: list[dict], conges: list[dict], heures_jour_min: int
"du_full_min": du_full,
"matin_excl_min": matin_ex,
"aprem_excl_min": aprem_ex,
"couvert_min": couvert,
"couvert_h": minutes_to_hhmm(couvert) if couvert else "",
"couvert_detail": couvert_detail(plages, retour_min, matin_ex, aprem_ex) if couvert else "",
"matin_nominal_min": matin_nom,
"aprem_nominal_min": aprem_nom,
"astreinte": p["date"] in astreintes,