Corrige le décompte des demi-journées restantes et le rendu des champs horaires
Une demi-journée passée non renseignée était exclue à tort du calcul de rattrapage (l'appli permet de saisir ses heures après coup, un jour passé vide n'est pas une perte définitive) : une semaine entièrement vide affichait "reste à faire ... sur 2 demi-journées" au lieu de 10. Corrige aussi le chevauchement de l'icône d'horloge sur les champs <input type="time"> de la page Réglages (largeur trop juste pour le texte + l'icône native).
This commit is contained in:
@ -123,12 +123,12 @@ def compute_week(pointages: list[dict], conges: list[dict], heures_jour_min: int
|
|||||||
jour["cible_matin"] = None
|
jour["cible_matin"] = None
|
||||||
jour["cible_aprem"] = None
|
jour["cible_aprem"] = None
|
||||||
|
|
||||||
today = datetime.today().strftime("%Y-%m-%d")
|
|
||||||
restant = total_du - total_travaille
|
restant = total_du - total_travaille
|
||||||
slots = [] # (jour, "matin"|"aprem", nominal_min)
|
slots = [] # (jour, "matin"|"aprem", nominal_min)
|
||||||
for jour in jours:
|
for jour in jours:
|
||||||
if jour["date"] < today:
|
# Un jour passé non renseigné n'est pas exclu : cette appli permet de saisir
|
||||||
continue # jour passé : impossible d'y rattraper quoi que ce soit
|
# ses heures après coup, donc une demi-journée vide reste "à faire" quelle
|
||||||
|
# que soit sa date.
|
||||||
cg = jour["conge"]
|
cg = jour["conge"]
|
||||||
du = jour["du_min"]
|
du = jour["du_min"]
|
||||||
if du == 0:
|
if du == 0:
|
||||||
|
|||||||
@ -25,18 +25,18 @@
|
|||||||
<form method="post" action="/settings/plages" class="settings-form">
|
<form method="post" action="/settings/plages" class="settings-form">
|
||||||
<label class="login-label" for="matin_debut">Présence matin</label>
|
<label class="login-label" for="matin_debut">Présence matin</label>
|
||||||
<div class="settings-range">
|
<div class="settings-range">
|
||||||
<input id="matin_debut" class="login-input settings-range-input" type="time" name="matin_debut" value="{{ plages.matin_debut }}">
|
<input id="matin_debut" class="login-input time-range-input" type="time" name="matin_debut" value="{{ plages.matin_debut }}">
|
||||||
<span class="settings-range-sep">→</span>
|
<span class="settings-range-sep">→</span>
|
||||||
<input id="matin_fin" class="login-input settings-range-input" type="time" name="matin_fin" value="{{ plages.matin_fin }}">
|
<input id="matin_fin" class="login-input time-range-input" type="time" name="matin_fin" value="{{ plages.matin_fin }}">
|
||||||
</div>
|
</div>
|
||||||
<label class="login-label" for="aprem_debut">Présence après-midi</label>
|
<label class="login-label" for="aprem_debut">Présence après-midi</label>
|
||||||
<div class="settings-range">
|
<div class="settings-range">
|
||||||
<input id="aprem_debut" class="login-input settings-range-input" type="time" name="aprem_debut" value="{{ plages.aprem_debut }}">
|
<input id="aprem_debut" class="login-input time-range-input" type="time" name="aprem_debut" value="{{ plages.aprem_debut }}">
|
||||||
<span class="settings-range-sep">→</span>
|
<span class="settings-range-sep">→</span>
|
||||||
<input id="aprem_fin" class="login-input settings-range-input" type="time" name="aprem_fin" value="{{ plages.aprem_fin }}">
|
<input id="aprem_fin" class="login-input time-range-input" type="time" name="aprem_fin" value="{{ plages.aprem_fin }}">
|
||||||
</div>
|
</div>
|
||||||
<label class="login-label" for="pause_dejeuner_fin">Reprise par défaut de l'après-midi</label>
|
<label class="login-label" for="pause_dejeuner_fin">Reprise par défaut de l'après-midi</label>
|
||||||
<input id="pause_dejeuner_fin" class="login-input settings-range-input" type="time" name="pause_dejeuner_fin" value="{{ plages.pause_dejeuner_fin }}">
|
<input id="pause_dejeuner_fin" class="login-input time-range-input" type="time" name="pause_dejeuner_fin" value="{{ plages.pause_dejeuner_fin }}">
|
||||||
<button class="login-btn" type="submit">Enregistrer</button>
|
<button class="login-btn" type="submit">Enregistrer</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -167,5 +167,13 @@
|
|||||||
.settings-range { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
|
.settings-range { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
|
||||||
.settings-range-input { width: 70px; text-align: center; }
|
.settings-range-input { width: 70px; text-align: center; }
|
||||||
.settings-range-sep { font-size: 12px; color: var(--muted); font-family: var(--mono); }
|
.settings-range-sep { font-size: 12px; color: var(--muted); font-family: var(--mono); }
|
||||||
|
.time-range-input {
|
||||||
|
width: 128px;
|
||||||
|
text-align: left;
|
||||||
|
padding-right: .4rem;
|
||||||
|
}
|
||||||
|
.time-range-input::-webkit-calendar-picker-indicator {
|
||||||
|
margin-left: .5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -115,6 +115,23 @@ def test_compute_week_cible_repartie_sur_seule_demi_journee_restante(freeze_toda
|
|||||||
assert vendredi["cible_aprem"] == "18:18"
|
assert vendredi["cible_aprem"] == "18:18"
|
||||||
|
|
||||||
|
|
||||||
|
def test_compute_week_semaine_totalement_vide_compte_les_10_demi_journees(freeze_today):
|
||||||
|
"""Régression : une semaine entièrement vide (rien saisi, y compris les jours déjà
|
||||||
|
passés) doit compter 10 demi-journées restantes, pas seulement celles à partir
|
||||||
|
d'aujourd'hui. Cette appli permet de saisir ses heures après coup — un jour passé
|
||||||
|
non renseigné reste "à faire", il n'est pas un manque définitivement perdu."""
|
||||||
|
dates = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16", "2026-07-17"]
|
||||||
|
pointages = [_pointage(d) for d in dates]
|
||||||
|
|
||||||
|
result = compute_week(pointages, [], HEURES_JOUR_MIN)
|
||||||
|
assert result["slots_restants"] == 10
|
||||||
|
assert result["reste_min"] == HEURES_JOUR_MIN * 5
|
||||||
|
# Rien n'est encore en retard puisque rien n'a encore été renseigné : le rythme
|
||||||
|
# normal (à répartir librement sur les 10 demi-journées) suffit, pas de cible imposée.
|
||||||
|
assert result["extra_min"] == 0
|
||||||
|
assert all(j["cible_matin"] is None and j["cible_aprem"] is None for j in result["jours"])
|
||||||
|
|
||||||
|
|
||||||
def test_compute_week_jour_futur_non_entame_recoit_une_cible_repartie(freeze_today):
|
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)."""
|
"""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"]
|
dates_completes = ["2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"]
|
||||||
|
|||||||
Reference in New Issue
Block a user