feat(dashboard): agregar scorecards de notas de crédito emitidas y recibidas
- Extiende KpiData con ncsEmitidas, ncsEmitidasPorRegimen, ncsRecibidas y ncsRecibidasPorRegimen. - En getKpis se reutilizan calcularNcsEmitidasPorRegimen y calcularNcsRecibidasPorRegimen en paralelo. - En el dashboard se agregan dos KpiCard y su desglose por régimen.
This commit is contained in:
@@ -1107,10 +1107,21 @@ export async function getKpis(
|
||||
const ctx = await resolveContribuyenteContext(pool, tenantId, contribuyenteId);
|
||||
const esEmisor = ctx.esEmisor;
|
||||
const esReceptor = ctx.esReceptor;
|
||||
const ingresosData = await calcularIngresosPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId);
|
||||
const egresosData = await calcularEgresosPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId);
|
||||
const adquisicionData = await calcularAdquisicionesMercancias(pool, tenantId, fechaInicio, fechaFin, conciliacion, contribuyenteId);
|
||||
const ivaData = await calcularIvaBalancePorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId);
|
||||
const [
|
||||
ingresosData,
|
||||
egresosData,
|
||||
adquisicionData,
|
||||
ivaData,
|
||||
ncsEmitidasData,
|
||||
ncsRecibidasData,
|
||||
] = await Promise.all([
|
||||
calcularIngresosPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId),
|
||||
calcularEgresosPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId),
|
||||
calcularAdquisicionesMercancias(pool, tenantId, fechaInicio, fechaFin, conciliacion, contribuyenteId),
|
||||
calcularIvaBalancePorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId),
|
||||
calcularNcsEmitidasPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId),
|
||||
calcularNcsRecibidasPorRegimen(pool, tenantId, fechaInicio, fechaFin, undefined, undefined, conciliacion, contribuyenteId),
|
||||
]);
|
||||
|
||||
// IVA a favor año actual: desde enero del año en curso
|
||||
const ivaAFavorAcumulado = await calcularIvaAFavorAcumulado(pool, tenantId, fechaFin, undefined, conciliacion, contribuyenteId);
|
||||
@@ -1163,6 +1174,10 @@ export async function getKpis(
|
||||
cfdisEmitidosPorRegimen: emitidosPorRegimen,
|
||||
cfdisRecibidos: recibidosPorRegimen.reduce((s: number, r: any) => s + r.total, 0),
|
||||
cfdisRecibidosPorRegimen: recibidosPorRegimen,
|
||||
ncsEmitidas: ncsEmitidasData.total,
|
||||
ncsEmitidasPorRegimen: ncsEmitidasData.porRegimen,
|
||||
ncsRecibidas: ncsRecibidasData.total,
|
||||
ncsRecibidasPorRegimen: ncsRecibidasData.porRegimen,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
AlertTriangle,
|
||||
ShoppingCart,
|
||||
CheckSquare,
|
||||
FileMinus,
|
||||
FilePlus,
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@horux/shared-ui';
|
||||
import { FiscalDisclaimer } from '@/components/fiscal-disclaimer';
|
||||
@@ -118,6 +120,15 @@ export default function DashboardPage() {
|
||||
? kpis?.ivaBalancePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
|
||||
: kpis?.ivaBalance || 0;
|
||||
|
||||
// Notas de crédito
|
||||
const ncsEmitidasDisplay = regimenSeleccionado
|
||||
? kpis?.ncsEmitidasPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
|
||||
: kpis?.ncsEmitidas || 0;
|
||||
|
||||
const ncsRecibidasDisplay = regimenSeleccionado
|
||||
? kpis?.ncsRecibidasPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
|
||||
: kpis?.ncsRecibidas || 0;
|
||||
|
||||
const ivaAnterior = regimenSeleccionado
|
||||
? kpisAnterior?.ivaBalancePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
|
||||
: kpisAnterior?.ivaBalance || 0;
|
||||
@@ -203,7 +214,7 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* KPIs */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<KpiCard
|
||||
title={regimenSeleccionado ? `Ingresos del Mes (${regimenSeleccionado})` : 'Ingresos del Mes'}
|
||||
value={ingresosDisplay}
|
||||
@@ -248,11 +259,25 @@ export default function DashboardPage() {
|
||||
}
|
||||
href={drillUrl('Balance IVA - CFDIs', {})}
|
||||
/>
|
||||
<KpiCard
|
||||
title={regimenSeleccionado ? `NCs Emitidas (${regimenSeleccionado})` : 'NCs Emitidas'}
|
||||
value={ncsEmitidasDisplay}
|
||||
icon={<FileMinus className="h-4 w-4" />}
|
||||
trend="neutral"
|
||||
trendValue="Notas de crédito emitidas"
|
||||
/>
|
||||
<KpiCard
|
||||
title={regimenSeleccionado ? `NCs Recibidas (${regimenSeleccionado})` : 'NCs Recibidas'}
|
||||
value={ncsRecibidasDisplay}
|
||||
icon={<FilePlus className="h-4 w-4" />}
|
||||
trend="neutral"
|
||||
trendValue="Notas de crédito recibidas"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Desglose por régimen */}
|
||||
{!regimenSeleccionado && kpis && (
|
||||
(kpis.ingresosPorRegimen.length > 1 || kpis.egresosPorRegimen.length > 1 || kpis.ivaBalancePorRegimen.length > 1) && (
|
||||
(kpis.ingresosPorRegimen.length > 1 || kpis.egresosPorRegimen.length > 1 || kpis.ivaBalancePorRegimen.length > 1 || kpis.ncsEmitidasPorRegimen.length > 1 || kpis.ncsRecibidasPorRegimen.length > 1) && (
|
||||
<div className="grid gap-4 md:grid-cols-2 3xl:grid-cols-3">
|
||||
{kpis.ingresosPorRegimen.length > 1 && (
|
||||
<Card>
|
||||
@@ -316,6 +341,46 @@ export default function DashboardPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
{kpis.ncsEmitidasPorRegimen.length > 1 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base font-medium">NCs Emitidas por Regimen</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{kpis.ncsEmitidasPorRegimen.map((r) => (
|
||||
<div key={r.regimenClave} className="flex items-center justify-between py-2 border-b last:border-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs font-mono font-bold bg-muted px-2 py-1 rounded">{r.regimenClave}</span>
|
||||
<span className="text-sm">{r.regimenDescripcion}</span>
|
||||
</div>
|
||||
<span className="text-sm font-semibold">{formatCurrency(r.monto)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
{kpis.ncsRecibidasPorRegimen.length > 1 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base font-medium">NCs Recibidas por Regimen</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{kpis.ncsRecibidasPorRegimen.map((r) => (
|
||||
<div key={r.regimenClave} className="flex items-center justify-between py-2 border-b last:border-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs font-mono font-bold bg-muted px-2 py-1 rounded">{r.regimenClave}</span>
|
||||
<span className="text-sm">{r.regimenDescripcion}</span>
|
||||
</div>
|
||||
<span className="text-sm font-semibold">{formatCurrency(r.monto)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user