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
107 changes: 72 additions & 35 deletions app/routes/admin/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,47 +121,84 @@ def manual_lending():
# Verbrauchsmaterialien laden
consumables = mongodb.find('consumables', {'deleted': {'$ne': True}}, sort=[('name', 1)])

# Hole aktuelle Ausleihen
# Hole aktuelle Ausleihen (Optimiert mit Aggregation zur Vermeidung von N+1 Problemen)
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
})
tool_lending_pipeline = [
{'$match': {'returned_at': None}},
{
'$lookup': {
'from': 'tools',
'localField': 'tool_barcode',
'foreignField': 'barcode',
'as': 'tool'
}
},
{'$unwind': '$tool'},
{
'$lookup': {
'from': 'workers',
'localField': 'worker_barcode',
'foreignField': 'barcode',
'as': 'worker'
}
},
{'$unwind': '$worker'},
{
'$project': {
'item_name': '$tool.name',
'item_barcode': '$tool.barcode',
'worker_name': {'$concat': ['$worker.firstname', ' ', '$worker.lastname']},
'worker_barcode': '$worker.barcode',
'action_date': '$lent_at',
'category': {'$literal': 'Werkzeug'},
'amount': {'$literal': None}
}
}
]
current_lendings.extend(mongodb.aggregate('lendings', tool_lending_pipeline))

# 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']
})
usage_pipeline = [
{
'$match': {
'used_at': {'$gte': thirty_days_ago},
'quantity': {'$lt': 0} # Nur Ausgaben (negative Werte), nicht Entnahmen
}
},
{
'$lookup': {
'from': 'consumables',
'localField': 'consumable_barcode',
'foreignField': 'barcode',
'as': 'consumable'
}
},
{'$unwind': '$consumable'},
{
'$lookup': {
'from': 'workers',
'localField': 'worker_barcode',
'foreignField': 'barcode',
'as': 'worker'
}
},
{'$unwind': '$worker'},
{
'$project': {
'item_name': '$consumable.name',
'item_barcode': '$consumable.barcode',
'worker_name': {'$concat': ['$worker.firstname', ' ', '$worker.lastname']},
'worker_barcode': '$worker.barcode',
'action_date': '$used_at',
'category': {'$literal': 'Verbrauchsmaterial'},
'amount': '$quantity'
}
}
]
current_lendings.extend(mongodb.aggregate('consumable_usages', usage_pipeline))

# Sortiere nach Datum (neueste zuerst)
def safe_date_key(lending):
Expand Down
156 changes: 87 additions & 69 deletions app/services/lending_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,56 +341,80 @@ 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)"""
try:
active_lendings = mongodb.find('lendings', {'returned_at': None})

# 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']})

if tool and worker:
enriched_lendings.append({
**lending,
'tool_name': tool['name'],
'worker_name': f"{worker['firstname']} {worker['lastname']}",
'lent_at': lending['lent_at']
})

# Sortiere nach Datum (neueste zuerst)
enriched_lendings.sort(key=lambda x: x.get('lent_at', datetime.min), reverse=True)
return enriched_lendings
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'},
{
'$addFields': {
'tool_name': '$tool_info.name',
'worker_name': {'$concat': ['$worker_info.firstname', ' ', '$worker_info.lastname']}
}
},
{'$project': {'tool_info': 0, 'worker_info': 0}},
{'$sort': {'lent_at': -1}}
]

return mongodb.aggregate('lendings', pipeline)

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"""
"""Holt die letzten Verbrauchsmaterial-Entnahmen (Optimiert mit Aggregation)"""
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]

# 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']})

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']
})

return enriched_usages
pipeline = [
{'$sort': {'used_at': -1}},
{'$limit': limit},
{
'$lookup': {
'from': 'consumables',
'localField': 'consumable_barcode',
'foreignField': 'barcode',
'as': 'consumable'
}
},
{'$unwind': '$consumable'},
{
'$lookup': {
'from': 'workers',
'localField': 'worker_barcode',
'foreignField': 'barcode',
'as': 'worker'
}
},
{'$unwind': '$worker'},
{
'$project': {
'consumable_name': '$consumable.name',
'quantity': '$quantity',
'worker_name': {'$concat': ['$worker.firstname', ' ', '$worker.lastname']},
'used_at': '$used_at'
}
}
]

return mongodb.aggregate('consumable_usages', pipeline)

except Exception as e:
logger.error(f"Fehler beim Laden der Verbrauchsmaterial-Entnahmen: [Interner Fehler]")
Expand All @@ -399,7 +423,7 @@ def get_recent_consumable_usage(limit: int = 10) -> list:
@staticmethod
def get_worker_consumable_history(worker_barcode: str) -> List[Dict[str, Any]]:
"""
Holt die Verbrauchsmaterial-Historie für einen Mitarbeiter
Holt die Verbrauchsmaterial-Historie für einen Mitarbeiter (Optimiert mit Aggregation)

Args:
worker_barcode: Barcode des Mitarbeiters
Expand All @@ -408,33 +432,27 @@ def get_worker_consumable_history(worker_barcode: str) -> List[Dict[str, Any]]:
List[Dict]: Liste der Verbrauchsmaterial-Ausgaben
"""
try:
# Hole alle Verbrauchsmaterial-Ausgaben des Mitarbeiters
usages = mongodb.find('consumable_usages', {'worker_barcode': worker_barcode})

# Erweitere mit Consumable-Informationen
enriched_usages = []
for usage in usages:
consumable = mongodb.find_one('consumables', {'barcode': usage['consumable_barcode']})
if consumable:
usage['consumable_name'] = consumable.get('name', '')
usage['consumable_barcode'] = usage['consumable_barcode']
enriched_usages.append(usage)

# Sortiere nach Datum (neueste zuerst)
def safe_date_key(usage):
used_at = usage.get('used_at')
if isinstance(used_at, str):
try:
return datetime.strptime(used_at, '%Y-%m-%d %H:%M:%S')
except (ValueError, TypeError):
return datetime.min
elif isinstance(used_at, datetime):
return used_at
else:
return datetime.min

enriched_usages.sort(key=safe_date_key, reverse=True)
return enriched_usages
pipeline = [
{'$match': {'worker_barcode': worker_barcode}},
{
'$lookup': {
'from': 'consumables',
'localField': 'consumable_barcode',
'foreignField': 'barcode',
'as': 'consumable_info'
}
},
{'$unwind': '$consumable_info'},
{
'$addFields': {
'consumable_name': '$consumable_info.name'
}
},
{'$project': {'consumable_info': 0}},
{'$sort': {'used_at': -1}}
]

return mongodb.aggregate('consumable_usages', pipeline)

except Exception as e:
logger.error(f"Fehler beim Laden der Verbrauchsmaterial-Historie: [Interner Fehler]")
Expand Down
Loading