""" 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)