fix(audit): corrige errores criticos y mayores, mejora UX/accesibilidad y optimiza rendimiento
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

- Arregla @require_auth, permisos, race conditions, locks de caja/stock
- Elimina N+1 en layaway, flotilla, dashboard y global_invoice
- Asegura folios atomicos para CFDI, ordenes de servicio y polizas
- Protege client_secret de MercadoLibre en backend
- Conecta botones/filtros de config, customers, accounting e invoicing
- Mejora accesibilidad (labels/aria-label) y estados de carga/vacio
- Limpia accounting.js obsoleto y consolida accounting.v9.js
- Actualiza cache busting a v32 y Service Worker a v32
- Documenta todo en docs/AUDIT_Y_MEJORAS_2026-06-15.md

Tests: 35 passed
This commit is contained in:
2026-06-29 23:54:58 +00:00
parent 59a4893e84
commit 2bdeb2973a
61 changed files with 2879 additions and 706 deletions

View File

@@ -118,3 +118,70 @@ def get_employee_stats():
finally:
cur.close()
conn.close()
@dashboard_stats_bp.route('/credit-alerts', methods=['GET'])
@require_auth()
def credit_alerts():
"""Credit sales that are overdue or due within the next 7 days."""
conn = get_tenant_conn(g.tenant_id)
cur = conn.cursor()
try:
cur.execute("""
SELECT s.id,
c.name as customer_name,
s.total,
s.created_at,
s.created_at + INTERVAL '30 days' as due_date,
COALESCE(SUM(sp.amount), 0) as paid
FROM sales s
JOIN customers c ON c.id = s.customer_id
LEFT JOIN sale_payments sp ON sp.sale_id = s.id
WHERE s.sale_type = 'credit'
AND s.status = 'completed'
GROUP BY s.id, c.name, s.total, s.created_at
HAVING s.total - COALESCE(SUM(sp.amount), 0) > 0
AND s.created_at + INTERVAL '30 days' <= NOW() + INTERVAL '30 days'
ORDER BY due_date
LIMIT 50
""")
rows = []
now = datetime.utcnow()
for r in cur.fetchall():
sale_id = r[0]
customer_name = r[1]
total = float(r[2]) if r[2] else 0
paid = float(r[5]) if r[5] else 0
balance = round(total - paid, 2)
created_at = r[3]
due_date = r[4]
days_until_due = (due_date.replace(tzinfo=None) - now).days if due_date else None
if days_until_due is None:
continue
status = 'overdue' if days_until_due < 0 else ('due_soon' if days_until_due <= 7 else 'current')
label = 'Vencida' if status == 'overdue' else ('Por vencer' if status == 'due_soon' else 'Al corriente')
rows.append({
'sale_id': sale_id,
'folio': f'VTA-{sale_id}',
'customer_name': customer_name,
'issue_date': created_at.isoformat() if created_at else None,
'due_date': due_date.isoformat() if due_date else None,
'days_until_due': days_until_due,
'total': total,
'paid': paid,
'balance': balance,
'status': status,
'status_label': label,
})
return jsonify({
'data': rows,
'overdue_count': sum(1 for r in rows if r['status'] == 'overdue'),
'due_soon_count': sum(1 for r in rows if r['status'] == 'due_soon'),
})
finally:
cur.close()
conn.close()