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.php — index()
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.php — index()
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.php — create()
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:
- Crear Cotizacion nueva — estado =
Tecnico, creadoPor = usuario autenticado - Crear VersionCotizacion — numeroVersion =
1, esLaMasReciente = true - Crear PresentacionTipoMaterialVersionCotizacion por cada material del formulario — cantidad + presentacionTipoMaterialId + versionCotizacionId
Luego de la transaccion (fuera de ella, con sus propios try/catch):
- Generar y guardar PDF en storage/public/ — Ruta:
proyecto_{id}/cotizacion_{num}/p{id}_c{num}_v{ver}.pdf - Enviar notificacion por correo via
SendMessageFactory
Controlador: app/Http/Controllers/Tecnico/CotizacionController.php — store()
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.php — versions()
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.php — show()
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.php — edit()
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:
- Cambiar estado de la cotizacion a
Tecnico Editada - Marcar todas las versiones anteriores como esLaMasReciente = false
- Crear nueva VersionCotizacion — numeroVersion = count(versiones existentes) + 1, esLaMasReciente = true
- 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.php — update()
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:freshelimina 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 | 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 float — double 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 { ... }