Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Magus-Technologies/facturacion_ilidesava/llms.txt

Use this file to discover all available pages before exploring further.

Descripción General

El sistema utiliza PhpSpreadsheet para generar exportaciones en formato Excel (.xlsx) con estilos profesionales, incluyendo encabezados formateados, bordes, colores alternados en filas y fórmulas para totales.

Controladores de Exportación

ProductoExportController

Maneja la exportación e importación de productos. Ubicación: app/Http/Controllers/Exports/ProductoExportController.php

Métodos Disponibles

  • descargarPlantilla(Request $request) - Descarga plantilla Excel para importar productos
  • descargarExcel(Request $request) - Exporta productos a Excel con filtros

VentaExportController

Genera múltiples tipos de reportes de ventas en Excel, TXT y PDF. Ubicación: app/Http/Controllers/Exports/VentaExportController.php

Métodos Disponibles

  • exportarTxt(Request $request) - Exporta registro de ventas en formato PLE 14.1 (SUNAT)
  • exportarExcel(Request $request) - Exporta ventas en formato Excel simple
  • reporteRVTA(Request $request) - Registro de Ventas formato SUNAT en Excel
  • reporteVentasProducto(Request $request) - Reporte de ventas agrupadas por producto
  • reporteGanancias(Request $request) - Reporte de ganancias por venta
  • exportarPdf(Request $request) - Exporta reporte de ventas en PDF

Rutas de Exportación

Rutas API (requieren autenticación)

Configuradas en routes/api.php:
// Productos
Route::get('productos/plantilla-excel', [ProductoExportController::class, 'descargarPlantilla']);
Route::get('productos/descargar-excel', [ProductoExportController::class, 'descargarExcel']);

// Ventas
Route::get('ventas/exportar-txt', [VentaExportController::class, 'exportarTxt'])
    ->middleware('permission:ventas.view');
Route::get('ventas/exportar-excel', [VentaExportController::class, 'exportarExcel'])
    ->middleware('permission:ventas.view');
Route::get('ventas/reporte-rvta', [VentaExportController::class, 'reporteRVTA'])
    ->middleware('permission:ventas.view');
Route::get('ventas/reporte-producto', [VentaExportController::class, 'reporteVentasProducto'])
    ->middleware('permission:ventas.view');
Route::get('ventas/reporte-ganancias', [VentaExportController::class, 'reporteGanancias'])
    ->middleware('permission:ventas.view');

Uso de PhpSpreadsheet

Importaciones Necesarias

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;

Crear un Nuevo Spreadsheet

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

Configurar Encabezados

$headers = ['Código', 'Nombre', 'Categoría', 'Stock', 'Precio'];
$col = 'A';
foreach ($headers as $header) {
    $sheet->setCellValue($col . '1', $header);
    $col++;
}

Aplicar Estilos a Encabezados

$headerStyle = [
    'font' => [
        'bold' => true,
        'size' => 11,
        'color' => ['rgb' => 'FFFFFF']
    ],
    'fill' => [
        'fillType' => Fill::FILL_SOLID,
        'startColor' => ['rgb' => '90BFEB']
    ],
    'borders' => [
        'allBorders' => [
            'borderStyle' => Border::BORDER_THIN,
            'color' => ['rgb' => '000000']
        ]
    ],
    'alignment' => [
        'horizontal' => Alignment::HORIZONTAL_CENTER,
        'vertical' => Alignment::VERTICAL_CENTER
    ]
];

$sheet->getStyle('A1:K1')->applyFromArray($headerStyle);

Filas con Colores Alternados

foreach ($productos as $index => $producto) {
    $row = $index + 5; // Comenzar después de encabezados
    
    // Datos
    $sheet->setCellValue('A' . $row, $producto->codigo);
    $sheet->setCellValue('B' . $row, $producto->nombre);
    
    // Color alternado
    if ($row % 2 == 0) {
        $sheet->getStyle('A' . $row . ':J' . $row)->applyFromArray([
            'fill' => [
                'fillType' => Fill::FILL_SOLID,
                'startColor' => ['rgb' => 'F9FAFB']
            ]
        ]);
    }
}

Plantilla de Importación de Productos

Características

  • 11 columnas: Código, Producto, Detalle, Categoría, Unidad, Moneda, Costo, Stock, Precio Venta, Precio Distribuidor, Precio Mayorista
  • Fila de ejemplo con formato destacado y comentario
  • Anchos de columna predefinidos para mejor legibilidad
  • Validación visual con colores diferenciados

Estructura de la Plantilla

$headers = [
    'A1' => 'Código',
    'B1' => 'Producto',
    'C1' => 'Detalle',
    'D1' => 'Categoría',
    'E1' => 'Unidad',
    'F1' => 'Moneda',
    'G1' => 'Costo',
    'H1' => 'Stock',
    'I1' => 'Precio Venta',
    'J1' => 'Precio Distribuidor',
    'K1' => 'Precio Mayorista',
];

Fila de Ejemplo

