import ExcelJS from 'exceljs'; import type { Pool } from 'pg'; export async function exportCfdisToExcel( pool: Pool, filters: { tipo?: string; estado?: string; fechaInicio?: string; fechaFin?: string } ): Promise { let whereClause = 'WHERE 1=1'; const params: any[] = []; let paramIndex = 1; if (filters.tipo) { whereClause += ` AND type = $${paramIndex++}`; params.push(filters.tipo); } if (filters.estado) { whereClause += ` AND status = $${paramIndex++}`; params.push(filters.estado); } if (filters.fechaInicio) { whereClause += ` AND fecha_emision >= $${paramIndex++}`; params.push(filters.fechaInicio); } if (filters.fechaFin) { whereClause += ` AND fecha_emision <= $${paramIndex++}`; params.push(filters.fechaFin); } const { rows: cfdis } = await pool.query(` SELECT uuid, type, serie, folio, fecha_emision, status, rfc_emisor, nombre_emisor, rfc_receptor, nombre_receptor, subtotal, subtotal_mxn, descuento, descuento_mxn, iva_traslado, iva_traslado_mxn, isr_retencion, isr_retencion_mxn, iva_retencion, iva_retencion_mxn, total, total_mxn, moneda, tipo_cambio, metodo_pago, forma_pago, uso_cfdi FROM cfdis ${whereClause} ORDER BY fecha_emision DESC `, params); const workbook = new ExcelJS.Workbook(); const sheet = workbook.addWorksheet('CFDIs'); sheet.columns = [ { header: 'UUID', key: 'uuid', width: 40 }, { header: 'Tipo', key: 'type', width: 10 }, { header: 'Serie', key: 'serie', width: 10 }, { header: 'Folio', key: 'folio', width: 10 }, { header: 'Fecha Emisión', key: 'fecha_emision', width: 15 }, { header: 'RFC Emisor', key: 'rfc_emisor', width: 15 }, { header: 'Nombre Emisor', key: 'nombre_emisor', width: 30 }, { header: 'RFC Receptor', key: 'rfc_receptor', width: 15 }, { header: 'Nombre Receptor', key: 'nombre_receptor', width: 30 }, { header: 'Subtotal', key: 'subtotal', width: 15 }, { header: 'Subtotal MXN', key: 'subtotal_mxn', width: 15 }, { header: 'IVA Trasladado', key: 'iva_traslado', width: 15 }, { header: 'IVA Trasladado MXN', key: 'iva_traslado_mxn', width: 15 }, { header: 'Total', key: 'total', width: 15 }, { header: 'Total MXN', key: 'total_mxn', width: 15 }, { header: 'Moneda', key: 'moneda', width: 8 }, { header: 'T.C.', key: 'tipo_cambio', width: 10 }, { header: 'Estado', key: 'status', width: 12 }, ]; sheet.getRow(1).font = { bold: true }; sheet.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4472C4' }, }; sheet.getRow(1).font = { bold: true, color: { argb: 'FFFFFFFF' } }; cfdis.forEach((cfdi: any) => { sheet.addRow({ ...cfdi, fecha_emision: new Date(cfdi.fecha_emision).toLocaleDateString('es-MX'), subtotal: Number(cfdi.subtotal), subtotal_mxn: Number(cfdi.subtotal_mxn), iva_traslado: Number(cfdi.iva_traslado), iva_traslado_mxn: Number(cfdi.iva_traslado_mxn), total: Number(cfdi.total), total_mxn: Number(cfdi.total_mxn), }); }); const buffer = await workbook.xlsx.writeBuffer(); return Buffer.from(buffer); } export async function exportReporteToExcel( pool: Pool, tipo: 'estado-resultados' | 'flujo-efectivo', fechaInicio: string, fechaFin: string ): Promise { const workbook = new ExcelJS.Workbook(); const sheet = workbook.addWorksheet(tipo === 'estado-resultados' ? 'Estado de Resultados' : 'Flujo de Efectivo'); if (tipo === 'estado-resultados') { const { rows: [totales] } = await pool.query(` SELECT COALESCE(SUM(CASE WHEN type = 'EMITIDO' AND tipo_comprobante = 'I' THEN subtotal_mxn ELSE 0 END), 0) as ingresos, COALESCE(SUM(CASE WHEN type = 'RECIBIDO' AND tipo_comprobante = 'I' THEN subtotal_mxn ELSE 0 END), 0) as egresos FROM cfdis WHERE status NOT IN ('Cancelado', '0') AND fecha_emision BETWEEN $1 AND $2 `, [fechaInicio, fechaFin]); sheet.columns = [ { header: 'Concepto', key: 'concepto', width: 40 }, { header: 'Monto', key: 'monto', width: 20 }, ]; sheet.addRow({ concepto: 'INGRESOS', monto: '' }); sheet.addRow({ concepto: 'Total Ingresos', monto: Number(totales?.ingresos || 0) }); sheet.addRow({ concepto: '', monto: '' }); sheet.addRow({ concepto: 'EGRESOS', monto: '' }); sheet.addRow({ concepto: 'Total Egresos', monto: Number(totales?.egresos || 0) }); sheet.addRow({ concepto: '', monto: '' }); sheet.addRow({ concepto: 'UTILIDAD NETA', monto: Number(totales?.ingresos || 0) - Number(totales?.egresos || 0) }); } const buffer = await workbook.xlsx.writeBuffer(); return Buffer.from(buffer); }