Formulario de Cotizaciones y Anotaciones Extra - Alejoso/Flujo-de-compras GitHub Wiki

Flujo de Cotizacion - Tecnico

Flujo general

Tecnico selecciona proyecto -> Crea cotizacion con materiales -> Sistema genera PDF y notifica -> Admin recibe la cotizacion
Proyecto -> Cotizacion (N por proyecto) -> VersionCotizacion (N por edicion) -> PresentacionTipoMaterialVersionCotizacion

1. Listado de proyectos (tecnico.project.index)

El tecnico ve todos los proyectos registrados en el sistema paginados de 12 en 12. Cada tarjeta muestra:

  • Nombre del proyecto
  • Ciudad y direccion
  • Costo total
  • Estado (En Negociacion, En Ejecucion, Finalizado)
  • Boton Ver cotizaciones — lleva al listado de cotizaciones del proyecto
  • Boton Nueva — abre el formulario de creacion de cotizacion

Controlador: app/Http/Controllers/Tecnico/ProjectController.phpindex() Vista: resources/views/tecnico/project/index.blade.php


2. Listado de cotizaciones (tecnico.cotizacion.index)

Muestra todas las cotizaciones de un proyecto en tarjetas. Cada tarjeta muestra:

  • Numero de cotizacion (Cotizacion 1, Cotizacion 2...)
  • Badge de estado
  • Cantidad de versiones
  • Fecha de creacion
  • Tecnico que la creo
  • Boton Ver versiones — lleva al historial de versiones

Un proyecto puede tener multiples cotizaciones independientes.

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpindex() Vista: resources/views/tecnico/cotizacion/index.blade.php


3. Formulario de cotizacion (tecnico.cotizacion.create)

Formulario dinamico donde el tecnico agrega materiales al proyecto. Cada fila contiene:

  • Select de material — elige el tipo de material (descripcion — especificacion)
  • Select de presentacion — se actualiza al elegir el material (ej: Rollo Grande, Unidad)
  • Unidad — se muestra automaticamente segun la presentacion seleccionada (ej: 500 m, 1/2 pulg)
  • Cantidad — numero ingresado por el tecnico

Las filas se agregan y eliminan sin recargar la pagina (JavaScript vanilla). Al enviar, se valida que haya al menos un material y que no se repita la misma combinacion material+presentacion.

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpcreate() Vista: resources/views/tecnico/cotizacion/create.blade.php


4. Guardado (StoreCotizacionRequest + store())

Validacion

Campo Regla
materiales requerido, array, minimo 1 elemento
materiales.*.presentacionTipoMaterialId requerido, debe existir en presentacion_tipo_materiales, sin duplicados (distinct)
materiales.*.cantidad requerido, numerico, minimo 0.01

Archivo: app/Http/Requests/StoreCotizacionRequest.php

Logica de guardado

Se ejecuta dentro de una transaccion Eloquent para garantizar consistencia:

  1. Crear Cotizacion nueva — estado = Tecnico, creadoPor = usuario autenticado
  2. Crear VersionCotizacion — numeroVersion = 1, esLaMasReciente = true
  3. Crear PresentacionTipoMaterialVersionCotizacion por cada material del formulario — cantidad + presentacionTipoMaterialId + versionCotizacionId

Luego de la transaccion (fuera de ella, con sus propios try/catch):

  1. Generar y guardar PDF en storage/public/ — Ruta: proyecto_{id}/cotizacion_{num}/p{id}_c{num}_v{ver}.pdf
  2. Enviar notificacion por correo via SendMessageFactory

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpstore()


5. Historial de versiones (tecnico.cotizacion.versions)

Muestra todas las versiones de una cotizacion especifica en una tabla:

Columna Descripcion
Version Numero de version (V1, V2...) + badge Actual si es la mas reciente
Fecha Fecha de creacion de la version
Acciones Ver detalle / Ver PDF / Editar materiales (solo en la version actual)

El boton Nueva Version aparece solo si existe una version actual y lleva al formulario de edicion.

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpversions() Vista: resources/views/tecnico/cotizacion/versions.blade.php


6. Detalle de version (tecnico.cotizacion.show)

Muestra todos los materiales incluidos en una version especifica:

