fix(alerts): limit alerts to 500 per type in SQL + frontend pagination with 'Ver más' + summary bar
This commit is contained in:
@@ -272,38 +272,72 @@ def record_initial(conn, inventory_id, branch_id, quantity, cost=None):
|
||||
return result
|
||||
|
||||
|
||||
def get_alerts(conn, branch_id=None):
|
||||
"""Get stock alerts: zero stock, below minimum, above maximum."""
|
||||
stock_map = get_stock_bulk(conn, branch_id)
|
||||
def get_alerts(conn, branch_id=None, limit_per_type=500):
|
||||
"""Get stock alerts: zero stock, below minimum, above maximum.
|
||||
Returns at most limit_per_type alerts per severity to avoid browser freeze.
|
||||
"""
|
||||
cur = conn.cursor()
|
||||
|
||||
where = "WHERE i.is_active = true"
|
||||
branch_filter = ""
|
||||
params = []
|
||||
if branch_id:
|
||||
where += " AND i.branch_id = %s"
|
||||
branch_filter = " AND i.branch_id = %s"
|
||||
params.append(branch_id)
|
||||
|
||||
# Use a single SQL query with window functions to rank and limit per type
|
||||
cur.execute(f"""
|
||||
SELECT i.id, i.part_number, i.name, i.min_stock, i.max_stock, i.branch_id
|
||||
FROM inventory i {where}
|
||||
""", params)
|
||||
WITH stock AS (
|
||||
SELECT inventory_id, COALESCE(SUM(quantity), 0) AS qty
|
||||
FROM inventory_operations
|
||||
GROUP BY inventory_id
|
||||
),
|
||||
alerts_raw AS (
|
||||
SELECT
|
||||
i.id AS inventory_id,
|
||||
i.part_number,
|
||||
i.name,
|
||||
COALESCE(s.qty, 0) AS stock,
|
||||
i.min_stock,
|
||||
i.max_stock,
|
||||
i.branch_id,
|
||||
CASE
|
||||
WHEN COALESCE(s.qty, 0) <= 0 THEN 'zero'
|
||||
WHEN i.min_stock IS NOT NULL AND COALESCE(s.qty, 0) < i.min_stock THEN 'low'
|
||||
WHEN i.max_stock IS NOT NULL AND COALESCE(s.qty, 0) > i.max_stock THEN 'over'
|
||||
END AS alert_type,
|
||||
CASE
|
||||
WHEN COALESCE(s.qty, 0) <= 0 THEN 'critical'
|
||||
WHEN i.min_stock IS NOT NULL AND COALESCE(s.qty, 0) < i.min_stock THEN 'warning'
|
||||
WHEN i.max_stock IS NOT NULL AND COALESCE(s.qty, 0) > i.max_stock THEN 'info'
|
||||
END AS severity
|
||||
FROM inventory i
|
||||
LEFT JOIN stock s ON s.inventory_id = i.id
|
||||
WHERE i.is_active = true {branch_filter}
|
||||
),
|
||||
ranked AS (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY alert_type ORDER BY inventory_id) AS rn
|
||||
FROM alerts_raw
|
||||
WHERE alert_type IS NOT NULL
|
||||
)
|
||||
SELECT inventory_id, part_number, name, stock, min_stock, max_stock, branch_id, alert_type, severity
|
||||
FROM ranked
|
||||
WHERE rn <= %s
|
||||
ORDER BY severity DESC, inventory_id
|
||||
""", params + [limit_per_type])
|
||||
|
||||
alerts = []
|
||||
for row in cur.fetchall():
|
||||
inv_id, part_num, name, min_s, max_s, br_id = row
|
||||
stock = stock_map.get(inv_id, 0)
|
||||
|
||||
if stock <= 0:
|
||||
alerts.append({'type': 'zero', 'severity': 'critical', 'inventory_id': inv_id,
|
||||
'part_number': part_num, 'name': name, 'stock': stock, 'branch_id': br_id})
|
||||
elif min_s and stock < min_s:
|
||||
alerts.append({'type': 'low', 'severity': 'warning', 'inventory_id': inv_id,
|
||||
'part_number': part_num, 'name': name, 'stock': stock,
|
||||
'min_stock': min_s, 'branch_id': br_id})
|
||||
elif max_s and stock > max_s:
|
||||
alerts.append({'type': 'over', 'severity': 'info', 'inventory_id': inv_id,
|
||||
'part_number': part_num, 'name': name, 'stock': stock,
|
||||
'max_stock': max_s, 'branch_id': br_id})
|
||||
alerts.append({
|
||||
'inventory_id': row[0],
|
||||
'part_number': row[1],
|
||||
'name': row[2],
|
||||
'stock': row[3],
|
||||
'min_stock': row[4],
|
||||
'max_stock': row[5],
|
||||
'branch_id': row[6],
|
||||
'type': row[7],
|
||||
'severity': row[8],
|
||||
})
|
||||
|
||||
cur.close()
|
||||
return alerts
|
||||
|
||||
Reference in New Issue
Block a user