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

884 lines
32 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 os
import json
import Web.modules.database.settings as cfg
from Web.modules.database.settings import MongoClient
import Web.modules.inventarsystem.data_protection as dp
def _get_client():
"""Hilfsfunktion für die Erstellung einer MongoClient-Instanz."""
return MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
# 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)