From 09efedad69835d9101616e15cdaedb2afe62fee4 Mon Sep 17 00:00:00 2001 From: AIIrondev Date: Wed, 9 Sep 2026 20:36:08 +0200 Subject: [PATCH] changes to the Mahnungssystem --- Web/app.py | 89 ++++++++++++++++++++++-------- Web/templates/mahnungen_admin.html | 7 ++- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/Web/app.py b/Web/app.py index e5993c8..aa56580 100755 --- a/Web/app.py +++ b/Web/app.py @@ -1381,11 +1381,31 @@ def update_appointment_statuses(): activation_user = str(appointment.get('User') or '').strip() activation_item_name = str(appointment.get('Item') or 'Termin') + # is_library_item expects an item document, not the stored item id. + item_doc_for_status = None + item_id_for_status = appointment.get('Item') + if item_id_for_status: + item_lookup = [{'_id': item_id_for_status}] + try: + item_lookup.insert(0, {'_id': ObjectId(str(item_id_for_status))}) + except (InvalidId, TypeError): + pass + try: + item_doc_for_status = items_col.find_one( + {'$or': item_lookup}, + {'ItemType': 1, 'is_library': 1} + ) + except Exception as item_lookup_error: + app.logger.warning( + f"Could not resolve item type for appointment {appointment.get('_id')}: {item_lookup_error}" + ) + item_is_library = it.is_library_item(item_doc_for_status) + # Aktuellen Status bestimmen new_status = au.get_current_status(appointment, log_changes=True, user='scheduler') # Wenn sich der Status geändert hat, aktualisiere in der Datenbank - if new_status != old_status and not it.is_library_item(appointment.get('Item')): + if new_status != old_status and not item_is_library: extra_fields = {} # --- Conflict resolver: planned → active transition --- @@ -1483,15 +1503,22 @@ def update_appointment_statuses(): # ----------------------------------------------------------------- # Mahnlauf für Bibliotheksartikel (Prüfung auf Überfälligkeit) # ----------------------------------------------------------------- - elif it.is_library_item(appointment.get('Item')): + elif item_is_library: appt = appointment # Verwende das aktuelle Dokument aus der Schleife if appt.get('Status') != 'active': continue - due_date_obj = appt.get('DueDate') + due_date_obj = appt.get('DueDate') or appt.get('End') if not due_date_obj: continue + # Backfill the deadline for loans created before DueDate was stored. + if not appt.get('DueDate') and appt.get('End'): + ausleihungen.update_one( + {'_id': appt['_id'], 'DueDate': {'$exists': False}}, + {'$set': {'DueDate': due_date_obj}} + ) + due_date_naive = due_date_obj.replace(tzinfo=None) if due_date_obj.tzinfo else due_date_obj days_overdue = (current_time_naive - due_date_naive).days @@ -1566,13 +1593,6 @@ def update_appointment_statuses(): except Exception as n_err: app.logger.warning(f"Fehler beim Erstellen der Admin-Notif (Stufe 2): {n_err}") - # 2. Web-Push Notification für Admins - if 'create_return_reminders' in globals(): - try: - create_return_reminders(title=title, body=body, url=target_url) - except Exception as p_err: - app.logger.error(f"Fehler beim Senden der Admin-Push (Stufe 2): {p_err}") - app.logger.warning(f"Mahnstufe 2 & Ausweis-Sperre für Schülerausweis '{student_name}' ({target_ausweis_id}) gesetzt.") # STUFE 1: >= 14 Tage überfällig -> Stufe 1 setzen & Admins benachrichtigen @@ -1597,13 +1617,6 @@ def update_appointment_statuses(): except Exception as n_err: app.logger.warning(f"Fehler beim Erstellen der Admin-Notif (Stufe 1): {n_err}") - # 2. Web-Push Notification für Admins - if 'create_return_reminders' in globals(): - try: - create_return_reminders(title=title, body=body, url=target_url) - except Exception as p_err: - app.logger.error(f"Fehler beim Senden der Admin-Push (Stufe 1): {p_err}") - app.logger.info(f"Mahnstufe 1 für Schülerausweis '{student_name}' ({target_ausweis_id}) gesetzt.") if updated_count > 0: @@ -3660,7 +3673,10 @@ def mahnungen_admin(): overdue_records = list(ausleihungen_col.find({ 'Status': 'active', - 'DueDate': {'$lt': current_time} + '$or': [ + {'DueDate': {'$lt': current_time}}, + {'DueDate': {'$exists': False}, 'End': {'$lt': current_time}}, + ] }).sort('DueDate', 1)) overdue_list = [] @@ -3705,7 +3721,7 @@ def mahnungen_admin(): student_email = student_card.get('email') or student_card.get('Email', '') is_blocked = student_card.get('is_blocked', False) - due_date_obj = record.get('DueDate') + due_date_obj = record.get('DueDate') or record.get('End') if due_date_obj: due_date_naive = due_date_obj.replace(tzinfo=None) if due_date_obj.tzinfo else due_date_obj days_overdue = (current_time_naive - due_date_naive).days @@ -4342,7 +4358,13 @@ def api_library_scan_action(): due_date = now + datetime.timedelta(days=borrow_duration_days) it.update_item_status(item_id, False, borrower_name) - au.add_ausleihung(item_id, borrower_name, now, due_date) + au.add_ausleihung( + item_id, + borrower_name, + now, + end_date=due_date, + due_date=due_date, + ) _append_audit_event_standalone( event_type='ausleihung_borrowed', @@ -7541,7 +7563,13 @@ def ausleihen(id): for unit in selected_units: unit_id = str(unit.get('_id')) it.update_item_status(unit_id, False, effective_borrower) - au.add_ausleihung(unit_id, effective_borrower, start_date, end_date=end_date) + au.add_ausleihung( + unit_id, + effective_borrower, + start_date, + end_date=end_date, + due_date=end_date if is_library_item else None, + ) _append_audit_event_standalone( event_type='ausleihung_returned', @@ -7632,7 +7660,13 @@ def ausleihen(id): if total_exemplare <= 1: it.update_item_status(id, False, effective_borrower) start_date = datetime.datetime.now(ZoneInfo("Europe/Berlin")) - au.add_ausleihung(id, effective_borrower, start_date, end_date=end_date) + au.add_ausleihung( + id, + effective_borrower, + start_date, + end_date=end_date, + due_date=end_date if is_library_item else None, + ) _append_audit_event_standalone( event_type='ausleihung_returned', payload={ @@ -7677,10 +7711,17 @@ def ausleihen(id): start_date = datetime.datetime.now(ZoneInfo("Europe/Berlin")) for exemplar in new_borrowed_exemplars: exemplar_id = f"{id}_{exemplar['number']}" - au.add_ausleihung(exemplar_id, effective_borrower, start_date, end_date=end_date, exemplar_data={ + au.add_ausleihung( + exemplar_id, + effective_borrower, + start_date, + end_date=end_date, + due_date=end_date if is_library_item else None, + exemplar_data={ 'parent_id': id, 'exemplar_number': exemplar['number'] - }) + } + ) _append_audit_event_standalone( event_type='ausleihung_returned', diff --git a/Web/templates/mahnungen_admin.html b/Web/templates/mahnungen_admin.html index c1bbd1e..ade6281 100644 --- a/Web/templates/mahnungen_admin.html +++ b/Web/templates/mahnungen_admin.html @@ -256,6 +256,7 @@ function submitEmailMahnung() { const loanId = document.getElementById('modalLoanId').value; const email = document.getElementById('modalEmail').value; const message = document.getElementById('modalMessage').value; + const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || ''; if (!email) { alert('Bitte geben Sie eine gültige E-Mail-Adresse ein.'); @@ -264,7 +265,7 @@ function submitEmailMahnung() { fetch('/mahnungen_send_email', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ loan_id: loanId, email: email, message: message }) }) .then(response => response.json()) @@ -289,9 +290,11 @@ function resetMahnung(loanId, studentName) { return; } + const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || ''; + fetch('/mahnungen_reset', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ loan_id: loanId }) }) .then(response => response.json())