Files
Inventarsystem/Web/modules/database/ausleihung.py
T

883 lines
31 KiB
Python
Executable File

"""
Ausleihungssystem (Borrowing System)
====================================
Dieses Modul verwaltet sämtliche Ausleihungen im Inventarsystem.
Es bietet alle Funktionen, um Ausleihungen zu planen, zu aktivieren,
zu beenden und zu stornieren.
Hauptfunktionen:
- Erstellen neuer Ausleihungen (geplant oder sofort aktiv)
- Aktualisieren von Ausleihungsdaten
- Abschließen von Ausleihungen (Rückgabe)
- Suchen und Abrufen von Ausleihungen nach verschiedenen Kriterien
- Verwaltung des Ausleihungs-Lebenszyklus
Sammlungsstruktur:
- ausleihungen: Speichert alle Ausleihungsdatensätze mit ihrem Status
- Status-Werte: 'planned' (geplant), 'active' (aktiv), 'completed' (abgeschlossen), 'cancelled' (storniert)
"""
'''
Copyright 2025-2026 AIIrondev
Licensed under the Inventarsystem EULA (Endbenutzer-Lizenzvertrag).
See Legal/LICENSE for the full license text.
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
For commercial licensing inquiries: https://github.com/AIIrondev
'''
from bson.objectid import ObjectId, InvalidId
import datetime
from dateutil import parser
import pytz
from datetime import timezone
import os
import json
import shutil
import Web.modules.database.settings as cfg
from Web.modules.database.settings import MongoClient
import Web.modules.inventarsystem.data_protection as dp
# Add this helper function after imports
def ensure_timezone_aware(dt):
"""Ensures a datetime is timezone-aware, using UTC if naive"""
if dt is None:
return None
if dt.tzinfo is None:
# Treat naive datetimes as UTC
return dt.replace(tzinfo=None)
return dt
def get_current_status(ausleihung, log_changes=False, user=None):
"""
Ermittelt den aktuellen Status einer Ausleihung basierend auf den Zeitstempeln
und dem gespeicherten Status. Diese Funktion berücksichtigt das aktuelle Datum
und aktualisiert den Status entsprechend dem realen Zustand.
Status-Werte:
- 'planned': Eine zukünftige Ausleihung, die noch nicht begonnen hat
- 'active': Eine aktive Ausleihung, die gerade läuft
- 'completed': Eine beendete Ausleihung
- 'cancelled': Eine stornierte Ausleihung
Args:
ausleihung (dict): Der Ausleihungsdatensatz
log_changes (bool): Ob Statusänderungen protokolliert werden sollen
user (str): Der Benutzer, der die Prüfung durchführt (für Logs)
Returns:
str: Der aktuelle Status ('planned', 'active', 'completed', 'cancelled')
"""
# Speichern Sie den ursprünglichen Status für Logging-Zwecke
original_status = ausleihung.get('Status', 'unknown')
# Bei stornierten Ausleihungen bleibt der Status immer storniert
if original_status == 'cancelled':
return 'cancelled'
# Wenn die Ausleihung bereits abgeschlossen ist, bleibt sie es
if original_status == 'completed':
return 'completed'
current_time = datetime.datetime.now()
start_time = ausleihung.get('Start')
end_time = ausleihung.get('End')
# Wenn kein Startdatum vorhanden ist, Status auf 'planned' setzen
if not start_time:
new_status = 'planned'
# Wenn die aktuelle Zeit vor dem Startdatum liegt, ist die Ausleihung geplant
elif current_time < start_time:
# DEBUG: Log info wenn Booking noch lange in der Zukunft ist
time_until_start = (start_time - current_time).total_seconds()
if time_until_start > 3600: # Mehr als 1 Stunde entfernt
ausleihung_id = str(ausleihung.get('_id', 'unknown'))[:12]
print(f"[DEBUG] Ausleihe {ausleihung_id} startet in {time_until_start/3600:.1f} Stunden ({start_time}), noch geplant")
new_status = 'planned'
# Wenn kein Enddatum gesetzt ist oder die aktuelle Zeit vor dem Enddatum liegt,
# ist die Ausleihung aktiv
elif not end_time or current_time < end_time:
new_status = 'active'
# Wenn die aktuelle Zeit nach dem Enddatum liegt, ist die Ausleihung beendet
else:
new_status = 'completed'
# Protokollieren Sie Statusänderungen, wenn aktiviert und eine Änderung stattgefunden hat
if log_changes and new_status != original_status and '_id' in ausleihung:
try:
# Importieren Sie das Modul nur bei Bedarf, um zirkuläre Importe zu vermeiden
import Web.modules.log.ausleihung_log as ausleihung_log
ausleihung_log.log_status_change(
str(ausleihung['_id']),
original_status,
new_status,
dp.encrypt_text(user)
)
except Exception as e:
print(f"Fehler beim Protokollieren der Statusänderung: {e}")
return new_status
def create_backup_database():
"""
Erstellt eine Sicherungskopie der Ausleihungsdatenbank.
Returns:
bool: True wenn Backup erfolgreich erstellt wurde, sonst False
"""
try:
# Verbindung zur Datenbank herstellen
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
# Backup-Verzeichnis erstellen, falls es nicht existiert
backup_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'backups')
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# Aktuelles Datum für den Dateinamen
current_date = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
backup_file = os.path.join(backup_dir, f'ausleihungen_backup_{current_date}.json')
# Ausleihungen abrufen und als JSON speichern
all_ausleihungen = list(ausleihungen.find({}))
# ObjectId in String umwandeln für JSON-Serialisierung
for ausleihung in all_ausleihungen:
ausleihung['_id'] = str(ausleihung['_id'])
if 'Start' in ausleihung and ausleihung['Start']:
ausleihung['Start'] = ausleihung['Start'].isoformat()
if 'End' in ausleihung and ausleihung['End']:
ausleihung['End'] = ausleihung['End'].isoformat()
if 'LastUpdated' in ausleihung and ausleihung['LastUpdated']:
ausleihung['LastUpdated'] = ausleihung['LastUpdated'].isoformat()
with open(backup_file, 'w', encoding='utf-8') as f:
json.dump(all_ausleihungen, f, ensure_ascii=False, indent=4)
# Log-Eintrag erstellen
log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, 'ausleihungen_backup.log')
with open(log_file, 'a', encoding='utf-8') as f:
f.write(f"{current_date}: Backup erstellt: {backup_file}, {len(all_ausleihungen)} Einträge\n")
client.close()
return True
except Exception as e:
# Fehler protokollieren
log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, 'ausleihungen_error.log')
with open(log_file, 'a', encoding='utf-8') as f:
f.write(f"{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}: Backup-Fehler: {str(e)}\n")
print(f"Fehler beim Erstellen des Backups: {e}")
return False
# === AUSLEIHUNG MANAGEMENT ===
def add_ausleihung(item_id, user, start_date, end_date=None, notes="", status="active", period=None, exemplar_data=None):
"""
Add a new borrowing record for an item.
Args:
item_id (str): ID of the item borrowed
user (str): Username of the borrower
start_date (datetime): Start date and time of the borrowing
end_date (datetime, optional): End date and time if already returned
notes (str, optional): Additional notes about the borrowing
status (str, optional): Status of the borrowing (active, completed, planned)
period (str, optional): School period for the borrowing
exemplar_data (dict, optional): Information about specific exemplar borrowed
Returns:
ObjectId: ID of the new borrowing record or None if failed
"""
try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
ausleihung = {
'Item': item_id,
'User': dp.encrypt_text(user),
'Start': start_date,
'Status': status
}
if end_date:
ausleihung['End'] = end_date
if notes:
ausleihung['Notes'] = notes
if period:
ausleihung['Period'] = period
if exemplar_data:
ausleihung['ExemplarData'] = exemplar_data
result = ausleihungen.insert_one(ausleihung)
ausleihung_id = result.inserted_id
client.close()
return ausleihung_id
except Exception as e:
print(f"Error adding ausleihung: {e}")
return None
def update_ausleihung(id, item_id=None, user_id=None, start=None, end=None, notes=None, status=None, period=None):
try:
# ID-Validierung
try:
doc_id = ObjectId(id) if isinstance(id, str) else id
except InvalidId:
print(f"Ungültiges ObjectId-Format: {id}")
return False
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
update_data = {}
if item_id is not None:
update_data['Item'] = item_id
if user_id is not None:
# WICHTIG: Gleiche Verschlüsselung wie in add_ausleihung nutzen!
update_data['User'] = dp.encrypt_text(user_id) if hasattr(dp, 'encrypt_text') else user_id
if start is not None:
update_data['Start'] = ensure_timezone_aware(start)
if end is not None:
update_data['End'] = ensure_timezone_aware(end)
if notes is not None:
update_data['Notes'] = notes
if status is not None:
update_data['Status'] = status
if period is not None:
update_data['Period'] = period
# Keine leeren Updates ausführen (falls nur None-Parameter übergeben wurden)
if not update_data:
client.close()
return True
# UTC Zeitstempel nutzen
update_data['LastUpdated'] = datetime.datetime.now(datetime.timezone.utc)
result = ausleihungen.update_one(
{'_id': doc_id},
{'$set': update_data}
)
client.close()
# Prüfen ob das Dokument überhaupt gefunden wurde (matched_count)
return result.matched_count > 0
except Exception as e:
print(f"Error updating ausleihung: {e}")
return False
def complete_ausleihung(id, end_time=None):
"""
Markiert eine Ausleihe als abgeschlossen, indem das Enddatum gesetzt
und der Status auf 'completed' geändert wird.
Args:
id (str): ID des abzuschließenden Ausleihungsdatensatzes
end_time (datetime, optional): Endzeitpunkt (Standard: aktuelle Zeit)
Returns:
bool: True bei Erfolg, sonst False
"""
try:
if end_time is None:
end_time = datetime.datetime.now()
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
item = db['items']
result = ausleihungen.update_one(
{'_id': ObjectId(id)},
{'$set': {
'End': end_time,
'Status': 'completed',
'LastUpdated': datetime.datetime.now()
}}
)
item.update_one(
{'_id': ObjectId(id)},
{'$set': {
'Verfuegbar': True,
'LastUpdated': datetime.datetime.now()
}}
)
client.close()
return result.modified_count > 0
except Exception as e:
# print(f"Error completing ausleihung: {e}") # Log the error
return False
def cancel_ausleihung(id):
"""
Storniert eine geplante Ausleihe durch Änderung des Status auf 'cancelled'.
Args:
id (str): ID der zu stornierenden Ausleihe
Returns:
bool: True bei Erfolg, sonst False
"""
try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
# Mark the booking as cancelled
result = ausleihungen.update_one(
{'_id': ObjectId(id)},
{'$set': {
'Status': 'cancelled',
'LastUpdated': datetime.datetime.now()
}}
)
client.close()
return result.modified_count > 0
except Exception as e:
# print(f"Error cancelling ausleihung: {e}") # Log the error
return False
def remove_ausleihung(id):
"""
Markiert einen Ausleihungsdatensatz als gelöscht (Soft-Delete).
Args:
id (str): ID des zu entfernenden Ausleihungsdatensatzes
Returns:
bool: True bei Erfolg, sonst False
"""
try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
now = datetime.datetime.now()
result = ausleihungen.update_one(
{'_id': ObjectId(id), 'Status': {'$ne': 'deleted'}},
{'$set': {
'Status': 'deleted',
'DeletedAt': now,
'LastUpdated': now,
}}
)
client.close()
return result.modified_count > 0
except Exception as e:
# print(f"Error removing ausleihung: {e}") # Log the error
return False
# === AUSLEIHUNG RETRIEVAL ===
def get_ausleihung(id):
"""
Ruft einen bestimmten Ausleihungsdatensatz anhand seiner ID ab.
Args:
id (str): ID des abzurufenden Ausleihungsdatensatzes
Returns:
dict: Der Ausleihungsdatensatz oder None, wenn nicht gefunden
"""
try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
ausleihung = ausleihungen.find_one({'_id': ObjectId(id)})
client.close()
return ausleihung
except Exception as e:
# print(f"Error retrieving ausleihung: {e}") # Log the error
return None
def get_ausleihungen(status=None, start=None, end=None, date_filter='overlap'):
try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
collection = db['ausleihungen']
query = {'Status': {'$ne': 'deleted'}}
# Status-Filter
if status is not None:
if isinstance(status, list):
allowed_status = [s for s in status if s != 'deleted']
query['Status'] = {'$in': allowed_status}
else:
query['Status'] = status if status != 'deleted' else '__blocked_deleted_status__'
# Datums-Parsing
if isinstance(start, str):
try:
start = parser.parse(start)
except Exception:
start = None
if isinstance(end, str):
try:
end = parser.parse(end)
except Exception:
end = None
# Datumsfilter-Logik
if start is not None and end is not None:
if date_filter == 'overlap':
query['$and'] = [
{'Start': {'$lte': end}},
{'$or': [
{'End': {'$gte': start}},
{'End': None}
]}
]
elif date_filter == 'start_in':
query['Start'] = {'$gte': start, '$lte': end}
elif date_filter == 'end_in':
query['End'] = {'$gte': start, '$lte': end}
elif date_filter == 'contained':
query['Start'] = {'$gte': start}
query['End'] = {'$exists': True, '$lte': end}
elif start is not None:
# Nur Startdatum angegeben -> alles was ab/nach start aktiv ist
query['$or'] = [{'End': {'$gte': start}}, {'End': None}]
elif end is not None:
# Nur Enddatum angegeben -> alles was vor end begonnen hat
query['Start'] = {'$lte': end}
results = list(collection.find(query))
client.close()
return results
except Exception as e:
# Sinnvolles Logging einbauen
return []
def get_active_ausleihungen(start=None, end=None):
"""
Ruft alle aktiven (laufenden) Ausleihungen ab.
Args:
start (str/datetime, optional): Startdatum für Datumsfilterung
end (str/datetime, optional): Enddatum für Datumsfilterung
Returns:
list: Liste aktiver Ausleihungsdatensätze
"""
return get_ausleihungen(status='active', start=start, end=end)
def get_planned_ausleihungen(start=None, end=None):
"""
Ruft alle geplanten Ausleihungen (Reservierungen) ab.
Args:
start (str/datetime, optional): Startdatum für Datumsfilterung
end (str/datetime, optional): Enddatum für Datumsfilterung
Returns:
list: Liste geplanter Ausleihungsdatensätze
"""
return get_ausleihungen(status='planned', start=start, end=end)
def get_completed_ausleihungen(start=None, end=None):
"""
Ruft alle abgeschlossenen Ausleihungen ab.
Args:
start (str/datetime, optional): Startdatum für Datumsfilterung
end (str/datetime, optional): Enddatum für Datumsfilterung
Returns:
list: Liste abgeschlossener Ausleihungsdatensätze
"""
return get_ausleihungen(status='completed', start=start, end=end)
def get_cancelled_ausleihungen(start=None, end=None):
"""
Ruft alle stornierten Ausleihungen ab.
Args:
start (str/datetime, optional): Startdatum für Datumsfilterung
end (str/datetime, optional): Enddatum für Datumsfilterung
Returns:
list: Liste stornierter Ausleihungsdatensätze
"""
return get_ausleihungen(status='cancelled', start=start, end=end)
# === SEARCH FUNCTIONS ===
def get_ausleihung_by_user(user_id, status=None, use_client_side_verification=True):
"""Ruft Ausleihungen für einen bestimmten Benutzer ab."""
try:
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
# Ensure proper encrypted query string if user IDs are encrypted in DB
search_user = dp.encrypt_text(user_id) if hasattr(dp, 'encrypt_text') else user_id
query = {'User': search_user, 'Status': {'$ne': 'deleted'}}
if not use_client_side_verification:
if status is not None:
if isinstance(status, list):
query['Status'] = {'$in': status}
else:
query['Status'] = status
else:
query['Status'] = {'$ne': 'cancelled'}
return list(ausleihungen.find(query))
all_ausleihungen = list(ausleihungen.find(query))
for record in all_ausleihungen:
current_status = get_current_status(record) if 'get_current_status' in globals() else record.get(
'Status')
record['VerifiedStatus'] = current_status
if status is None:
return all_ausleihungen
filtered_results = []
target_statuses = status if isinstance(status, list) else [status]
for record in all_ausleihungen:
v_status = record.get('VerifiedStatus')
if v_status in target_statuses and v_status != 'deleted':
filtered_results.append(record)
return filtered_results
except Exception as e:
print(f"Error retrieving ausleihungen for user {user_id}: {e}")
return []
def get_ausleihung_by_item(item_id, status=None, include_history=False):
"""Get most recent ausleihung record for a specific item."""
try:
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
query = {'Item': item_id, 'Status': {'$ne': 'deleted'}}
if status and not include_history:
query['Status'] = status
record = ausleihungen.find_one(query, sort=[('Start', -1)])
if record:
record['_id'] = str(record['_id'])
return record
except Exception as e:
print(f"Error getting ausleihung by item: {e}")
return None
def get_ausleihungen_by_date_range(start_date, end_date, status=None):
"""Ruft Ausleihungen ab, die in einem bestimmten Zeitraum aktiv waren."""
return get_ausleihungen(status=status, start=start_date, end=end_date)
def check_ausleihung_conflict(item_id, start_date, end_date, period=None):
"""Prüft, ob es Konflikte mit bestehenden Ausleihungen oder aktiven Ausleihen gibt."""
try:
start_date = ensure_timezone_aware(start_date)
end_date = ensure_timezone_aware(end_date)
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
booking_date = start_date.date()
all_bookings = list(ausleihungen.find({
'Item': item_id,
'Status': {'$in': ['planned', 'active']}
}))
if period is not None:
period_int = int(period)
for booking in all_bookings:
booking_start = ensure_timezone_aware(booking.get('Start'))
if booking_start and booking_start.date() == booking_date:
booking_period = booking.get('Period')
if booking_period is not None and int(booking_period) == period_int:
return True
for booking in all_bookings:
b_start = ensure_timezone_aware(booking.get('Start'))
if not b_start:
continue
b_end = ensure_timezone_aware(booking.get('End')) or (b_start + datetime.timedelta(hours=1))
# Simplified and standard time range collision check
if start_date < b_end and end_date > b_start:
return True
return False
except Exception as e:
print(f"Error checking booking conflicts: {e}")
return True # Return conflict on exception for safety
def check_booking_period_range_conflict(item_id, start_date, end_date, period=None, period_end=None):
"""Checks for conflicts with existing bookings, supporting period ranges."""
try:
start_date = ensure_timezone_aware(start_date)
end_date = ensure_timezone_aware(end_date)
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
booking_date = start_date.date()
all_bookings = list(ausleihungen.find({
'Item': item_id,
'Status': {'$in': ['planned', 'active']}
}))
if period is not None:
period_start = int(period)
periods_to_check = [period_start]
if period_end is not None:
periods_to_check = list(range(period_start, int(period_end) + 1))
for booking in all_bookings:
b_start = ensure_timezone_aware(booking.get('Start'))
if b_start and b_start.date() == booking_date:
booking_period = booking.get('Period')
try:
booking_period_int = int(booking_period) if booking_period is not None else None
except Exception:
booking_period_int = None
if booking_period_int in periods_to_check:
return True
for booking in all_bookings:
b_start = ensure_timezone_aware(booking.get('Start'))
if not b_start:
continue
b_end = ensure_timezone_aware(booking.get('End')) or (b_start + datetime.timedelta(hours=1))
if start_date < b_end and end_date > b_start:
return True
return False
except Exception as e:
print(f"Error checking booking range conflicts: {e}")
return True
def get_ausleihungen_starting_now(current_time):
"""Ruft Ausleihungen ab, die jetzt beginnen sollen."""
try:
current_time = ensure_timezone_aware(current_time)
start_time = current_time - datetime.timedelta(hours=3)
end_time = current_time + datetime.timedelta(hours=1)
today = current_time.date()
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
query = {
'Status': 'planned',
'$or': [
{'Start': {'$lte': end_time, '$gte': start_time}},
{
'Period': {'$exists': True},
'Start': {
'$gte': datetime.datetime.combine(today, datetime.time.min, tzinfo=datetime.timezone.utc),
'$lt': datetime.datetime.combine(today + datetime.timedelta(days=1), datetime.time.min,
tzinfo=datetime.timezone.utc)
}
}
]
}
return list(ausleihungen.find(query))
except Exception as e:
print(f"Error in get_ausleihungen_starting_now: {e}")
return []
def get_ausleihungen_ending_now(current_time):
"""Ruft Ausleihungen ab, die jetzt enden sollen."""
try:
current_time = ensure_timezone_aware(current_time)
start_time = current_time - datetime.timedelta(minutes=15)
end_time = current_time + datetime.timedelta(minutes=5)
today = current_time.date()
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
query = {
'Status': 'active',
'$or': [
{'End': {'$gte': start_time, '$lte': end_time}},
{
'Period': {'$exists': True},
'Start': {
'$gte': datetime.datetime.combine(today, datetime.time.min, tzinfo=datetime.timezone.utc),
'$lt': datetime.datetime.combine(today + datetime.timedelta(days=1), datetime.time.min,
tzinfo=datetime.timezone.utc)
}
}
]
}
return list(ausleihungen.find(query))
except Exception as e:
print(f"Error in get_ausleihungen_ending_now: {e}")
return []
def activate_ausleihung(id):
"""Aktiviert eine geplante Ausleihe."""
try:
doc_id = ObjectId(id) if isinstance(id, str) else id
with _get_client() as client:
db = client[cfg.MONGODB_DB]
ausleihungen = db['ausleihungen']
result = ausleihungen.update_one(
{'_id': doc_id, 'Status': 'planned'},
{'$set': {
'Status': 'active',
'LastUpdated': datetime.datetime.now(datetime.timezone.utc)
}}
)
return result.modified_count > 0
except Exception as e:
print(f"Error activating ausleihung: {e}")
return False
def reset_item_completely(item_id):
"""Setzt den Ausleihstatus eines Items vollständig zurück."""
try:
doc_id = ObjectId(item_id) if isinstance(item_id, str) else item_id
with _get_client() as client:
db = client[cfg.MONGODB_DB]
items_collection = db['items']
ausleihungen_collection = db['ausleihungen']
item = items_collection.find_one({'_id': doc_id})
if not item:
return {'success': False, 'message': 'Item nicht gefunden'}
item_name = item.get('Name', 'Unbekannt')
now_utc = datetime.datetime.now(datetime.timezone.utc)
# 1. Bulk update active borrowings
update_res = ausleihungen_collection.update_many(
{
'Item': str(item_id),
'Status': {'$in': ['active', 'planned']}
},
{
'$set': {
'Status': 'completed',
'End': now_utc,
'LastUpdated': now_utc,
'CompletedBy': 'System Reset'
}
}
)
completed_count = update_res.modified_count
# 2. Reset Item document with valid PyMongo update query format
update_query = {
'$set': {
'Verfuegbar': True,
'LastUpdated': now_utc
}
}
unsets = {}
if 'User' in item:
unsets['User'] = ''
if 'BorrowerInfo' in item:
unsets['BorrowerInfo'] = ''
if 'ExemplareStatus' in item:
unsets['ExemplareStatus'] = ''
if unsets:
update_query['$unset'] = unsets
result = items_collection.update_one({'_id': doc_id}, update_query)
return {
'success': True,
'message': f'Item "{item_name}" wurde erfolgreich zurückgesetzt',
'details': {
'completed_borrowings': completed_count,
'item_reset': result.modified_count > 0
}
}
except Exception as e:
print(f"Error resetting item: {e}")
return {'success': False, 'message': f'Fehler beim Zurücksetzen: {e}'}
# === KOMPATIBILITÄTSFUNKTIONEN ===
def add_planned_booking(item_id, user, start_date, end_date, notes="", period=None):
return add_ausleihung(item_id, user, start_date, end_date, notes, status='planned', period=period)
def check_booking_conflict(item_id, start_date, end_date, period=None):
return check_ausleihung_conflict(item_id, start_date, end_date, period)
def mark_booking_active(booking_id, ausleihung_id=None):
try:
doc_id = ObjectId(booking_id) if isinstance(booking_id, str) else booking_id
update_data = {
'Status': 'active',
'LastUpdated': datetime.datetime.now(datetime.timezone.utc)
}
if ausleihung_id:
update_data['AusleihungId'] = ausleihung_id
with _get_client() as client:
db = client[cfg.MONGODB_DB]
res = db['ausleihungen'].update_one({'_id': doc_id}, {'$set': update_data})
return res.matched_count > 0
except Exception as e:
print(f"Error activating booking: {e}")
return activate_ausleihung(booking_id)
def get_bookings_starting_now(current_time):
return get_ausleihungen_starting_now(current_time)
def get_bookings_ending_now(current_time):
return get_ausleihungen_ending_now(current_time)