// Estilo para fila de ejemplo
$ejemploStyle = [
    'font' => [
        'italic' => true,
        'color' => ['rgb' => '5C4A00'],
        'size' => 10
    ],
    'fill' => [
        'fillType' => Fill::FILL_SOLID,
        'startColor' => ['rgb' => 'FFF9C4'] // Amarillo claro
    ],
    'borders' => [
        'allBorders' => [
            'borderStyle' => Border::BORDER_THIN,
            'color' => ['rgb' => 'F0C040']
        ]
    ]
];

$sheet->setCellValue('A2', 'PROD-001');
$sheet->setCellValue('B2', 'Nombre del producto');
$sheet->setCellValue('C2', 'Descripción opcional');
$sheet->setCellValue('D2', 'Repuestos');
$sheet->setCellValue('E2', 'UNIDAD');
$sheet->setCellValue('F2', 'PEN');
$sheet->setCellValue('G2', 10.50);
$sheet->setCellValue('H2', 100);
$sheet->setCellValue('I2', 15.00);
$sheet->setCellValue('J2', 13.00);
$sheet->setCellValue('K2', 12.00);

$sheet->getStyle('A2:K2')->applyFromArray($ejemploStyle);

Comentario en Celda

$sheet->getComment('A2')->getText()
    ->createTextRun('Fila de ejemplo — eliminar antes de importar');
$sheet->getComment('A2')->setWidth('200pt');
$sheet->getComment('A2')->setHeight('40pt');

Descargar Plantilla

// Frontend
const token = localStorage.getItem('auth_token');
const url = `/api/productos/plantilla-excel?token=${token}`;

window.open(url, '_blank');

Exportación de Productos

Con Filtros de Búsqueda

$almacen = $request->get('almacen', '1');
$busqueda = $request->get('texto', '');

$query = DB::table("view_productos_$almacen")
    ->where('id_empresa', $user->id_empresa);

if (!empty($busqueda)) {
    $query->where(function($q) use ($busqueda) {
        $q->where('codigo', 'like', "%$busqueda%")
          ->orWhere('nombre', 'like', "%$busqueda%")
          ->orWhere('descripcion', 'like', "%$busqueda%")
          ->orWhere('cod_barra', 'like', "%$busqueda%");
    });
}

$productos = $query->orderBy('codigo')->get();

Título y Metadata

// Título principal
$sheet->setCellValue('A1', 'REPORTE DE PRODUCTOS - ALMACÉN ' . $almacen);
$sheet->mergeCells('A1:J1');
$sheet->getStyle('A1')->applyFromArray([
    'font' => ['bold' => true, 'size' => 14, 'color' => ['rgb' => 'FFFFFF']],
    'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F97316']],
    'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
]);

// Fecha de generación
$sheet->setCellValue('A2', 'Generado: ' . date('d/m/Y H:i:s'));
$sheet->mergeCells('A2:J2');

Totales con Fórmulas

$totalRow = $row + 1;
$sheet->setCellValue('E' . $totalRow, 'TOTAL:');
$sheet->setCellValue('F' . $totalRow, '=SUM(F' . ($headerRow + 1) . ':F' . ($row - 1) . ')');

$sheet->getStyle('E' . $totalRow . ':F' . $totalRow)->applyFromArray([
    'font' => ['bold' => true],
    'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'FEF3C7']],
    'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
]);

Exportación de Ventas

Registro de Ventas Simple

// Exportar ventas de un periodo
const mes = '03';
const anio = '2026';
const token = localStorage.getItem('auth_token');

const url = `/api/ventas/exportar-excel?mes=${mes}&anio=${anio}`;

fetch(url, {
    headers: {
        'Authorization': `Bearer ${token}`
    }
})
.then(response => response.blob())
.then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `ventas-${anio}-${mes}.xlsx`;
    a.click();
});

Registro de Ventas SUNAT (RVTA)

Formato oficial para presentación a SUNAT:
const url = `/api/ventas/reporte-rvta?mes=${mes}&anio=${anio}`;
Columnas incluidas:
  • CUO (Código Único de Operación)
  • Fecha Emisión
  • Tipo Doc / Serie / Número
  • Tipo Doc Cliente / Nro Doc Cliente / Razón Social
  • Base Imponible / IGV / Exonerado / Inafecto
  • ISC / ICBPER / Otros
  • Total / Moneda / Estado

Reporte de Ventas por Producto

Agrupa y suma ventas por producto:
const url = `/api/ventas/reporte-producto?mes=${mes}&anio=${anio}`;
Columnas incluidas:
  • Código
  • Producto
  • Unidad
  • Cantidad Vendida
  • Número de Ventas
  • Subtotal / IGV / Total

Reporte de Ganancias

Calcula ganancia = Precio Venta - Costo:
const url = `/api/ventas/reporte-ganancias?mes=${mes}&anio=${anio}`;
Columnas incluidas:
  • Documento / Fecha / Cliente
  • Producto / Cantidad
  • Precio Venta / Costo
  • Total Venta / Ganancia
