Structurer les permanences par vacances
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run

This commit is contained in:
root 2026-08-20 20:12:19 +00:00
parent f9537e74f1
commit 650ac39e90
2 changed files with 7 additions and 2 deletions

View file

@ -38,7 +38,7 @@
<div class="card mb-3"><div class="card-header"><strong>Importer les calendriers</strong></div><div class="card-body"><div class="row g-2"><div class="col-md-6"><form method="post" action="{{ url_for('planning.import_school_vacations') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><input type="hidden" name="year" value="{{ year }}"><div class="input-group"><select class="form-select" name="zone"><option value="C" selected>Zone C</option><option value="B">Zone B</option><option value="A">Zone A</option></select><button class="btn btn-outline-primary">Importer vacances</button></div><small class="text-muted">Changer de zone remplace les périodes scolaires importées pour lautre zone.</small></form></div><div class="col-md-6"><form method="post" action="{{ url_for('planning.import_public_holidays') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><input type="hidden" name="year" value="{{ year }}"><button class="btn btn-outline-primary">Importer jours fériés {{ year }}</button></form></div></div></div></div>
<div class="card mb-3"><div class="card-header"><strong>Permanences dans les vacances</strong><small class="d-block text-muted">Sélectionnez plusieurs dates puis lhoraire type à appliquer.</small></div><div class="card-body"><form method="post" action="{{ url_for('planning.save_permanences') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><input type="hidden" name="year" value="{{ year }}"><div class="row g-2 align-items-end"><div class="col-md-8"><label class="form-label">Horaire type</label><select class="form-select" name="template_id" required><option value="">Choisir…</option>{% for template in templates %}<option value="{{ template.id }}">{{ template.name }} ({{ template.start_time.strftime('%H:%M') }}{{ template.end_time.strftime('%H:%M') }})</option>{% endfor %}</select></div><div class="col-md-4"><button class="btn btn-success w-100" {% if not templates %}disabled{% endif %}>Affecter les dates sélectionnées</button></div></div><div class="row g-2 mt-2">{% for option in permanence_options %}<div class="col-xl-3 col-lg-4 col-md-6"><label class="border rounded p-2 d-block"><input type="checkbox" name="work_dates" value="{{ option.date.isoformat() }}" {% if option.entry %}checked{% endif %}> <strong>{{ days.get(option.date.weekday()) }} {{ option.date.strftime('%d/%m/%Y') }}</strong><br><small>{{ option.closure.name }}{% if option.entry and option.entry.template %} · {{ option.entry.template.name }} · {{ option.entry.start_time.strftime('%H:%M') }}{{ option.entry.end_time.strftime('%H:%M') }}{% endif %}</small></label></div>{% else %}<div class="col-12 text-muted">Aucune période de vacances importée pour cette période. Importez dabord le calendrier scolaire.</div>{% endfor %}</div></form>
<div class="card mb-3"><div class="card-header"><strong>Permanences dans les vacances</strong><small class="d-block text-muted">Sélectionnez plusieurs dates puis lhoraire type à appliquer.</small></div><div class="card-body"><form method="post" action="{{ url_for('planning.save_permanences') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><input type="hidden" name="year" value="{{ year }}"><div class="row g-2 align-items-end"><div class="col-md-8"><label class="form-label">Horaire type</label><select class="form-select" name="template_id" required><option value="">Choisir…</option>{% for template in templates %}<option value="{{ template.id }}">{{ template.name }} ({{ template.start_time.strftime('%H:%M') }}{{ template.end_time.strftime('%H:%M') }})</option>{% endfor %}</select></div><div class="col-md-4"><button class="btn btn-success w-100" {% if not templates %}disabled{% endif %}>Affecter les dates sélectionnées</button></div></div>{% for group in permanence_groups %}<div class="border-top mt-3 pt-2"><h6 class="mb-2"><i class="bi bi-calendar2-week me-1"></i>{{ group.closure.name }} <small class="text-muted">({{ group.closure.start_date.strftime('%d/%m/%Y') }} au {{ group.closure.end_date.strftime('%d/%m/%Y') }})</small></h6><div class="row g-2">{% for option in group.options %}<div class="col-xl-3 col-lg-4 col-md-6"><label class="border rounded p-2 d-block"><input type="checkbox" name="work_dates" value="{{ option.date.isoformat() }}" {% if option.entry %}checked{% endif %}> <strong>{{ days.get(option.date.weekday()) }} {{ option.date.strftime('%d/%m/%Y') }}</strong><br><small>{% if option.entry and option.entry.template %}{{ option.entry.template.name }} · {{ option.entry.start_time.strftime('%H:%M') }}{{ option.entry.end_time.strftime('%H:%M') }}{% else %}Pas de permanence{% endif %}</small></label></div>{% endfor %}</div></div>{% else %}<div class="text-muted mt-3">Aucune période de vacances importée pour cette période. Importez dabord le calendrier scolaire.</div>{% endfor %}</form>
{% set selected_permanences = permanence_options|selectattr('entry')|list %}
<hr><h6><i class="bi bi-list-check me-1"></i>Récapitulatif des permanences</h6>
{% if selected_permanences %}<div class="table-responsive"><table class="table table-sm align-middle mb-0"><thead><tr><th>Date</th><th>Vacances</th><th>Horaire type</th><th>Horaires détaillés</th><th></th></tr></thead><tbody>{% for option in selected_permanences %}<tr><td><strong>{{ days.get(option.date.weekday()) }}</strong><br>{{ option.date.strftime('%d/%m/%Y') }}</td><td>{{ option.closure.name }}</td><td>{{ option.entry.template.name if option.entry.template else 'Horaire personnalisé' }}</td><td>{{ option.entry.start_time.strftime('%H:%M') }}{{ option.entry.end_time.strftime('%H:%M') }}{% if option.entry.lunch_start and option.entry.lunch_end %}<br><small class="text-muted">Pause {{ option.entry.lunch_start.strftime('%H:%M') }}{{ option.entry.lunch_end.strftime('%H:%M') }}</small>{% endif %}</td><td><form method="post" action="{{ url_for('planning.delete_permanence', id=option.entry.id) }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button class="btn btn-sm btn-outline-danger" title="Supprimer"><i class="bi bi-trash"></i></button></form></td></tr>{% endfor %}</tbody></table></div>{% else %}<p class="text-muted mb-0">Aucune permanence enregistrée pour cette période.</p>{% endif %}</div></div>

View file

@ -123,6 +123,7 @@ def time_tracking():
planned_minutes_by_month[month] = planned_minutes_by_month.get(month, 0) + day_minutes
current += timedelta(days=1)
permanence_options = []
permanence_groups = []
for closure in closures:
if closure.closure_type not in ("vacances", "vacances_scolaires"):
continue
@ -133,6 +134,10 @@ def time_tracking():
existing = work_day_map.get(current_day)
permanence_options.append({"date": current_day, "closure": closure, "entry": existing})
current_day += timedelta(days=1)
if permanence_options:
group_options = [item for item in permanence_options if item["closure"].id == closure.id]
if group_options:
permanence_groups.append({"closure": closure, "options": group_options})
actual = sum(item.actual_minutes or 0 for item in entries)
# Synthèse mensuelle : les permanences sont isolées du temps de travail
# planifié habituel, et les formations viennent des convocations validées.
@ -172,7 +177,7 @@ def time_tracking():
})
return render_template(
"planning/time_tracking.html", year=year, period_start=period_start, period_end=period_end, time_config=config, templates=templates,
assignments=assignments, closures=closures, permanence_options=permanence_options, entries=entries,
assignments=assignments, closures=closures, permanence_options=permanence_options, permanence_groups=permanence_groups, entries=entries,
days={0: "Lundi", 1: "Mardi", 2: "Mercredi", 3: "Jeudi", 4: "Vendredi", 5: "Samedi", 6: "Dimanche"},
months={1: "Janvier", 2: "Février", 3: "Mars", 4: "Avril", 5: "Mai", 6: "Juin", 7: "Juillet", 8: "Août", 9: "Septembre", 10: "Octobre", 11: "Novembre", 12: "Décembre"},
planned_minutes=planned, actual_minutes=actual,