'use client'; import { useState, useMemo } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { DashboardShell } from '@/components/layouts/dashboard-shell'; import { Card, CardContent, CardHeader, CardTitle, Button, SortableHeader, Input } from '@horux/shared-ui'; import { apiClient } from '@/lib/api/client'; import { formatCurrency, toCfdiDate } from '@/lib/utils'; import { exportToExcel } from '@/lib/export-excel'; import { useTableSort } from '@horux/shared-ui'; import { CfdiViewerModal } from '@/components/cfdi/cfdi-viewer-modal'; import { Eye, Download, CheckSquare, Square, EyeOff, Filter, RotateCcw } from 'lucide-react'; import type { Cfdi } from '@horux/shared'; import { useContribuyenteStore } from '@/stores/contribuyente-store'; const TIPO_ALERTA = 'discrepancia-regimen'; const EXCEL_COLUMNS = [ { header: 'UUID', key: 'uuid', width: 40 }, { header: 'Fecha', key: '_fecha', width: 15 }, { header: 'RFC Emisor', key: 'rfcEmisor', width: 15 }, { header: 'Nombre Emisor', key: 'nombreEmisor', width: 30 }, { header: 'RFC Receptor', key: 'rfcReceptor', width: 15 }, { header: 'Nombre Receptor', key: 'nombreReceptor', width: 30 }, { header: 'Regimen Receptor', key: 'regimenReceptor', width: 18 }, { header: 'Total MXN', key: '_totalMxn', width: 15 }, ]; function prepareRows(data: any[]) { return data.map((c) => ({ ...c, _fecha: toCfdiDate(c.fechaEmision).toLocaleDateString('es-MX'), _totalMxn: Number(c.totalMxn || 0), regimenReceptor: c.regimenReceptor || c.regimenFiscalReceptor || '', })); } export default function DiscrepanciaRegimenPage() { const [selectedCfdi, setSelectedCfdi] = useState(null); const [checked, setChecked] = useState>(new Set()); const [view, setView] = useState<'activos' | 'descartados'>('activos'); const { selectedContribuyenteId } = useContribuyenteStore(); const queryClient = useQueryClient(); // Filters const [fechaDesde, setFechaDesde] = useState(''); const [fechaHasta, setFechaHasta] = useState(''); const [regimenFilter, setRegimenFilter] = useState(''); // Activos (lo que aparece en la alerta) const activosQ = useQuery({ queryKey: ['drilldown-discrepancia', selectedContribuyenteId], queryFn: async () => { const params = new URLSearchParams(); if (selectedContribuyenteId) params.set('contribuyenteId', selectedContribuyenteId); const res = await apiClient.get(`/alertas/drilldown/discrepancia-regimen?${params}`); return res.data; }, enabled: view === 'activos', }); // Descartados (lo que ya se marcó para ignorar) const descartadosQ = useQuery({ queryKey: ['descartados-discrepancia', selectedContribuyenteId], queryFn: async () => { const params = new URLSearchParams({ tipoAlerta: TIPO_ALERTA }); if (selectedContribuyenteId) params.set('contribuyenteId', selectedContribuyenteId); const res = await apiClient.get<{ data: Cfdi[] }>(`/alertas/descartados?${params}`); return res.data.data; }, enabled: view === 'descartados', }); const data = view === 'activos' ? activosQ.data : descartadosQ.data; const isLoading = view === 'activos' ? activosQ.isLoading : descartadosQ.isLoading; // Extract unique regímenes for the filter dropdown const regimenesUnicos = useMemo(() => { if (!data) return []; const set = new Set(); data.forEach((c: any) => { const reg = c.regimenReceptor || c.regimenFiscalReceptor; if (reg) set.add(reg); }); return [...set].sort(); }, [data]); // Apply filters: fecha + regimen (descartados already excluded by backend) const visibleData = useMemo(() => { if (!data) return []; let filtered = data; if (fechaDesde) { filtered = filtered.filter(c => toCfdiDate(c.fechaEmision).toISOString() >= fechaDesde); } if (fechaHasta) { filtered = filtered.filter(c => toCfdiDate(c.fechaEmision).toISOString() <= fechaHasta + 'T23:59:59'); } if (regimenFilter) { filtered = filtered.filter((c: any) => (c.regimenReceptor || c.regimenFiscalReceptor) === regimenFilter); } return filtered; }, [data, fechaDesde, fechaHasta, regimenFilter]); const { sortedData, toggleSort, getSortIndicator } = useTableSort( visibleData, { fecha: (c) => toCfdiDate(c.fechaEmision).getTime(), total: (c) => Number(c.totalMxn || 0), }, 'fecha', ); const handleExport = () => { if (!sortedData || sortedData.length === 0) return; exportToExcel(prepareRows(sortedData), EXCEL_COLUMNS, 'cfdis-discrepancia-regimen'); }; const toggleCheck = (id: string) => { setChecked(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; const toggleSelectAll = () => { if (!sortedData) return; if (checked.size === sortedData.length) { setChecked(new Set()); } else { setChecked(new Set(sortedData.map(c => String(c.id)))); } }; const invalidateAll = () => { queryClient.invalidateQueries({ queryKey: ['drilldown-discrepancia'] }); queryClient.invalidateQueries({ queryKey: ['descartados-discrepancia'] }); queryClient.invalidateQueries({ queryKey: ['alertas-automaticas'] }); queryClient.invalidateQueries({ queryKey: ['alertas'] }); }; const handleDescartar = async () => { const cfdiIds = [...checked].map(id => Number(id)); try { await apiClient.post('/alertas/descartar', { cfdiIds, tipoAlerta: TIPO_ALERTA }); setChecked(new Set()); invalidateAll(); } catch { alert('Error al descartar'); } }; const handleRestaurar = async () => { const cfdiIds = [...checked].map(id => Number(id)); try { await apiClient.delete('/alertas/descartar', { data: { cfdiIds, tipoAlerta: TIPO_ALERTA } }); setChecked(new Set()); invalidateAll(); } catch { alert('Error al restaurar'); } }; const handleChangeView = (next: 'activos' | 'descartados') => { setView(next); setChecked(new Set()); }; const handleClearFilters = () => { setFechaDesde(''); setFechaHasta(''); setRegimenFilter(''); }; const hasActiveFilters = fechaDesde || fechaHasta || regimenFilter; const allChecked = sortedData && sortedData.length > 0 && checked.size === sortedData.length; return (
{view === 'activos' ? 'Facturas recibidas con régimen fiscal que no coincide con los regímenes activos' : 'CFDIs descartados manualmente — ignorados en la alerta'}
{/* Toggle Activos / Descartados */}
{checked.size > 0 && view === 'activos' && ( )} {checked.size > 0 && view === 'descartados' && ( )} {data && data.length > 0 && ( )}
{/* Filters */}
Filtros:
setFechaDesde(e.target.value)} className="h-8 w-[150px] text-sm" />
setFechaHasta(e.target.value)} className="h-8 w-[150px] text-sm" />
{hasActiveFilters && ( )}
{isLoading ? (
Cargando...
) : !sortedData || sortedData.length === 0 ? (
{hasActiveFilters ? 'No hay resultados con los filtros seleccionados' : view === 'activos' ? 'No hay discrepancias nuevas' : 'No hay CFDIs descartados'}
) : (
toggleSort('fecha')} /> toggleSort('total')} /> {sortedData.map((cfdi: any) => ( ))}
UUID RFC Emisor Nombre Emisor Régimen Receptor
{cfdi.uuid?.substring(0, 8)} {toCfdiDate(cfdi.fechaEmision).toLocaleDateString('es-MX')} {cfdi.rfcEmisor} {cfdi.nombreEmisor} {cfdi.regimenReceptor} {formatCurrency(Number(cfdi.totalMxn))}

{sortedData.length} CFDI{sortedData.length !== 1 ? 's' : ''} {view === 'activos' ? 'con discrepancia' : 'descartados'} {hasActiveFilters && data && ` (de ${data.length} total)`}

)}
setSelectedCfdi(null)} />
); }