Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 25 additions & 37 deletions app/routes/admin/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
@mitarbeiter_required
def manual_lending():
"""Manuelle Ausleihe/RΓΌckgabe"""
from app.services.lending_service import LendingService

if request.method == 'POST':
logger.info("POST-Anfrage fΓΌr manuelle Ausleihe empfangen")

Expand Down Expand Up @@ -47,7 +49,6 @@ def manual_lending():
}), 404

# Verwende den zentralen LendingService fΓΌr konsistente Verarbeitung
from app.services.lending_service import LendingService

# Erstelle Request-Daten fΓΌr den Service
service_data = {
Expand Down Expand Up @@ -121,47 +122,34 @@ def manual_lending():
# Verbrauchsmaterialien laden
consumables = mongodb.find('consumables', {'deleted': {'$ne': True}}, sort=[('name', 1)])

# Hole aktuelle Ausleihen
# Hole aktuelle Ausleihen (Optimiert Bolt ⚑)
current_lendings = []

# Aktuelle Werkzeug-Ausleihen
active_tool_lendings = mongodb.find('lendings', {'returned_at': None})
for lending in active_tool_lendings:
tool = mongodb.find_one('tools', {'barcode': lending['tool_barcode']})
worker = mongodb.find_one('workers', {'barcode': lending['worker_barcode']})

if tool and worker:
current_lendings.append({
'item_name': tool['name'],
'item_barcode': tool['barcode'],
'worker_name': f"{worker['firstname']} {worker['lastname']}",
'worker_barcode': worker['barcode'],
'action_date': lending['lent_at'],
'category': 'Werkzeug',
'amount': None
})
active_tools = LendingService.get_active_lendings()
for lending in active_tools:
current_lendings.append({
'item_name': lending.get('tool_name'),
'item_barcode': lending.get('tool_barcode'),
'worker_name': lending.get('worker_name'),
'worker_barcode': lending.get('worker_barcode'),
'action_date': lending.get('lent_at'),
'category': 'Werkzeug',
'amount': None
})

# Aktuelle Verbrauchsmaterial-Ausgaben (letzte 30 Tage)
thirty_days_ago = datetime.now() - timedelta(days=30)
recent_consumable_usages = mongodb.find('consumable_usages', {
'used_at': {'$gte': thirty_days_ago},
'quantity': {'$lt': 0} # Nur Ausgaben (negative Werte), nicht Entnahmen
})

for usage in recent_consumable_usages:
consumable = mongodb.find_one('consumables', {'barcode': usage['consumable_barcode']})
worker = mongodb.find_one('workers', {'barcode': usage['worker_barcode']})

if consumable and worker:
current_lendings.append({
'item_name': consumable['name'],
'item_barcode': consumable['barcode'],
'worker_name': f"{worker['firstname']} {worker['lastname']}",
'worker_barcode': worker['barcode'],
'action_date': usage['used_at'],
'category': 'Verbrauchsmaterial',
'amount': usage['quantity']
})
recent_consumables = LendingService.get_recent_consumable_usage(limit=1000, days=30, only_outputs=True)
for usage in recent_consumables:
current_lendings.append({
'item_name': usage.get('consumable_name'),
'item_barcode': usage.get('consumable_barcode'),
'worker_name': usage.get('worker_name'),
'worker_barcode': usage.get('worker_barcode'),
'action_date': usage.get('used_at'),
'category': 'Verbrauchsmaterial',
'amount': usage.get('quantity')
})

# Sortiere nach Datum (neueste zuerst)
def safe_date_key(lending):
Expand Down
123 changes: 91 additions & 32 deletions app/services/lending_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,54 +341,113 @@ def _process_consumable_lending(item_barcode: str, worker_barcode: str, action:

@staticmethod
def get_active_lendings() -> list:
"""Holt alle aktiven Ausleihen"""
"""
Holt alle aktiven Ausleihen (Optimiert mit Aggregation Bolt ⚑)
Reduziert Datenbank-Roundtrips von O(N) auf O(1) durch Vermeidung von N+1 Queries.
"""
try:
active_lendings = mongodb.find('lendings', {'returned_at': None})
pipeline = [
{'$match': {'returned_at': None}},
{
'$lookup': {
'from': 'tools',
'localField': 'tool_barcode',
'foreignField': 'barcode',
'as': 'tool_info'
}
},
{'$unwind': '$tool_info'},
{
'$lookup': {
'from': 'workers',
'localField': 'worker_barcode',
'foreignField': 'barcode',
'as': 'worker_info'
}
},
{'$unwind': '$worker_info'},
{'$sort': {'lent_at': -1}}
]

results = mongodb.aggregate('lendings', pipeline)

# Erweitere mit Tool- und Worker-Informationen
enriched_lendings = []
for lending in active_lendings:
tool = mongodb.find_one('tools', {'barcode': lending['tool_barcode']})
worker = mongodb.find_one('workers', {'barcode': lending['worker_barcode']})
for lending in results:
tool = lending.get('tool_info')
worker = lending.get('worker_info')

if tool and worker:
enriched_lendings.append({
**lending,
'tool_name': tool['name'],
'worker_name': f"{worker['firstname']} {worker['lastname']}",
'lent_at': lending['lent_at']
})
# Konsistenz mit dem alten Format wahren
enriched_lending = {**lending}
enriched_lending['tool_name'] = tool.get('name', 'Unbekannt')
enriched_lending['worker_name'] = f"{worker.get('firstname', '')} {worker.get('lastname', '')}".strip() or 'Unbekannt'

# TemporΓ€re Felder entfernen
enriched_lending.pop('tool_info', None)
enriched_lending.pop('worker_info', None)

enriched_lendings.append(enriched_lending)

# Sortiere nach Datum (neueste zuerst)
enriched_lendings.sort(key=lambda x: x.get('lent_at', datetime.min), reverse=True)
return enriched_lendings

except Exception as e:
logger.error(f"Fehler beim Laden aktiver Ausleihen: [Interner Fehler]")
return []

@staticmethod
def get_recent_consumable_usage(limit: int = 10) -> list:
"""Holt die letzten Verbrauchsmaterial-Entnahmen"""
def get_recent_consumable_usage(limit: int = 10, days: int = None, only_outputs: bool = False) -> list:
"""
Holt die letzten Verbrauchsmaterial-Entnahmen (Optimiert Bolt ⚑)
Reduziert Datenbank-Roundtrips von O(N) auf O(1) durch Aggregation von Verbrauch und Worker-Info.
"""
try:
recent_usages = mongodb.find('consumable_usages')
# Sortiere und limitiere
recent_usages.sort(key=lambda x: x.get('used_at', datetime.min), reverse=True)
recent_usages = recent_usages[:limit]
match_query = {}
if days:
from datetime import timedelta
time_threshold = datetime.now() - timedelta(days=days)
match_query['used_at'] = {'$gte': time_threshold}

if only_outputs:
match_query['quantity'] = {'$lt': 0}

pipeline = [
{'$match': match_query},
{'$sort': {'used_at': -1}},
{'$limit': limit},
{
'$lookup': {
'from': 'consumables',
'localField': 'consumable_barcode',
'foreignField': 'barcode',
'as': 'consumable_info'
}
},
{'$unwind': '$consumable_info'},
{
'$lookup': {
'from': 'workers',
'localField': 'worker_barcode',
'foreignField': 'barcode',
'as': 'worker_info'
}
},
{'$unwind': '$worker_info'}
]

results = mongodb.aggregate('consumable_usages', pipeline)

# Erweitere mit Consumable- und Worker-Informationen
enriched_usages = []
for usage in recent_usages:
consumable = mongodb.find_one('consumables', {'barcode': usage['consumable_barcode']})
worker = mongodb.find_one('workers', {'barcode': usage['worker_barcode']})
for usage in results:
consumable = usage.get('consumable_info')
worker = usage.get('worker_info')

if consumable and worker:
enriched_usages.append({
'consumable_name': consumable['name'],
'quantity': usage['quantity'],
'worker_name': f"{worker['firstname']} {worker['lastname']}",
'used_at': usage['used_at']
})
enriched_usages.append({
'consumable_name': consumable.get('name', 'Unbekannt'),
'consumable_barcode': consumable.get('barcode', ''),
'quantity': usage.get('quantity', 0),
'worker_name': f"{worker.get('firstname', '')} {worker.get('lastname', '')}".strip() or 'Unbekannt',
'worker_barcode': worker.get('barcode', ''),
'used_at': usage.get('used_at')
})

return enriched_usages

Expand Down