Dashboard de Monitorización
Exportar contenido
Panel de control para supervisar el estado de los envíos a VeriFactu y gestionar errores.
- Ve a Panel de Negocio > VeriFactu (o usa ruta directa
/owner/verifactu/dashboard). - Revisa las tarjetas de estadísticas — resumen de envíos.
- Filtra por estado, negocio o rango de fechas — ajusta la vista.
- Revisa la distribución de errores — identifica problemas recurrentes.
- Consulta la tendencia diaria — gráfico de envíos vs errores (últimos 7 días).
- Navega por la tabla de facturas — últimas 100 facturas emitidas.
Tarjetas de estadísticas
Sección titulada «Tarjetas de estadísticas»| Tarjeta | Métrica | Color |
|---|---|---|
| Total Facturas | Facturas emitidas (no borradores) | Azul |
| Enviadas | Status = SENT | Verde |
| Pendientes | Status = PENDING_RETRY | Amarillo |
| Errores | Status = ERROR + FAILED | Rojo |
Tasa de éxito:
Tasa = (Enviadas / Total con status) × 100Filtros disponibles
Sección titulada «Filtros disponibles»| Filtro | Valores |
|---|---|
| Estado VeriFactu | Todos, Enviadas, Error, Pendiente Reintento, Fallidas, No Enviadas |
| Negocio | Todos, [lista de negocios] |
| Fecha Inicio | Selector de fecha |
| Fecha Fin | Selector de fecha |
Distribución de errores
Sección titulada «Distribución de errores»Tabla con los códigos de error más frecuentes:
| Código | Descripción | Ocurrencias |
|---|---|---|
| 9999 | Error interno del servicio | 15 |
| TIMEOUT | Timeout de conexión | 8 |
| VAL-001 | NIF del emisor inválido | 3 |
Tendencia diaria
Sección titulada «Tendencia diaria»Gráfico de barras horizontales que muestra:
- Barra verde: Envíos exitosos
- Barra roja: Errores
- Total: Suma de ambos
Ejemplo:
16 feb [████████████ 12] [██ 2] 14 total15 feb [██████████ 10] [████ 4] 14 totalComo se que lo hice bien
Sección titulada «Como se que lo hice bien»- Las tarjetas muestran números actualizados.
- Los filtros funcionan y reducen la lista de facturas.
- La tabla de errores muestra códigos reales si existen errores.
- El gráfico de tendencias muestra datos de los últimos 7 días.
Detalles tecnicos — Dashboard de Monitorizacion
Sección titulada «Detalles tecnicos — Dashboard de Monitorizacion»Componente: VeriFactuDashboard
Ubicación: src/features/invoices/sub-modules/verifactu/pages/VeriFactuDashboard.tsx
Estado del componente:
const [stats, setStats] = useState<VeriFactuStats | null>(null);const [invoices, setInvoices] = useState<Invoice[]>([]);const [errorDistribution, setErrorDistribution] = useState<ErrorDistribution[]>([]);const [dailyTrends, setDailyTrends] = useState<DailyTrend[]>([]);const [filterStatus, setFilterStatus] = useState<string>("all");const [filterBusiness, setFilterBusiness] = useState<string>("all");const [filterStartDate, setFilterStartDate] = useState<string>("");const [filterEndDate, setFilterEndDate] = useState<string>("");Query de facturas:
let query = supabase .from("invoices") .select(` *, businesses!inner(name), customers!inner(name) `) .neq("status", "draft") .order("issue_date", { ascending: false }) .limit(100);
// Aplicar filtrosif (filterStatus === "not_sent") { query = query.is("verifactu_status", null);} else if (filterStatus !== "all") { query = query.eq("verifactu_status", filterStatus.toUpperCase());}
if (filterBusiness !== "all") { query = query.eq("business_id", filterBusiness);}
if (filterStartDate) { query = query.gte("issue_date", filterStartDate);}
if (filterEndDate) { query = query.lte("issue_date", filterEndDate);}Cálculo de estadísticas:
const totalInvoices = invoiceData?.length || 0;const sentInvoices = invoiceData?.filter(i => i.verifactu_status === 'SENT').length || 0;const errorInvoices = invoiceData?.filter(i => i.verifactu_status === 'ERROR').length || 0;const pendingRetryInvoices = invoiceData?.filter(i => i.verifactu_status === 'PENDING_RETRY').length || 0;const failedInvoices = invoiceData?.filter(i => i.verifactu_status === 'FAILED').length || 0;const successRate = totalInvoices > 0 ? (sentInvoices / totalInvoices) * 100 : 0;Distribución de errores:
const errorMap = new Map<string, { description: string; count: number }>();invoiceData?.forEach(invoice => { if (invoice.verifactu_error_code && (invoice.verifactu_status === 'ERROR' || invoice.verifactu_status === 'FAILED')) { const key = invoice.verifactu_error_code; const existing = errorMap.get(key); if (existing) { existing.count++; } else { errorMap.set(key, { description: invoice.verifactu_error_description || "Unknown error", count: 1, }); } }});Tendencia diaria (últimos 7 días):
const last7Days = Array.from({ length: 7 }, (_, i) => { const date = new Date(); date.setDate(date.getDate() - (6 - i)); return date.toISOString().split('T')[0];});
const trends = last7Days.map(date => { const dayInvoices = invoiceData?.filter(i => i.verifactu_sent_at?.startsWith(date) ) || []; return { date, sent: dayInvoices.filter(i => i.verifactu_status === 'SENT').length, errors: dayInvoices.filter(i => i.verifactu_status === 'ERROR' || i.verifactu_status === 'FAILED' ).length, };});Queries útiles
Tasa de éxito por día (últimos 30 días):
SELECT DATE(verifactu_sent_at) as fecha, COUNT(*) FILTER (WHERE verifactu_status = 'SENT') as enviadas, COUNT(*) as total, ROUND( COUNT(*) FILTER (WHERE verifactu_status = 'SENT')::numeric / NULLIF(COUNT(*), 0) * 100, 2 ) as tasa_exito_pctFROM invoicesWHERE business_id = '<business_id>' AND verifactu_sent_at >= CURRENT_DATE - INTERVAL '30 days'GROUP BY DATE(verifactu_sent_at)ORDER BY fecha DESC;Top 10 errores más frecuentes:
SELECT verifactu_error_code, verifactu_error_description, COUNT(*) as ocurrencias, ARRAY_AGG(invoice_number ORDER BY issue_date DESC) as ejemplosFROM invoicesWHERE business_id = '<business_id>' AND verifactu_error_code IS NOT NULLGROUP BY verifactu_error_code, verifactu_error_descriptionORDER BY ocurrencias DESCLIMIT 10;