Compare commits

..

5 Commits

Author SHA1 Message Date
Aiirondev_dev acfb633cda changes to the Scanning processing
Release Inventarsystem / release-docker (push) Successful in 2m27s
2026-09-14 09:40:59 +02:00
Aiirondev_dev 23dfb7d719 changes to the authentification
Release Inventarsystem / release-docker (push) Successful in 2m27s
2026-09-14 09:23:09 +02:00
Aiirondev_dev 6b9cf8a024 changes to th the authentification for the page
Release Inventarsystem / release-docker (push) Successful in 2m25s
2026-09-14 09:13:43 +02:00
Aiirondev_dev 69bb02b7dc changes to the fuplicate import
Release Inventarsystem / release-docker (push) Successful in 13m24s
2026-09-14 09:03:02 +02:00
Aiirondev_dev 6a3a5d6373 Changes to the library scanning function
Release Inventarsystem / release-docker (push) Successful in 3m11s
2026-09-10 19:42:32 +02:00
3 changed files with 54 additions and 11 deletions
+10 -4
View File
@@ -17,7 +17,6 @@ Features:
- Booking and reservation of items
"""
from random import random
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
from werkzeug.utils import secure_filename
from werkzeug.middleware.proxy_fix import ProxyFix
@@ -2189,7 +2188,7 @@ def _upload_student_cards_excel():
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_user', False):
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library_view'))
@@ -4277,6 +4276,7 @@ def api_library_scan_action():
- scan student card id
- scan media code (ISBN/Code_4)
- borrow if available, otherwise return (toggle behavior)
- action='borrow' can be used by scan modes that must never return an item
"""
if 'username' not in session:
return jsonify({'ok': False, 'message': 'Nicht angemeldet.'}), 401
@@ -4286,6 +4286,7 @@ def api_library_scan_action():
return jsonify({'ok': False, 'message': 'Schülerausweis-Modul ist deaktiviert.'}), 403
payload = request.get_json(silent=True) or {}
requested_action = str(payload.get('action') or 'toggle').strip().lower()
student_card_id = us.normalize_student_card_id(payload.get('student_card_id') or payload.get('card_id'))
item_code_raw = str(payload.get('item_code') or payload.get('code') or '').strip()
duration_raw = str(payload.get('borrow_duration_days') or '').strip()
@@ -4389,6 +4390,12 @@ def api_library_scan_action():
'message': f"{item_doc.get('Name', 'Medium')} wurde ausgeliehen."
}), 200
if requested_action == 'borrow':
return jsonify({
'ok': False,
'message': f"Medium ist bereits ausgeliehen und wurde nicht verändert.",
}), 409
# Toggle back: item is currently borrowed -> return
current_borrower = str(item_doc.get('User') or '').strip()
current_permissions = us.get_effective_permissions(session['username'])
@@ -5162,7 +5169,6 @@ def student_card_class_barcode_download():
"""
Download PDF with student card barcodes filtered by a specific class from dropdown.
"""
from flask import request, session, redirect, url_for, send_file, flash
if 'username' not in session:
return redirect(url_for('login'))
@@ -5356,7 +5362,7 @@ def student_card_single_barcode_download(card_id):
return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
if not current_permissions['actions'].get('can_manage_settings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library_view'))
if not cfg.MODULES.is_enabled('student_cards'):
+1 -1
View File
@@ -1376,7 +1376,7 @@
<li><a class="dropdown-item" href="{{ url_for('mahnungen_admin') }}">Mahnungen</a></li>
{% endif %}
{% if student_cards_module_enabled %}
{% if current_permissions.actions.get('can_manage_users', False) %}
{% if current_permissions.actions.get('can_manage_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('student_cards_admin') }}">Bibliotheksausweis</a></li>
{% endif %}
{% endif %}
+43 -6
View File
@@ -1414,7 +1414,8 @@
},
body: JSON.stringify({
student_card_id: activeStudentCardId,
item_code: scannedCode
item_code: scannedCode,
action: 'borrow'
})
});
@@ -1425,7 +1426,10 @@
updatePhysicalScannerModal(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
} else if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok', {
actionLabel: 'Mit nächstem Ausweis starten',
onAction: startNextStudentCardScan
});
updatePhysicalScannerModal('Ausleihe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
@@ -1491,7 +1495,10 @@
if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok', {
actionLabel: 'Mit nächstem Ausweis starten',
onAction: startNextStudentCardScan
});
updatePhysicalScannerModal('Ausleihe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
@@ -1657,21 +1664,51 @@
if (kind) el.classList.add(kind);
}
function showSmallConfirm(message, kind='ok') {
function showSmallConfirm(message, kind='ok', action = null) {
// Append the small helper text in German
const helper = 'Sie können fortfahren. Dies ist nur eine kleine Benachrichtigung.';
const el = document.createElement('div');
el.className = `small-popup ${kind === 'error' ? 'error' : 'ok'}`;
el.innerHTML = `<div>${escapeHtml(String(message || ''))}</div><div style="opacity:0.9; margin-left:8px; font-size:0.85em;">${helper}</div><div class="close-x">&times;</div>`;
el.innerHTML = `<div>${escapeHtml(String(message || ''))}</div><div style="opacity:0.9; margin-left:8px; font-size:0.85em;">${helper}</div>`;
if (action && typeof action.onAction === 'function') {
const actionButton = document.createElement('button');
actionButton.type = 'button';
actionButton.textContent = action.actionLabel || 'Weiter';
actionButton.style.marginLeft = '8px';
actionButton.style.padding = '6px 10px';
actionButton.style.cursor = 'pointer';
actionButton.addEventListener('click', () => {
if (el.parentNode) el.parentNode.removeChild(el);
action.onAction();
});
el.appendChild(actionButton);
}
const closeButton = document.createElement('div');
closeButton.className = 'close-x';
closeButton.innerHTML = '&times;';
el.appendChild(closeButton);
document.body.appendChild(el);
// close handler
el.querySelector('.close-x').addEventListener('click', () => {
closeButton.addEventListener('click', () => {
if (el && el.parentNode) el.parentNode.removeChild(el);
});
// auto remove after 3 seconds
setTimeout(() => { try { if (el && el.parentNode) el.parentNode.removeChild(el); } catch(e){} }, 3000);
}
async function startNextStudentCardScan() {
setActiveStudentCard('');
setScanStatus('Bereit für den nächsten Ausweis.', 'warn');
updatePhysicalScannerModal('Bereit für den nächsten Ausweis.', 'warn');
const mode = document.getElementById('scanModeSelect')?.value;
if (!physicalScannerModalOpen && (mode === 'quick_toggle' || mode === 'continuous') && !scannerRunning) {
await startScanner();
}
}
function setActiveStudentCard(cardId) {
activeStudentCardId = (cardId || '').trim().toUpperCase();
const input = document.getElementById('activeStudentCard');