643 lines
26 KiB
Python
643 lines
26 KiB
Python
import json
|
|
from datetime import datetime, timedelta
|
|
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort, session, flash
|
|
from flask_login import current_user, login_required
|
|
from functools import wraps
|
|
from sqlalchemy import desc
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
from models import GuestRequest, Job, Printer, User, UserPermission, Notification, get_cached_session
|
|
from utils.logging_config import get_logger
|
|
|
|
guest_blueprint = Blueprint('guest', __name__)
|
|
logger = get_logger("guest")
|
|
|
|
# Hilfsfunktionen
|
|
def can_approve_jobs(user_id):
|
|
"""Prüft, ob ein Benutzer Anfragen genehmigen darf."""
|
|
with get_cached_session() as db_session:
|
|
permission = db_session.query(UserPermission).filter_by(user_id=user_id).first()
|
|
if not permission:
|
|
return False
|
|
return permission.can_approve_jobs
|
|
|
|
def approver_required(f):
|
|
"""Decorator zur Prüfung der Genehmigungsberechtigung."""
|
|
@wraps(f)
|
|
@login_required
|
|
def decorated_function(*args, **kwargs):
|
|
if not can_approve_jobs(current_user.id):
|
|
abort(403, "Keine Berechtigung zum Genehmigen von Anfragen")
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
# Gast-Routen
|
|
@guest_blueprint.route('/request', methods=['GET'])
|
|
def guest_request_form():
|
|
"""Formular für Gastanfragen anzeigen."""
|
|
with get_cached_session() as db_session:
|
|
printers = db_session.query(Printer).filter_by(active=True).all()
|
|
# Drucker-Liste von der Session trennen für Template-Verwendung
|
|
db_session.expunge_all()
|
|
return render_template('guest_request.html', printers=printers)
|
|
|
|
@guest_blueprint.route('/requests/overview', methods=['GET'])
|
|
def guest_requests_overview():
|
|
"""Öffentliche Übersicht aller Druckanträge mit zensierten persönlichen Daten."""
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
# Alle Gastanfragen mit eager loading des printer-Relationships laden
|
|
guest_requests = db_session.query(GuestRequest).options(
|
|
joinedload(GuestRequest.printer)
|
|
).order_by(desc(GuestRequest.created_at)).all()
|
|
|
|
# Daten für Gäste aufbereiten (persönliche Daten zensieren)
|
|
public_requests = []
|
|
for req in guest_requests:
|
|
# Name zensieren: Nur ersten Buchstaben und letzten Buchstaben anzeigen
|
|
censored_name = "***"
|
|
if req.name and len(req.name) > 0:
|
|
if len(req.name) == 1:
|
|
censored_name = req.name[0] + "***"
|
|
elif len(req.name) == 2:
|
|
censored_name = req.name[0] + "***" + req.name[-1]
|
|
else:
|
|
censored_name = req.name[0] + "***" + req.name[-1]
|
|
|
|
# E-Mail zensieren
|
|
censored_email = "***@***.***"
|
|
if req.email and "@" in req.email:
|
|
email_parts = req.email.split("@")
|
|
if len(email_parts[0]) > 2:
|
|
censored_email = email_parts[0][:2] + "***@" + email_parts[1]
|
|
else:
|
|
censored_email = "***@" + email_parts[1]
|
|
|
|
# Grund zensieren (nur erste 20 Zeichen anzeigen)
|
|
censored_reason = "***"
|
|
if req.reason:
|
|
if len(req.reason) > 20:
|
|
censored_reason = req.reason[:20] + "***"
|
|
else:
|
|
censored_reason = req.reason[:10] + "***" if len(req.reason) > 10 else "***"
|
|
|
|
# Drucker-Info laden (jetzt durch eager loading verfügbar)
|
|
printer_name = "Unbekannt"
|
|
if req.printer:
|
|
printer_name = req.printer.name
|
|
|
|
# Job-Status laden, falls vorhanden
|
|
job_status = None
|
|
if req.job_id:
|
|
job = db_session.query(Job).filter_by(id=req.job_id).first()
|
|
if job:
|
|
job_status = job.status
|
|
|
|
public_requests.append({
|
|
"id": req.id,
|
|
"name": censored_name,
|
|
"email": censored_email,
|
|
"reason": censored_reason,
|
|
"duration_min": req.duration_min,
|
|
"created_at": req.created_at,
|
|
"status": req.status,
|
|
"printer_name": printer_name,
|
|
"job_status": job_status
|
|
})
|
|
|
|
logger.info(f"Öffentliche Druckanträge-Übersicht aufgerufen - {len(public_requests)} Einträge")
|
|
|
|
return render_template('guest_requests_overview.html', requests=public_requests)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Laden der öffentlichen Druckanträge-Übersicht: {str(e)}")
|
|
return render_template('guest_requests_overview.html', requests=[], error="Fehler beim Laden der Daten")
|
|
|
|
@guest_blueprint.route('/request/<int:request_id>', methods=['GET'])
|
|
def guest_request_status(request_id):
|
|
"""Status einer Gastanfrage anzeigen."""
|
|
with get_cached_session() as db_session:
|
|
# Guest Request mit eager loading des printer-Relationships laden
|
|
guest_request = db_session.query(GuestRequest).options(
|
|
joinedload(GuestRequest.printer)
|
|
).filter_by(id=request_id).first()
|
|
|
|
if not guest_request:
|
|
abort(404, "Anfrage nicht gefunden")
|
|
|
|
# Nur wenn Status "approved" ist, OTP generieren und anzeigen
|
|
otp_code = None
|
|
if guest_request.status == "approved" and not guest_request.otp_code:
|
|
otp_code = guest_request.generate_otp()
|
|
db_session.commit()
|
|
|
|
# Zugehörigen Job laden, falls vorhanden
|
|
job = None
|
|
if guest_request.job_id:
|
|
job = db_session.query(Job).filter_by(id=guest_request.job_id).first()
|
|
|
|
# Objekte explizit von der Session trennen, um sie außerhalb verwenden zu können
|
|
db_session.expunge(guest_request)
|
|
if job:
|
|
db_session.expunge(job)
|
|
|
|
return render_template('guest_status.html',
|
|
request=guest_request,
|
|
job=job,
|
|
otp_code=otp_code)
|
|
|
|
# API-Endpunkte
|
|
@guest_blueprint.route('/api/guest/requests', methods=['POST'])
|
|
def api_create_guest_request():
|
|
"""Neue Gastanfrage erstellen."""
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({"error": "Keine Daten erhalten"}), 400
|
|
|
|
# Pflichtfelder prüfen
|
|
name = data.get('name')
|
|
if not name:
|
|
return jsonify({"error": "Name ist erforderlich"}), 400
|
|
|
|
# Optionale Felder
|
|
email = data.get('email')
|
|
reason = data.get('reason')
|
|
duration_min = data.get('duration_min', 60) # Standard: 1 Stunde
|
|
printer_id = data.get('printer_id')
|
|
|
|
# IP-Adresse erfassen
|
|
author_ip = request.remote_addr
|
|
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
# Drucker prüfen
|
|
if printer_id:
|
|
printer = db_session.query(Printer).filter_by(id=printer_id, active=True).first()
|
|
if not printer:
|
|
return jsonify({"error": "Ungültiger Drucker ausgewählt"}), 400
|
|
|
|
# Neue Anfrage erstellen
|
|
guest_request = GuestRequest(
|
|
name=name,
|
|
email=email,
|
|
reason=reason,
|
|
duration_min=duration_min,
|
|
printer_id=printer_id,
|
|
author_ip=author_ip
|
|
)
|
|
|
|
db_session.add(guest_request)
|
|
db_session.commit()
|
|
|
|
# Benachrichtigung für Genehmiger erstellen
|
|
Notification.create_for_approvers(
|
|
notification_type="guest_request",
|
|
payload={
|
|
"request_id": guest_request.id,
|
|
"name": guest_request.name,
|
|
"created_at": guest_request.created_at.isoformat(),
|
|
"status": guest_request.status
|
|
}
|
|
)
|
|
|
|
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}")
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"request_id": guest_request.id,
|
|
"status": guest_request.status,
|
|
"redirect_url": url_for('guest.guest_request_status', request_id=guest_request.id)
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Erstellen der Gastanfrage: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/guest/requests/<int:request_id>', methods=['GET'])
|
|
def api_get_guest_request(request_id):
|
|
"""Status einer Gastanfrage abrufen."""
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
|
if not guest_request:
|
|
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
|
|
|
# OTP wird nie über die API zurückgegeben
|
|
response_data = guest_request.to_dict()
|
|
response_data.pop("otp_code", None)
|
|
|
|
return jsonify(response_data)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Abrufen der Gastanfrage: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/requests/<int:request_id>/approve', methods=['POST'])
|
|
@approver_required
|
|
def api_approve_request(request_id):
|
|
"""Gastanfrage genehmigen."""
|
|
try:
|
|
data = request.get_json() or {}
|
|
approval_notes = data.get('notes', '')
|
|
printer_id = data.get('printer_id') # Optional: Drucker zuweisen/ändern
|
|
|
|
with get_cached_session() as db_session:
|
|
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
|
if not guest_request:
|
|
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
|
|
|
if guest_request.status != "pending":
|
|
return jsonify({"error": "Anfrage wurde bereits bearbeitet"}), 400
|
|
|
|
# Drucker validieren, falls angegeben
|
|
if printer_id:
|
|
printer = db_session.query(Printer).filter_by(id=printer_id, active=True).first()
|
|
if not printer:
|
|
return jsonify({"error": "Ungültiger Drucker ausgewählt"}), 400
|
|
guest_request.printer_id = printer_id
|
|
|
|
# Sicherstellen, dass ein Drucker zugewiesen ist
|
|
if not guest_request.printer_id:
|
|
return jsonify({"error": "Kein Drucker zugewiesen. Bitte wählen Sie einen Drucker aus."}), 400
|
|
|
|
# Anfrage genehmigen
|
|
guest_request.status = "approved"
|
|
guest_request.processed_by = current_user.id
|
|
guest_request.processed_at = datetime.now()
|
|
guest_request.approval_notes = approval_notes
|
|
|
|
# OTP generieren
|
|
otp_plain = guest_request.generate_otp()
|
|
|
|
# Zugehörigen Job erstellen
|
|
start_time = datetime.now() + timedelta(minutes=5) # Start in 5 Minuten
|
|
end_time = start_time + timedelta(minutes=guest_request.duration_min)
|
|
|
|
# Admin-Benutzer als Eigentümer verwenden
|
|
admin_user = db_session.query(User).filter_by(role="admin").first()
|
|
|
|
job = Job(
|
|
name=f"Gastauftrag: {guest_request.name}",
|
|
description=guest_request.reason or "Gastauftrag",
|
|
user_id=admin_user.id,
|
|
printer_id=guest_request.printer_id,
|
|
start_at=start_time,
|
|
end_at=end_time,
|
|
status="scheduled",
|
|
duration_minutes=guest_request.duration_min,
|
|
owner_id=admin_user.id
|
|
)
|
|
|
|
db_session.add(job)
|
|
db_session.flush() # ID generieren
|
|
|
|
# Job-ID in Gastanfrage speichern
|
|
guest_request.job_id = job.id
|
|
|
|
db_session.commit()
|
|
|
|
logger.info(f"Gastanfrage {request_id} genehmigt von Admin {current_user.id} ({current_user.name})")
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"status": "approved",
|
|
"job_id": job.id,
|
|
"otp": otp_plain, # Nur in dieser Antwort wird der OTP-Klartext zurückgegeben
|
|
"approved_by": current_user.name,
|
|
"approved_at": guest_request.processed_at.isoformat(),
|
|
"notes": approval_notes
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Genehmigen der Gastanfrage: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/requests/<int:request_id>/deny', methods=['POST'])
|
|
@approver_required
|
|
def api_deny_request(request_id):
|
|
"""Gastanfrage ablehnen."""
|
|
try:
|
|
data = request.get_json() or {}
|
|
rejection_reason = data.get('reason', '')
|
|
|
|
if not rejection_reason.strip():
|
|
return jsonify({"error": "Ablehnungsgrund ist erforderlich"}), 400
|
|
|
|
with get_cached_session() as db_session:
|
|
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
|
if not guest_request:
|
|
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
|
|
|
if guest_request.status != "pending":
|
|
return jsonify({"error": "Anfrage wurde bereits bearbeitet"}), 400
|
|
|
|
# Anfrage ablehnen
|
|
guest_request.status = "denied"
|
|
guest_request.processed_by = current_user.id
|
|
guest_request.processed_at = datetime.now()
|
|
guest_request.rejection_reason = rejection_reason
|
|
|
|
db_session.commit()
|
|
|
|
logger.info(f"Gastanfrage {request_id} abgelehnt von Admin {current_user.id} ({current_user.name}): {rejection_reason}")
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"status": "denied",
|
|
"rejected_by": current_user.name,
|
|
"rejected_at": guest_request.processed_at.isoformat(),
|
|
"reason": rejection_reason
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Ablehnen der Gastanfrage: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/jobs/start/<string:otp>', methods=['POST'])
|
|
def api_start_job_with_otp(otp):
|
|
"""Job mit OTP starten."""
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
# Alle genehmigten Anfragen mit OTP durchsuchen
|
|
guest_requests = db_session.query(GuestRequest).filter_by(status="approved").all()
|
|
|
|
valid_request = None
|
|
for req in guest_requests:
|
|
if req.verify_otp(otp):
|
|
valid_request = req
|
|
break
|
|
|
|
if not valid_request:
|
|
return jsonify({"error": "Ungültiger oder abgelaufener Code"}), 400
|
|
|
|
if not valid_request.job_id:
|
|
return jsonify({"error": "Kein Job mit diesem Code verknüpft"}), 400
|
|
|
|
# Job laden
|
|
job = db_session.query(Job).filter_by(id=valid_request.job_id).first()
|
|
if not job:
|
|
return jsonify({"error": "Job nicht gefunden"}), 404
|
|
|
|
# Job-Status prüfen
|
|
if job.status != "scheduled":
|
|
return jsonify({"error": "Job kann nicht gestartet werden"}), 400
|
|
|
|
# Grace-Period prüfen (5 Minuten vor bis 5 Minuten nach geplantem Start)
|
|
now = datetime.now()
|
|
start_time = job.start_at
|
|
grace_start = start_time - timedelta(minutes=5)
|
|
grace_end = start_time + timedelta(minutes=5)
|
|
|
|
if now < grace_start:
|
|
return jsonify({
|
|
"error": f"Der Job kann erst ab {grace_start.strftime('%H:%M')} Uhr gestartet werden"
|
|
}), 400
|
|
|
|
if now > job.end_at:
|
|
return jsonify({"error": "Der Job ist bereits abgelaufen"}), 400
|
|
|
|
# Job starten
|
|
job.status = "active"
|
|
job.start_at = now # Aktualisiere Startzeit auf jetzt
|
|
|
|
# OTP als verwendet markieren
|
|
valid_request.otp_used_at = now
|
|
|
|
db_session.commit()
|
|
|
|
logger.info(f"Job {job.id} mit OTP {otp} gestartet von IP: {request.remote_addr}")
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"job_id": job.id,
|
|
"status": job.status,
|
|
"started_at": job.start_at.isoformat(),
|
|
"end_at": job.end_at.isoformat()
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Starten des Jobs mit OTP: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/notifications', methods=['GET'])
|
|
@login_required
|
|
def api_get_notifications():
|
|
"""Benachrichtigungen für den aktuellen Benutzer abrufen."""
|
|
try:
|
|
# Zeitstempel für Filter (nur neue Benachrichtigungen)
|
|
since = request.args.get('since')
|
|
if since:
|
|
try:
|
|
since_date = datetime.fromisoformat(since)
|
|
except ValueError:
|
|
return jsonify({"error": "Ungültiges Datumsformat"}), 400
|
|
else:
|
|
since_date = None
|
|
|
|
with get_cached_session() as db_session:
|
|
query = db_session.query(Notification).filter_by(
|
|
user_id=current_user.id,
|
|
read=False
|
|
)
|
|
|
|
if since_date:
|
|
query = query.filter(Notification.created_at > since_date)
|
|
|
|
notifications = query.order_by(desc(Notification.created_at)).all()
|
|
|
|
return jsonify({
|
|
"count": len(notifications),
|
|
"notifications": [n.to_dict() for n in notifications]
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Abrufen der Benachrichtigungen: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/notifications/<int:notification_id>/read', methods=['POST'])
|
|
@login_required
|
|
def api_mark_notification_read(notification_id):
|
|
"""Benachrichtigung als gelesen markieren."""
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
notification = db_session.query(Notification).filter_by(
|
|
id=notification_id,
|
|
user_id=current_user.id
|
|
).first()
|
|
|
|
if not notification:
|
|
return jsonify({"error": "Benachrichtigung nicht gefunden"}), 404
|
|
|
|
notification.read = True
|
|
db_session.commit()
|
|
|
|
return jsonify({"success": True})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Markieren der Benachrichtigung als gelesen: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/admin/requests', methods=['GET'])
|
|
@approver_required
|
|
def api_get_all_requests():
|
|
"""Alle Gastanfragen für Admins abrufen."""
|
|
try:
|
|
# Filter-Parameter
|
|
status_filter = request.args.get('status', 'all') # all, pending, approved, denied
|
|
limit = int(request.args.get('limit', 50))
|
|
offset = int(request.args.get('offset', 0))
|
|
|
|
with get_cached_session() as db_session:
|
|
# Query mit eager loading
|
|
query = db_session.query(GuestRequest).options(
|
|
joinedload(GuestRequest.printer),
|
|
joinedload(GuestRequest.job),
|
|
joinedload(GuestRequest.processed_by_user)
|
|
)
|
|
|
|
# Status-Filter anwenden
|
|
if status_filter != 'all':
|
|
query = query.filter(GuestRequest.status == status_filter)
|
|
|
|
# Sortierung: Pending zuerst, dann nach Erstellungsdatum
|
|
query = query.order_by(
|
|
desc(GuestRequest.status == 'pending'),
|
|
desc(GuestRequest.created_at)
|
|
)
|
|
|
|
# Pagination
|
|
total_count = query.count()
|
|
requests = query.offset(offset).limit(limit).all()
|
|
|
|
# Daten für Admin aufbereiten
|
|
admin_requests = []
|
|
for req in requests:
|
|
request_data = req.to_dict()
|
|
|
|
# Zusätzliche Admin-Informationen
|
|
request_data.update({
|
|
"can_be_processed": req.status == "pending",
|
|
"is_overdue": (
|
|
req.status == "approved" and
|
|
req.job and
|
|
req.job.end_at < datetime.now()
|
|
) if req.job else False,
|
|
"time_since_creation": (datetime.now() - req.created_at).total_seconds() / 3600 if req.created_at else 0 # Stunden
|
|
})
|
|
|
|
admin_requests.append(request_data)
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"requests": admin_requests,
|
|
"pagination": {
|
|
"total": total_count,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"has_more": (offset + limit) < total_count
|
|
},
|
|
"stats": {
|
|
"total": total_count,
|
|
"pending": db_session.query(GuestRequest).filter_by(status='pending').count(),
|
|
"approved": db_session.query(GuestRequest).filter_by(status='approved').count(),
|
|
"denied": db_session.query(GuestRequest).filter_by(status='denied').count()
|
|
}
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Abrufen der Admin-Gastanfragen: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/admin/requests/<int:request_id>', methods=['GET'])
|
|
@approver_required
|
|
def api_get_request_details(request_id):
|
|
"""Detaillierte Informationen zu einer Gastanfrage für Admins abrufen."""
|
|
try:
|
|
with get_cached_session() as db_session:
|
|
guest_request = db_session.query(GuestRequest).options(
|
|
joinedload(GuestRequest.printer),
|
|
joinedload(GuestRequest.job),
|
|
joinedload(GuestRequest.processed_by_user)
|
|
).filter_by(id=request_id).first()
|
|
|
|
if not guest_request:
|
|
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
|
|
|
# Vollständige Admin-Informationen
|
|
request_data = guest_request.to_dict()
|
|
|
|
# Verfügbare Drucker für Zuweisung
|
|
available_printers = db_session.query(Printer).filter_by(active=True).all()
|
|
request_data["available_printers"] = [p.to_dict() for p in available_printers]
|
|
|
|
# Job-Historie falls vorhanden
|
|
if guest_request.job:
|
|
job_data = guest_request.job.to_dict()
|
|
job_data["is_active"] = guest_request.job.status in ["scheduled", "running"]
|
|
job_data["is_overdue"] = guest_request.job.end_at < datetime.now() if guest_request.job.end_at else False
|
|
request_data["job_details"] = job_data
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"request": request_data
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Abrufen der Gastanfrage-Details: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
@guest_blueprint.route('/api/admin/requests/<int:request_id>/update', methods=['PUT'])
|
|
@approver_required
|
|
def api_update_request(request_id):
|
|
"""Gastanfrage aktualisieren (nur für Admins)."""
|
|
try:
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({"error": "Keine Daten erhalten"}), 400
|
|
|
|
with get_cached_session() as db_session:
|
|
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
|
|
if not guest_request:
|
|
return jsonify({"error": "Anfrage nicht gefunden"}), 404
|
|
|
|
# Erlaubte Felder für Updates
|
|
allowed_fields = ['printer_id', 'duration_min', 'approval_notes', 'rejection_reason']
|
|
changes_made = False
|
|
|
|
for field in allowed_fields:
|
|
if field in data:
|
|
if field == 'printer_id' and data[field]:
|
|
# Drucker validieren
|
|
printer = db_session.query(Printer).filter_by(id=data[field], active=True).first()
|
|
if not printer:
|
|
return jsonify({"error": "Ungültiger Drucker ausgewählt"}), 400
|
|
|
|
setattr(guest_request, field, data[field])
|
|
changes_made = True
|
|
|
|
if changes_made:
|
|
guest_request.processed_by = current_user.id
|
|
guest_request.processed_at = datetime.now()
|
|
db_session.commit()
|
|
|
|
logger.info(f"Gastanfrage {request_id} aktualisiert von Admin {current_user.id}")
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"message": "Anfrage erfolgreich aktualisiert",
|
|
"updated_by": current_user.name,
|
|
"updated_at": guest_request.processed_at.isoformat()
|
|
})
|
|
else:
|
|
return jsonify({"error": "Keine Änderungen vorgenommen"}), 400
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Aktualisieren der Gastanfrage: {str(e)}")
|
|
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
|
|
|
# Admin-Routen
|
|
@guest_blueprint.route('/admin/requests', methods=['GET'])
|
|
@approver_required
|
|
def admin_requests_management():
|
|
"""Admin-Oberfläche für die Verwaltung von Gastanfragen."""
|
|
return render_template('admin_guest_requests.html') |