um Poolfahrzeuge erweitert

This commit is contained in:
Erik Thiele
2026-05-19 20:22:18 +02:00
parent 60a5dc39b2
commit 0199a21a66
12 changed files with 487 additions and 43 deletions

View File

@@ -7,7 +7,7 @@
## Run
- Create the environment exactly as documented in `README.md`: `python3 -m venv .venv`, `source .venv/bin/activate`, `pip install -r requirements.txt`.
- Start the app with `python3 app.py`. `app.py` calls `app.run(debug=True)` directly under `if __name__ == "__main__"`.
- Default local URL is `http://127.0.0.1:5000`.
- Default local URL is `http://127.0.0.1:5006`.
## Data And Side Effects
- The app writes to repo-local files next to `app.py`: SQLite database `inventory.db` and log file `inventory.log`.

View File

@@ -1,4 +1,4 @@
# Verwaltung fuer Tuerchips und Parkkarten
# Verwaltung fuer Tuerchips, Parkkarten und Poolfahrzeuge
## Voraussetzungen
- Python 3.10 oder neuer
@@ -15,7 +15,14 @@ pip install -r requirements.txt
python3 app.py
```
Die Anwendung ist danach unter `http://127.0.0.1:5000` erreichbar.
Die Anwendung ist danach unter `http://127.0.0.1:5006` erreichbar.
## Start mit Docker
```bash
docker compose up --build
```
Die Anwendung ist danach ebenfalls unter `http://127.0.0.1:5006` erreichbar.
## Funktionen
- Anmeldung fuer Bearbeiter und Admins
@@ -24,8 +31,8 @@ Die Anwendung ist danach unter `http://127.0.0.1:5000` erreichbar.
- Admin kann Passwort-Reset fuer Bearbeiter ausloesen
- Admin kann vorhandene Bestandsdaten per CSV importieren
- User anlegen
- Ausgabe von Tuerchips und Parkkarten
- Rueckgabe von Tuerchips und Parkkarten
- Ausgabe von Tuerchips, Parkkarten und Poolfahrzeugen
- Rueckgabe von Tuerchips, Parkkarten und Poolfahrzeugen
- Uebersicht mit Suche und letzten Bewegungen
- Einfache Logdatei mit Datum, Medium und bearbeitendem Mitarbeiter
- Anzeige der letzten Logeintraege im Webinterface
@@ -60,7 +67,8 @@ Erika Muster;Parkkarte;PARK-2001;Import
- Eine optionale Kopfzeile `User;Typ;Kennung;Aktion` wird automatisch erkannt und uebersprungen.
- Unterstuetzte Typen sind `Tuerchip` und `Parkkarte`.
- Unterstuetzte Typen sind `Tuerchip`, `Parkkarte` und `Poolfahrzeug`.
- Bei der Ausgabe von `Poolfahrzeug` wird das Kennzeichen als Kennung erfasst.
- Die Aktion `Import` uebernimmt vorhandene aktive Bestandsdaten in die Datenbank.
- Falls ein User noch nicht existiert, wird er beim Import automatisch angelegt.
- Bereits vergebene Kennungen oder widerspruechliche aktive Zuordnungen werden gesammelt als Fehler angezeigt; der Import wird erst ausgefuehrt, wenn keine Fehler mehr vorhanden sind.

138
app.py
View File

