Ajoute les astreintes : saisie de la fin de dernière activité, retour au bureau décalé de 11h de repos
Un nouveau bouton AS marque un jour d'astreinte. Ce jour et son lendemain,
une saisie « Fin activité » enregistre l'heure de fin de la dernière
intervention hors horaires (elle peut être le matin si la nuit a dépassé
minuit). L'app en déduit l'heure de retour au bureau (fin + 11h de repos
consécutif) et retire du décompte de la semaine toute la présence avant
cette heure : heures dues réduites, présence pointée avant le retour non
comptée, préconisations Fin cible démarrées à l'heure de retour sans
avertissement de retard, demi-journée entièrement en repos considérée
comme remplie. Les exports .ods gagnent les colonnes Astreinte et Retour,
et les stats n'ont plus les retards liés au repos.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
21
app/stats.py
21
app/stats.py
@ -3,9 +3,13 @@ import statistics as _st
|
||||
from datetime import datetime, timedelta, date as _date
|
||||
|
||||
from models import (
|
||||
load_week_pointages, load_conges, list_all_weeks, load_plages, heures_jour_min,
|
||||
load_week_pointages, load_conges, load_astreintes, list_all_weeks,
|
||||
load_plages, heures_jour_min,
|
||||
)
|
||||
from calcul import (
|
||||
hhmm_to_minutes, minutes_to_hhmm, heures_travaillees, heures_dues,
|
||||
retour_apres_repos,
|
||||
)
|
||||
from calcul import hhmm_to_minutes, minutes_to_hhmm, heures_travaillees, heures_dues
|
||||
|
||||
MOIS_FR = [
|
||||
"janvier", "février", "mars", "avril", "mai", "juin",
|
||||
@ -60,6 +64,7 @@ def _compliance_over(all_days: list[tuple], cg_set: set, plages: dict) -> dict:
|
||||
def compute_all_stats(user_id: str = "") -> dict:
|
||||
all_weeks = sorted(list_all_weeks(user_id))
|
||||
conges = load_conges(user_id)
|
||||
astreintes = set(load_astreintes(user_id))
|
||||
h_jour = heures_jour_min()
|
||||
plages = load_plages(user_id)
|
||||
cg_set = {(c["date"], c["type"]) for c in conges}
|
||||
@ -107,6 +112,9 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
|
||||
matin_filled = mc or (bool(p.get("matin_entree")) and bool(p.get("matin_sortie")))
|
||||
aprem_filled = ac or (bool(p.get("aprem_entree")) and bool(p.get("aprem_sortie")))
|
||||
# Retour au bureau après 11h de repos : calculé depuis la fin de la
|
||||
# dernière activité d'astreinte (saisie sur le jour ou la veille qualifiés)
|
||||
retour_min = retour_apres_repos(date, p_by_date, plages, astreintes)
|
||||
|
||||
all_days.append((date, p))
|
||||
d = datetime.strptime(date, "%Y-%m-%d").date()
|
||||
@ -119,8 +127,8 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
week_has_incomplete = True
|
||||
continue
|
||||
|
||||
trav = heures_travaillees(p)
|
||||
du = heures_dues(date, conges, h_jour)
|
||||
trav = heures_travaillees(p, retour_min)
|
||||
du = heures_dues(date, conges, h_jour, plages, retour_min)
|
||||
week_delta += trav - du
|
||||
|
||||
if not mc:
|
||||
@ -129,6 +137,9 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
v = hhmm_to_minutes(p[slot])
|
||||
slot_pairs[slot].append((v, date))
|
||||
op, thr = TARGETS[slot]
|
||||
# Une arrivée décalée par le repos de 11h n'est pas un retard
|
||||
if slot == "matin_entree" and retour_min:
|
||||
thr = max(thr, retour_min)
|
||||
comp_n[slot] += 1
|
||||
comp_ok[slot] += 1 if (v <= thr if op == "le" else v >= thr) else 0
|
||||
if not ac:
|
||||
@ -137,6 +148,8 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
v = hhmm_to_minutes(p[slot])
|
||||
slot_pairs[slot].append((v, date))
|
||||
op, thr = TARGETS[slot]
|
||||
if slot == "aprem_entree" and retour_min:
|
||||
thr = max(thr, retour_min)
|
||||
comp_n[slot] += 1
|
||||
comp_ok[slot] += 1 if (v <= thr if op == "le" else v >= thr) else 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user