Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7727bfd94a | |||
| 55f34e0462 | |||
| 5a3a180b88 | |||
| 07ad592bd9 | |||
| fa299cde06 | |||
| bb2c3f52e8 | |||
| 58ea4e5716 |
+180
-83
@@ -3312,13 +3312,17 @@ def library_view():
|
||||
def library_loans_admin():
|
||||
"""Admin overview for library borrowings and damaged library items."""
|
||||
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!','error')
|
||||
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!',
|
||||
'error')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
current_permissions = us.get_effective_permissions(session['username'])
|
||||
|
||||
if not current_permissions['pages'].get('library_loans_admin', False):
|
||||
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!','error')
|
||||
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!',
|
||||
'error')
|
||||
return redirect(url_for('library_view'))
|
||||
|
||||
if not cfg.MODULES.is_enabled('library'):
|
||||
@@ -3337,7 +3341,6 @@ def library_loans_admin():
|
||||
return _format_money_value(value)
|
||||
|
||||
def safe_decrypt(val):
|
||||
"""Helper to decrypt strings that start with enc:: or standard encrypted strings."""
|
||||
if not val or not isinstance(val, str):
|
||||
return val or ''
|
||||
try:
|
||||
@@ -3345,19 +3348,48 @@ def library_loans_admin():
|
||||
except Exception:
|
||||
return val
|
||||
|
||||
# Hilfsfunktion zur Formatierung der Schadenshistorie
|
||||
def format_damage_history(reports, repairs):
|
||||
fmt_reports = []
|
||||
for r in (reports or []):
|
||||
fmt_reports.append({
|
||||
'description': r.get('description', ''),
|
||||
'reported_by': r.get('reported_by', ''),
|
||||
'reported_at': fmt_dt(r.get('reported_at'))
|
||||
})
|
||||
|
||||
fmt_repairs = []
|
||||
for rep in (repairs or []):
|
||||
res_reports = []
|
||||
for res in rep.get('resolved_reports', []):
|
||||
res_reports.append({
|
||||
'description': res.get('description', ''),
|
||||
'reported_by': res.get('reported_by', ''),
|
||||
'reported_at': fmt_dt(res.get('reported_at'))
|
||||
})
|
||||
fmt_repairs.append({
|
||||
'repaired_by': rep.get('repaired_by', ''),
|
||||
'repaired_at': fmt_dt(rep.get('repaired_at')),
|
||||
'resolved_reports': res_reports
|
||||
})
|
||||
return fmt_reports, fmt_repairs
|
||||
|
||||
client = None
|
||||
try:
|
||||
client = MongoClient(MONGODB_HOST, MONGODB_PORT)
|
||||
db = client[MONGODB_DB]
|
||||
items_col = db['items']
|
||||
ausleihungen_col = db['ausleihungen']
|
||||
users_col = db['users']
|
||||
student_cards_col = db['student_cards']
|
||||
|
||||
# WICHTIG: 'DamageRepairs': 1 zur Projektion hinzugefügt!
|
||||
library_items = list(items_col.find(
|
||||
{'ItemType': {'$in': LIBRARY_ITEM_TYPES}, 'Deleted': {'$ne': True}},
|
||||
{'Name': 1, 'Code_4': 1, 'Anschaffungskosten': 1, 'Condition': 1, 'HasDamage': 1, 'DamageReports': 1,
|
||||
'Verfuegbar': 1, 'User': 1, 'ItemType': 1, 'Author': 1, 'ISBN': 1}
|
||||
{
|
||||
'Name': 1, 'Code_4': 1, 'Anschaffungskosten': 1, 'Condition': 1,
|
||||
'HasDamage': 1, 'DamageReports': 1, 'DamageRepairs': 1,
|
||||
'Verfuegbar': 1, 'User': 1, 'ItemType': 1, 'Author': 1, 'ISBN': 1
|
||||
}
|
||||
))
|
||||
item_map = {str(item['_id']): item for item in library_items if item.get('_id')}
|
||||
item_ids = list(item_map.keys())
|
||||
@@ -3370,42 +3402,29 @@ def library_loans_admin():
|
||||
'Klasse': 1, 'klasse': 1, 'Class': 1, 'school_class': 1}
|
||||
).sort('Start', -1))
|
||||
|
||||
# class_map aufbauen (wie zuvor besprochen)
|
||||
class_map = {}
|
||||
student_cards = list(student_cards_col.find({}, {'SchülerName': 1, 'Klasse': 1, 'AusweisId': 1}))
|
||||
for sc in student_cards:
|
||||
dec_cls = safe_decrypt(sc.get('Klasse'))
|
||||
all_cards = list(student_cards_col.find({}, {'SchülerName': 1, 'Klasse': 1, 'AusweisId': 1}))
|
||||
for card in all_cards:
|
||||
raw_cls = card.get('Klasse')
|
||||
dec_cls = safe_decrypt(raw_cls)
|
||||
if not dec_cls:
|
||||
continue
|
||||
|
||||
dec_name = safe_decrypt(sc.get('SchülerName'))
|
||||
raw_name = sc.get('SchülerName')
|
||||
ausweis_id = sc.get('AusweisId')
|
||||
raw_name = card.get('SchülerName')
|
||||
dec_name = safe_decrypt(raw_name)
|
||||
ausweis_id = card.get('AusweisId')
|
||||
|
||||
if dec_name:
|
||||
class_map[dec_name.strip().lower()] = dec_cls
|
||||
class_map[dec_name] = dec_cls
|
||||
if raw_name:
|
||||
class_map[raw_name] = dec_cls
|
||||
if ausweis_id:
|
||||
class_map[str(ausweis_id).strip().lower()] = dec_cls
|
||||
|
||||
users = list(users_col.find({}, {'Username': 1, 'Name': 1, 'User': 1, 'Klasse': 1, 'klasse': 1, 'Class': 1,
|
||||
'school_class': 1}))
|
||||
for u in users:
|
||||
raw_cls = u.get('Klasse') or u.get('klasse') or u.get('Class') or u.get('school_class') or ''
|
||||
dec_cls = safe_decrypt(raw_cls)
|
||||
if not dec_cls:
|
||||
continue
|
||||
|
||||
for key in [u.get('Username'), u.get('Name'), u.get('User')]:
|
||||
if key:
|
||||
dec_key = safe_decrypt(key)
|
||||
class_map[key] = dec_cls
|
||||
class_map[dec_key] = dec_cls
|
||||
class_map[dec_key.strip().lower()] = dec_cls
|
||||
|
||||
active_item_ids = set()
|
||||
loan_entries = []
|
||||
|
||||
for record in active_records:
|
||||
item_id = str(record.get('Item') or '')
|
||||
if item_id and record.get('Status') == 'active':
|
||||
@@ -3417,26 +3436,24 @@ def library_loans_admin():
|
||||
|
||||
invoice_data = record.get('InvoiceData') or {}
|
||||
condition_value = str(item_doc.get('Condition', '')).strip().lower()
|
||||
item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(
|
||||
item_doc.get('DamageReports'))
|
||||
damage_reports = item_doc.get('DamageReports', []) or []
|
||||
|
||||
raw_reports = item_doc.get('DamageReports', [])
|
||||
raw_repairs = item_doc.get('DamageRepairs', [])
|
||||
item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(raw_reports)
|
||||
|
||||
fmt_reports, fmt_repairs = format_damage_history(raw_reports, raw_repairs)
|
||||
|
||||
raw_user = record.get('User', '')
|
||||
decrypted_user = safe_decrypt(raw_user)
|
||||
|
||||
direct_class = (
|
||||
user_class = (
|
||||
safe_decrypt(record.get('Klasse'))
|
||||
or safe_decrypt(record.get('klasse'))
|
||||
or safe_decrypt(record.get('Class'))
|
||||
or safe_decrypt(record.get('school_class'))
|
||||
)
|
||||
|
||||
user_class = (
|
||||
direct_class
|
||||
or class_map.get(decrypted_user)
|
||||
or class_map.get(decrypted_user.strip().lower())
|
||||
or class_map.get(raw_user)
|
||||
or ''
|
||||
or class_map.get(raw_user, '')
|
||||
or class_map.get(decrypted_user, '')
|
||||
or class_map.get(decrypted_user.strip().lower(), '')
|
||||
or '—'
|
||||
)
|
||||
|
||||
loan_entries.append({
|
||||
@@ -3447,7 +3464,7 @@ def library_loans_admin():
|
||||
'item_author': item_doc.get('Author', ''),
|
||||
'item_isbn': item_doc.get('ISBN', ''),
|
||||
'item_cost_raw': item_doc.get('Anschaffungskosten', ''),
|
||||
'user': decrypted_user,
|
||||
'user': decrypted_user if decrypted_user else raw_user,
|
||||
'klasse': user_class,
|
||||
'status': record.get('Status', ''),
|
||||
'start': fmt_dt(record.get('Start')),
|
||||
@@ -3455,24 +3472,32 @@ def library_loans_admin():
|
||||
'period': record.get('Period', ''),
|
||||
'notes': record.get('Notes', ''),
|
||||
'invoice_number': invoice_data.get('invoice_number', ''),
|
||||
'invoice_amount': fmt_money(invoice_data.get('amount')) if invoice_data.get('amount') is not None else fmt_money(item_doc.get('Anschaffungskosten')),
|
||||
'invoice_amount': fmt_money(invoice_data.get('amount')) if invoice_data.get(
|
||||
'amount') is not None else fmt_money(item_doc.get('Anschaffungskosten')),
|
||||
'invoice_paid': bool(invoice_data.get('paid', False)),
|
||||
'invoice_paid_at': fmt_dt(invoice_data.get('paid_at')) if isinstance(invoice_data.get('paid_at'), datetime.datetime) else '',
|
||||
'invoice_corrections_count': len(record.get('InvoiceCorrections', []) or []),
|
||||
'invoice_paid_at': fmt_dt(invoice_data.get('paid_at')) if isinstance(invoice_data.get('paid_at'),
|
||||
datetime.datetime) else '',
|
||||
'has_damage': item_has_damage,
|
||||
'damage_count': len(damage_reports),
|
||||
'damage_text': (damage_reports[0].get('description', '') if damage_reports else ''),
|
||||
'damage_count': len(raw_reports),
|
||||
'damage_text': fmt_reports[0]['description'] if fmt_reports else '',
|
||||
'damage_reports': fmt_reports,
|
||||
'damage_repairs': fmt_repairs
|
||||
})
|
||||
|
||||
damaged_items = []
|
||||
for item_doc in library_items:
|
||||
item_id = str(item_doc.get('_id') or '')
|
||||
condition_value = str(item_doc.get('Condition', '')).strip().lower()
|
||||
damage_reports = item_doc.get('DamageReports', []) or []
|
||||
item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(damage_reports)
|
||||
|
||||
raw_reports = item_doc.get('DamageReports', [])
|
||||
raw_repairs = item_doc.get('DamageRepairs', [])
|
||||
item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(raw_reports)
|
||||
|
||||
if not item_has_damage or item_id in active_item_ids:
|
||||
continue
|
||||
|
||||
fmt_reports, fmt_repairs = format_damage_history(raw_reports, raw_repairs)
|
||||
|
||||
damaged_items.append({
|
||||
'id': item_id,
|
||||
'name': item_doc.get('Name', ''),
|
||||
@@ -3480,11 +3505,13 @@ def library_loans_admin():
|
||||
'author': item_doc.get('Author', ''),
|
||||
'isbn': item_doc.get('ISBN', ''),
|
||||
'condition': item_doc.get('Condition', ''),
|
||||
'damage_count': len(damage_reports),
|
||||
'damage_text': (damage_reports[0].get('description', '') if damage_reports else ''),
|
||||
'available': bool(item_doc.get('Verfuegbar', False)),
|
||||
'last_updated': fmt_dt(item_doc.get('LastUpdated')),
|
||||
'acquisition_costs': item_doc.get('Anschaffungskosten', "")
|
||||
'acquisition_costs': item_doc.get('Anschaffungskosten', ""),
|
||||
'damage_count': len(raw_reports),
|
||||
'damage_text': fmt_reports[0]['description'] if fmt_reports else '',
|
||||
'damage_reports': fmt_reports,
|
||||
'damage_repairs': fmt_repairs
|
||||
})
|
||||
|
||||
return render_template(
|
||||
@@ -9214,7 +9241,7 @@ def resolve_repaired_item_funct(item_id, action, new_code_4="", current_user="ad
|
||||
"""
|
||||
Verarbeitet Items aus der Reparatur und aktualisiert die Serien-Counts
|
||||
sowie Parent/Child-Abhängigkeiten beim Löschen korrekt.
|
||||
Entfernt Schadens-Flags komplett aus Items und zugehörigen Ausleihen.
|
||||
Verschiebt gemeldete Schäden in die Historie (DamageRepairs) bei Reparatur/Austausch.
|
||||
"""
|
||||
if not ObjectId.is_valid(item_id):
|
||||
return False, "Ungültige Item-ID."
|
||||
@@ -9231,19 +9258,24 @@ def resolve_repaired_item_funct(item_id, action, new_code_4="", current_user="ad
|
||||
return False, "Item nicht in der Datenbank gefunden."
|
||||
|
||||
series_group_id = item.get('SeriesGroupId')
|
||||
now = datetime.datetime.now()
|
||||
|
||||
damage_unset = {
|
||||
'HasDamage': "",
|
||||
legacy_damage_unset = {
|
||||
'has_damage': "",
|
||||
'DamageCount': "",
|
||||
'damage_count': "",
|
||||
'DamageText': "",
|
||||
'damage_text': "",
|
||||
'is_damaged': "",
|
||||
'IsDamaged': "",
|
||||
'IsDamaged': ""
|
||||
}
|
||||
|
||||
borrowings_unset = legacy_damage_unset.copy()
|
||||
borrowings_unset.update({
|
||||
'HasDamage': "",
|
||||
'Condition': "",
|
||||
'DamageReports': ""
|
||||
}
|
||||
})
|
||||
|
||||
# --- OPTION 1: DELETE ---
|
||||
if action == 'delete':
|
||||
@@ -9285,24 +9317,36 @@ def resolve_repaired_item_funct(item_id, action, new_code_4="", current_user="ad
|
||||
|
||||
return True, "Item wurde gelöscht und der Bestand aller Serien-Items angepasst."
|
||||
|
||||
current_reports = item.get('DamageReports', [])
|
||||
repair_entry = {
|
||||
'repaired_by': current_user,
|
||||
'repaired_at': now,
|
||||
'action_taken': action,
|
||||
'resolved_reports': current_reports
|
||||
}
|
||||
|
||||
# --- OPTION 2: REPLACE ---
|
||||
elif action == 'replace':
|
||||
if action == 'replace':
|
||||
items.update_one(
|
||||
{'_id': ObjectId(item_id)},
|
||||
{
|
||||
'$push': {'DamageRepairs': repair_entry},
|
||||
'$set': {
|
||||
'Code_4': str(new_code_4).strip(),
|
||||
'Verfuegbar': True,
|
||||
'LastUpdated': datetime.datetime.now()
|
||||
'HasDamage': False,
|
||||
'Condition': 'gut',
|
||||
'DamageReports': [],
|
||||
'LastUpdated': now
|
||||
},
|
||||
'$unset': damage_unset
|
||||
'$unset': legacy_damage_unset
|
||||
}
|
||||
)
|
||||
|
||||
if borrowings is not None:
|
||||
borrowings.update_many(
|
||||
{'$or': [{'item_id': str(item_id)}, {'item_id': ObjectId(item_id)}]},
|
||||
{'$unset': damage_unset}
|
||||
{'$unset': borrowings_unset}
|
||||
)
|
||||
|
||||
return True, f"Item wurde ersetzt. Neuer Code 4 zugewiesen: {new_code_4}."
|
||||
@@ -9312,18 +9356,22 @@ def resolve_repaired_item_funct(item_id, action, new_code_4="", current_user="ad
|
||||
items.update_one(
|
||||
{'_id': ObjectId(item_id)},
|
||||
{
|
||||
'$push': {'DamageRepairs': repair_entry},
|
||||
'$set': {
|
||||
'Verfuegbar': True,
|
||||
'LastUpdated': datetime.datetime.now()
|
||||
'HasDamage': False,
|
||||
'Condition': 'gut',
|
||||
'DamageReports': [],
|
||||
'LastUpdated': now
|
||||
},
|
||||
'$unset': damage_unset
|
||||
'$unset': legacy_damage_unset
|
||||
}
|
||||
)
|
||||
|
||||
if borrowings is not None:
|
||||
borrowings.update_many(
|
||||
{'$or': [{'item_id': str(item_id)}, {'item_id': ObjectId(item_id)}]},
|
||||
{'$unset': damage_unset}
|
||||
{'$unset': borrowings_unset}
|
||||
)
|
||||
|
||||
return True, "Item wurde repariert und ist wieder regulär verfügbar."
|
||||
@@ -9375,7 +9423,7 @@ def library_item_invoices(item_id):
|
||||
item_id_str = str(item_id_obj)
|
||||
|
||||
# ==========================================
|
||||
# 1. RECHNUNGSHISTORIE (Bestehender Code)
|
||||
# 1. RECHNUNGSHISTORIE (Aus Ausleihen)
|
||||
# ==========================================
|
||||
borrow_docs = list(ausleihungen.find(
|
||||
{
|
||||
@@ -9407,7 +9455,9 @@ def library_item_invoices(item_id):
|
||||
entries.append({
|
||||
'borrow_id': str(borrow_doc.get('_id')),
|
||||
'borrow_status': borrow_doc.get('Status', ''),
|
||||
'borrow_user': decrypt_text(borrow_doc.get('User', '')),
|
||||
'borrow_user': decrypt_text(borrow_doc.get('User', '')) if hasattr(borrow_doc,
|
||||
'get') and borrow_doc.get(
|
||||
'User') else '',
|
||||
'borrow_start': borrow_doc.get('Start').strftime('%d.%m.%Y %H:%M') if isinstance(
|
||||
borrow_doc.get('Start'), datetime.datetime) else '',
|
||||
'borrow_end': borrow_doc.get('End').strftime('%d.%m.%Y %H:%M') if isinstance(borrow_doc.get('End'),
|
||||
@@ -9424,36 +9474,83 @@ def library_item_invoices(item_id):
|
||||
})
|
||||
|
||||
# ==========================================
|
||||
# 2. SCHADENSHISTORIE (Neuer Code)
|
||||
# 2. SCHADENSHISTORIE (Neues Format + Fallback)
|
||||
# ==========================================
|
||||
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', []))
|
||||
# A: Aktive / gemeldete Schäden aus 'DamageReports'
|
||||
damage_reports = item_doc.get('DamageReports', [])
|
||||
if not isinstance(damage_reports, list):
|
||||
damage_reports = []
|
||||
|
||||
for d in item_damages:
|
||||
for d in damage_reports:
|
||||
if not isinstance(d, dict): continue
|
||||
|
||||
# Rechnungs-Metadaten anhängen, falls vorhanden
|
||||
extra_info = ""
|
||||
if d.get('invoice_number'):
|
||||
amount = d.get('invoice_amount', '')
|
||||
extra_info = f"\n(Mit Rechnung: {d['invoice_number']} - {amount}€)"
|
||||
|
||||
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'),
|
||||
'raw_date': d.get('reported_at'),
|
||||
'source': str(d.get('source', 'Meldung')).capitalize(),
|
||||
'user': d.get('reported_by', 'System/Admin'),
|
||||
'description': d.get('description', 'Keine Beschreibung') + extra_info,
|
||||
'status': 'Offen',
|
||||
'borrow_id': None
|
||||
})
|
||||
|
||||
# B: Schäden, die in Ausleih-Dokumenten vermerkt sind
|
||||
# B: Behobene Schäden aus 'DamageRepairs'
|
||||
damage_repairs = item_doc.get('DamageRepairs', [])
|
||||
if not isinstance(damage_repairs, list):
|
||||
damage_repairs = []
|
||||
|
||||
for r in damage_repairs:
|
||||
if not isinstance(r, dict): continue
|
||||
resolved_list = r.get('resolved_reports', [])
|
||||
repaired_at = r.get('repaired_at')
|
||||
repaired_by = r.get('repaired_by', 'Admin')
|
||||
action_taken = r.get('action_taken', 'repair')
|
||||
|
||||
# Status anhand der Aktion bestimmen
|
||||
status_text = 'Repariert' if action_taken == 'repair' else 'Erledigt'
|
||||
|
||||
if resolved_list and isinstance(resolved_list, list):
|
||||
for res in resolved_list:
|
||||
# Rechnungs-Metadaten aus dem ehemals offenen Report
|
||||
extra_info = ""
|
||||
if res.get('invoice_number'):
|
||||
amount = res.get('invoice_amount', '')
|
||||
extra_info = f"\n(Mit Rechnung: {res['invoice_number']} - {amount}€)"
|
||||
|
||||
raw_damages.append({
|
||||
'raw_date': res.get('reported_at') or repaired_at,
|
||||
'source': f"{status_text} von {repaired_by}",
|
||||
'user': res.get('reported_by', 'System/Admin'),
|
||||
'description': res.get('description', 'Keine Beschreibung') + extra_info,
|
||||
'status': status_text,
|
||||
'borrow_id': None
|
||||
})
|
||||
else:
|
||||
# Fallback, falls keine genauen Reports hinterlegt sind
|
||||
raw_damages.append({
|
||||
'raw_date': repaired_at,
|
||||
'source': 'Reparatur',
|
||||
'user': repaired_by,
|
||||
'description': 'Schaden behoben',
|
||||
'status': status_text,
|
||||
'borrow_id': None
|
||||
})
|
||||
|
||||
# C: Legacy-Fallback: Schäden, die evtl. nur in alten 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
|
||||
{'Condition': {'$exists': True, '$ne': ''}}
|
||||
]
|
||||
}
|
||||
))
|
||||
@@ -9469,7 +9566,7 @@ def library_item_invoices(item_id):
|
||||
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",
|
||||
'source': "Ausleihe",
|
||||
'user': borrow_user,
|
||||
'description': desc,
|
||||
'status': d_data.get('status', 'Gemeldet'),
|
||||
@@ -9501,7 +9598,7 @@ def library_item_invoices(item_id):
|
||||
'isbn': item_doc.get('ISBN', ''),
|
||||
},
|
||||
invoices=entries,
|
||||
damages=damage_entries, # << NEU
|
||||
damages=damage_entries,
|
||||
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')
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
<div class="muted">{{ e.item_author or '—' }}</div>
|
||||
<div class="mono">{{ e.item_code or '—' }}</div>
|
||||
<div style="margin-top:6px;">
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{{ url_for('library_item_invoices', item_id=e.item_id) }}">Rechnungen</a>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{{ url_for('library_item_invoices', item_id=e.item_id) }}">Rechnungen / Historie</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -322,9 +322,9 @@
|
||||
{% if e.invoice_number %}
|
||||
<div class="mono">{{ e.invoice_number }}</div>
|
||||
<div class="muted">{{ e.invoice_amount }}</div>
|
||||
{% if e.invoice_corrections_count %}
|
||||
<!--{% if e.invoice_corrections_count %}
|
||||
<div class="muted" style="color:#7c2d12;">{{ e.invoice_corrections_count }} Korrektur(en)</div>
|
||||
{% endif %}
|
||||
{% endif %} -->
|
||||
<div style="margin-top:6px;">
|
||||
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('admin_view_invoice_pdf', borrow_id=e.id) }}" target="_blank" rel="noopener">PDF öffnen</a>
|
||||
</div>
|
||||
@@ -363,13 +363,13 @@
|
||||
<button type="button" class="btn btn-warning btn-sm" onclick="openRepairModal('{{ e.item_id }}', '{{ e.item_code }}')">Reparieren / Ersetzen</button>
|
||||
{% endif %}
|
||||
|
||||
{% if e.invoice_number %}
|
||||
<!--{% if e.invoice_number %}
|
||||
<form method="post" action="{{ url_for('admin_add_invoice_correction', borrow_id=e.id) }}" onsubmit="return confirm('Korrekturbuchung hinzufügen?');" style="display: flex; gap: 6px; align-items: center; flex-wrap: wrap;">
|
||||
<input type="text" name="correction_reason" value="Korrektur zu {{ e.invoice_number }}" placeholder="Korrekturgrund" required style="padding:6px; border:1px solid #ddd; border-radius:6px; min-width:160px; max-width:180px;">
|
||||
<input type="text" name="amount_delta" placeholder="z.B. -{{ e.invoice_amount }}" style="padding:6px; border:1px solid #ddd; border-radius:6px; width:120px;">
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm">Korrektur</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}-->
|
||||
|
||||
{% if e.status in ['active', 'planned'] %}
|
||||
<form method="post" action="{{ url_for('admin_reset_borrowing', borrow_id=e.id) }}" onsubmit="return confirm('Ausleihe zurücksetzen?');">
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
</div>
|
||||
<div class="head-actions">
|
||||
<a class="btn btn-outline-secondary" href="{{ url_for('library_loans_admin') }}">Zur Bibliotheks-Ausleihenverwaltung</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('library_admin') }}">Bibliothek öffnen</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('library_view') }}">Bibliothek öffnen</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1531,22 +1531,22 @@
|
||||
}
|
||||
const isbnField = document.getElementById('isbn');
|
||||
const infoContainer = document.getElementById('book-info-container');
|
||||
|
||||
|
||||
if (!isbnField || !infoContainer) {
|
||||
alert('Fehler: Erforderliche Formularelemente nicht gefunden.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const isbn = normalizeIsbnClient(isbnField.value);
|
||||
|
||||
|
||||
if (!isbn) {
|
||||
infoContainer.innerHTML = '<div class="error-message">Bitte geben Sie eine ISBN oder einen Barcode ein.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Show loading indicator
|
||||
infoContainer.innerHTML = '<div class="loading-spinner">Informationen werden abgerufen...</div>';
|
||||
|
||||
|
||||
// Make API request to fetch book data
|
||||
fetch(`/fetch_book_info/${isbn}`)
|
||||
.then(response => {
|
||||
@@ -1564,10 +1564,10 @@
|
||||
infoContainer.innerHTML = `<div class="error-message">${data.error}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Store book data globally for import functionality
|
||||
currentBookData = data;
|
||||
|
||||
|
||||
// Display book information with import button
|
||||
infoContainer.innerHTML = `
|
||||
<div class="book-info">
|
||||
@@ -1602,23 +1602,23 @@
|
||||
alert('Keine Buchdaten verfügbar zum Import.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get form fields
|
||||
const nameField = document.getElementById('name');
|
||||
const descriptionField = document.getElementById('beschreibung');
|
||||
const priceField = document.getElementById('anschaffungskosten'); // <-- NEU
|
||||
|
||||
|
||||
if (!nameField || !descriptionField) {
|
||||
alert('Fehler: Formularfelder nicht gefunden.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Generate book title
|
||||
let bookTitle = currentBookData.title || '';
|
||||
if (currentBookData.authors && currentBookData.authors !== 'Unknown Author') {
|
||||
bookTitle += ` - ${currentBookData.authors}`;
|
||||
}
|
||||
|
||||
|
||||
// Generate description
|
||||
let description = '';
|
||||
if (currentBookData.description && currentBookData.description !== 'No description available') {
|
||||
@@ -1639,11 +1639,11 @@
|
||||
description += `\nSeiten: ${currentBookData.pageCount}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Import the data into form fields
|
||||
nameField.value = bookTitle;
|
||||
descriptionField.value = description;
|
||||
|
||||
|
||||
// Preis in das Formularfeld eintragen
|
||||
if (priceField && currentBookData.price !== null && currentBookData.price !== undefined) {
|
||||
// Wandelt den Float (z.B. 12.25) in einen String mit Komma (12,25) um
|
||||
@@ -1655,14 +1655,14 @@
|
||||
if (currentBookData.thumbnail) {
|
||||
downloadBookCover(currentBookData.thumbnail);
|
||||
}
|
||||
|
||||
|
||||
// Show success message
|
||||
const infoContainer = document.getElementById('book-info-container');
|
||||
const successMessage = document.createElement('div');
|
||||
successMessage.className = 'import-success-message';
|
||||
successMessage.textContent = 'Buchdaten erfolgreich in das Formular übernommen!';
|
||||
infoContainer.appendChild(successMessage);
|
||||
|
||||
|
||||
// Remove success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
if (successMessage.parentNode) {
|
||||
|
||||
Reference in New Issue
Block a user