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:
Jacquin Antoine
2026-09-08 22:05:10 +02:00
parent c31fcd28ec
commit 065d73e15c
9 changed files with 665 additions and 58 deletions

View File

@ -8,12 +8,15 @@ from odf.table import Table, TableColumn, TableRow, TableCell
from odf.text import P
from calcul import compute_week, minutes_to_hhmm
from models import load_week_pointages, load_conges, list_all_weeks, load_plages, heures_jour_min
from models import (
load_week_pointages, load_conges, load_astreintes, list_all_weeks,
load_plages, heures_jour_min,
)
JOURS_FR = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"]
HEADERS = [
"Date", "Jour", "Matin entrée", "Matin sortie", "Aprem entrée", "Aprem sortie",
"Travaillé", "Dû", "Δ jour", "Congé",
"Travaillé", "Dû", "Δ jour", "Congé", "Astreinte", "Retour",
]
@ -57,6 +60,12 @@ def _congé_label(cg: set) -> str:
return " + ".join(parts)
def _astreinte_label(jour: dict) -> str:
if jour.get("astreinte_fin"):
return f"fin {jour['astreinte_fin']}"
return "Astreinte" if jour.get("astreinte") else ""
def _jour_row(jour: dict, nom: str) -> TableRow:
row = TableRow()
for v in (
@ -66,6 +75,8 @@ def _jour_row(jour: dict, nom: str) -> TableRow:
jour["travaille"], jour["du"],
("+" if jour["delta_min"] > 0 else "") + jour["delta"],
_congé_label(jour["conge"]),
_astreinte_label(jour),
jour.get("retour"),
):
row.addElement(_cell(v))
return row
@ -76,7 +87,7 @@ def _totals_row(bold: Style, travaille_min: int, du_min: int) -> TableRow:
for v in ("Total", "", "", "", "", "",
minutes_to_hhmm(travaille_min), minutes_to_hhmm(du_min),
("+" if travaille_min - du_min > 0 else "") + minutes_to_hhmm(travaille_min - du_min),
""):
"", "", ""):
row.addElement(_cell(v, bold))
return row
@ -92,6 +103,7 @@ def export_semaine_ods(year: int, week: int, user_id: str) -> bytes:
h_jour = heures_jour_min()
plages = load_plages(user_id)
conges = load_conges(user_id)
astreintes = load_astreintes(user_id)
dates = _week_dates(year, week)
raw = load_week_pointages(year, week, user_id)
@ -101,7 +113,7 @@ def export_semaine_ods(year: int, week: int, user_id: str) -> bytes:
"aprem_entree": None, "aprem_sortie": None})
for d in dates
]
result = compute_week(pointages, conges, h_jour, plages)
result = compute_week(pointages, conges, h_jour, plages, astreintes)
table = Table(name=f"Semaine {week:02d}-{year}")
_size_columns(table, wide_col)
@ -123,6 +135,7 @@ def export_annee_ods(year: int, user_id: str) -> bytes:
h_jour = heures_jour_min()
plages = load_plages(user_id)
conges = load_conges(user_id)
astreintes = load_astreintes(user_id)
weeks = sorted(wk for yr, wk in list_all_weeks(user_id) if yr == year)
@ -136,7 +149,7 @@ def export_annee_ods(year: int, user_id: str) -> bytes:
"aprem_entree": None, "aprem_sortie": None})
for d in dates
]
result = compute_week(pointages, conges, h_jour, plages)
result = compute_week(pointages, conges, h_jour, plages, astreintes)
travaille_min = sum(j["travaille_min"] for j in result["jours"])
du_min = sum(j["du_min"] for j in result["jours"])
par_semaine.append((week, result, travaille_min, du_min))