This commit is contained in:
+65
-24
@@ -1381,11 +1381,31 @@ def update_appointment_statuses():
|
|||||||
activation_user = str(appointment.get('User') or '').strip()
|
activation_user = str(appointment.get('User') or '').strip()
|
||||||
activation_item_name = str(appointment.get('Item') or 'Termin')
|
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
|
# Aktuellen Status bestimmen
|
||||||
new_status = au.get_current_status(appointment, log_changes=True, user='scheduler')
|
new_status = au.get_current_status(appointment, log_changes=True, user='scheduler')
|
||||||
|
|
||||||
# Wenn sich der Status geändert hat, aktualisiere in der Datenbank
|
# 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 = {}
|
extra_fields = {}
|
||||||
|
|
||||||
# --- Conflict resolver: planned → active transition ---
|
# --- Conflict resolver: planned → active transition ---
|
||||||
@@ -1483,15 +1503,22 @@ def update_appointment_statuses():
|
|||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# Mahnlauf für Bibliotheksartikel (Prüfung auf Überfälligkeit)
|
# 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
|
appt = appointment # Verwende das aktuelle Dokument aus der Schleife
|
||||||
if appt.get('Status') != 'active':
|
if appt.get('Status') != 'active':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
due_date_obj = appt.get('DueDate')
|
due_date_obj = appt.get('DueDate') or appt.get('End')
|
||||||
if not due_date_obj:
|
if not due_date_obj:
|
||||||
continue
|
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
|
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
|
days_overdue = (current_time_naive - due_date_naive).days
|
||||||
|
|
||||||
@@ -1566,13 +1593,6 @@ def update_appointment_statuses():
|
|||||||
except Exception as n_err:
|
except Exception as n_err:
|
||||||
app.logger.warning(f"Fehler beim Erstellen der Admin-Notif (Stufe 2): {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.")
|
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
|
# STUFE 1: >= 14 Tage überfällig -> Stufe 1 setzen & Admins benachrichtigen
|
||||||
@@ -1597,13 +1617,6 @@ def update_appointment_statuses():
|
|||||||
except Exception as n_err:
|
except Exception as n_err:
|
||||||
app.logger.warning(f"Fehler beim Erstellen der Admin-Notif (Stufe 1): {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.")
|
app.logger.info(f"Mahnstufe 1 für Schülerausweis '{student_name}' ({target_ausweis_id}) gesetzt.")
|
||||||
|
|
||||||
if updated_count > 0:
|
if updated_count > 0:
|
||||||
@@ -3660,7 +3673,10 @@ def mahnungen_admin():
|
|||||||
|
|
||||||
overdue_records = list(ausleihungen_col.find({
|
overdue_records = list(ausleihungen_col.find({
|
||||||
'Status': 'active',
|
'Status': 'active',
|
||||||
'DueDate': {'$lt': current_time}
|
'$or': [
|
||||||
|
{'DueDate': {'$lt': current_time}},
|
||||||
|
{'DueDate': {'$exists': False}, 'End': {'$lt': current_time}},
|
||||||
|
]
|
||||||
}).sort('DueDate', 1))
|
}).sort('DueDate', 1))
|
||||||
|
|
||||||
overdue_list = []
|
overdue_list = []
|
||||||
@@ -3705,7 +3721,7 @@ def mahnungen_admin():
|
|||||||
student_email = student_card.get('email') or student_card.get('Email', '')
|
student_email = student_card.get('email') or student_card.get('Email', '')
|
||||||
is_blocked = student_card.get('is_blocked', False)
|
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:
|
if due_date_obj:
|
||||||
due_date_naive = due_date_obj.replace(tzinfo=None) if due_date_obj.tzinfo else 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
|
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)
|
due_date = now + datetime.timedelta(days=borrow_duration_days)
|
||||||
|
|
||||||
it.update_item_status(item_id, False, borrower_name)
|
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(
|
_append_audit_event_standalone(
|
||||||
event_type='ausleihung_borrowed',
|
event_type='ausleihung_borrowed',
|
||||||
@@ -7541,7 +7563,13 @@ def ausleihen(id):
|
|||||||
for unit in selected_units:
|
for unit in selected_units:
|
||||||
unit_id = str(unit.get('_id'))
|
unit_id = str(unit.get('_id'))
|
||||||
it.update_item_status(unit_id, False, effective_borrower)
|
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(
|
_append_audit_event_standalone(
|
||||||
event_type='ausleihung_returned',
|
event_type='ausleihung_returned',
|
||||||
@@ -7632,7 +7660,13 @@ def ausleihen(id):
|
|||||||
if total_exemplare <= 1:
|
if total_exemplare <= 1:
|
||||||
it.update_item_status(id, False, effective_borrower)
|
it.update_item_status(id, False, effective_borrower)
|
||||||
start_date = datetime.datetime.now(ZoneInfo("Europe/Berlin"))
|
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(
|
_append_audit_event_standalone(
|
||||||
event_type='ausleihung_returned',
|
event_type='ausleihung_returned',
|
||||||
payload={
|
payload={
|
||||||
@@ -7677,10 +7711,17 @@ def ausleihen(id):
|
|||||||
start_date = datetime.datetime.now(ZoneInfo("Europe/Berlin"))
|
start_date = datetime.datetime.now(ZoneInfo("Europe/Berlin"))
|
||||||
for exemplar in new_borrowed_exemplars:
|
for exemplar in new_borrowed_exemplars:
|
||||||
exemplar_id = f"{id}_{exemplar['number']}"
|
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,
|
'parent_id': id,
|
||||||
'exemplar_number': exemplar['number']
|
'exemplar_number': exemplar['number']
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
_append_audit_event_standalone(
|
_append_audit_event_standalone(
|
||||||
event_type='ausleihung_returned',
|
event_type='ausleihung_returned',
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ function submitEmailMahnung() {
|
|||||||
const loanId = document.getElementById('modalLoanId').value;
|
const loanId = document.getElementById('modalLoanId').value;
|
||||||
const email = document.getElementById('modalEmail').value;
|
const email = document.getElementById('modalEmail').value;
|
||||||
const message = document.getElementById('modalMessage').value;
|
const message = document.getElementById('modalMessage').value;
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
alert('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
|
alert('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
|
||||||
@@ -264,7 +265,7 @@ function submitEmailMahnung() {
|
|||||||
|
|
||||||
fetch('/mahnungen_send_email', {
|
fetch('/mahnungen_send_email', {
|
||||||
method: 'POST',
|
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 })
|
body: JSON.stringify({ loan_id: loanId, email: email, message: message })
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
@@ -289,9 +290,11 @@ function resetMahnung(loanId, studentName) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||||
|
|
||||||
fetch('/mahnungen_reset', {
|
fetch('/mahnungen_reset', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken },
|
||||||
body: JSON.stringify({ loan_id: loanId })
|
body: JSON.stringify({ loan_id: loanId })
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
|||||||
Reference in New Issue
Block a user