Ir al contenido

Exportar Gastos

Exportación de gastos a formato Excel para análisis externo o envío a asesor.

src/features/expenses/components/ExportSheet.tsx
<ExportSheet
open={exportOpen}
onOpenChange={setExportOpen}
onExport={handleExport}
/>
  • Selección de período con calendarios (fecha inicio y fin)
  • Validación de fechas (no futuras, inicio < fin)
  • Exportación a Excel con formato profesional
  • Incluye todos los campos relevantes del gasto
  • Cálculos agregados (subtotales, totales de IVA)
const handleExport = async (startDate: Date, endDate: Date) => {
// 1. Obtener gastos del período
const { data: expenses } = await supabase
.from('expenses')
.select('*')
.eq('business_id', currentBusinessId)
.gte('expense_date', format(startDate, 'yyyy-MM-dd'))
.lte('expense_date', format(endDate, 'yyyy-MM-dd'))
.order('expense_date', { ascending: true });
// 2. Generar Excel
const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(
expenses.map(exp => ({
'Fecha': format(new Date(exp.expense_date), 'dd/MM/yyyy'),
'Concepto': exp.name,
'Proveedor': exp.vendor_name || '-',
'NIF Proveedor': exp.vendor_tax_id || '-',
'Categoría': exp.category || '-',
'Nº Factura': exp.invoice_number || '-',
'Base Imponible': exp.amount,
'IVA (%)': exp.tax_rate,
'IVA (€)': exp.tax_amount,
'Total': exp.total_amount,
'IVA Deducible': exp.deductible_tax_amount || 0,
'% Deducible': exp.deductible_percentage || 0,
'Estado': exp.status === 'verified' ? 'Verificado' : 'Registrado',
}))
);
// 3. Aplicar formato
worksheet['!cols'] = [
{ wch: 12 }, // Fecha
{ wch: 30 }, // Concepto
{ wch: 25 }, // Proveedor
{ wch: 12 }, // NIF
{ wch: 20 }, // Categoría
{ wch: 15 }, // Factura
{ wch: 12 }, // Base
{ wch: 8 }, // IVA %
{ wch: 10 }, // IVA €
{ wch: 12 }, // Total
{ wch: 12 }, // Deducible
{ wch: 10 }, // %
{ wch: 12 }, // Estado
];
// 4. Agregar totales
const totalBase = expenses.reduce((sum, e) => sum + (e.amount || 0), 0);
const totalIVA = expenses.reduce((sum, e) => sum + (e.tax_amount || 0), 0);
const totalGeneral = expenses.reduce((sum, e) => sum + e.total_amount, 0);
const totalDeducible = expenses.reduce((sum, e) => sum + (e.deductible_tax_amount || 0), 0);
XLSX.utils.sheet_add_aoa(worksheet, [
['', '', '', '', '', 'TOTALES:', totalBase, '', totalIVA, totalGeneral, totalDeducible]
], { origin: -1 });
// 5. Agregar hoja al libro
XLSX.utils.book_append_sheet(workbook, worksheet, 'Gastos');
// 6. Descargar
const fileName = `Gastos_${format(startDate, 'yyyy-MM-dd')}_${format(endDate, 'yyyy-MM-dd')}.xlsx`;
XLSX.writeFile(workbook, fileName);
toast({
title: "Exportación completada",
description: `${expenses.length} gastos exportados a Excel`,
});
};
FechaConceptoProveedorNIF ProveedorCategoríaNº FacturaBase ImponibleIVA (%)IVA (€)TotalIVA Deducible% DeducibleEstado
15/01/2024Licencia softwareMicrosoftB12345678SoftwareINV-2024-001200,002142,00242,0042,00100Verificado
TOTALES:1.500,00315,001.815,00315,00
// Ambas fechas obligatorias
if (!startDate || !endDate) {
toast({
title: "Error",
description: "Por favor selecciona ambas fechas",
variant: "destructive",
});
return;
}
// Fecha inicio < fecha fin
if (startDate > endDate) {
toast({
title: "Error",
description: "La fecha de inicio debe ser anterior a la fecha de fin",
variant: "destructive",
});
return;
}
// No fechas futuras
const today = new Date();
if (startDate > today || endDate > today) {
toast({
title: "Error",
description: "Las fechas no pueden ser futuras",
variant: "destructive",
});
return;
}