Columna Fuente
Material TipoMaterial -> Material -> descripcion
Especificacion TipoMaterial -> Tipo -> especificacion
Presentacion PresentacionTipoMaterial -> Presentacion -> nombre
Unidad PresentacionTipoMaterial -> cantidadPresentacion + Tipo -> UnidadMedida -> abreviatura
Cantidad PresentacionTipoMaterialVersionCotizacion -> cantidad

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpshow() Vista: resources/views/tecnico/cotizacion/show.blade.php


7. Edicion de cotizacion (tecnico.cotizacion.edit + update())

Al editar una cotizacion no se modifica la version existente, sino que se crea una nueva version con los materiales actualizados. Esto preserva el historial completo de cambios.

Formulario de edicion (edit())

El formulario es identico al de creacion, con la diferencia de que la tabla de materiales viene precargada con los materiales de la version actual.

Datos que carga el controlador

Dato Fuente
Proyecto Proyecto::findOrFail($projectId)
Version actual VersionCotizacion con todas las relaciones de materiales
Catalogo de materiales (tmData) Todos los TipoMaterial con presentaciones, construido por CotizacionBuilder
Materiales precargados (materialesVersion) Materiales de la version actual, construido por CotizacionBuilder

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpedit() Vista: resources/views/tecnico/cotizacion/edit.blade.php

Validacion (UpdateCotizacionRequest)

Campo Regla
materiales requerido, array, minimo 1 elemento
materiales.*.presentacionTipoMaterialId requerido, debe existir en presentacion_tipo_materiales, sin duplicados (distinct)
materiales.*.cantidad requerido, numerico, minimo 0.01

Archivo: app/Http/Requests/UpdateCotizacionRequest.php

Logica de guardado (update())

Paso 1 — Determinar PDF anterior

Antes de la transaccion, se obtiene la version marcada como esLaMasReciente y se guarda su pdfPath si no es la version 1.

Paso 2 — Transaccion (atomica) → crearNuevaVersion()

Logica extraida al metodo privado crearNuevaVersion(Cotizacion, array): int:

  1. Cambiar estado de la cotizacion a Tecnico Editada
  2. Marcar todas las versiones anteriores como esLaMasReciente = false
  3. Crear nueva VersionCotizacion — numeroVersion = count(versiones existentes) + 1, esLaMasReciente = true
  4. Crear PresentacionTipoMaterialVersionCotizacion por cada material

Si la transaccion falla, se muestra un mensaje de error y se redirige al historial.

Paso 3 — Eliminar PDF anterior → CotizacionPdfBuilder::eliminarPdfAnterior()

Logica delegada a CotizacionPdfBuilder. Si existia un PDF de la version anterior en storage, lo elimina y limpia el campo pdfPath de esa version.

Paso 4 — Generar nuevo PDF → CotizacionPdfBuilder::generarYGuardarPdf()

Genera y guarda el PDF de la nueva version. Si falla, la cotizacion queda guardada pero se muestra un mensaje de advertencia.

Paso 5 — Notificacion por correo → enviarEmailEdicion()

Logica extraida al metodo privado enviarEmailEdicion(Cotizacion, Proyecto, VersionCotizacion): void. Envia correo via SendMessageFactory.

Controlador: app/Http/Controllers/Tecnico/CotizacionController.phpupdate()


8. PDF (pdfView + pdfDownload)

  • Preview (pdfView): Muestra el PDF renderizado en el navegador dentro del layout del tecnico.
  • Descarga (pdfDownload): Genera el PDF al vuelo con DomPDF y lo descarga directamente.

El nombre del archivo descargado sigue el patron: p{proyectoId}_c{numCotizacion}_v{numVersion}.pdf

La columna Cantidad en el PDF se muestra como: {cantidad} {presentacion} de {unidades} (ej: 3 Rollo Grande de 500 m). Si no tiene presentacion, muestra {cantidad} {unidades}.

Vista PDF: resources/views/pdf/cotizacion.blade.php Vista preview: resources/views/tecnico/cotizacion/pdf-view.blade.php


9. Rutas

