feat: Rueckfrage, bevor jemand von der Zentrale verdraengt wird

Ein einzelner Klick konnte den aktuellen Besetzer der Zentrale verdraengen.
Sitzt dort jemand anderes, kommt jetzt erst eine Rueckfrage, die die
betroffene Person nennt und sagt, was mit ihr passiert - je nach Lage
Rueckstellung auf das eigene Tuerschild, Vermerk als Stamm-Besetzer, oder
der Hinweis dass sie ohnehin dorthin gehoert. Ist die Zentrale frei, wird
ohne Rueckfrage besetzt.

Eine Rueckgabe zu erzwingen waere die naheliegende Alternative, wurde aber
bewusst nicht gebaut: wer abends ohne Freigabe nach Hause geht, wuerde die
Zentrale damit sperren. Am naechsten Morgen koennte niemand den Empfang
besetzen, weil der Vorgaenger nicht da ist um freizugeben. Genau diese
Sperre soll das Feature vermeiden.

Nebenbei eine Ungenauigkeit behoben: die Seite hat behauptet, der Besetzer
werde "auf das eigene Tuerschild zurueckgesetzt" - auch dann, wenn er gar
keines hat. Die drei Faelle werden jetzt unterschieden.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 09:14:45 +00:00
co-authored by Claude Opus 5
parent 0867221a3c
commit 5d2bd8eff8
3 changed files with 82 additions and 12 deletions
+18 -2
View File
@@ -221,11 +221,27 @@ gefahrlos neu ladbar:
| Lage | Angebot | | Lage | Angebot |
|------|---------| |------|---------|
| Zentrale frei | **Zentrale besetzen** | | Zentrale frei | **Zentrale besetzen**, ohne Rueckfrage |
| jemand anderes sitzt darauf | **Zentrale uebernehmen**, mit Hinweis was mit dieser Person passiert | | jemand anderes sitzt darauf | **Zentrale uebernehmen** mit Rueckfrage, siehe unten |
| man sitzt selbst darauf | **Zurueck auf mein Tuerschild** | | man sitzt selbst darauf | **Zurueck auf mein Tuerschild** |
| es ist der eigene Stammplatz | kein Knopf, nur der Hinweis dass nichts zu tun ist | | es ist der eigene Stammplatz | kein Knopf, nur der Hinweis dass nichts zu tun ist |
### Rueckfrage beim Verdraengen
Sitzt bereits jemand anderes auf der Zentrale, kommt vor der Uebernahme eine
Rueckfrage. Sie nennt die betroffene Person und was mit ihr passiert:
- sie gehoert auf die Zentrale und kommt automatisch zurueck, sobald wieder
freigegeben wird
- sie hat ein eigenes Tuerschild und wird dorthin zurueckgesetzt
- sie hat kein eigenes Tuerschild und wird als Stamm-Besetzer vermerkt
Eine Rueckgabe wird dagegen **nicht** erzwungen, und das ist Absicht: wer abends
ohne Freigabe nach Hause geht, wuerde die Zentrale sonst sperren. Am naechsten
Morgen koennte niemand den Empfang besetzen, weil der Vorgaenger nicht da ist um
freizugeben, und es muesste jedes Mal ein Administrator eingreifen. Die Rueckfrage
verhindert das versehentliche Verdraengen, ohne diese Sperre zu erzeugen.
Jede Aktion leitet auf diese Seite zurueck (POST/Redirect/GET). Man landet also nie Jede Aktion leitet auf diese Seite zurueck (POST/Redirect/GET). Man landet also nie
auf einer Formular-Antwort, die beim Neuladen die Aktion wiederholen wuerde, und sieht auf einer Formular-Antwort, die beim Neuladen die Aktion wiederholen wuerde, und sieht
nach jedem Klick den neuen Zustand samt Bestaetigung. nach jedem Klick den neuen Zustand samt Bestaetigung.
+22 -7
View File
@@ -608,7 +608,7 @@ def _zentrale_state(conn, target, user):
st = { st = {
"occupant": None, "is_here": False, "home": None, "occupant": None, "is_here": False, "home": None,
"home_blocker": None, "is_owner": False, "owner_waiting": None, "home_blocker": None, "is_owner": False, "owner_waiting": None,
"occupant_is_owner": False, "occupant_is_owner": False, "occupant_has_home": False,
} }
if not target: if not target:
return st return st
@@ -626,10 +626,12 @@ def _zentrale_state(conn, target, user):
# Sitzt der aktuelle Besetzer auf seinem Stammplatz? Dann kommt er nach einer # Sitzt der aktuelle Besetzer auf seinem Stammplatz? Dann kommt er nach einer
# Uebernahme von allein zurueck - das gehoert auf den Knopf geschrieben. # Uebernahme von allein zurueck - das gehoert auf den Knopf geschrieben.
if st["occupant"]: if st["occupant"]:
st["occupant_is_owner"] = conn.execute(""" occ_home = conn.execute("""
SELECT 1 FROM home_assignments SELECT wled_device_id, segment FROM home_assignments WHERE user_azure_id = ?
WHERE user_azure_id = ? AND wled_device_id = ? AND segment = ? """, (st["occupant"]["azure_id"],)).fetchone()
""", (st["occupant"]["azure_id"], target["id"], target["segment"])).fetchone() is not None st["occupant_has_home"] = occ_home is not None
st["occupant_is_owner"] = bool(occ_home) and \
(occ_home["wled_device_id"], occ_home["segment"]) == (target["id"], target["segment"])
if not user: if not user:
return st return st
@@ -702,8 +704,13 @@ async def zentrale_page_for_extension(request: Request, extension: str):
@app.post("/api/zentrale/uebernehmen") @app.post("/api/zentrale/uebernehmen")
async def zentrale_takeover(extension: str = Form(...)): async def zentrale_takeover(extension: str = Form(...), confirm: str = Form("")):
"""Mitarbeiter setzt sich selbst auf das Busylight der Zentrale.""" """Mitarbeiter setzt sich selbst auf das Busylight der Zentrale.
Sitzt bereits jemand anderes darauf, braucht es eine ausdrueckliche
Bestaetigung. Eine Rueckgabe zu erzwingen waere die Alternative, wuerde die
Zentrale aber sperren, sobald jemand ohne Freigabe nach Hause geht.
"""
conn = database.get_db() conn = database.get_db()
try: try:
target = _selfservice_target(conn) target = _selfservice_target(conn)
@@ -714,6 +721,14 @@ async def zentrale_takeover(extension: str = Form(...)):
if not user: if not user:
return _zentrale_redirect(extension, "err_unbekannt") return _zentrale_redirect(extension, "err_unbekannt")
if confirm != "ja":
besetzt = conn.execute("""
SELECT 1 FROM assignments
WHERE wled_device_id = ? AND segment = ? AND user_azure_id != ?
""", (target["id"], target["segment"], user["azure_id"])).fetchone()
if besetzt:
return _zentrale_redirect(extension, "bestaetigen")
already = conn.execute(""" already = conn.execute("""
SELECT 1 FROM assignments SELECT 1 FROM assignments
WHERE user_azure_id = ? AND wled_device_id = ? AND segment = ? WHERE user_azure_id = ? AND wled_device_id = ? AND segment = ?
+42 -3
View File
@@ -147,6 +147,39 @@
</div> </div>
{% endif %} {% endif %}
{% elif done == 'bestaetigen' and occupant %}
{# Zweiter Schritt: verdraengen ist nie ein Versehen #}
<div class="alert alert-warning">
<strong><i class="bi bi-exclamation-triangle"></i> Wirklich uebernehmen?</strong>
<div class="mt-2"><small>
<strong>{{ occupant.display_name }}</strong> ist derzeit auf der Zentrale
eingetragen. Bitte nur uebernehmen, wenn diese Person den Empfang
tatsaechlich verlassen hat.
<br>
{% if occupant_is_owner %}
{{ occupant.display_name }} gehoert auf die Zentrale und kommt automatisch
zurueck, sobald du sie wieder freigibst.
{% elif occupant_has_home %}
{{ occupant.display_name }} wird dabei auf das eigene Tuerschild
zurueckgesetzt.
{% else %}
{{ occupant.display_name }} hat kein eigenes Tuerschild und gehoert damit
auf die Zentrale. Die Person wird als Stamm-Besetzer vermerkt und kommt
automatisch zurueck, sobald du wieder freigibst.
{% endif %}
</small></div>
</div>
<form method="post" action="/api/zentrale/uebernehmen" class="mb-2">
<input type="hidden" name="extension" value="{{ user.phone_extension }}">
<input type="hidden" name="confirm" value="ja">
<button type="submit" class="btn btn-warning w-100">
<i class="bi bi-check2-circle"></i> Ja, Zentrale uebernehmen
</button>
</form>
<a href="/zentrale/{{ user.phone_extension }}" class="btn btn-outline-secondary w-100 btn-sm">
Abbrechen
</a>
{% else %} {% else %}
<form method="post" action="/api/zentrale/uebernehmen" class="mb-2"> <form method="post" action="/api/zentrale/uebernehmen" class="mb-2">
<input type="hidden" name="extension" value="{{ user.phone_extension }}"> <input type="hidden" name="extension" value="{{ user.phone_extension }}">
@@ -156,10 +189,16 @@
</button> </button>
<small class="text-muted"> <small class="text-muted">
{% if occupant and occupant_is_owner %} {% if occupant and occupant_is_owner %}
{{ occupant.display_name }} gehoert auf die Zentrale und kommt automatisch {{ occupant.display_name }} sitzt darauf und gehoert dorthin. Nach einer
zurueck, sobald du sie wieder freigibst. Uebernahme kommt sie oder er automatisch zurueck, sobald du freigibst.
Du wirst noch um Bestaetigung gebeten.
{% elif occupant and occupant_has_home %}
{{ occupant.display_name }} sitzt darauf und wird dabei auf das eigene
Tuerschild zurueckgesetzt. Du wirst noch um Bestaetigung gebeten.
{% elif occupant %} {% elif occupant %}
{{ occupant.display_name }} wird dabei auf das eigene Tuerschild zurueckgesetzt. {{ occupant.display_name }} sitzt darauf und gehoert auf die Zentrale,
kommt also automatisch zurueck, sobald du freigibst.
Du wirst noch um Bestaetigung gebeten.
{% else %} {% else %}
Die Zentrale ist frei. Die Zentrale ist frei.
{% endif %} {% endif %}