Files
HoruxDespachos/apps/web/app/(dashboard)/impuestos/page.tsx
2026-04-27 22:09:36 -06:00

644 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState } from 'react';
import { Header } from '@/components/layouts/header';
import { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@horux/shared-ui';
import { KpiCard, PeriodSelector, RegimenSelector } from '@horux/shared-ui';
import { useIvaMensual, useIsrMensual, useResumenIva, useResumenIsr, useResumenIsrDesglosado, useCoeficiente } from '@/lib/hooks/use-impuestos';
import { setCoeficiente as setCoeficienteApi } from '@/lib/api/impuestos';
import { useQueryClient } from '@tanstack/react-query';
import { useRegimenesDelPeriodo } from '@/lib/hooks/use-dashboard';
import { Calculator, TrendingUp, TrendingDown, Receipt, Settings, Wallet, CheckSquare, Download } from 'lucide-react';
import { cn } from '@horux/shared-ui';
import { FiscalDisclaimer } from '@/components/fiscal-disclaimer';
import { exportToExcel } from '@/lib/export-excel';
import { ActivosFijosTab } from '@/components/impuestos/activos-fijos-tab';
const meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];
function getMonthRange(year: number, month: number) {
const start = `${year}-${String(month).padStart(2, '0')}-01`;
const lastDay = new Date(year, month, 0).getDate();
const end = `${year}-${String(month).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;
return { start, end };
}
export default function ImpuestosPage() {
const now = new Date();
const defaultRange = getMonthRange(now.getFullYear(), now.getMonth() + 1);
const [fechaInicio, setFechaInicio] = useState(defaultRange.start);
const [fechaFin, setFechaFin] = useState(defaultRange.end);
const [activeTab, setActiveTab] = useState<'iva' | 'isr' | 'activos-fijos'>('iva');
const [regimenSeleccionado, setRegimenSeleccionado] = useState<string | null>(null);
const [conciliacion, setConciliacion] = useState(false);
const [considerarActivos, setConsiderarActivos] = useState(true);
const [considerarNCs, setConsiderarNCs] = useState(true);
const año = new Date(fechaInicio + 'T00:00:00').getFullYear();
const mes = new Date(fechaInicio + 'T00:00:00').getMonth() + 1;
const queryClient = useQueryClient();
const [coefInput, setCoefInput] = useState('');
const [savingCoef, setSavingCoef] = useState(false);
const { data: ivaMensual, isLoading: ivaLoading } = useIvaMensual(año, conciliacion, considerarActivos, considerarNCs);
const { data: isrMensual, isLoading: isrLoading } = useIsrMensual(año, conciliacion, regimenSeleccionado, considerarActivos, considerarNCs);
const { data: resumenIva } = useResumenIva(fechaInicio, fechaFin, conciliacion, considerarActivos, considerarNCs);
const { data: resumenIsr } = useResumenIsr(fechaInicio, fechaFin, conciliacion, considerarActivos, considerarNCs);
const { data: resumenIsrDesglose } = useResumenIsrDesglosado(fechaFin, conciliacion, considerarActivos, considerarNCs);
const { data: coefData } = useCoeficiente(año);
const { data: regimenesPeriodo, isLoading: regimenesLoading } = useRegimenesDelPeriodo(fechaInicio, fechaFin, conciliacion);
const regimenesDisponibles = regimenesPeriodo || [];
if (regimenSeleccionado && regimenesDisponibles.length > 0 &&
!regimenesDisponibles.find(r => r.clave === regimenSeleccionado)) {
setRegimenSeleccionado(null);
}
const formatCurrency = (value: number) =>
new Intl.NumberFormat('es-MX', {
style: 'currency',
currency: 'MXN',
minimumFractionDigits: 0,
}).format(value);
const drillUrl = (titulo: string, filters: Record<string, string>) => {
const p = new URLSearchParams({ titulo, fechaInicio, fechaFin, status: 'vigente', ...filters });
if (regimenSeleccionado) {
if (filters.type === 'EMITIDO') p.set('regimenEmisor', regimenSeleccionado);
if (filters.type === 'RECIBIDO') p.set('regimenReceptor', regimenSeleccionado);
}
return `/drill-down?${p}`;
};
return (
<>
<Header title="Control de Impuestos">
<PeriodSelector
fechaInicio={fechaInicio}
fechaFin={fechaFin}
onChange={(fi, ff) => { setFechaInicio(fi); setFechaFin(ff); }}
/>
</Header>
<main className="p-6 space-y-6">
{/* Filtros */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<RegimenSelector
regimenes={regimenesDisponibles}
selected={regimenSeleccionado}
onChange={setRegimenSeleccionado}
isLoading={regimenesLoading}
/>
<button
onClick={() => setConciliacion(!conciliacion)}
className={cn(
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
conciliacion
? 'bg-primary/10 text-primary border border-primary/30'
: 'hover:bg-accent'
)}
>
<CheckSquare className="h-4 w-4" />
Conciliación
</button>
<button
onClick={() => setConsiderarActivos(!considerarActivos)}
className={cn(
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
considerarActivos
? 'bg-primary/10 text-primary border border-primary/30'
: 'hover:bg-accent'
)}
title="Si está inactivo, no se consideran facturas tipo I con uso de CFDI I01-I08 (compras de activos fijos)."
>
<CheckSquare className="h-4 w-4" />
Considerar activos
</button>
<button
onClick={() => setConsiderarNCs(!considerarNCs)}
className={cn(
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
considerarNCs
? 'bg-primary/10 text-primary border border-primary/30'
: 'hover:bg-accent'
)}
title="Si está inactivo, no se consideran facturas tipo E con tipo de relación 01 (notas de crédito)."
>
<CheckSquare className="h-4 w-4" />
Considerar NCs
</button>
</div>
</div>
<div className="flex gap-2">
<Button
variant={activeTab === 'iva' ? 'default' : 'outline'}
onClick={() => setActiveTab('iva')}
>
<Receipt className="h-4 w-4 mr-2" />
IVA
</Button>
<Button
variant={activeTab === 'isr' ? 'default' : 'outline'}
onClick={() => setActiveTab('isr')}
>
<Calculator className="h-4 w-4 mr-2" />
ISR
</Button>
<Button
variant={activeTab === 'activos-fijos' ? 'default' : 'outline'}
onClick={() => setActiveTab('activos-fijos')}
>
<Wallet className="h-4 w-4 mr-2" />
Activos Fijos
</Button>
</div>
{activeTab === 'iva' && (
<>
{/* IVA KPIs */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
<KpiCard
title={regimenSeleccionado ? `IVA Trasladado (${regimenSeleccionado})` : 'IVA Trasladado'}
value={
regimenSeleccionado
? resumenIva?.trasladadoPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.trasladado || 0
}
icon={<TrendingUp className="h-4 w-4" />}
subtitle="Cobrado a clientes"
href={drillUrl('IVA Trasladado - CFDIs Emitidos', { bucket: 'causado' })}
/>
<KpiCard
title={regimenSeleccionado ? `IVA Acreditable (${regimenSeleccionado})` : 'IVA Acreditable'}
value={
regimenSeleccionado
? resumenIva?.acreditablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.acreditable || 0
}
icon={<TrendingDown className="h-4 w-4" />}
subtitle="Pagado a proveedores"
href={drillUrl('IVA Acreditable - CFDIs Recibidos', { bucket: 'acreditable' })}
/>
{(() => {
const val = regimenSeleccionado
? resumenIva?.retenidoPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.retenido || 0;
return (
<KpiCard
title={regimenSeleccionado ? `IVA Retenido (${regimenSeleccionado})` : 'IVA Retenido'}
value={val}
icon={<Receipt className="h-4 w-4" />}
trend={val > 0 ? 'up' : val < 0 ? 'down' : 'neutral'}
trendValue={val > 0 ? 'A favor' : val < 0 ? 'En contra' : 'Neutro'}
/>
);
})()}
{(() => {
const t = regimenSeleccionado
? resumenIva?.trasladadoPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.trasladado || 0;
const a = regimenSeleccionado
? resumenIva?.acreditablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.acreditable || 0;
const ret = regimenSeleccionado
? resumenIva?.retenidoPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIva?.retenido || 0;
const res = t - a - ret;
return (
<KpiCard
title={regimenSeleccionado ? `Resultado (${regimenSeleccionado})` : 'Resultado del Periodo'}
value={res}
icon={<Calculator className="h-4 w-4" />}
trend={res > 0 ? 'up' : res < 0 ? 'down' : 'neutral'}
trendValue={res > 0 ? 'Por pagar' : res < 0 ? 'A favor' : 'Neutro'}
/>
);
})()}
<KpiCard
title="Acumulado Anual"
value={resumenIva?.acumuladoAnual || 0}
icon={<Receipt className="h-4 w-4" />}
trend={(resumenIva?.acumuladoAnual || 0) < 0 ? 'up' : 'neutral'}
trendValue={(resumenIva?.acumuladoAnual || 0) < 0 ? 'Saldo a favor' : ''}
/>
</div>
{/* IVA Mensual Table */}
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">Histórico IVA {año}</CardTitle>
{ivaMensual && ivaMensual.length > 0 && (
<Button variant="outline" size="sm" onClick={() => exportToExcel(
ivaMensual.map(r => ({
Mes: meses[r.mes - 1],
Trasladado: r.ivaTrasladado,
Acreditable: r.ivaAcreditable,
Retenido: r.ivaRetenido,
Resultado: r.resultado,
Acumulado: r.acumulado,
})),
[
{ header: 'Mes', key: 'Mes', width: 12 },
{ header: 'Trasladado', key: 'Trasladado', width: 18 },
{ header: 'Acreditable', key: 'Acreditable', width: 18 },
{ header: 'Retenido', key: 'Retenido', width: 18 },
{ header: 'Resultado', key: 'Resultado', width: 18 },
{ header: 'Acumulado', key: 'Acumulado', width: 18 },
],
`iva-mensual-${año}`,
)}>
<Download className="h-4 w-4 mr-1" /> Excel
</Button>
)}
</CardHeader>
<CardContent>
{ivaLoading ? (
<div className="text-center py-8 text-muted-foreground">
Cargando...
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b text-left text-sm text-muted-foreground">
<th className="pb-3 font-medium">Mes</th>
<th className="pb-3 font-medium text-right">Trasladado</th>
<th className="pb-3 font-medium text-right">Acreditable</th>
<th className="pb-3 font-medium text-right">Retenido</th>
<th className="pb-3 font-medium text-right">Resultado</th>
<th className="pb-3 font-medium text-right">Acumulado</th>
<th className="pb-3 font-medium">Estado</th>
</tr>
</thead>
<tbody className="text-sm">
{ivaMensual?.map((row) => (
<tr key={row.mes} className="border-b hover:bg-muted/50">
<td className="py-3 font-medium">{meses[row.mes - 1]}</td>
<td className="py-3 text-right">
{formatCurrency(row.ivaTrasladado)}
</td>
<td className="py-3 text-right">
{formatCurrency(row.ivaAcreditable)}
</td>
<td className="py-3 text-right">
{formatCurrency(row.ivaRetenido)}
</td>
<td
className={`py-3 text-right font-medium ${
row.resultado > 0
? 'text-destructive'
: 'text-success'
}`}
>
{formatCurrency(row.resultado)}
</td>
<td
className={`py-3 text-right font-medium ${
row.acumulado > 0
? 'text-destructive'
: 'text-success'
}`}
>
{formatCurrency(row.acumulado)}
</td>
<td className="py-3">
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${
row.estado === 'declarado'
? 'bg-success/10 text-success'
: 'bg-warning/10 text-warning'
}`}
>
{row.estado === 'declarado' ? 'Declarado' : 'Pendiente'}
</span>
</td>
</tr>
))}
{(!ivaMensual || ivaMensual.length === 0) && (
<tr>
<td colSpan={7} className="py-8 text-center text-muted-foreground">
No hay registros de IVA para este año
</td>
</tr>
)}
</tbody>
</table>
</div>
)}
</CardContent>
</Card>
</>
)}
{activeTab === 'isr' && (
<>
{/* ISR KPIs */}
{(() => {
const bg = regimenSeleccionado
? resumenIsr?.baseGravablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)
: null;
const showUtilidad = true;
const ingSel = regimenSeleccionado
? resumenIsr?.ingresosPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIsr?.ingresosAcumulados || 0;
const dedSel = regimenSeleccionado
? resumenIsr?.deduccionesPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: resumenIsr?.deducciones || 0;
// ISR a pagar filtered by regime
const bgSelForKpi = regimenSeleccionado
? resumenIsr?.baseGravablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)
: null;
const isrCausadoSel = regimenSeleccionado
? (bgSelForKpi?.isrCausado || 0)
: resumenIsr?.isrCausado || 0;
const isrRetenidoSel = regimenSeleccionado ? 0 : resumenIsr?.isrRetenido || 0;
const isrAPagarSel = Math.max(0, isrCausadoSel - isrRetenidoSel);
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
<KpiCard
title={regimenSeleccionado ? `Ingresos ISR (${regimenSeleccionado})` : 'Ingresos Acumulados'}
value={ingSel}
icon={<TrendingUp className="h-4 w-4" />}
href={drillUrl('Ingresos ISR - CFDIs Emitidos', { bucket: 'ingresos' })}
/>
<KpiCard
title={regimenSeleccionado ? `Deducciones (${regimenSeleccionado})` : 'Deducciones'}
value={dedSel}
icon={<TrendingDown className="h-4 w-4" />}
href={drillUrl('Deducciones - CFDIs Recibidos', { bucket: 'gastos' })}
/>
<KpiCard
title={regimenSeleccionado ? `Base Gravable (${regimenSeleccionado})` : 'Base Gravable'}
value={regimenSeleccionado ? (bg?.baseGravable ?? 0) : resumenIsr?.baseGravable || 0}
icon={<Calculator className="h-4 w-4" />}
subtitle={bg ? (bg.formula === 'ingresos-deducciones' ? 'Ingresos - Deducciones' : 'Solo ingresos') : undefined}
/>
<KpiCard
title={regimenSeleccionado ? `ISR a Pagar (${regimenSeleccionado})` : 'ISR a Pagar'}
value={isrAPagarSel}
icon={<Receipt className="h-4 w-4" />}
trend={isrAPagarSel > 0 ? 'up' : 'neutral'}
/>
<KpiCard
title={regimenSeleccionado ? `Utilidad del Periodo (${regimenSeleccionado})` : 'Utilidad del Periodo'}
value={ingSel - dedSel}
icon={<Wallet className="h-4 w-4" />}
trend={(ingSel - dedSel) > 0 ? 'up' : 'down'}
subtitle="Ingresos - Deducciones"
/>
</div>
);
})()}
{/* ISR Info + Coeficiente */}
{(() => {
// Regímenes PF no usan coeficiente de utilidad
const REGIMENES_PF = ['605', '606', '612', '621', '625'];
const isResicoPF = regimenSeleccionado === '626'; // RESICO PF also doesn't use coeficiente
const showCoeficiente = !regimenSeleccionado || (!REGIMENES_PF.includes(regimenSeleccionado) && !isResicoPF);
return (
<div className={`grid gap-4 ${showCoeficiente ? 'lg:grid-cols-3' : ''}`}>
<Card className={showCoeficiente ? 'lg:col-span-2' : ''}>
<CardHeader>
<CardTitle className="text-base">Cálculo de ISR del Periodo</CardTitle>
</CardHeader>
<CardContent>
{(() => {
const desglose = resumenIsrDesglose;
if (!desglose) {
return <div className="text-sm text-muted-foreground">Cargando</div>;
}
const { delPeriodo, anteriores, total, mesFinal, anio } = desglose;
const labelMesFinal = `${meses[mesFinal - 1]} ${anio}`;
const labelAnteriores =
mesFinal === 1
? '(sin meses anteriores)'
: mesFinal === 2
? `(${meses[0]})`
: `(${meses[0]}-${meses[mesFinal - 2]})`;
// Resolver per-régimen si hay régimen seleccionado, igual patrón que antes.
const ingPer = regimenSeleccionado
? delPeriodo.ingresosPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: delPeriodo.ingresosAcumulados || 0;
const ingAnt = regimenSeleccionado
? anteriores.ingresosPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: anteriores.ingresosAcumulados || 0;
const dedPer = regimenSeleccionado
? delPeriodo.deduccionesPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: delPeriodo.deducciones || 0;
const dedAnt = regimenSeleccionado
? anteriores.deduccionesPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: anteriores.deducciones || 0;
const bgTotal = regimenSeleccionado
? total.baseGravablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.baseGravable || 0
: total.baseGravable || 0;
const causadoTotal = regimenSeleccionado
? total.baseGravablePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.isrCausado || 0
: total.isrCausado || 0;
const retenido = total.isrRetenido || 0;
const aPagar = Math.max(0, causadoTotal - (regimenSeleccionado ? 0 : retenido));
return (
<div className="space-y-1">
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">Ingresos del periodo ({labelMesFinal})</span>
<span className="font-medium">{formatCurrency(ingPer)}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">(+) Ingresos acumulados anteriores {labelAnteriores}</span>
<span className="font-medium">{formatCurrency(ingAnt)}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">() Deducciones del periodo ({labelMesFinal})</span>
<span className="font-medium">{formatCurrency(dedPer)}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">() Deducciones acumuladas anteriores {labelAnteriores}</span>
<span className="font-medium">{formatCurrency(dedAnt)}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="font-medium">(=) Base gravable acumulada</span>
<span className={cn('font-medium', bgTotal < 0 ? 'text-destructive' : '')}>
{formatCurrency(bgTotal)}
</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">ISR causado (acumulado)</span>
<span className="font-medium">{formatCurrency(causadoTotal)}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">() ISR retenido (acumulado)</span>
<span className="font-medium">{formatCurrency(regimenSeleccionado ? 0 : retenido)}</span>
</div>
<div className="flex justify-between py-2 bg-muted/50 px-4 rounded-lg mt-2">
<span className="font-medium">ISR a pagar</span>
<span className="font-bold text-lg">{formatCurrency(aPagar)}</span>
</div>
</div>
);
})()}
</CardContent>
</Card>
{showCoeficiente && (
<Card>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<Settings className="h-4 w-4" />
Coeficiente de Utilidad {año}
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
Se utiliza para calcular el ISR de regimenes como General de Ley (601) y otros.
</p>
<div className="space-y-2">
<label className="text-sm font-medium">Coeficiente actual</label>
{coefData?.coeficiente !== null && coefData?.coeficiente !== undefined ? (
<p className="text-2xl font-bold">{coefData.coeficiente}</p>
) : (
<p className="text-sm text-destructive">No configurado</p>
)}
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Actualizar coeficiente</label>
<div className="flex gap-2">
<Input
type="number"
step="0.0001"
min="0"
max="1"
placeholder="Ej: 0.3521"
value={coefInput}
onChange={(e) => setCoefInput(e.target.value)}
className="h-9"
/>
<Button
size="sm"
disabled={!coefInput || savingCoef}
onClick={async () => {
const val = parseFloat(coefInput);
if (isNaN(val) || val < 0 || val > 1) return;
setSavingCoef(true);
try {
await setCoeficienteApi(año, val);
setCoefInput('');
queryClient.invalidateQueries({ queryKey: ['coeficiente'] });
queryClient.invalidateQueries({ queryKey: ['isr-resumen'] });
} catch {
alert('Error al guardar');
} finally {
setSavingCoef(false);
}
}}
>
{savingCoef ? 'Guardando...' : 'Guardar'}
</Button>
</div>
</div>
<p className="text-xs text-muted-foreground">
Este valor se obtiene de la declaracion anual del ejercicio anterior. No se sobrescribe entre años.
</p>
</div>
</CardContent>
</Card>
)}
</div>
);
})()}
{/* ISR Monthly Table */}
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">Histórico ISR {año}</CardTitle>
{isrMensual && isrMensual.length > 0 && (
<Button variant="outline" size="sm" onClick={() => exportToExcel(
isrMensual.map(r => ({
Mes: meses[r.mes - 1],
Ingresos: r.ingresosAcumulados,
'Ingresos Acumulados': r.ingresosAcum,
Deducciones: r.deducciones,
'Deducciones Acumuladas': r.deduccionesAcum,
'Base Gravable Acumulada': r.baseGravableAcum,
})),
[
{ header: 'Mes', key: 'Mes', width: 12 },
{ header: 'Ingresos', key: 'Ingresos', width: 18 },
{ header: 'Ingresos Acumulados', key: 'Ingresos Acumulados', width: 22 },
{ header: 'Deducciones', key: 'Deducciones', width: 18 },
{ header: 'Deducciones Acumuladas', key: 'Deducciones Acumuladas', width: 22 },
{ header: 'Base Gravable Acumulada', key: 'Base Gravable Acumulada', width: 22 },
],
`isr-mensual-${año}`,
)}>
<Download className="h-4 w-4 mr-1" /> Excel
</Button>
)}
</CardHeader>
<CardContent>
{isrLoading ? (
<div className="text-center py-8 text-muted-foreground">Cargando...</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b text-left text-sm text-muted-foreground">
<th className="pb-3 font-medium">Mes</th>
<th className="pb-3 font-medium text-right">Ingresos</th>
<th className="pb-3 font-medium text-right">Ingresos Acum.</th>
<th className="pb-3 font-medium text-right">Deducciones</th>
<th className="pb-3 font-medium text-right">Deducciones Acum.</th>
<th className="pb-3 font-medium text-right">Base Gravable Acum.</th>
</tr>
</thead>
<tbody className="text-sm">
{isrMensual?.map((row) => (
<tr key={row.mes} className="border-b hover:bg-muted/50">
<td className="py-3 font-medium">{meses[row.mes - 1]}</td>
<td className="py-3 text-right">{formatCurrency(row.ingresosAcumulados)}</td>
<td className="py-3 text-right">{formatCurrency(row.ingresosAcum)}</td>
<td className="py-3 text-right">{formatCurrency(row.deducciones)}</td>
<td className="py-3 text-right">{formatCurrency(row.deduccionesAcum)}</td>
<td className={cn(
'py-3 text-right font-medium',
row.baseGravableAcum < 0 ? 'text-destructive' : ''
)}>
{formatCurrency(row.baseGravableAcum)}
</td>
</tr>
))}
{(!isrMensual || isrMensual.length === 0) && (
<tr>
<td colSpan={6} className="py-8 text-center text-muted-foreground">
No hay registros de ISR para este año
</td>
</tr>
)}
</tbody>
</table>
</div>
)}
</CardContent>
</Card>
</>
)}
{activeTab === 'activos-fijos' && (
<ActivosFijosTab año={año} mes={mes} />
)}
<FiscalDisclaimer />
</main>
</>
);
}