feat(conceptos): add Excel export with extra columns
- Add exportConceptosToExcel function that fetches all filtered conceptos (limit 10000) and generates Excel - Export button now works for both CFDIs and Conceptos tabs - Extra columns in Conceptos Excel (not visible in table): Nombre Emisor, Nombre Receptor, Descuento, IVA Trasladado, IVA Retencion, ISR Retencion, Tipo Comprobante, Estatus CFDI
This commit is contained in:
@@ -5,7 +5,7 @@ import { useDebounce } from '@horux/shared-ui';
|
|||||||
import { Header } from '@/components/layouts/header';
|
import { Header } from '@/components/layouts/header';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription, Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Popover, PopoverTrigger, PopoverContent } from '@horux/shared-ui';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription, Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Popover, PopoverTrigger, PopoverContent } from '@horux/shared-ui';
|
||||||
import { useCfdis, useCfdiConceptos, useCreateCfdi, useDeleteCfdi } from '@/lib/hooks/use-cfdi';
|
import { useCfdis, useCfdiConceptos, useCreateCfdi, useDeleteCfdi } from '@/lib/hooks/use-cfdi';
|
||||||
import { getCfdis, createManyCfdis, searchEmisores, searchReceptores, type EmisorReceptor } from '@/lib/api/cfdi';
|
import { getCfdis, createManyCfdis, searchEmisores, searchReceptores, getAllCfdiConceptos, type EmisorReceptor } from '@/lib/api/cfdi';
|
||||||
import { cancelarFactura, downloadPdf, downloadXml } from '@/lib/api/facturacion';
|
import { cancelarFactura, downloadPdf, downloadXml } from '@/lib/api/facturacion';
|
||||||
import type { CfdiFilters, CfdiConceptoFilters, TipoCfdi, Cfdi } from '@horux/shared';
|
import type { CfdiFilters, CfdiConceptoFilters, TipoCfdi, Cfdi } from '@horux/shared';
|
||||||
import type { CreateCfdiData } from '@/lib/api/cfdi';
|
import type { CreateCfdiData } from '@/lib/api/cfdi';
|
||||||
@@ -502,6 +502,64 @@ export default function CfdiPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportConceptosToExcel = async () => {
|
||||||
|
if (!conceptosData?.data.length) return;
|
||||||
|
|
||||||
|
setExporting(true);
|
||||||
|
try {
|
||||||
|
const allFilters: CfdiConceptoFilters = { ...conceptoFilters, page: 1, limit: 10000 };
|
||||||
|
const allData = await getAllCfdiConceptos(allFilters);
|
||||||
|
const rows = allData.data;
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
alert('No hay datos para exportar');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportData = rows.map(c => ({
|
||||||
|
'Fecha CFDI': c.cfdiFechaEmision ? new Date(c.cfdiFechaEmision).toLocaleDateString('es-MX') : '',
|
||||||
|
'UUID': c.cfdiUuid || '',
|
||||||
|
'Tipo Comprobante': formatTipoComprobante(c.cfdiTipoComprobante),
|
||||||
|
'Estatus CFDI': c.cfdiStatus === 'Vigente' || c.cfdiStatus === '1' ? 'Vigente' : 'Cancelado',
|
||||||
|
'RFC Emisor': c.cfdiRfcEmisor || '',
|
||||||
|
'Nombre Emisor': c.cfdiNombreEmisor || '',
|
||||||
|
'RFC Receptor': c.cfdiRfcReceptor || '',
|
||||||
|
'Nombre Receptor': c.cfdiNombreReceptor || '',
|
||||||
|
'Clave ProdServ': c.claveProdServ || '',
|
||||||
|
'No. Identificación': c.noIdentificacion || '',
|
||||||
|
'Descripción': c.descripcion,
|
||||||
|
'Cantidad': c.cantidad,
|
||||||
|
'Unidad': c.claveUnidad || c.unidad || '',
|
||||||
|
'Valor Unitario': c.valorUnitario,
|
||||||
|
'Importe': c.importe,
|
||||||
|
'Descuento': c.descuento,
|
||||||
|
'IVA Trasladado': c.ivaTraslado,
|
||||||
|
'IVA Retención': c.ivaRetencion,
|
||||||
|
'ISR Retención': c.isrRetencion,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ws = XLSX.utils.json_to_sheet(exportData);
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, 'Conceptos');
|
||||||
|
|
||||||
|
const colWidths = Object.keys(exportData[0]).map(key => ({
|
||||||
|
wch: Math.max(key.length, ...exportData.map(row => String(row[key as keyof typeof row]).length))
|
||||||
|
}));
|
||||||
|
ws['!cols'] = colWidths;
|
||||||
|
|
||||||
|
const excelBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||||
|
const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||||||
|
|
||||||
|
const fileName = `conceptos_${new Date().toISOString().split('T')[0]}.xlsx`;
|
||||||
|
saveAs(blob, fileName);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting conceptos:', error);
|
||||||
|
alert('Error al exportar conceptos');
|
||||||
|
} finally {
|
||||||
|
setExporting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDownloadPdf = async (facturapiId: string | null) => {
|
const handleDownloadPdf = async (facturapiId: string | null) => {
|
||||||
if (!facturapiId) return;
|
if (!facturapiId) return;
|
||||||
try {
|
try {
|
||||||
@@ -1039,8 +1097,8 @@ export default function CfdiPage() {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{data && data.data.length > 0 && (
|
{((activeTab === 'cfdis' && data && data.data.length > 0) || (activeTab === 'conceptos' && conceptosData && conceptosData.data.length > 0)) && (
|
||||||
<Button variant="outline" onClick={exportToExcel} disabled={exporting}>
|
<Button variant="outline" onClick={activeTab === 'cfdis' ? exportToExcel : exportConceptosToExcel} disabled={exporting}>
|
||||||
{exporting ? (
|
{exporting ? (
|
||||||
<Loader2 className="h-4 w-4 mr-1 animate-spin" />
|
<Loader2 className="h-4 w-4 mr-1 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user