Características especiales:
  • Ganancias positivas en verde (059669)
  • Ganancias negativas en rojo (DC2626)
  • Cálculo de margen de ganancia en porcentaje

Exportación TXT (Formato PLE SUNAT)

Registro de Ventas PLE 14.1

Formato texto con 35 campos separados por pipe (|):
$campos = [
    $periodo,           // 1. Periodo (YYYYMM00)
    $cuo,               // 2. CUO (M000000001)
    'M-1',              // 3. Correlativo asiento
    $fechaEmision,      // 4. Fecha emisión (DD/MM/YYYY)
    $fechaVencimiento,  // 5. Fecha vencimiento
    $codSunat,          // 6. Tipo comprobante (01, 03, 07, etc.)
    $venta->serie,      // 7. Serie
    $numero,            // 8. Número (8 dígitos)
    '',                 // 9. Número final
    $tipoDocId,         // 10. Tipo doc identidad (1=DNI, 6=RUC)
    $docCliente,        // 11. Número doc identidad
    $nombreCliente,     // 12. Razón social
    '0.00',             // 13. Exportación
    $baseImponible,     // 14. Base imponible gravada
    '0.00',             // 15. Descuento base
    $igv,               // 16. IGV
    // ... hasta 35 campos
];

$lines[] = implode('|', $campos) . '|';

Nombre de Archivo PLE

$indicadorContenido = count($lines) > 0 ? '1' : '0';
$filename = "LE{$ruc}{$periodo}140100{$indicadorContenido}111.TXT";
// Ejemplo: LE20612706702202603001401001111.TXT

Descargar TXT

const url = `/api/ventas/exportar-txt?mes=${mes}&anio=${anio}`;

fetch(url, {
    headers: { 'Authorization': `Bearer ${token}` }
})
.then(response => response.blob())
.then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `registro-ventas-${anio}-${mes}.txt`;
    a.click();
});

Configuración de Anchos de Columna

// Anchos fijos
$sheet->getColumnDimension('A')->setWidth(15);
$sheet->getColumnDimension('B')->setWidth(35);
$sheet->getColumnDimension('C')->setWidth(40);

// Auto ajuste
foreach (range('A', 'J') as $col) {
    $sheet->getColumnDimension($col)->setAutoSize(true);
}

// Altura de fila
$sheet->getRowDimension(1)->setRowHeight(32);

Formato de Números

// Formato con 2 decimales y separador de miles
$sheet->getStyle('E5:G' . $row)->getNumberFormat()->setFormatCode('#,##0.00');

// Formato entero con separador de miles
$sheet->getStyle('D5:E' . $row)->getNumberFormat()->setFormatCode('#,##0');

Generar y Descargar Archivo

Método 1: Headers y Output Directo

$writer = new Xlsx($spreadsheet);
$filename = 'productos-' . date('Y-m-d-His') . '.xlsx';

header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');

$writer->save('php://output');
exit;

Método 2: Archivo Temporal

$writer = new Xlsx($spreadsheet);
$fileName = 'plantilla-productos-importar.xlsx';
$tempFile = tempnam(sys_get_temp_dir(), $fileName);
$writer->save($tempFile);

return response()->download($tempFile, $fileName)->deleteFileAfterSend(true);

Importación de Productos

Ver ProductoImportController para la lectura e importación de archivos Excel: Rutas disponibles:
Route::post('productos/leer-excel', [ProductoImportController::class, 'leerExcel']);
Route::post('productos/importar-lista', [ProductoImportController::class, 'importarLista']);

Mejores Prácticas

  1. Usar estilos consistentes - Define arrays de estilos reutilizables
  2. Colores alternados - Mejora legibilidad con filas de color alternado
  3. Formato de números - Aplica formato numérico apropiado (decimales, moneda)
  4. Anchos de columna - Configura anchos adecuados o usa autoSize
  5. Fórmulas para totales - Usa fórmulas Excel en lugar de valores estáticos
  6. Headers de descarga - Configura correctamente Content-Type y Content-Disposition
  7. Archivos temporales - Limpia archivos temporales después de descargar
  8. Validación de datos - Valida datos antes de exportar para evitar errores
  9. Manejo de errores - Captura excepciones y registra en logs
  10. Permisos - Verifica permisos del usuario antes de exportar datos sensibles

Paleta de Colores Utilizados

// Encabezados
'90BFEB' // Azul claro
'E5E7EB' // Gris claro
'F3F4F6' // Gris muy claro

// Filas alternadas
'F9FAFB' // Blanco hueso

// Totales
'FEF3C7' // Amarillo claro
'F59E0B' // Naranja
'92400E' // Marrón oscuro

// Ejemplo
'FFF9C4' // Amarillo muy claro
'F0C040' // Amarillo dorado

// Títulos
'F97316' // Naranja brillante

// Estados
'059669' // Verde (ganancia positiva)
'DC2626' // Rojo (ganancia negativa)

Build docs developers (and LLMs) love