feat(cfdi-viewer): mostrar complemento de pago en facturas tipo P
Para CFDIs tipo P (Pago) el visor ahora muestra: - Monto pagado - Fecha de pago - Número de parcialidad - UUID relacionado (factura pagada) - Saldo insoluto - Impuestos del pago (ISR/IVA/IEPS retenciones y traslados) Además se ocultan para tipo P: - Tabla de conceptos (dummy) - Bloque de totales tradicional (subtotal/IVA/total) - Sección CFDIs relacionados (reemplazada por UUID pagado) El complemento de pago se renderiza en una card verde destacada.
This commit is contained in:
@@ -204,8 +204,8 @@ export const CfdiInvoice = forwardRef<HTMLDivElement, CfdiInvoiceProps>(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CFDIs Relacionados */}
|
||||
{(cfdi.cfdiTipoRelacion || cfdi.cfdisRelacionados) && (
|
||||
{/* CFDIs Relacionados (no-P) */}
|
||||
{(cfdi.cfdiTipoRelacion || cfdi.cfdisRelacionados) && cfdi.tipoComprobante !== 'P' && (
|
||||
<div className="bg-amber-50 rounded-lg p-3 mb-5 border border-amber-200">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg className="w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -239,6 +239,82 @@ export const CfdiInvoice = forwardRef<HTMLDivElement, CfdiInvoiceProps>(
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Complemento de Pago (solo tipo P) */}
|
||||
{cfdi.tipoComprobante === 'P' && (
|
||||
<div className="bg-emerald-50 rounded-lg p-4 mb-5 border border-emerald-200">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg className="w-5 h-5 text-emerald-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-6a1 1 0 11-2 0 1 1 0 012 0z" />
|
||||
</svg>
|
||||
<div className="flex-1 min-w-0 space-y-2">
|
||||
<p className="text-xs font-semibold text-emerald-800 uppercase tracking-wide">Complemento de Pago</p>
|
||||
{cfdi.montoPago > 0 && (
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-700">Monto pagado</span>
|
||||
<span className="text-sm font-bold text-emerald-700">{formatCurrency(cfdi.montoPago)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.fechaPagoP && (
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-700">Fecha de pago</span>
|
||||
<span className="text-sm font-medium text-gray-800">{formatDate(cfdi.fechaPagoP)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.numParcialidad && (
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-700">Parcialidad</span>
|
||||
<span className="text-sm font-medium text-gray-800">{cfdi.numParcialidad}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.uuidRelacionado && (
|
||||
<div className="pt-1">
|
||||
<span className="text-sm text-gray-700 font-medium">UUID relacionado (factura pagada):</span>
|
||||
<div className="flex flex-wrap gap-2 mt-1">
|
||||
{cfdi.uuidRelacionado.split('|').map((uuid) => uuid.trim()).filter(Boolean).map((uuid, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-block px-2 py-0.5 bg-white border border-emerald-200 rounded text-xs font-mono text-gray-600"
|
||||
>
|
||||
{uuid}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.saldoInsoluto && (
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-700">Saldo insoluto</span>
|
||||
<span className="text-sm font-medium text-gray-800">{cfdi.saldoInsoluto}</span>
|
||||
</div>
|
||||
)}
|
||||
{/* Impuestos del pago */}
|
||||
{(cfdi.isrRetencionPago > 0 || cfdi.ivaTrasladoPago > 0 || cfdi.ivaRetencionPago > 0 || cfdi.iepsTrasladoPago > 0 || cfdi.iepsRetencionPago > 0) && (
|
||||
<div className="pt-2 border-t border-emerald-100">
|
||||
<p className="text-xs font-medium text-emerald-700 mb-1">Impuestos del pago</p>
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
|
||||
{cfdi.isrRetencionPago > 0 && (
|
||||
<div className="flex justify-between"><span className="text-gray-600">ISR Ret.</span><span className="text-red-600">-{formatCurrency(cfdi.isrRetencionPago)}</span></div>
|
||||
)}
|
||||
{cfdi.ivaTrasladoPago > 0 && (
|
||||
<div className="flex justify-between"><span className="text-gray-600">IVA Tras.</span><span>{formatCurrency(cfdi.ivaTrasladoPago)}</span></div>
|
||||
)}
|
||||
{cfdi.ivaRetencionPago > 0 && (
|
||||
<div className="flex justify-between"><span className="text-gray-600">IVA Ret.</span><span className="text-red-600">-{formatCurrency(cfdi.ivaRetencionPago)}</span></div>
|
||||
)}
|
||||
{cfdi.iepsTrasladoPago > 0 && (
|
||||
<div className="flex justify-between"><span className="text-gray-600">IEPS Tras.</span><span>{formatCurrency(cfdi.iepsTrasladoPago)}</span></div>
|
||||
)}
|
||||
{cfdi.iepsRetencionPago > 0 && (
|
||||
<div className="flex justify-between"><span className="text-gray-600">IEPS Ret.</span><span className="text-red-600">-{formatCurrency(cfdi.iepsRetencionPago)}</span></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Datos del Comprobante */}
|
||||
<div className="grid grid-cols-4 gap-3 mb-5">
|
||||
<div className="bg-gray-50 rounded-lg p-3 text-center">
|
||||
@@ -272,8 +348,8 @@ export const CfdiInvoice = forwardRef<HTMLDivElement, CfdiInvoiceProps>(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Conceptos */}
|
||||
{conceptos && conceptos.length > 0 && (
|
||||
{/* Conceptos — ocultar para tipo P (concepto dummy) */}
|
||||
{conceptos && conceptos.length > 0 && cfdi.tipoComprobante !== 'P' && (
|
||||
<div className="mb-5">
|
||||
<h3 className="text-xs text-gray-500 uppercase tracking-wide font-medium mb-2 flex items-center gap-2">
|
||||
<span className="w-1 h-4 bg-blue-600 rounded-full"></span>
|
||||
@@ -323,45 +399,47 @@ export const CfdiInvoice = forwardRef<HTMLDivElement, CfdiInvoiceProps>(
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Totales */}
|
||||
<div className="flex justify-end mb-5">
|
||||
<div className="w-80 bg-gray-50 rounded-lg overflow-hidden">
|
||||
<div className="divide-y divide-gray-200">
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">Subtotal</span>
|
||||
<span className="font-medium">{formatCurrency(cfdi.subtotal)}</span>
|
||||
{/* Totales — ocultar bloque tradicional para tipo P */}
|
||||
{cfdi.tipoComprobante !== 'P' && (
|
||||
<div className="flex justify-end mb-5">
|
||||
<div className="w-80 bg-gray-50 rounded-lg overflow-hidden">
|
||||
<div className="divide-y divide-gray-200">
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">Subtotal</span>
|
||||
<span className="font-medium">{formatCurrency(cfdi.subtotal)}</span>
|
||||
</div>
|
||||
{cfdi.descuento > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">Descuento</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.descuento)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.ivaTraslado > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">IVA (16%)</span>
|
||||
<span className="font-medium">{formatCurrency(cfdi.ivaTraslado)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.ivaRetencion > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">IVA Retenido</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.ivaRetencion)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.isrRetencion > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">ISR Retenido</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.isrRetencion)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-blue-700 text-white py-3 px-4 flex justify-between items-center">
|
||||
<span className="font-semibold">TOTAL</span>
|
||||
<span className="text-xl font-bold">{formatCurrency(cfdi.total)}</span>
|
||||
</div>
|
||||
{cfdi.descuento > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">Descuento</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.descuento)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.ivaTraslado > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">IVA (16%)</span>
|
||||
<span className="font-medium">{formatCurrency(cfdi.ivaTraslado)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.ivaRetencion > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">IVA Retenido</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.ivaRetencion)}</span>
|
||||
</div>
|
||||
)}
|
||||
{cfdi.isrRetencion > 0 && (
|
||||
<div className="flex justify-between py-2.5 px-4">
|
||||
<span className="text-gray-600">ISR Retenido</span>
|
||||
<span className="font-medium text-red-600">-{formatCurrency(cfdi.isrRetencion)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-blue-700 text-white py-3 px-4 flex justify-between items-center">
|
||||
<span className="font-semibold">TOTAL</span>
|
||||
<span className="text-xl font-bold">{formatCurrency(cfdi.total)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Timbre Fiscal Digital */}
|
||||
<div className="bg-gradient-to-r from-gray-100 to-gray-50 rounded-lg p-4 border border-gray-200">
|
||||
|
||||
Reference in New Issue
Block a user