um Poolfahrzeuge erweitert
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
## Run
|
## Run
|
||||||
- Create the environment exactly as documented in `README.md`: `python3 -m venv .venv`, `source .venv/bin/activate`, `pip install -r requirements.txt`.
|
- 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__"`.
|
- 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
|
## Data And Side Effects
|
||||||
- The app writes to repo-local files next to `app.py`: SQLite database `inventory.db` and log file `inventory.log`.
|
- The app writes to repo-local files next to `app.py`: SQLite database `inventory.db` and log file `inventory.log`.
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -1,4 +1,4 @@
|
|||||||
# Verwaltung fuer Tuerchips und Parkkarten
|
# Verwaltung fuer Tuerchips, Parkkarten und Poolfahrzeuge
|
||||||
|
|
||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
- Python 3.10 oder neuer
|
- Python 3.10 oder neuer
|
||||||
@@ -15,7 +15,14 @@ pip install -r requirements.txt
|
|||||||
python3 app.py
|
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
|
## Funktionen
|
||||||
- Anmeldung fuer Bearbeiter und Admins
|
- 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 Passwort-Reset fuer Bearbeiter ausloesen
|
||||||
- Admin kann vorhandene Bestandsdaten per CSV importieren
|
- Admin kann vorhandene Bestandsdaten per CSV importieren
|
||||||
- User anlegen
|
- User anlegen
|
||||||
- Ausgabe von Tuerchips und Parkkarten
|
- Ausgabe von Tuerchips, Parkkarten und Poolfahrzeugen
|
||||||
- Rueckgabe von Tuerchips und Parkkarten
|
- Rueckgabe von Tuerchips, Parkkarten und Poolfahrzeugen
|
||||||
- Uebersicht mit Suche und letzten Bewegungen
|
- Uebersicht mit Suche und letzten Bewegungen
|
||||||
- Einfache Logdatei mit Datum, Medium und bearbeitendem Mitarbeiter
|
- Einfache Logdatei mit Datum, Medium und bearbeitendem Mitarbeiter
|
||||||
- Anzeige der letzten Logeintraege im Webinterface
|
- 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.
|
- 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.
|
- Die Aktion `Import` uebernimmt vorhandene aktive Bestandsdaten in die Datenbank.
|
||||||
- Falls ein User noch nicht existiert, wird er beim Import automatisch angelegt.
|
- 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.
|
- 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
138
app.py
@@ -26,6 +26,7 @@ logging.basicConfig(
|
|||||||
ASSET_LABELS = {
|
ASSET_LABELS = {
|
||||||
"chip": "Tuerchip",
|
"chip": "Tuerchip",
|
||||||
"parking_card": "Parkkarte",
|
"parking_card": "Parkkarte",
|
||||||
|
"pool_vehicle": "Poolfahrzeug",
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTION_LABELS = {
|
ACTION_LABELS = {
|
||||||
@@ -38,6 +39,12 @@ PRINT_DESCRIPTIONS = {
|
|||||||
"return": "Rueckgabebestaetigung fuer die Ruecknahme eines Mediums",
|
"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_ACTIONS = {"import", "ausgabe", "assign"}
|
||||||
IMPORT_HEADER = ["user", "typ", "kennung", "aktion"]
|
IMPORT_HEADER = ["user", "typ", "kennung", "aktion"]
|
||||||
|
|
||||||
@@ -79,13 +86,15 @@ def init_db() -> None:
|
|||||||
chip_assigned_at TEXT,
|
chip_assigned_at TEXT,
|
||||||
parking_card_code TEXT,
|
parking_card_code TEXT,
|
||||||
parking_card_assigned_at TEXT,
|
parking_card_assigned_at TEXT,
|
||||||
|
pool_vehicle_code TEXT,
|
||||||
|
pool_vehicle_assigned_at TEXT,
|
||||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS transactions (
|
CREATE TABLE IF NOT EXISTS transactions (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id INTEGER NOT NULL,
|
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,
|
asset_code TEXT NOT NULL,
|
||||||
handled_by TEXT,
|
handled_by TEXT,
|
||||||
action TEXT NOT NULL CHECK (action IN ('assign', 'return')),
|
action TEXT NOT NULL CHECK (action IN ('assign', 'return')),
|
||||||
@@ -112,6 +121,35 @@ def init_db() -> None:
|
|||||||
if "handled_by" not in columns:
|
if "handled_by" not in columns:
|
||||||
db.execute("ALTER TABLE transactions ADD COLUMN handled_by TEXT")
|
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(
|
admin_exists = db.execute(
|
||||||
"SELECT id FROM staff_users WHERE role = 'admin' LIMIT 1"
|
"SELECT id FROM staff_users WHERE role = 'admin' LIMIT 1"
|
||||||
).fetchone()
|
).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:
|
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(
|
existing = db.execute(
|
||||||
f"SELECT id FROM users WHERE {column_name} = ?",
|
f"SELECT id FROM users WHERE {column_name} = ?",
|
||||||
(asset_code,),
|
(asset_code,),
|
||||||
@@ -210,6 +252,9 @@ def normalize_asset_type(value: str) -> str | None:
|
|||||||
"parkkarte": "parking_card",
|
"parkkarte": "parking_card",
|
||||||
"parking_card": "parking_card",
|
"parking_card": "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)
|
return aliases.get(normalized)
|
||||||
|
|
||||||
@@ -461,17 +506,20 @@ def import_data() -> str:
|
|||||||
|
|
||||||
current_chip_code = user["chip_code"] if user else None
|
current_chip_code = user["chip_code"] if user else None
|
||||||
current_card_code = user["parking_card_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
|
user_id = user["id"] if user else None
|
||||||
|
|
||||||
if pending_user is not None:
|
if pending_user is not None:
|
||||||
current_chip_code = pending_user["chip_code"]
|
current_chip_code = pending_user["chip_code"]
|
||||||
current_card_code = pending_user["parking_card_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:
|
if user is None and pending_user is None:
|
||||||
pending_user = {
|
pending_user = {
|
||||||
"full_name": full_name,
|
"full_name": full_name,
|
||||||
"chip_code": None,
|
"chip_code": None,
|
||||||
"parking_card_code": None,
|
"parking_card_code": None,
|
||||||
|
"pool_vehicle_code": None,
|
||||||
}
|
}
|
||||||
operations.append(("user", pending_user))
|
operations.append(("user", pending_user))
|
||||||
|
|
||||||
@@ -480,6 +528,8 @@ def import_data() -> str:
|
|||||||
asset_type == "chip" and current_chip_code == asset_code
|
asset_type == "chip" and current_chip_code == asset_code
|
||||||
) or (
|
) or (
|
||||||
asset_type == "parking_card" and current_card_code == asset_code
|
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:
|
if not assigned_to_same_user:
|
||||||
errors.append(f"Zeile {index}: Kennung '{asset_code}' ist bereits vergeben.")
|
errors.append(f"Zeile {index}: Kennung '{asset_code}' ist bereits vergeben.")
|
||||||
@@ -504,12 +554,18 @@ def import_data() -> str:
|
|||||||
continue
|
continue
|
||||||
if pending_user is not None:
|
if pending_user is not None:
|
||||||
pending_user["chip_code"] = asset_code
|
pending_user["chip_code"] = asset_code
|
||||||
else:
|
elif asset_type == "parking_card":
|
||||||
if current_card_code and current_card_code != asset_code:
|
if current_card_code and current_card_code != asset_code:
|
||||||
errors.append(f"Zeile {index}: User '{full_name}' hat bereits eine andere Parkkarte.")
|
errors.append(f"Zeile {index}: User '{full_name}' hat bereits eine andere Parkkarte.")
|
||||||
continue
|
continue
|
||||||
if pending_user is not None:
|
if pending_user is not None:
|
||||||
pending_user["parking_card_code"] = asset_code
|
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(
|
operations.append(
|
||||||
(
|
(
|
||||||
@@ -549,11 +605,16 @@ def import_data() -> str:
|
|||||||
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
(payload["asset_code"], resolved_user_id),
|
(payload["asset_code"], resolved_user_id),
|
||||||
)
|
)
|
||||||
else:
|
elif payload["asset_type"] == "parking_card":
|
||||||
db.execute(
|
db.execute(
|
||||||
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
(payload["asset_code"], resolved_user_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(
|
db.execute(
|
||||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
|
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
|
||||||
@@ -577,7 +638,8 @@ def index() -> str:
|
|||||||
params: tuple[str, ...] = ()
|
params: tuple[str, ...] = ()
|
||||||
user_query = """
|
user_query = """
|
||||||
SELECT id, full_name, email, department, chip_code, chip_assigned_at,
|
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
|
FROM users
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -586,10 +648,11 @@ def index() -> str:
|
|||||||
user_query += """
|
user_query += """
|
||||||
WHERE full_name LIKE ?
|
WHERE full_name LIKE ?
|
||||||
OR email LIKE ?
|
OR email LIKE ?
|
||||||
OR chip_code LIKE ?
|
OR chip_code LIKE ?
|
||||||
OR parking_card_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"
|
user_query += " ORDER BY full_name COLLATE NOCASE"
|
||||||
users = db.execute(user_query, params).fetchall()
|
users = db.execute(user_query, params).fetchall()
|
||||||
@@ -599,7 +662,8 @@ def index() -> str:
|
|||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS users,
|
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 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
|
FROM users
|
||||||
"""
|
"""
|
||||||
).fetchone()
|
).fetchone()
|
||||||
@@ -624,6 +688,7 @@ def index() -> str:
|
|||||||
"users": stats["users"],
|
"users": stats["users"],
|
||||||
"active_chips": stats["active_chips"],
|
"active_chips": stats["active_chips"],
|
||||||
"active_cards": stats["active_cards"],
|
"active_cards": stats["active_cards"],
|
||||||
|
"active_pool_vehicles": stats["active_pool_vehicles"],
|
||||||
},
|
},
|
||||||
search_query=search_query,
|
search_query=search_query,
|
||||||
asset_labels=ASSET_LABELS,
|
asset_labels=ASSET_LABELS,
|
||||||
@@ -660,14 +725,19 @@ def create_user() -> str:
|
|||||||
@login_required
|
@login_required
|
||||||
def assign_asset() -> str:
|
def assign_asset() -> str:
|
||||||
db = get_db()
|
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":
|
if request.method == "POST":
|
||||||
user_id = request.form.get("user_id", "").strip()
|
user_id = request.form.get("user_id", "").strip()
|
||||||
asset_type = request.form.get("asset_type", "").strip()
|
|
||||||
asset_code = request.form.get("asset_code", "").strip()
|
asset_code = request.form.get("asset_code", "").strip()
|
||||||
handled_by = g.current_staff["full_name"]
|
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.")
|
flash("Bitte alle Felder fuer die Ausgabe ausfuellen.")
|
||||||
return redirect(url_for("assign_asset"))
|
return redirect(url_for("assign_asset"))
|
||||||
|
|
||||||
@@ -677,7 +747,7 @@ def assign_asset() -> str:
|
|||||||
return redirect(url_for("assign_asset"))
|
return redirect(url_for("assign_asset"))
|
||||||
|
|
||||||
if asset_code_in_use(db, asset_type, asset_code):
|
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"))
|
return redirect(url_for("assign_asset"))
|
||||||
|
|
||||||
if asset_type == "chip":
|
if asset_type == "chip":
|
||||||
@@ -688,7 +758,7 @@ def assign_asset() -> str:
|
|||||||
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
(asset_code, user_id),
|
(asset_code, user_id),
|
||||||
)
|
)
|
||||||
else:
|
elif asset_type == "parking_card":
|
||||||
if user["parking_card_code"]:
|
if user["parking_card_code"]:
|
||||||
flash("Dieser User hat bereits eine aktive Parkkarte.")
|
flash("Dieser User hat bereits eine aktive Parkkarte.")
|
||||||
return redirect(url_for("assign_asset"))
|
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 = ?",
|
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
(asset_code, user_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(
|
db.execute(
|
||||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
|
"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))
|
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()
|
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"])
|
@app.route("/return", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def return_asset() -> str:
|
def return_asset() -> str:
|
||||||
db = get_db()
|
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":
|
if request.method == "POST":
|
||||||
user_id = request.form.get("user_id", "").strip()
|
user_id = request.form.get("user_id", "").strip()
|
||||||
asset_type = request.form.get("asset_type", "").strip()
|
|
||||||
handled_by = g.current_staff["full_name"]
|
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.")
|
flash("Bitte User und Typ fuer die Rueckgabe waehlen.")
|
||||||
return redirect(url_for("return_asset"))
|
return redirect(url_for("return_asset"))
|
||||||
|
|
||||||
@@ -730,7 +815,13 @@ def return_asset() -> str:
|
|||||||
flash("Ausgewaehlter User wurde nicht gefunden.")
|
flash("Ausgewaehlter User wurde nicht gefunden.")
|
||||||
return redirect(url_for("return_asset"))
|
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:
|
if not asset_code:
|
||||||
flash("Fuer diesen User ist kein entsprechendes Medium aktiv hinterlegt.")
|
flash("Fuer diesen User ist kein entsprechendes Medium aktiv hinterlegt.")
|
||||||
return redirect(url_for("return_asset"))
|
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 = ?",
|
"UPDATE users SET chip_code = NULL, chip_assigned_at = NULL WHERE id = ?",
|
||||||
(user_id,),
|
(user_id,),
|
||||||
)
|
)
|
||||||
else:
|
elif asset_type == "parking_card":
|
||||||
db.execute(
|
db.execute(
|
||||||
"UPDATE users SET parking_card_code = NULL, parking_card_assigned_at = NULL WHERE id = ?",
|
"UPDATE users SET parking_card_code = NULL, parking_card_assigned_at = NULL WHERE id = ?",
|
||||||
(user_id,),
|
(user_id,),
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
db.execute(
|
||||||
|
"UPDATE users SET pool_vehicle_code = NULL, pool_vehicle_assigned_at = NULL WHERE id = ?",
|
||||||
|
(user_id,),
|
||||||
|
)
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'return')",
|
"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))
|
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()
|
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")
|
@app.route("/transactions/<int:transaction_id>/print")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
platforms:
|
platforms:
|
||||||
- linux/amd64
|
- linux/amd64
|
||||||
image: gitea.teamthiele.de/ethiele/keyVerwaltung:latest
|
image: gitea.teamthiele.de/ethiele/keyverwaltung:latest
|
||||||
ports:
|
ports:
|
||||||
- "5006:5006"
|
- "5006:5006"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
BIN
inventory.db
BIN
inventory.db
Binary file not shown.
277
inventory.log
277
inventory.log
@@ -719,3 +719,280 @@
|
|||||||
2026-05-18 21:19:15,878 INFO * Restarting with stat
|
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,201 WARNING * Debugger is active!
|
||||||
2026-05-18 21:19:16,216 INFO * Debugger PIN: 428-899-358
|
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 [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||||
|
* 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 [33mPress CTRL+C to quit[0m
|
||||||
|
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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 09:00:24,238 INFO 127.0.0.1 - - [19/May/2026 09:00:24] "[32mGET /logout HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 09:00:30,346 INFO 127.0.0.1 - - [19/May/2026 09:00:30] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 09:01:30,896 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "[32mPOST /assign HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 09:01:30,945 INFO 127.0.0.1 - - [19/May/2026 09:01:30] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:36:36,490 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "[32mGET /logout HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:36:36,529 INFO 127.0.0.1 - - [19/May/2026 19:36:36] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:36:46,776 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:36:46,833 INFO 127.0.0.1 - - [19/May/2026 19:36:46] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:37:47,727 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "[32mPOST /assign HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:37:47,765 INFO 127.0.0.1 - - [19/May/2026 19:37:47] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /return HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:38:16,528 INFO 127.0.0.1 - - [19/May/2026 19:38:16] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:38:18,326 INFO 127.0.0.1 - - [19/May/2026 19:38:18] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /assign HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:38:40,683 INFO 127.0.0.1 - - [19/May/2026 19:38:40] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:39:10,025 INFO 127.0.0.1 - - [19/May/2026 19:39:10] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /return HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:39:21,070 INFO 127.0.0.1 - - [19/May/2026 19:39:21] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:39:37,911 INFO 127.0.0.1 - - [19/May/2026 19:39:37] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:41:56,887 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "[32mGET /logout HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:41:56,929 INFO 127.0.0.1 - - [19/May/2026 19:41:56] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:42:02,095 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:42:02,183 INFO 127.0.0.1 - - [19/May/2026 19:42:02] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:08,662 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "[32mPOST /admin/staff HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:08,696 INFO 127.0.0.1 - - [19/May/2026 19:44:08] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:20,804 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "[32mGET /logout HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:20,862 INFO 127.0.0.1 - - [19/May/2026 19:44:20] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:28,199 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:28,260 INFO 127.0.0.1 - - [19/May/2026 19:44:28] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:50,603 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "[32mPOST /set-password HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:44:50,722 INFO 127.0.0.1 - - [19/May/2026 19:44:50] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:01,966 INFO 127.0.0.1 - - [19/May/2026 19:45:01] "[32mPOST /set-password HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:02,057 INFO 127.0.0.1 - - [19/May/2026 19:45:02] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:11,107 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "[32mGET /logout HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:11,157 INFO 127.0.0.1 - - [19/May/2026 19:45:11] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:19,164 INFO 127.0.0.1 - - [19/May/2026 19:45:19] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:34,144 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "[32mPOST /login HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 19:45:34,212 INFO 127.0.0.1 - - [19/May/2026 19:45:34] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[35m[1mPOST /assign HTTP/1.1[0m" 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 [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||||
|
* 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 [33mPress CTRL+C to quit[0m
|
||||||
|
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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:01:23,982 INFO 127.0.0.1 - - [19/May/2026 20:01:23] "[35m[1mPOST /assign HTTP/1.1[0m" 500 -
|
||||||
|
2026-05-19 20:01:24,002 INFO 127.0.0.1 - - [19/May/2026 20:01:24] "[36mGET /assign?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:01:24,007 INFO 127.0.0.1 - - [19/May/2026 20:01:24] "[36mGET /assign?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /assign HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:02:47,540 INFO 127.0.0.1 - - [19/May/2026 20:02:47] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:03:29,326 INFO 127.0.0.1 - - [19/May/2026 20:03:29] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /return HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:04:44,234 INFO 127.0.0.1 - - [19/May/2026 20:04:44] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:04:49,397 INFO 127.0.0.1 - - [19/May/2026 20:04:49] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[32mPOST /assign HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:07:40,726 INFO 127.0.0.1 - - [19/May/2026 20:07:40] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
2026-05-19 20:07:49,234 INFO 127.0.0.1 - - [19/May/2026 20:07:49] "[36mGET /static/favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 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] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-section-title">
|
<div class="page-section-title">
|
||||||
<h1>Ausgabe</h1>
|
<h1>Ausgabe</h1>
|
||||||
<p>Tuerchips und Parkkarten an bestehende User ausgeben.</p>
|
<p>Tuerchips, Parkkarten und Poolfahrzeuge an bestehende User ausgeben.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="card form-card">
|
<section class="card form-card">
|
||||||
<div class="card-body">
|
<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') }}">
|
<form method="post" action="{{ url_for('assign_asset') }}">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">User</label>
|
<label class="form-label">User</label>
|
||||||
@@ -21,17 +21,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Typ</label>
|
<label class="form-label">Typ</label>
|
||||||
<select class="form-select" name="asset_type" required>
|
<select class="form-select" name="asset_type" id="asset_type" required>
|
||||||
<option value="chip">Tuerchip</option>
|
<option value="chip" {% if selected_asset_type == 'chip' %}selected{% endif %}>Tuerchip</option>
|
||||||
<option value="parking_card">Parkkarte</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Kennung</label>
|
<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" required placeholder="z. B. CHIP-1001">
|
<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>
|
</div>
|
||||||
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Ausgabe speichern</button>
|
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Ausgabe speichern</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</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 %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
|
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +162,11 @@
|
|||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(255, 255, 255, 0.18);
|
background: rgba(255, 255, 255, 0.18);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.stat-card-pool .stat-icon {
|
||||||
|
margin-right: 0.15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card.stat-card.stat-card-users {
|
.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">
|
<img class="navbar-brand-logo me-3" src="{{ url_for('static', filename='cancom.svg') }}" alt="CANCOM Logo">
|
||||||
<span class="navbar-brand-wordmark">
|
<span class="navbar-brand-wordmark">
|
||||||
<strong>CANCOM BU Sued/West</strong>
|
<strong>CANCOM BU Sued/West</strong>
|
||||||
<span>Tuerchip und Parkkartenverwaltung</span>
|
<span>Tuerchip-, Parkkarten- und Poolfahrzeugverwaltung</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-nav flex-row order-md-last top-actions">
|
<div class="navbar-nav flex-row order-md-last top-actions">
|
||||||
|
|||||||
@@ -51,6 +51,15 @@
|
|||||||
<div class="stat-icon"><i class="ti ti-credit-card"></i></div>
|
<div class="stat-icon"><i class="ti ti-credit-card"></i></div>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -70,6 +79,7 @@
|
|||||||
<th>Abteilung</th>
|
<th>Abteilung</th>
|
||||||
<th>Tuerchip</th>
|
<th>Tuerchip</th>
|
||||||
<th>Parkkarte</th>
|
<th>Parkkarte</th>
|
||||||
|
<th>Poolfahrzeug</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -94,6 +104,14 @@
|
|||||||
-
|
-
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</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>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="card card-md login-card">
|
<section class="card card-md login-card">
|
||||||
<div class="card-body">
|
<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') }}">
|
<form method="post" action="{{ url_for('login') }}">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Benutzername</label>
|
<label class="form-label">Benutzername</label>
|
||||||
|
|||||||
@@ -279,7 +279,7 @@
|
|||||||
<td>{{ asset_labels[transaction.asset_type] }}</td>
|
<td>{{ asset_labels[transaction.asset_type] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Kennung</th>
|
<th>{% if transaction.asset_type == 'pool_vehicle' %}Kennzeichen{% else %}Kennung{% endif %}</th>
|
||||||
<td>{{ transaction.asset_code }}</td>
|
<td>{{ transaction.asset_code }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-section-title">
|
<div class="page-section-title">
|
||||||
<h1>Rueckgabe</h1>
|
<h1>Rueckgabe</h1>
|
||||||
<p>Ausgegebene Tuerchips und Parkkarten wieder zuruecknehmen.</p>
|
<p>Ausgegebene Tuerchips, Parkkarten und Poolfahrzeuge wieder zuruecknehmen.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="card form-card">
|
<section class="card form-card">
|
||||||
<div class="card-body">
|
<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') }}">
|
<form method="post" action="{{ url_for('return_asset') }}">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">User</label>
|
<label class="form-label">User</label>
|
||||||
@@ -21,13 +21,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Typ</label>
|
<label class="form-label">Typ</label>
|
||||||
<select class="form-select" name="asset_type" required>
|
<select class="form-select" name="asset_type" id="asset_type" required>
|
||||||
<option value="chip">Tuerchip</option>
|
<option value="chip" {% if selected_asset_type == 'chip' %}selected{% endif %}>Tuerchip</option>
|
||||||
<option value="parking_card">Parkkarte</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Rueckgabe speichern</button>
|
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Rueckgabe speichern</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</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 %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user