Compare commits

...

3 Commits

2 changed files with 160 additions and 115 deletions
+146 -112
View File
@@ -10248,69 +10248,58 @@ def get_period_times(booking_date, period_num):
"""---------------------------------------------------------Borrowing-----------------------------------------------------------------"""
@app.route('/my_borrowed_items')
def my_borrowed_items():
"""
Zeigt alle vom aktuellen Benutzer ausgeliehenen und geplanten Objekte an.
Returns:
Response: Gerendertes Template mit den ausgeliehenen und geplanten Objekten des Benutzers
"""
if 'username' not in session:
flash('Bitte melden Sie sich an, um Ihre ausgeliehenen Objekte anzuzeigen', 'error')
return redirect(url_for('login', next=request.path))
username = session['username']
client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB]
items_collection = db.items
ausleihungen_collection = db.ausleihungen
# Get current time for comparison
current_time = datetime.datetime.now()
# Get items currently borrowed by the user (where Verfuegbar=false and User=username)
borrowed_items = list(items_collection.find({'Verfuegbar': False, 'User': encrypt_text(username)}))
# Get active and planned ausleihungen for the user
active_ausleihungen = list(ausleihungen_collection.find({
'User': encrypt_text(username),
'Status': 'active'
items_collection = db['items']
ausleihungen_collection = db['ausleihungen']
all_ausleihungen = list(ausleihungen_collection.find({
'Status': {'$in': ['active', 'planned']}
}))
planned_ausleihungen = list(ausleihungen_collection.find({
'User': encrypt_text(username),
'Status': 'planned'
}))
# Process items
active_items = []
planned_items = []
processed_item_ids = set() # Keep track of processed item IDs to avoid duplicates
# First, process items that are directly marked as borrowed by the user
for item in borrowed_items:
# Convert ObjectId to string for template
item['_id'] = str(item['_id'])
active_items.append(item)
processed_item_ids.add(item['_id'])
# Process active appointments
for appointment in active_ausleihungen:
# Get the item ID from the appointment
processed_item_ids = set()
for appointment in all_ausleihungen:
raw_user = appointment.get('User', '')
try:
decrypted_user = decrypt_text(raw_user) if raw_user else ''
except Exception as e:
app.logger.error(f"Entschlüsselungsfehler: {e}")
decrypted_user = ''
if decrypted_user != username:
continue
item_id = appointment.get('Item')
if not item_id or str(item_id) in processed_item_ids:
continue # Skip if we already processed this item or no item ID
# Get item details
item_obj = items_collection.find_one({'_id': ObjectId(item_id)})
if not item_id:
continue
try:
if isinstance(item_id, str):
query_id = ObjectId(item_id)
else:
query_id = item_id
item_obj = items_collection.find_one({'_id': query_id})
except Exception:
item_obj = None
if item_obj:
# Convert ObjectId to string for template
item_obj['_id'] = str(item_obj['_id'])
# Add appointment data
item_obj['AppointmentData'] = {
'id': str(appointment['_id']),
'start': appointment.get('Start'),
@@ -10319,54 +10308,43 @@ def my_borrowed_items():
'period': appointment.get('Period'),
'status': appointment.get('VerifiedStatus', appointment.get('Status')),
}
# Mark that this item is part of an active appointment
item_obj['ActiveAppointment'] = True
# Add to the list only if not already there
if str(item_obj['_id']) not in processed_item_ids:
active_items.append(item_obj)
processed_item_ids.add(str(item_obj['_id']))
# Process planned appointments
for appointment in planned_ausleihungen:
item_id = appointment.get('Item')
if not item_id:
continue
item_obj = items_collection.find_one({'_id': ObjectId(item_id)})
if item_obj:
item_obj['_id'] = str(item_obj['_id'])
# Add appointment data
item_obj['AppointmentData'] = {
'id': str(appointment['_id']),
'start': appointment.get('Start'),
'end': appointment.get('End'),
'notes': appointment.get('Notes'),
'period': appointment.get('Period'),
'status': appointment.get('Status'),
}
planned_items.append(item_obj)
status = appointment.get('Status')
if status == 'active':
item_obj['ActiveAppointment'] = True
if str(item_obj['_id']) not in processed_item_ids:
active_items.append(item_obj)
processed_item_ids.add(str(item_obj['_id']))
elif status == 'planned':
planned_items.append(item_obj)
all_borrowed_items = list(items_collection.find({'Verfuegbar': False}))
for item in all_borrowed_items:
raw_item_user = item.get('User', '')
try:
dec_item_user = decrypt_text(raw_item_user) if raw_item_user else ''
except Exception:
dec_item_user = ''
if dec_item_user == username and str(item['_id']) not in processed_item_ids:
item['_id'] = str(item['_id'])
item['ActiveAppointment'] = True
item['AppointmentData'] = {'status': 'active (no document)'}
active_items.append(item)
processed_item_ids.add(item['_id'])
client.close()
# DEBUG: Log what we're passing to the template
app.logger.info(f"Passing {len(active_items)} active items and {len(planned_items)} planned items to template")
if planned_items:
for i, item in enumerate(planned_items):
app.logger.info(f"Planned item {i+1}: {item['Name']}, Appointment ID: {item['AppointmentData']['id']}")
# DEBUG Logging
app.logger.info(
f"Passing {len(active_items)} active items and {len(planned_items)} planned items to template for user {username}")
return render_template(
'my_borrowed_items.html',
items=active_items,
planned_items=planned_items
)
@app.route('/api/push/vapid-key', methods=['GET'])
def get_vapid_key():
"""
@@ -10606,53 +10584,109 @@ def notifications_unread_status():
client.close()
@app.route('/admin/damaged_items')
@app.route('/admin/damaged_items')
def admin_damaged_items():
"""Admin-Übersicht aller aktiven und vergangenen Ausleihen."""
"""Admin-Übersicht aller defekten, reparierten oder zerstörten Items."""
if 'username' not in session:
flash('Administratorrechte erforderlich.', 'error')
flash('Anmeldung erforderlich.', 'error')
return redirect(url_for('login'))
from modules.inventarsystem.data_protection import decrypt_text
from bson.objectid import ObjectId
client = None
try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB]
ausleihungen_col = db['ausleihungen']
items_col = db['items']
ausleihungen_col = db['ausleihungen']
ausleihungen = list(ausleihungen_col.find().sort('Start', -1))
# 1. Alle Items suchen, die einen Schaden haben, repariert oder zerstört sind
query = {
'$or': [
{'HasDamage': True},
{'DamageReports': {'$exists': True, '$not': {'$size': 0}}},
{'Condition': {'$in': ['destroyed', 'broken', 'damaged', 'repaired', 'fixed']}}
]
}
for record in ausleihungen:
raw_user = record.get('User', '')
if raw_user:
record['User'] = decrypt_text(raw_user)
raw_items = list(items_col.find(query).sort('LastUpdated', -1))
damaged_items = []
item_id = record.get('Item')
if item_id:
try:
item_doc = items_col.find_one({'_id': ObjectId(item_id)})
if item_doc:
if item_doc.get('User'):
item_doc['User'] = decrypt_text(item_doc['User'])
record['ItemDetails'] = item_doc
except Exception as e:
app.logger.warning(f"Konnte Item {item_id} für Ausleihe {record.get('_id')} nicht laden: {e}")
for item in raw_items:
# Dynamischen Status ermitteln (wie zuvor besprochen)
condition_value = str(item.get('Condition', '')).strip().lower()
has_damage_flag = bool(item.get('HasDamage'))
has_reports = bool(item.get('DamageReports'))
if condition_value == 'destroyed':
status_text = 'Komplett zerstört'
status_color = 'danger' # Rot
elif condition_value in ['repaired', 'fixed'] or (has_reports and not has_damage_flag):
status_text = 'Bereits repariert'
status_color = 'success' # Grün
else:
status_text = 'Aktuelles Problem'
status_color = 'warning' # Gelb
# Schadensberichte analysieren
damage_reports = item.get('DamageReports', [])
latest_report = damage_reports[-1] if damage_reports else {}
# Prüfen, ob es eine aktive oder geplante Ausleihe für dieses Item gibt
active_borrow = ausleihungen_col.find_one({
'Item': item.get('_id'), # Suche mit ObjectId
'Status': {'$in': ['active', 'planned']}
})
# Falls die ID in der Ausleihen-Collection als String hinterlegt ist (Fallback)
if not active_borrow:
active_borrow = ausleihungen_col.find_one({
'Item': str(item.get('_id')),
'Status': {'$in': ['active', 'planned']}
})
# Benutzernamen der Ausleihe entschlüsseln
if active_borrow and active_borrow.get('User'):
active_borrow['User'] = decrypt_text(active_borrow['User'])
# Direkt am Item hinterlegten Nutzer entschlüsseln
item_user = decrypt_text(item.get('User')) if item.get('User') else ''
# Datenstruktur exakt für dein Jinja2 Template aufbauen
damaged_items.append({
'id': str(item['_id']),
'name': item.get('Name', ''),
'code': item.get('Code_4', ''),
'item_type': item.get('ItemType', ''),
'author': item.get('Author', item.get('Autor', '')), # Deckt beide Schreibweisen ab
'isbn': item.get('ISBN', ''),
'borrow_user': item_user,
'damage_count': len(damage_reports),
'condition': item.get('Condition', '-'),
'available': item.get('Verfuegbar', False),
# Letzte Meldung
'latest_damage_description': latest_report.get('description', ''),
'latest_damage_by': latest_report.get('reported_by', ''),
'latest_damage_at': latest_report.get('reported_at', ''),
# Status & Ausleihe
'status_text': status_text,
'status_color': status_color,
'active_borrow': active_borrow
})
return render_template(
'admin_damaged_items.html',
ausleihungen=ausleihungen,
'admin_damaged_items.html',
damaged_items=damaged_items,
library_module_enabled=cfg.MODULES.is_enabled('library'),
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
mail_module_enabled=cfg.MODULES.is_enabled('mail')
)
except Exception as exc:
app.logger.error(f"Fehler beim Laden der Ausleihen-Verwaltung: {exc}")
flash('Fehler beim Laden der Ausleihen-Übersicht.', 'error')
app.logger.error(f"Fehler beim Laden der defekten Items: {exc}")
flash('Fehler beim Laden der Übersicht.', 'error')
return redirect(url_for('home_admin'))
finally:
if client:
+14 -3
View File
@@ -7,7 +7,7 @@
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:16px;">
<div>
<h1 style="margin:0;">Defekte Items</h1>
<p style="margin:6px 0 0; color:#64748b;">Eigenes Verwaltungsfenster fuer gemeldete Defekte mit schneller Reparatur-Funktion.</p>
<p style="margin:6px 0 0; color:#64748b;">Eigenes Verwaltungsfenster für gemeldete Defekte mit schneller Reparatur-Funktion.</p>
</div>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<a class="btn btn-outline-secondary" href="{{ url_for('admin_borrowings') }}">Ausleihen</a>
@@ -42,7 +42,13 @@
{% if item.isbn %}<div style="font-size:0.82rem; color:#64748b;">ISBN: {{ item.isbn }}</div>{% endif %}
</td>
<td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;">
<div style="display:inline-block; padding:3px 9px; border-radius:999px; background:#fee2e2; color:#991b1b; font-size:0.75rem; font-weight:700;">Defekt</div>
<!-- HIER IST DIE ÄNDERUNG: Dynamischer Status-Badge -->
<div class="badge bg-{{ item.status_color }} rounded-pill" style="font-size:0.75rem; font-weight:700; padding:4px 10px;">
{{ item.status_text }}
</div>
<!-- ENDE DER ÄNDERUNG -->
<div style="font-size:0.84rem; color:#475569; margin-top:6px;">Meldungen: {{ item.damage_count }}</div>
<div style="font-size:0.84rem; color:#475569;">Condition: {{ item.condition or '-' }}</div>
<div style="font-size:0.84rem; color:#475569;">Verfuegbar: {{ 'Ja' if item.available else 'Nein' }}</div>
@@ -66,7 +72,12 @@
{% endif %}
</td>
<td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;">
<!-- Button wird ausgeblendet, falls das Item bereits repariert ist -->
{% if item.condition not in ['repaired', 'fixed'] %}
<button class="btn btn-success btn-sm" onclick="repairDamage('{{ item.id }}', this)">Als repariert markieren</button>
{% else %}
<span class="text-success" style="font-size: 0.85rem;"><i class="bi bi-check-circle"></i> Repariert</span>
{% endif %}
</td>
</tr>
{% endfor %}
@@ -118,4 +129,4 @@ function repairDamage(itemId, button) {
});
}
</script>
{% endblock %}
{% endblock %}