@@ -26,6 +26,7 @@ logging.basicConfig(
ASSET_LABELS = {
"chip": "Tuerchip",
"parking_card": "Parkkarte",
"pool_vehicle": "Poolfahrzeug",
}
ACTION_LABELS = {
@@ -38,6 +39,12 @@ PRINT_DESCRIPTIONS = {
"return": "Rueckgabebestaetigung fuer die Ruecknahme eines Mediums",
}
INPUT_PLACEHOLDERS = {
"chip": "z. B. CHIP-1001",
"parking_card": "z. B. PARK-2001",
"pool_vehicle": "z. B. GZ-CC-123",
}
IMPORT_ACTIONS = {"import", "ausgabe", "assign"}
IMPORT_HEADER = ["user", "typ", "kennung", "aktion"]
@@ -79,13 +86,15 @@ def init_db() -> None:
chip_assigned_at TEXT,
parking_card_code TEXT,
parking_card_assigned_at TEXT,
pool_vehicle_code TEXT,
pool_vehicle_assigned_at TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
asset_type TEXT NOT NULL CHECK (asset_type IN ('chip', 'parking_card')),
asset_type TEXT NOT NULL CHECK (asset_type IN ('chip', 'parking_card', 'pool_vehicle')),
asset_code TEXT NOT NULL,
handled_by TEXT,
action TEXT NOT NULL CHECK (action IN ('assign', 'return')),
@@ -112,6 +121,35 @@ def init_db() -> None:
if "handled_by" not in columns:
db.execute("ALTER TABLE transactions ADD COLUMN handled_by TEXT")
transactions_sql = db.execute(
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'transactions'"
).fetchone()
if transactions_sql and "pool_vehicle" not in (transactions_sql["sql"] or ""):
db.execute("DROP TABLE transactions")
db.execute(
"""
CREATE TABLE transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
asset_type TEXT NOT NULL CHECK (asset_type IN ('chip', 'parking_card', 'pool_vehicle')),
asset_code TEXT NOT NULL,
handled_by TEXT,
action TEXT NOT NULL CHECK (action IN ('assign', 'return')),
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
)
"""
)
user_columns = {
row["name"]
for row in db.execute("PRAGMA table_info(users)").fetchall()
}
if "pool_vehicle_code" not in user_columns:
db.execute("ALTER TABLE users ADD COLUMN pool_vehicle_code TEXT")
if "pool_vehicle_assigned_at" not in user_columns:
db.execute("ALTER TABLE users ADD COLUMN pool_vehicle_assigned_at TEXT")
admin_exists = db.execute(
"SELECT id FROM staff_users WHERE role = 'admin' LIMIT 1"
).fetchone()
@@ -194,7 +232,11 @@ def read_recent_logs(limit: int = 20) -> list[str]:
def asset_code_in_use(db: sqlite3.Connection, asset_type: str, asset_code: str) -> bool:
column_name = "chip_code" if asset_type == "chip" else "parking_card_code"
column_name = {
"chip": "chip_code",
"parking_card": "parking_card_code",
"pool_vehicle": "pool_vehicle_code",
}[asset_type]
existing = db.execute(
f"SELECT id FROM users WHERE {column_name} = ?",
(asset_code,),
@@ -210,6 +252,9 @@ def normalize_asset_type(value: str) -> str | None:
"parkkarte": "parking_card",
"parking_card": "parking_card",
"parking card": "parking_card",
"poolfahrzeug": "pool_vehicle",
"pool vehicle": "pool_vehicle",
"pool_vehicle": "pool_vehicle",
}
return aliases.get(normalized)
@@ -461,17 +506,20 @@ def import_data() -> str:
current_chip_code = user["chip_code"] if user else None
current_card_code = user["parking_card_code"] if user else None
current_vehicle_code = user["pool_vehicle_code"] if user else None
user_id = user["id"] if user else None
if pending_user is not None:
current_chip_code = pending_user["chip_code"]
current_card_code = pending_user["parking_card_code"]
current_vehicle_code = pending_user["pool_vehicle_code"]
if user is None and pending_user is None:
pending_user = {
"full_name": full_name,
"chip_code": None,
"parking_card_code": None,
"pool_vehicle_code": None,
}
operations.append(("user", pending_user))
@@ -480,6 +528,8 @@ def import_data() -> str:
asset_type == "chip" and current_chip_code == asset_code
) or (
asset_type == "parking_card" and current_card_code == asset_code
) or (
asset_type == "pool_vehicle" and current_vehicle_code == asset_code
)
if not assigned_to_same_user:
errors.append(f"Zeile {index}: Kennung '{asset_code}' ist bereits vergeben.")
@@ -504,12 +554,18 @@ def import_data() -> str:
continue
if pending_user is not None:
pending_user["chip_code"] = asset_code
else:
elif asset_type == "parking_card":
if current_card_code and current_card_code != asset_code:
errors.append(f"Zeile {index}: User '{full_name}' hat bereits eine andere Parkkarte.")
continue
if pending_user is not None:
pending_user["parking_card_code"] = asset_code
else:
if current_vehicle_code and current_vehicle_code != asset_code:
errors.append(f"Zeile {index}: User '{full_name}' hat bereits ein anderes Poolfahrzeug.")
continue
if pending_user is not None:
pending_user["pool_vehicle_code"] = asset_code
operations.append(
(
@@ -549,11 +605,16 @@ def import_data() -> str:
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(payload["asset_code"], resolved_user_id),
)
else:
elif payload["asset_type"] == "parking_card":
db.execute(
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(payload["asset_code"], resolved_user_id),
)
else:
db.execute(
"UPDATE users SET pool_vehicle_code = ?, pool_vehicle_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(payload["asset_code"], resolved_user_id),
)
db.execute(
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
@@ -577,7 +638,8 @@ def index() -> str:
params: tuple[str, ...] = ()
user_query = """
SELECT id, full_name, email, department, chip_code, chip_assigned_at,
parking_card_code, parking_card_assigned_at
parking_card_code, parking_card_assigned_at,
pool_vehicle_code, pool_vehicle_assigned_at
FROM users
"""
@@ -586,10 +648,11 @@ def index() -> str:
user_query += """
WHERE full_name LIKE ?
OR email LIKE ?
OR chip_code LIKE ?
OR parking_card_code LIKE ?
OR chip_code LIKE ?
OR parking_card_code LIKE ?
OR pool_vehicle_code LIKE ?
"""
params = (like_value, like_value, like_value, like_value)
params = (like_value, like_value, like_value, like_value, like_value)
user_query += " ORDER BY full_name COLLATE NOCASE"
users = db.execute(user_query, params).fetchall()
@@ -599,7 +662,8 @@ def index() -> str:
SELECT
COUNT(*) AS users,
SUM(CASE WHEN chip_code IS NOT NULL AND chip_code != '' THEN 1 ELSE 0 END) AS active_chips,
SUM(CASE WHEN parking_card_code IS NOT NULL AND parking_card_code != '' THEN 1 ELSE 0 END) AS active_cards
SUM(CASE WHEN parking_card_code IS NOT NULL AND parking_card_code != '' THEN 1 ELSE 0 END) AS active_cards,
SUM(CASE WHEN pool_vehicle_code IS NOT NULL AND pool_vehicle_code != '' THEN 1 ELSE 0 END) AS active_pool_vehicles
FROM users
"""
).fetchone()
@@ -624,6 +688,7 @@ def index() -> str:
"users": stats["users"],
"active_chips": stats["active_chips"],
"active_cards": stats["active_cards"],
"active_pool_vehicles": stats["active_pool_vehicles"],
},
search_query=search_query,
asset_labels=ASSET_LABELS,
@@ -660,14 +725,19 @@ def create_user() -> str:
@login_required
def assign_asset() -> str:
db = get_db()
asset_type = request.form.get("asset_type", "chip").strip() if request.method == "POST" else request.args.get("asset_type", "chip").strip()
if asset_type not in {"chip", "parking_card", "pool_vehicle"}:
asset_type = "chip"
if request.method == "POST":
user_id = request.form.get("user_id", "").strip()
asset_type = request.form.get("asset_type", "").strip()
asset_code = request.form.get("asset_code", "").strip()
handled_by = g.current_staff["full_name"]
if not user_id or asset_type not in {"chip", "parking_card"} or not asset_code:
if not user_id or asset_type not in {"chip", "parking_card", "pool_vehicle"}:
flash("Bitte alle Felder fuer die Ausgabe ausfuellen.")
return redirect(url_for("assign_asset"))
if not asset_code:
flash("Bitte alle Felder fuer die Ausgabe ausfuellen.")
return redirect(url_for("assign_asset"))
@@ -677,7 +747,7 @@ def assign_asset() -> str:
return redirect(url_for("assign_asset"))
if asset_code_in_use(db, asset_type, asset_code):
flash("Diese Kennung ist bereits vergeben.")
flash("Dieses Kennzeichen ist bereits vergeben." if asset_type == "pool_vehicle" else "Diese Kennung ist bereits vergeben.")
return redirect(url_for("assign_asset"))
if asset_type == "chip":
@@ -688,7 +758,7 @@ def assign_asset() -> str:
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(asset_code, user_id),
)
else:
elif asset_type == "parking_card":
if user["parking_card_code"]:
flash("Dieser User hat bereits eine aktive Parkkarte.")
return redirect(url_for("assign_asset"))
@@ -696,6 +766,14 @@ def assign_asset() -> str:
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(asset_code, user_id),
)
else:
if user["pool_vehicle_code"]:
flash("Dieser User hat bereits ein aktives Poolfahrzeug.")
return redirect(url_for("assign_asset"))
db.execute(
"UPDATE users SET pool_vehicle_code = ?, pool_vehicle_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
(asset_code, user_id),
)
db.execute(
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
@@ -708,20 +786,27 @@ def assign_asset() -> str:
return redirect(url_for("print_transaction", transaction_id=transaction_id))
users = db.execute("SELECT id, full_name FROM users ORDER BY full_name COLLATE NOCASE").fetchall()
return render_template("assign_asset.html", users=users)
return render_template(
"assign_asset.html",
users=users,
selected_asset_type=asset_type,
input_placeholder=INPUT_PLACEHOLDERS[asset_type],
)
@app.route("/return", methods=["GET", "POST"])
@login_required
def return_asset() -> str:
db = get_db()
asset_type = request.form.get("asset_type", "chip").strip() if request.method == "POST" else request.args.get("asset_type", "chip").strip()
if asset_type not in {"chip", "parking_card", "pool_vehicle"}:
asset_type = "chip"
if request.method == "POST":
user_id = request.form.get("user_id", "").strip()
asset_type = request.form.get("asset_type", "").strip()
handled_by = g.current_staff["full_name"]
if not user_id or asset_type not in {"chip", "parking_card"}:
if not user_id or asset_type not in {"chip", "parking_card", "pool_vehicle"}:
flash("Bitte User und Typ fuer die Rueckgabe waehlen.")
return redirect(url_for("return_asset"))
@@ -730,7 +815,13 @@ def return_asset() -> str:
flash("Ausgewaehlter User wurde nicht gefunden.")
return redirect(url_for("return_asset"))
asset_code = user["chip_code"] if asset_type == "chip" else user["parking_card_code"]
asset_code = (
user["chip_code"]
if asset_type == "chip"
else user["parking_card_code"]
if asset_type == "parking_card"
else user["pool_vehicle_code"]
)
if not asset_code:
flash("Fuer diesen User ist kein entsprechendes Medium aktiv hinterlegt.")
return redirect(url_for("return_asset"))
@@ -740,11 +831,16 @@ def return_asset() -> str:
"UPDATE users SET chip_code = NULL, chip_assigned_at = NULL WHERE id = ?",
(user_id,),
)
else:
elif asset_type == "parking_card":
db.execute(
"UPDATE users SET parking_card_code = NULL, parking_card_assigned_at = NULL WHERE id = ?",
(user_id,),
)
else:
db.execute(
"UPDATE users SET pool_vehicle_code = NULL, pool_vehicle_assigned_at = NULL WHERE id = ?",
(user_id,),
)
db.execute(
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'return')",
@@ -757,7 +853,11 @@ def return_asset() -> str:
return redirect(url_for("print_transaction", transaction_id=transaction_id))
users = db.execute("SELECT id, full_name FROM users ORDER BY full_name COLLATE NOCASE").fetchall()
return render_template("return_asset.html", users=users)
return render_template(
"return_asset.html",
users=users,
selected_asset_type=asset_type,
)
@app.route("/transactions/<int:transaction_id>/print")

View File

@@ -7,7 +7,7 @@ services:
dockerfile: Dockerfile
platforms:
- linux/amd64
image: gitea.teamthiele.de/ethiele/keyVerwaltung:latest
image: gitea.teamthiele.de/ethiele/keyverwaltung:latest
ports:
- "5006:5006"
restart: unless-stopped

Binary file not shown.

View File

@@ -719,3 +719,280 @@
2026-05-18 21:19:15,878 INFO * Restarting with stat
2026-05-18 21:19:16,201 WARNING * Debugger is active!
2026-05-18 21:19:16,216 INFO * Debugger PIN: 428-899-358
2026-05-18 21:20:04,905 INFO 127.0.0.1 - - [18/May/2026 21:20:04] "GET / HTTP/1.1" 200 -
2026-05-18 21:20:04,931 INFO 127.0.0.1 - - [18/May/2026 21:20:04] "GET /static/cancom.svg HTTP/1.1" 200 -
2026-05-18 21:20:04,958 INFO 127.0.0.1 - - [18/May/2026 21:20:04] "GET /static/favicon.ico HTTP/1.1" 200 -
2026-05-19 08:59:28,642 INFO WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5006
* Running on http://10.2.200.65:5006
2026-05-19 08:59:28,642 INFO Press CTRL+C to quit
2026-05-19 08:59:28,646 INFO * Restarting with stat
2026-05-19 08:59:28,946 WARNING * Debugger is active!
2026-05-19 08:59:28,984 INFO * Debugger PIN: 428-899-358
2026-05-19 08:59:36,119 INFO 127.0.0.1 - - [19/May/2026 08:59:36] "GET / HTTP/1.1" 200 -
2026-05-19 08:59:36,367 INFO 127.0.0.1 - - [19/May/2026 08:59:36] "GET /static/cancom.svg HTTP/1.1" 200 -
2026-05-19 09:00:09,239 INFO 127.0.0.1 - - [19/May/2026 09:00:09] "GET /users/new HTTP/1.1" 200 -
2026-05-19 09:00:09,285 INFO 127.0.0.1 - - [19/May/2026 09:00:09] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:10,898 INFO 127.0.0.1 - - [19/May/2026 09:00:10] "GET /assign HTTP/1.1" 200 -
2026-05-19 09:00:10,933 INFO 127.0.0.1 - - [19/May/2026 09:00:10] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:18,472 INFO 127.0.0.1 - - [19/May/2026 09:00:18] "GET /assign HTTP/1.1" 200 -
2026-05-19 09:00:18,496 INFO 127.0.0.1 - - [19/May/2026 09:00:18] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:21,083 INFO 127.0.0.1 - - [19/May/2026 09:00:21] "GET /return HTTP/1.1" 200 -
2026-05-19 09:00:21,117 INFO 127.0.0.1 - - [19/May/2026 09:00:21] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:24,238 INFO 127.0.0.1 - - [19/May/2026 09:00:24] "GET /logout HTTP/1.1" 302 -
2026-05-19 09:00:24,255 INFO 127.0.0.1 - - [19/May/2026 09:00:24] "GET /login HTTP/1.1" 200 -
2026-05-19 09:00:24,285 INFO 127.0.0.1 - - [19/May/2026 09:00:24] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:24,299 INFO 127.0.0.1 - - [19/May/2026 09:00:24] "GET /static/favicon.ico HTTP/1.1" 200 -
2026-05-19 09:00:30,232 INFO 127.0.0.1 - - [19/May/2026 09:00:30] "POST /login HTTP/1.1" 302 -
2026-05-19 09:00:30,269 INFO 127.0.0.1 - - [19/May/2026 09:00:30] "GET / HTTP/1.1" 200 -
2026-05-19 09:00:30,342 INFO 127.0.0.1 - - [19/May/2026 09:00:30] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:30,346 INFO 127.0.0.1 - - [19/May/2026 09:00:30] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 09:00:44,664 INFO 127.0.0.1 - - [19/May/2026 09:00:44] "GET /admin/import HTTP/1.1" 200 -
2026-05-19 09:00:44,694 INFO 127.0.0.1 - - [19/May/2026 09:00:44] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:00:54,099 INFO 127.0.0.1 - - [19/May/2026 09:00:54] "GET /admin/staff HTTP/1.1" 200 -
2026-05-19 09:00:54,133 INFO 127.0.0.1 - - [19/May/2026 09:00:54] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:06,202 INFO 127.0.0.1 - - [19/May/2026 09:01:06] "GET / HTTP/1.1" 200 -
2026-05-19 09:01:06,228 INFO 127.0.0.1 - - [19/May/2026 09:01:06] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:21,990 INFO 127.0.0.1 - - [19/May/2026 09:01:21] "GET /assign HTTP/1.1" 200 -
2026-05-19 09:01:22,029 INFO 127.0.0.1 - - [19/May/2026 09:01:22] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:30,896 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "POST /assign HTTP/1.1" 302 -
2026-05-19 09:01:30,907 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "GET /assign HTTP/1.1" 200 -
2026-05-19 09:01:30,931 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:30,945 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 09:01:37,960 INFO 127.0.0.1 - - [19/May/2026 09:01:37] "GET / HTTP/1.1" 200 -
2026-05-19 09:01:37,993 INFO 127.0.0.1 - - [19/May/2026 09:01:37] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:38,265 INFO 127.0.0.1 - - [19/May/2026 09:01:38] "GET / HTTP/1.1" 200 -
2026-05-19 09:01:38,293 INFO 127.0.0.1 - - [19/May/2026 09:01:38] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 09:01:41,573 INFO 127.0.0.1 - - [19/May/2026 09:01:41] "GET /transactions/4/print HTTP/1.1" 200 -
2026-05-19 09:01:41,596 INFO 127.0.0.1 - - [19/May/2026 09:01:41] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:36:27,486 INFO 127.0.0.1 - - [19/May/2026 19:36:27] "GET / HTTP/1.1" 200 -
2026-05-19 19:36:27,549 INFO 127.0.0.1 - - [19/May/2026 19:36:27] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:36:36,490 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "GET /logout HTTP/1.1" 302 -
2026-05-19 19:36:36,498 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "GET /login HTTP/1.1" 200 -
2026-05-19 19:36:36,520 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:36:36,529 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:36:46,776 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "POST /login HTTP/1.1" 302 -
2026-05-19 19:36:46,801 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "GET / HTTP/1.1" 200 -
2026-05-19 19:36:46,828 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:36:46,833 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:37:12,390 INFO 127.0.0.1 - - [19/May/2026 19:37:12] "GET / HTTP/1.1" 200 -
2026-05-19 19:37:12,412 INFO 127.0.0.1 - - [19/May/2026 19:37:12] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:26,587 INFO 127.0.0.1 - - [19/May/2026 19:37:26] "GET /users/new HTTP/1.1" 200 -
2026-05-19 19:37:26,612 INFO 127.0.0.1 - - [19/May/2026 19:37:26] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:34,017 INFO 127.0.0.1 - - [19/May/2026 19:37:34] "GET /assign HTTP/1.1" 200 -
2026-05-19 19:37:34,036 INFO 127.0.0.1 - - [19/May/2026 19:37:34] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:47,727 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "POST /assign HTTP/1.1" 302 -
2026-05-19 19:37:47,736 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "GET /assign HTTP/1.1" 200 -
2026-05-19 19:37:47,760 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:47,765 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:37:50,839 INFO 127.0.0.1 - - [19/May/2026 19:37:50] "GET / HTTP/1.1" 200 -
2026-05-19 19:37:50,864 INFO 127.0.0.1 - - [19/May/2026 19:37:50] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:56,289 INFO 127.0.0.1 - - [19/May/2026 19:37:56] "GET / HTTP/1.1" 200 -
2026-05-19 19:37:56,308 INFO 127.0.0.1 - - [19/May/2026 19:37:56] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:37:57,640 INFO 127.0.0.1 - - [19/May/2026 19:37:57] "GET / HTTP/1.1" 200 -
2026-05-19 19:37:57,655 INFO 127.0.0.1 - - [19/May/2026 19:37:57] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:10,246 INFO 127.0.0.1 - - [19/May/2026 19:38:10] "GET /return HTTP/1.1" 200 -
2026-05-19 19:38:10,269 INFO 127.0.0.1 - - [19/May/2026 19:38:10] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:16,491 INFO rueckgabe | user=Erik Thiele | typ=Tuerchip | kennung=120 | bearbeiter=Erik Thiele
2026-05-19 19:38:16,492 INFO 127.0.0.1 - - [19/May/2026 19:38:16] "POST /return HTTP/1.1" 302 -
2026-05-19 19:38:16,501 INFO 127.0.0.1 - - [19/May/2026 19:38:16] "GET /transactions/5/print HTTP/1.1" 200 -
2026-05-19 19:38:16,521 INFO 127.0.0.1 - - [19/May/2026 19:38:16] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:16,528 INFO 127.0.0.1 - - [19/May/2026 19:38:16] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:38:18,296 INFO 127.0.0.1 - - [19/May/2026 19:38:18] "GET / HTTP/1.1" 200 -
2026-05-19 19:38:18,316 INFO 127.0.0.1 - - [19/May/2026 19:38:18] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:18,326 INFO 127.0.0.1 - - [19/May/2026 19:38:18] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:38:30,366 INFO 127.0.0.1 - - [19/May/2026 19:38:30] "GET /assign HTTP/1.1" 200 -
2026-05-19 19:38:30,387 INFO 127.0.0.1 - - [19/May/2026 19:38:30] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:40,646 INFO ausgabe | user=Erik Thiele | typ=Tuerchip | kennung=120 | bearbeiter=Erik Thiele
2026-05-19 19:38:40,647 INFO 127.0.0.1 - - [19/May/2026 19:38:40] "POST /assign HTTP/1.1" 302 -
2026-05-19 19:38:40,657 INFO 127.0.0.1 - - [19/May/2026 19:38:40] "GET /transactions/6/print HTTP/1.1" 200 -
2026-05-19 19:38:40,677 INFO 127.0.0.1 - - [19/May/2026 19:38:40] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:38:40,683 INFO 127.0.0.1 - - [19/May/2026 19:38:40] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:39:09,992 INFO 127.0.0.1 - - [19/May/2026 19:39:09] "GET / HTTP/1.1" 200 -
2026-05-19 19:39:10,016 INFO 127.0.0.1 - - [19/May/2026 19:39:10] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:39:10,025 INFO 127.0.0.1 - - [19/May/2026 19:39:10] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:39:11,969 INFO 127.0.0.1 - - [19/May/2026 19:39:11] "GET /return HTTP/1.1" 200 -
2026-05-19 19:39:11,995 INFO 127.0.0.1 - - [19/May/2026 19:39:11] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:39:21,037 INFO rueckgabe | user=Erik Thiele | typ=Tuerchip | kennung=120 | bearbeiter=Erik Thiele
2026-05-19 19:39:21,038 INFO 127.0.0.1 - - [19/May/2026 19:39:21] "POST /return HTTP/1.1" 302 -
2026-05-19 19:39:21,047 INFO 127.0.0.1 - - [19/May/2026 19:39:21] "GET /transactions/7/print HTTP/1.1" 200 -
2026-05-19 19:39:21,066 INFO 127.0.0.1 - - [19/May/2026 19:39:21] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:39:21,070 INFO 127.0.0.1 - - [19/May/2026 19:39:21] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:39:37,875 INFO 127.0.0.1 - - [19/May/2026 19:39:37] "GET / HTTP/1.1" 200 -
2026-05-19 19:39:37,899 INFO 127.0.0.1 - - [19/May/2026 19:39:37] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:39:37,911 INFO 127.0.0.1 - - [19/May/2026 19:39:37] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:41:56,887 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "GET /logout HTTP/1.1" 302 -
2026-05-19 19:41:56,898 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "GET /login HTTP/1.1" 200 -
2026-05-19 19:41:56,919 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:41:56,929 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:42:02,095 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "POST /login HTTP/1.1" 302 -
2026-05-19 19:42:02,118 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "GET / HTTP/1.1" 200 -
2026-05-19 19:42:02,177 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:42:02,183 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:42:23,475 INFO 127.0.0.1 - - [19/May/2026 19:42:23] "GET /admin/staff HTTP/1.1" 200 -
2026-05-19 19:42:23,495 INFO 127.0.0.1 - - [19/May/2026 19:42:23] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:43:41,915 INFO 127.0.0.1 - - [19/May/2026 19:43:41] "GET /users/new HTTP/1.1" 200 -
2026-05-19 19:43:41,950 INFO 127.0.0.1 - - [19/May/2026 19:43:41] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:43:45,744 INFO 127.0.0.1 - - [19/May/2026 19:43:45] "GET /admin/staff HTTP/1.1" 200 -
2026-05-19 19:43:45,769 INFO 127.0.0.1 - - [19/May/2026 19:43:45] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:44:08,662 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "POST /admin/staff HTTP/1.1" 302 -
2026-05-19 19:44:08,671 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "GET /admin/staff HTTP/1.1" 200 -
2026-05-19 19:44:08,692 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:44:08,696 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:44:20,804 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "GET /logout HTTP/1.1" 302 -
2026-05-19 19:44:20,816 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "GET /login HTTP/1.1" 200 -
2026-05-19 19:44:20,837 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:44:20,862 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:44:28,199 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "POST /login HTTP/1.1" 302 -
2026-05-19 19:44:28,218 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "GET /set-password HTTP/1.1" 200 -
2026-05-19 19:44:28,238 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:44:28,260 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:44:50,603 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "POST /set-password HTTP/1.1" 302 -
2026-05-19 19:44:50,658 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "GET /set-password HTTP/1.1" 200 -
2026-05-19 19:44:50,718 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:44:50,722 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:45:01,966 INFO 127.0.0.1 - - [19/May/2026 19:45:01] "POST /set-password HTTP/1.1" 302 -
2026-05-19 19:45:01,988 INFO 127.0.0.1 - - [19/May/2026 19:45:01] "GET / HTTP/1.1" 200 -
2026-05-19 19:45:02,047 INFO 127.0.0.1 - - [19/May/2026 19:45:02] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:45:02,057 INFO 127.0.0.1 - - [19/May/2026 19:45:02] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:45:11,107 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "GET /logout HTTP/1.1" 302 -
2026-05-19 19:45:11,116 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "GET /login HTTP/1.1" 200 -
2026-05-19 19:45:11,137 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:45:11,157 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:45:19,164 INFO 127.0.0.1 - - [19/May/2026 19:45:19] "POST /login HTTP/1.1" 302 -
2026-05-19 19:45:19,171 INFO 127.0.0.1 - - [19/May/2026 19:45:19] "GET /login HTTP/1.1" 200 -
2026-05-19 19:45:19,234 INFO 127.0.0.1 - - [19/May/2026 19:45:19] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:45:34,144 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "POST /login HTTP/1.1" 302 -
2026-05-19 19:45:34,172 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "GET / HTTP/1.1" 200 -
2026-05-19 19:45:34,202 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:45:34,212 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 19:46:18,384 INFO 127.0.0.1 - - [19/May/2026 19:46:18] "GET /transactions/6/print HTTP/1.1" 200 -
2026-05-19 19:46:18,417 INFO 127.0.0.1 - - [19/May/2026 19:46:18] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:47:38,224 INFO 127.0.0.1 - - [19/May/2026 19:47:38] "GET / HTTP/1.1" 200 -
2026-05-19 19:47:38,247 INFO 127.0.0.1 - - [19/May/2026 19:47:38] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:57:20,498 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 19:57:20,601 INFO * Restarting with stat
2026-05-19 19:57:22,594 WARNING * Debugger is active!
2026-05-19 19:57:22,624 INFO * Debugger PIN: 428-899-358
2026-05-19 19:57:28,967 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 19:57:28,995 INFO * Restarting with stat
2026-05-19 19:57:29,204 WARNING * Debugger is active!
2026-05-19 19:57:29,215 INFO * Debugger PIN: 428-899-358
2026-05-19 19:57:36,603 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 19:57:36,627 INFO * Restarting with stat
2026-05-19 19:57:38,136 WARNING * Debugger is active!
2026-05-19 19:57:38,173 INFO * Debugger PIN: 428-899-358
2026-05-19 19:58:54,755 INFO 127.0.0.1 - - [19/May/2026 19:58:54] "GET / HTTP/1.1" 200 -
2026-05-19 19:58:54,809 INFO 127.0.0.1 - - [19/May/2026 19:58:54] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:58:57,065 INFO 127.0.0.1 - - [19/May/2026 19:58:57] "GET / HTTP/1.1" 200 -
2026-05-19 19:58:57,083 INFO 127.0.0.1 - - [19/May/2026 19:58:57] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:59:08,469 INFO 127.0.0.1 - - [19/May/2026 19:59:08] "GET /assign HTTP/1.1" 200 -
2026-05-19 19:59:08,506 INFO 127.0.0.1 - - [19/May/2026 19:59:08] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 19:59:21,896 INFO 127.0.0.1 - - [19/May/2026 19:59:21] "GET /assign HTTP/1.1" 200 -
2026-05-19 19:59:22,179 INFO 127.0.0.1 - - [19/May/2026 19:59:22] "GET /static/cancom.svg HTTP/1.1" 200 -
2026-05-19 19:59:22,194 INFO 127.0.0.1 - - [19/May/2026 19:59:22] "GET /static/favicon.ico HTTP/1.1" 200 -
2026-05-19 19:59:44,462 INFO 127.0.0.1 - - [19/May/2026 19:59:44] "POST /assign HTTP/1.1" 500 -
2026-05-19 19:59:44,500 INFO 127.0.0.1 - - [19/May/2026 19:59:44] "GET /assign?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
2026-05-19 19:59:44,510 INFO 127.0.0.1 - - [19/May/2026 19:59:44] "GET /assign?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
2026-05-19 19:59:44,592 INFO 127.0.0.1 - - [19/May/2026 19:59:44] "GET /assign?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
2026-05-19 19:59:44,594 INFO 127.0.0.1 - - [19/May/2026 19:59:44] "GET /assign?__debugger__=yes&cmd=resource&f=console.png&s=SnLrAdctl57EpB7alHco HTTP/1.1" 200 -
2026-05-19 20:00:19,837 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 20:00:19,877 INFO * Restarting with stat
2026-05-19 20:00:20,122 WARNING * Debugger is active!
2026-05-19 20:00:20,135 INFO * Debugger PIN: 428-899-358
2026-05-19 20:00:52,818 INFO WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5006
* Running on http://192.168.10.191:5006
2026-05-19 20:00:52,818 INFO Press CTRL+C to quit
2026-05-19 20:00:52,819 INFO * Restarting with stat
2026-05-19 20:00:52,968 WARNING * Debugger is active!
2026-05-19 20:00:52,981 INFO * Debugger PIN: 428-899-358
2026-05-19 20:00:58,425 INFO 127.0.0.1 - - [19/May/2026 20:00:58] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:01:00,056 INFO 127.0.0.1 - - [19/May/2026 20:01:00] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:01:00,081 INFO 127.0.0.1 - - [19/May/2026 20:01:00] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:01:01,229 INFO 127.0.0.1 - - [19/May/2026 20:01:01] "GET / HTTP/1.1" 200 -
2026-05-19 20:01:01,252 INFO 127.0.0.1 - - [19/May/2026 20:01:01] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:01:11,315 INFO 127.0.0.1 - - [19/May/2026 20:01:11] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:01:11,339 INFO 127.0.0.1 - - [19/May/2026 20:01:11] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:01:23,982 INFO 127.0.0.1 - - [19/May/2026 20:01:23] "POST /assign HTTP/1.1" 500 -
2026-05-19 20:01:24,002 INFO 127.0.0.1 - - [19/May/2026 20:01:24] "GET /assign?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 -
2026-05-19 20:01:24,007 INFO 127.0.0.1 - - [19/May/2026 20:01:24] "GET /assign?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 304 -
2026-05-19 20:01:24,018 INFO 127.0.0.1 - - [19/May/2026 20:01:24] "GET /assign?__debugger__=yes&cmd=resource&f=console.png&s=BKWhsNSqaeiPep8siyqT HTTP/1.1" 200 -
2026-05-19 20:01:48,757 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 20:01:48,805 INFO * Restarting with stat
2026-05-19 20:01:49,068 WARNING * Debugger is active!
2026-05-19 20:01:49,083 INFO * Debugger PIN: 428-899-358
2026-05-19 20:02:25,283 INFO 127.0.0.1 - - [19/May/2026 20:02:25] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:02:26,977 INFO 127.0.0.1 - - [19/May/2026 20:02:26] "GET / HTTP/1.1" 200 -
2026-05-19 20:02:27,001 INFO 127.0.0.1 - - [19/May/2026 20:02:27] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:02:35,268 INFO 127.0.0.1 - - [19/May/2026 20:02:35] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:02:35,292 INFO 127.0.0.1 - - [19/May/2026 20:02:35] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:02:47,485 INFO ausgabe | user=Erik Thiele | typ=Poolfahrzeug | kennung=GZ-CC-433E | bearbeiter=Erik Thiele
2026-05-19 20:02:47,486 INFO 127.0.0.1 - - [19/May/2026 20:02:47] "POST /assign HTTP/1.1" 302 -
2026-05-19 20:02:47,511 INFO 127.0.0.1 - - [19/May/2026 20:02:47] "GET /transactions/1/print HTTP/1.1" 200 -
2026-05-19 20:02:47,530 INFO 127.0.0.1 - - [19/May/2026 20:02:47] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:02:47,540 INFO 127.0.0.1 - - [19/May/2026 20:02:47] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:03:29,290 INFO 127.0.0.1 - - [19/May/2026 20:03:29] "GET / HTTP/1.1" 200 -
2026-05-19 20:03:29,316 INFO 127.0.0.1 - - [19/May/2026 20:03:29] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:03:29,326 INFO 127.0.0.1 - - [19/May/2026 20:03:29] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:04:24,371 INFO 127.0.0.1 - - [19/May/2026 20:04:24] "GET /?q=GZ-CC-433E HTTP/1.1" 200 -
2026-05-19 20:04:24,398 INFO 127.0.0.1 - - [19/May/2026 20:04:24] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:04:36,099 INFO 127.0.0.1 - - [19/May/2026 20:04:36] "GET /return HTTP/1.1" 200 -
2026-05-19 20:04:36,121 INFO 127.0.0.1 - - [19/May/2026 20:04:36] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:04:44,190 INFO rueckgabe | user=Erik Thiele | typ=Poolfahrzeug | kennung=GZ-CC-433E | bearbeiter=Erik Thiele
2026-05-19 20:04:44,191 INFO 127.0.0.1 - - [19/May/2026 20:04:44] "POST /return HTTP/1.1" 302 -
2026-05-19 20:04:44,199 INFO 127.0.0.1 - - [19/May/2026 20:04:44] "GET /transactions/2/print HTTP/1.1" 200 -
2026-05-19 20:04:44,228 INFO 127.0.0.1 - - [19/May/2026 20:04:44] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:04:44,234 INFO 127.0.0.1 - - [19/May/2026 20:04:44] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:04:49,367 INFO 127.0.0.1 - - [19/May/2026 20:04:49] "GET / HTTP/1.1" 200 -
2026-05-19 20:04:49,391 INFO 127.0.0.1 - - [19/May/2026 20:04:49] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:04:49,397 INFO 127.0.0.1 - - [19/May/2026 20:04:49] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:06:17,108 INFO 127.0.0.1 - - [19/May/2026 20:06:17] "GET / HTTP/1.1" 200 -
2026-05-19 20:06:17,146 INFO 127.0.0.1 - - [19/May/2026 20:06:17] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:07:26,942 INFO 127.0.0.1 - - [19/May/2026 20:07:26] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:07:26,965 INFO 127.0.0.1 - - [19/May/2026 20:07:26] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:07:40,687 INFO ausgabe | user=Erik Thiele | typ=Poolfahrzeug | kennung=GZ-CC-433E | bearbeiter=Erik Thiele
2026-05-19 20:07:40,688 INFO 127.0.0.1 - - [19/May/2026 20:07:40] "POST /assign HTTP/1.1" 302 -
2026-05-19 20:07:40,696 INFO 127.0.0.1 - - [19/May/2026 20:07:40] "GET /transactions/3/print HTTP/1.1" 200 -
2026-05-19 20:07:40,714 INFO 127.0.0.1 - - [19/May/2026 20:07:40] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:07:40,726 INFO 127.0.0.1 - - [19/May/2026 20:07:40] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:07:49,202 INFO 127.0.0.1 - - [19/May/2026 20:07:49] "GET / HTTP/1.1" 200 -
2026-05-19 20:07:49,227 INFO 127.0.0.1 - - [19/May/2026 20:07:49] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:07:49,234 INFO 127.0.0.1 - - [19/May/2026 20:07:49] "GET /static/favicon.ico HTTP/1.1" 304 -
2026-05-19 20:08:34,139 INFO 127.0.0.1 - - [19/May/2026 20:08:34] "GET /?q=GZ-CC HTTP/1.1" 200 -
2026-05-19 20:08:34,164 INFO 127.0.0.1 - - [19/May/2026 20:08:34] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:11:43,873 INFO 127.0.0.1 - - [19/May/2026 20:11:43] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:11:43,905 INFO 127.0.0.1 - - [19/May/2026 20:11:43] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:11:56,587 INFO 127.0.0.1 - - [19/May/2026 20:11:56] "GET / HTTP/1.1" 200 -
2026-05-19 20:11:56,610 INFO 127.0.0.1 - - [19/May/2026 20:11:56] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:14:51,208 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 20:14:51,240 INFO * Restarting with stat
2026-05-19 20:14:52,984 WARNING * Debugger is active!
2026-05-19 20:14:53,007 INFO * Debugger PIN: 428-899-358
2026-05-19 20:15:29,765 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
2026-05-19 20:15:29,795 INFO * Restarting with stat
2026-05-19 20:15:30,023 WARNING * Debugger is active!
2026-05-19 20:15:30,035 INFO * Debugger PIN: 428-899-358
2026-05-19 20:15:59,485 INFO 127.0.0.1 - - [19/May/2026 20:15:59] "GET / HTTP/1.1" 200 -
2026-05-19 20:15:59,542 INFO 127.0.0.1 - - [19/May/2026 20:15:59] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:16:00,687 INFO 127.0.0.1 - - [19/May/2026 20:16:00] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:16:00,710 INFO 127.0.0.1 - - [19/May/2026 20:16:00] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:16:18,443 INFO 127.0.0.1 - - [19/May/2026 20:16:18] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:16:18,928 INFO 127.0.0.1 - - [19/May/2026 20:16:18] "GET /static/cancom.svg HTTP/1.1" 200 -
2026-05-19 20:16:18,947 INFO 127.0.0.1 - - [19/May/2026 20:16:18] "GET /static/favicon.ico HTTP/1.1" 200 -
2026-05-19 20:16:20,679 INFO 127.0.0.1 - - [19/May/2026 20:16:20] "GET /assign HTTP/1.1" 200 -
2026-05-19 20:16:20,701 INFO 127.0.0.1 - - [19/May/2026 20:16:20] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:18:44,322 INFO 127.0.0.1 - - [19/May/2026 20:18:44] "GET / HTTP/1.1" 200 -
2026-05-19 20:18:44,350 INFO 127.0.0.1 - - [19/May/2026 20:18:44] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:19:57,184 INFO 127.0.0.1 - - [19/May/2026 20:19:57] "GET / HTTP/1.1" 200 -
2026-05-19 20:19:57,209 INFO 127.0.0.1 - - [19/May/2026 20:19:57] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:20:47,653 INFO 127.0.0.1 - - [19/May/2026 20:20:47] "GET / HTTP/1.1" 200 -
2026-05-19 20:20:47,677 INFO 127.0.0.1 - - [19/May/2026 20:20:47] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:20:50,056 INFO 127.0.0.1 - - [19/May/2026 20:20:50] "GET / HTTP/1.1" 200 -
2026-05-19 20:20:50,076 INFO 127.0.0.1 - - [19/May/2026 20:20:50] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:21:17,827 INFO 127.0.0.1 - - [19/May/2026 20:21:17] "GET / HTTP/1.1" 200 -
2026-05-19 20:21:17,850 INFO 127.0.0.1 - - [19/May/2026 20:21:17] "GET /static/cancom.svg HTTP/1.1" 304 -
2026-05-19 20:21:44,466 INFO 127.0.0.1 - - [19/May/2026 20:21:44] "GET / HTTP/1.1" 200 -
2026-05-19 20:21:44,485 INFO 127.0.0.1 - - [19/May/2026 20:21:44] "GET /static/cancom.svg HTTP/1.1" 304 -

View File

@@ -3,12 +3,12 @@
{% block content %}
<div class="page-section-title">
<h1>Ausgabe</h1>
<p>Tuerchips und Parkkarten an bestehende User ausgeben.</p>
<p>Tuerchips, Parkkarten und Poolfahrzeuge an bestehende User ausgeben.</p>
</div>
<section class="card form-card">
<div class="card-body">
<h2 class="card-title"><i class="ti ti-key me-1"></i>Chip oder Parkkarte ausgeben</h2>
<h2 class="card-title"><i class="ti ti-key me-1"></i>Medium ausgeben</h2>
<form method="post" action="{{ url_for('assign_asset') }}">
<div class="mb-3">
<label class="form-label">User</label>
@@ -21,17 +21,39 @@
</div>
<div class="mb-3">
<label class="form-label">Typ</label>
<select class="form-select" name="asset_type" required>
<option value="chip">Tuerchip</option>
<option value="parking_card">Parkkarte</option>
<select class="form-select" name="asset_type" id="asset_type" required>
<option value="chip" {% if selected_asset_type == 'chip' %}selected{% endif %}>Tuerchip</option>
<option value="parking_card" {% if selected_asset_type == 'parking_card' %}selected{% endif %}>Parkkarte</option>
<option value="pool_vehicle" {% if selected_asset_type == 'pool_vehicle' %}selected{% endif %}>Poolfahrzeug</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Kennung</label>
<input class="form-control" type="text" name="asset_code" required placeholder="z. B. CHIP-1001">
<label class="form-label" id="asset_code_label">{% if selected_asset_type == 'pool_vehicle' %}Kennzeichen{% else %}Kennung{% endif %}</label>
<input class="form-control" type="text" name="asset_code" id="asset_code" required placeholder="{% if selected_asset_type == 'pool_vehicle' %}z. B. GZ-CC-123{% else %}{{ input_placeholder }}{% endif %}">
</div>
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Ausgabe speichern</button>
</form>
</div>
</section>
<script>
(() => {
const select = document.getElementById("asset_type");
const label = document.getElementById("asset_code_label");
const input = document.getElementById("asset_code");
if (!select || !label || !input) return;
const syncField = () => {
if (select.value === "pool_vehicle") {
label.textContent = "Kennzeichen";
input.placeholder = "z. B. GZ-CC-123";
} else {
label.textContent = "Kennung";
input.placeholder = select.value === "chip" ? "z. B. CHIP-1001" : "z. B. PARK-2001";
}
};
select.addEventListener("change", syncField);
syncField();
})();
</script>
{% endblock %}

View File

@@ -118,7 +118,7 @@
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
gap: 1rem;
}
@@ -162,6 +162,11 @@
border-radius: 999px;
background: rgba(255, 255, 255, 0.18);
font-size: 1.5rem;
flex: 0 0 auto;
}
.card.stat-card-pool .stat-icon {
margin-right: 0.15rem;
}
.card.stat-card.stat-card-users {
@@ -489,7 +494,7 @@
<img class="navbar-brand-logo me-3" src="{{ url_for('static', filename='cancom.svg') }}" alt="CANCOM Logo">
<span class="navbar-brand-wordmark">
<strong>CANCOM BU Sued/West</strong>
<span>Tuerchip und Parkkartenverwaltung</span>
<span>Tuerchip-, Parkkarten- und Poolfahrzeugverwaltung</span>
</span>
</div>
<div class="navbar-nav flex-row order-md-last top-actions">

View File

@@ -51,6 +51,15 @@
<div class="stat-icon"><i class="ti ti-credit-card"></i></div>
</div>
</div>
<div class="card stat-card stat-card-pool" style="background: linear-gradient(135deg, #1f6f8b 0%, #14505f 100%); color: #ffffff;">
<div class="card-body">
<div>
<div class="stat-label">Poolfahrzeuge</div>
<div class="stat-value">{{ stats.active_pool_vehicles }}</div>
</div>
<div class="stat-icon"><i class="ti ti-car"></i></div>
</div>
</div>
</div>
</div>
</section>
@@ -70,6 +79,7 @@
<th>Abteilung</th>
<th>Tuerchip</th>
<th>Parkkarte</th>
<th>Poolfahrzeug</th>
</tr>
</thead>
<tbody>
@@ -94,6 +104,14 @@
-
{% endif %}
</td>
<td>
{% if user.pool_vehicle_code %}
{{ user.pool_vehicle_code }}<br>
<span class="muted">seit {{ user.pool_vehicle_assigned_at }}</span>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -3,7 +3,7 @@
{% block content %}
<section class="card card-md login-card">
<div class="card-body">
<h2 class="text-center mb-4"><i class="ti ti-lock me-1"></i>Signage Admin</h2>
<h2 class="text-center mb-4"><i class="ti ti-lock me-1"></i>Chipverwaltung</h2>
<form method="post" action="{{ url_for('login') }}">
<div class="mb-3">
<label class="form-label">Benutzername</label>

View File

@@ -279,7 +279,7 @@
<td>{{ asset_labels[transaction.asset_type] }}</td>
</tr>
<tr>
<th>Kennung</th>
<th>{% if transaction.asset_type == 'pool_vehicle' %}Kennzeichen{% else %}Kennung{% endif %}</th>
<td>{{ transaction.asset_code }}</td>
</tr>
<tr>

View File

@@ -3,12 +3,12 @@
{% block content %}
<div class="page-section-title">
<h1>Rueckgabe</h1>
<p>Ausgegebene Tuerchips und Parkkarten wieder zuruecknehmen.</p>
<p>Ausgegebene Tuerchips, Parkkarten und Poolfahrzeuge wieder zuruecknehmen.</p>
</div>
<section class="card form-card">
<div class="card-body">
<h2 class="card-title"><i class="ti ti-arrow-back-up me-1"></i>Chip oder Parkkarte zuruecknehmen</h2>
<h2 class="card-title"><i class="ti ti-arrow-back-up me-1"></i>Medium zuruecknehmen</h2>
<form method="post" action="{{ url_for('return_asset') }}">
<div class="mb-3">
<label class="form-label">User</label>
@@ -21,13 +21,27 @@
</div>
<div class="mb-3">
<label class="form-label">Typ</label>
<select class="form-select" name="asset_type" required>
<option value="chip">Tuerchip</option>
<option value="parking_card">Parkkarte</option>
<select class="form-select" name="asset_type" id="asset_type" required>
<option value="chip" {% if selected_asset_type == 'chip' %}selected{% endif %}>Tuerchip</option>
<option value="parking_card" {% if selected_asset_type == 'parking_card' %}selected{% endif %}>Parkkarte</option>
<option value="pool_vehicle" {% if selected_asset_type == 'pool_vehicle' %}selected{% endif %}>Poolfahrzeug</option>
</select>
</div>
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Rueckgabe speichern</button>
</form>
</div>
</section>
<script>
(() => {
const select = document.getElementById("asset_type");
if (!select) return;
const syncField = () => {
select.title = select.value === "pool_vehicle" ? "Poolfahrzeug" : select.options[select.selectedIndex].text;
};
select.addEventListener("change", syncField);
syncField();
})();
</script>
{% endblock %}