Metodo URL Nombre
GET /tecnico/project tecnico.project.index
GET /tecnico/project/{id}/cotizacion tecnico.cotizacion.index
GET /tecnico/project/{id}/cotizacion/create tecnico.cotizacion.create
POST /tecnico/project/{id}/cotizacion tecnico.cotizacion.store
GET /tecnico/project/{id}/cotizacion/{cotizacionId}/versiones tecnico.cotizacion.versions
GET /tecnico/project/{id}/cotizacion/{versionId} tecnico.cotizacion.show
GET /tecnico/project/{id}/cotizacion/{versionId}/edit tecnico.cotizacion.edit
PATCH /tecnico/project/{id}/cotizacion/{versionId}/update tecnico.cotizacion.update
GET /tecnico/project/{id}/cotizacion/{versionId}/pdf tecnico.cotizacion.pdfView
GET /tecnico/project/{id}/cotizacion/{versionId}/pdf/download tecnico.cotizacion.pdfDownload
GET /materiales/buscar tecnico.materiales.search

Todas protegidas por el middleware tecnico.


10. Modelos involucrados

Proyecto
    Cotizacion (N por proyecto)
        VersionCotizacion (N por edicion)
            PresentacionTipoMaterialVersionCotizacion (1 por material agregado)
                PresentacionTipoMaterial
                    Presentacion (Rollo Grande, Rollo Pequeño, Unidad)
                    TipoMaterial
                        Material (descripcion)
                        Tipo (especificacion)
                            UnidadMedida (nombre, abreviatura)

11. Arquitectura del controlador (CotizacionController)

El controlador delega responsabilidades para evitar logica acumulada:

Responsabilidad Clase
Construir datos para vistas (materiales, tmData) CotizacionBuilder
Generar, guardar y eliminar PDFs CotizacionPdfBuilder
Enviar correos SendMessageFactory
Textos y mensajes flash resources/lang/es/tecnico_cotizacion.php

Metodos privados del controlador

Metodo Descripcion
crearNuevaVersion(Cotizacion, array): int Transaccion de creacion de nueva version con sus materiales
enviarEmailEdicion(Cotizacion, Proyecto, VersionCotizacion): void Envio de correo de notificacion al editar

Metodos de CotizacionPdfBuilder

Metodo Descripcion
generarYGuardarPdf(int, Proyecto): void Genera el PDF y lo guarda en storage
eliminarPdfAnterior(?string, ?VersionCotizacion): void Elimina el PDF anterior del storage
cargarVersionConRelaciones(int): VersionCotizacion Carga una version con todas sus relaciones
prepararDatosPdf(VersionCotizacion): array Prepara los datos para renderizar el PDF

12. Seeder

Comando

php artisan migrate:fresh --seed

migrate:fresh elimina todas las tablas y las vuelve a crear. Usar solo en desarrollo.

Solo seeders sin resetear:

php artisan db:seed

Datos que inserta

Tabla Registros
users Admin + 1 Tecnico
clientes 2 clientes de ejemplo
proveedores 1 proveedor de ejemplo
proyectos 2 proyectos
unidad_medidas Metro (m), Pulgada (pulg)
tipos Rojo, Negro, Blanco, Verde, PVC
materiales Alambre #12, Tubo Conduit
tipo_materiales 5 combinaciones (Alambre #12 x4 colores, Tubo Conduit PVC)
presentaciones Rollo Grande, Rollo Pequeño, Unidad
presentacion_tipo_materiales 13 combinaciones (presentacion + tipo_material + cantidadPresentacion)

Presentaciones por tipo de material

Material Presentacion Cantidad
Alambre #12 Rojo/Negro/Blanco/Verde Rollo Grande 500 m
Alambre #12 Rojo/Negro/Blanco/Verde Rollo Pequeño 100 m
Alambre #12 Rojo/Negro/Blanco/Verde Unidad 1 m
Tubo Conduit PVC Unidad 1/2 pulg

Credenciales de acceso

Rol Email Contrasena
Admin [email protected] password
Tecnico [email protected] password

13. Float vs Double Anotaciones

En PHP, double no existe como tipo nativo para type hints. Aunque en la base de datos la columna se declare como DOUBLE, en PHP solo existe floatdouble es simplemente un alias interno del motor, no un tipo que se pueda declarar explícitamente.

// Correcto — PHP acepta float como type hint
public function getCostoTotal(): ?float { ... }
public function setCostoTotal(?float $costoTotal): void { ... }

// Error fatal — double no es un tipo válido en PHP
public function getCostoTotal(): ?double { ... }
public function setCostoTotal(?double $costoTotal): void { ... }