Generación de Cotizaciones en PDF - Alejoso/Flujo-de-compras GitHub Wiki

Descripción general

Se implementó la capacidad de generar, almacenar y visualizar PDFs de cotizaciones. El sistema mantiene siempre el PDF de la primera versión (V1) y el de la versión más reciente, eliminando los intermedios. Las versiones intermedias pueden visualizarse como HTML y descargarse como PDF bajo demanda.


Configuración inicial requerida

Después de clonar el proyecto o migrar la base de datos, ejecutar:

php artisan migrate
php artisan storage:link

storage:link crea el enlace simbólico entre public/storage y storage/app/public, necesario para que los PDFs guardados sean accesibles desde el navegador.


Instalación

Se instaló la librería barryvdh/laravel-dompdf que permite convertir HTML/CSS a PDF:

composer require barryvdh/laravel-dompdf

Cambios en la base de datos

Se agregó la columna pdfPath a la tabla version_cotizaciones:

$table->string('pdfPath')->nullable();

Almacena la ruta relativa del archivo PDF dentro de storage/app/public/. Es nullable porque el registro se crea antes de generar el PDF.

Ejemplo de valor guardado: cotizaciones/cotizacion_1_v3.pdf


Archivos modificados / creados

Modelo — app/Models/VersionCotizacion.php

  • Se agregó pdfPath al $fillable
  • Se agregaron getPdfPath() y setPdfPath()

Migración — database/migrations/..._create_version_cotizaciones_table.php

  • Se agregó la columna pdfPath nullable

Controller — app/Http/Controllers/Tecnico/CotizacionController.php

Se agregaron los siguientes métodos:

Método Tipo Descripción
cargarVersionConRelaciones() private Carga la versión con todos los eager loads necesarios
prepararDatosPdf() private Prepara $tecnico, $fecha, $materiales y $numeroCotizacion
generarYGuardarPdf() private Genera el PDF con dompdf y lo guarda en disco
pdfView() public Retorna la vista HTML que imita el documento PDF
pdfDownload() public Genera el PDF al vuelo y lo retorna como descarga sin guardarlo

Lógica de gestión de PDFs en store() y update():

store()  -> genera PDF de V1, nunca se elimina

update() -> si la versión más reciente NO es V1:
               elimina su PDF del disco
               pone pdfPath = null en BD
           crea nueva versión
           genera su PDF

Naming de archivos:

cotizaciones/cotizacion_{cotizacionId}_v{numeroVersion}.pdf

Vista PDF — resources/views/pdf/cotizacion.blade.php

Template HTML usado por dompdf. Sin lógica PHP, solo HTML con clases CSS. Recibe las variables ya procesadas desde el controller:

Variable Contenido
$tecnico Usuario creador de la cotización
$project Proyecto asociado
$fecha Fecha formateada en español
$materiales Array con cantidad, unidades, descripción y especificación
$numeroCotizacion ID de la cotización
$version Versión actual

Vista previa — resources/views/tecnico/cotizacion/pdf-view.blade.php

Vista web que imita visualmente el documento PDF. Muestra un botón Descargar PDF que llama a pdfDownload(). Solo aparece para versiones intermedias (ni V1 ni la más reciente).

Estilos — public/css/tecnico.css

Se agregaron las clases cot-doc-* al final del archivo:

Clase Uso
.cot-doc-wrapper Contenedor del documento (fondo blanco, tipografía serif)
.cot-doc-header Encabezado con datos del técnico
.cot-doc-title Título de la cotización
.cot-doc-date Fecha centrada
.cot-doc-intro Párrafo introductorio
.cot-doc-table Tabla de materiales
.cot-doc-page-number Número de página fijo en el pie (solo PDF)

Rutas — routes/web.php

GET /tecnico/project/{id}/cotizacion/{versionId}/pdf          -> pdfView
GET /tecnico/project/{id}/cotizacion/{versionId}/pdf/download -> pdfDownload

Comportamiento por versión

Versión PDF guardado Botón "Ver PDF"
V1 (inicial) Sí, permanente No
V2, V3... (intermedias) No (eliminado al crear siguiente) Sí -> abre vista HTML + descarga
Vn (más reciente) Sí, hasta que se cree otra No

Formulario de creación

Se unificó el formulario de creación con el de edición:

  • Selector dropdown para elegir el material
  • Botón Agregar a la lista que inserta la fila en la tabla
  • Validación de duplicados en frontend (data-id) y backend (regla distinct)
  • Archivos modificados: StoreCotizacionRequest.php, UpdateCotizacionRequest.php, create.blade.php, edit.blade.php