feat(library): add damage history view alongside invoices for items
Release Inventarsystem / release-docker (push) Successful in 2m14s
Release Inventarsystem / release-docker (push) Successful in 2m14s
This commit is contained in:
+76
-3
@@ -9335,9 +9335,10 @@ def resolve_repaired_item_funct(item_id, action, new_code_4="", current_user="ad
|
||||
print(f"Error resolving repair for item {item_id}: {e}")
|
||||
return False, "Ein Datenbankfehler ist aufgetreten."
|
||||
|
||||
|
||||
@app.route('/admin/library/items/<item_id>/invoices', methods=['GET'])
|
||||
def library_item_invoices(item_id):
|
||||
"""Show all stored invoices for one specific library item."""
|
||||
"""Show all stored invoices and damages for one specific library item."""
|
||||
if 'username' not in session:
|
||||
flash(
|
||||
'Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!',
|
||||
@@ -9373,6 +9374,9 @@ def library_item_invoices(item_id):
|
||||
item_id_obj = item_doc.get('_id')
|
||||
item_id_str = str(item_id_obj)
|
||||
|
||||
# ==========================================
|
||||
# 1. RECHNUNGSHISTORIE (Bestehender Code)
|
||||
# ==========================================
|
||||
borrow_docs = list(ausleihungen.find(
|
||||
{
|
||||
'Item': {'$in': [item_id_str, item_id_obj]},
|
||||
@@ -9419,6 +9423,74 @@ def library_item_invoices(item_id):
|
||||
'invoice_paid_by': invoice_data.get('paid_by', ''),
|
||||
})
|
||||
|
||||
# ==========================================
|
||||
# 2. SCHADENSHISTORIE (Neuer Code)
|
||||
# ==========================================
|
||||
raw_damages = []
|
||||
|
||||
# A: Schäden, die manuell am Element (Item-Dokument) hinterlegt wurden
|
||||
item_damages = item_doc.get('Damages', [])
|
||||
if not isinstance(item_damages, list): item_damages = []
|
||||
# Fallback falls sie klein geschrieben wurden
|
||||
if isinstance(item_doc.get('damages', []), list):
|
||||
item_damages.extend(item_doc.get('damages', []))
|
||||
|
||||
for d in item_damages:
|
||||
if not isinstance(d, dict): continue
|
||||
raw_damages.append({
|
||||
'raw_date': d.get('date') or d.get('created_at'),
|
||||
'source': 'Manuell / Element',
|
||||
'user': d.get('reported_by') or d.get('user') or 'System/Admin',
|
||||
'description': d.get('description') or d.get('reason') or 'Keine Beschreibung',
|
||||
'status': d.get('status', 'Unbekannt'),
|
||||
'borrow_id': None
|
||||
})
|
||||
|
||||
# B: Schäden, die in Ausleih-Dokumenten vermerkt sind
|
||||
damage_borrow_docs = list(ausleihungen.find(
|
||||
{
|
||||
'Item': {'$in': [item_id_str, item_id_obj]},
|
||||
'$or': [
|
||||
{'Damage': {'$exists': True, '$ne': ''}},
|
||||
{'DamageData': {'$exists': True, '$ne': {}}},
|
||||
{'Condition': {'$exists': True, '$ne': ''}} # Falls Zustand gemeldet wurde
|
||||
]
|
||||
}
|
||||
))
|
||||
|
||||
for b in damage_borrow_docs:
|
||||
borrow_user = decrypt_text(b.get('User', '')) if b.get('User') else '—'
|
||||
|
||||
d_data = b.get('DamageData') or {}
|
||||
if not isinstance(d_data, dict): d_data = {}
|
||||
|
||||
desc = d_data.get('description') or b.get('Damage') or b.get('Condition')
|
||||
|
||||
if desc and str(desc).strip():
|
||||
raw_damages.append({
|
||||
'raw_date': d_data.get('reported_at') or b.get('End') or b.get('Start'),
|
||||
'source': f"Ausleihe",
|
||||
'user': borrow_user,
|
||||
'description': desc,
|
||||
'status': d_data.get('status', 'Gemeldet'),
|
||||
'borrow_id': str(b.get('_id'))
|
||||
})
|
||||
|
||||
# Daten chronologisch (neueste zuerst) sortieren
|
||||
def get_sort_key(entry):
|
||||
d = entry['raw_date']
|
||||
return d if isinstance(d, datetime.datetime) else datetime.datetime.min
|
||||
|
||||
raw_damages.sort(key=get_sort_key, reverse=True)
|
||||
|
||||
# Datums-Objekte für das Template formatieren
|
||||
damage_entries = []
|
||||
for entry in raw_damages:
|
||||
d_date = entry.pop('raw_date')
|
||||
entry['date'] = d_date.strftime('%d.%m.%Y %H:%M') if isinstance(d_date, datetime.datetime) else str(
|
||||
d_date or 'Unbekannt')
|
||||
damage_entries.append(entry)
|
||||
|
||||
return render_template(
|
||||
'library_item_invoices.html',
|
||||
item={
|
||||
@@ -9429,14 +9501,15 @@ def library_item_invoices(item_id):
|
||||
'isbn': item_doc.get('ISBN', ''),
|
||||
},
|
||||
invoices=entries,
|
||||
damages=damage_entries, # << NEU
|
||||
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 e:
|
||||
app.logger.error(f"Error loading invoice history for item {item_id}: {e}")
|
||||
flash('Fehler beim Laden der Rechnungshistorie.', 'error')
|
||||
app.logger.error(f"Error loading invoice/damage history for item {item_id}: {e}")
|
||||
flash('Fehler beim Laden der Rechnungs- oder Schadenshistorie.', 'error')
|
||||
return redirect(url_for('library_loans_admin'))
|
||||
finally:
|
||||
if client:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Rechnungen Element - {{ APP_VERSION }}{% endblock %}
|
||||
{% block title %}Rechnungs- & Schadenshistorie - {{ APP_VERSION }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
@@ -39,6 +39,14 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin: 36px 0 16px 0;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.invoice-card {
|
||||
background: var(--ui-surface);
|
||||
border: 1px solid #e2e8f0;
|
||||
@@ -115,7 +123,7 @@
|
||||
|
||||
<div class="invoice-history-shell">
|
||||
<div class="invoice-history-head">
|
||||
<h1>Rechnungshistorie pro Element</h1>
|
||||
<h1>Rechnungs- & Schadenshistorie pro Element</h1>
|
||||
<div class="head-meta">
|
||||
<span><strong>Element:</strong> {{ item.name or '—' }}</span>
|
||||
<span><strong>Code:</strong> {{ item.code or '—' }}</span>
|
||||
@@ -128,6 +136,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BEREICH: RECHNUNGEN -->
|
||||
<h2 class="section-header">Ausgestellte Rechnungen</h2>
|
||||
<div class="invoice-card">
|
||||
{% if invoices %}
|
||||
<table class="invoice-table">
|
||||
@@ -137,7 +147,7 @@
|
||||
<th>Betrag</th>
|
||||
<th>Ausleihe</th>
|
||||
<th>Status</th>
|
||||
<th>Schaden</th>
|
||||
<th>Schaden / Grund</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -184,5 +194,50 @@
|
||||
<div class="empty-state">Für dieses Element wurden noch keine Rechnungen gespeichert.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- BEREICH: SCHÄDEN -->
|
||||
<h2 class="section-header">Gemeldete Schäden</h2>
|
||||
<div class="invoice-card">
|
||||
{% if damages %}
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Quelle</th>
|
||||
<th>Nutzer / Verursacher</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in damages %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="muted">{{ row.date }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="mono">{{ row.source }}</div>
|
||||
{% if row.borrow_id %}
|
||||
<div class="muted" style="font-size: 0.8rem;">ID: <span title="{{ row.borrow_id }}">{{ row.borrow_id[:6] }}...</span></div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ row.user }}</td>
|
||||
<td>{{ row.description }}</td>
|
||||
<td>
|
||||
<!-- Einfache Logik, um bei reparierten Objekten einen grünen Badge zu zeigen -->
|
||||
{% if row.status|lower in ['repariert', 'erledigt', 'geschlossen', 'bezahlt'] %}
|
||||
<span class="badge-pill badge-completed">{{ row.status }}</span>
|
||||
{% else %}
|
||||
<span class="badge-pill badge-open">{{ row.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="empty-state">Für dieses Element wurden bislang keine Schäden erfasst.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user