Llantas / Inspecciones
Generada 2026-08-13. Fuente: exploración de código de controlgmg-v2 + observación manual del navegador (QA).
Backend
Sección titulada «Backend»- Modelo principal:
App\Core\Tires\Tire(app/Core/Tires/Tire.php), tablatires.- Nota: existen otros dos archivos llamados
Tire.phpen el repo (src/Tires/Main/Models/Tire.phpysrc/ControlData/Tires/Models/Tire.php) que no se exploraron a fondo — por el namespace y el uso consistente en los tests (App\Core\Tires\Tire), parecen ser modelos secundarios/legacy para el móduloControlData(dashboards KPI) o para un boundarysrc/en migración. Por verificar si se usan en algo relevante para el tester.
- Nota: existen otros dos archivos llamados
- Fillable clave:
dot, economic_number, tire_origin_id, tire_brand_id, tire_size_id, tire_type_id, tire_warehouse_id, floor, last_warehouse_in_at, first_inspection_id, last_inspection_id. - Relación polimórfica “assignable”:
assignable()→morphTo()->with('tires')(con varianteassignable_with_trashed()). Los campos de la relación sonassignable_id/assignable_type(visto en los tests:assignable_typese guarda comoTrailer::class, o sea el FQCN completo, no un alias corto tipo'trailer'). Nota:getAssignableNameAttribute()compara contra el string'unit'(assignable_type === 'unit') para decidir entre “Tractor”/“Remolque” — posible inconsistencia con el FQCN usado al escribir (por verificar, puede ser un accessor legacy/roto). - Relaciones:
user(belongsTo, withTrashed),tire_brand/tire_size/tire_type/tire_warehouse/tire_origin(belongsTo catálogos, todos withTrashed),inspection_details(hasManyInspectionDetail),tire_movements(hasManyTireMovement). - Catálogos relacionados (
app/Core/Catalogs/):TireBrand,TireOrigin,TireSize,TireType,TireWarehouse— corresponden a los filtros “Marca”, “Origen”, “Medida”, “Tipo”, “Almacén” observados en QA. - Estados/constantes de log (
Tire):CREATED, ASSIGNED, UNMOUNTED, SCRAPPED, EXPRESS_TIRE, POSITION_CHANGED, WAREHOUSE_CHANGED. - Soft deletes: sí (
SoftDeletestrait) — hay endpoint de restore. getActionUrlAttribute()apunta a/llantas?id={id}— confirma la ruta frontend canónica.getModule()usaModule::llantas(config/modules.php) — igual que Remolques,has_team_scope: falseya confirmado por investigación previa.- Inspecciones: modelo
Inspection(concommonWith()) yInspectionDetail— no se leyó el archivo completo, peroTirereferenciafirst_inspection_id/last_inspection_id/first_inspection_detail_id/last_inspection_detail_idcomo columnas propias (desnormalización de “última/primera inspección” en la fila de la llanta). Por verificar: estructura completa deInspection/InspectionDetail/InspectionAlert(hay un subsistema de alertas de inspección con flujo de aprobación — ver rutas abajo). - Garage: hay un controlador separado
Tires\GarageControllerpara la vista/llantas/garaje(asignar/quitar llantas de unidades y remolques), distinto deTireController(CRUD del catálogo de llantas) y deInspectionController.
Registradas en routes/web.php (líneas ~326-337) bajo un grupo con prefijo tires (auth), cargando:
| Archivo | Prefijo efectivo | Contenido |
|---|---|---|
routes/tires/tires.php |
/tires |
CRUD de llantas: GET / (paginado), GET /get/{tire}, GET /search, POST /store, PUT /update/{tire}, DELETE {tire}, PATCH {tire} (restore), POST /export, POST {tire}/comment, POST /return/{tire}, DELETE /delete-by-warehouse/{warehouse} (botón “Borrado por almacén”) |
routes/tires/garage.php |
/tires |
Vista Garage: GET /garage/get-units, /garage/get-trailers, /garage/search-trailer, /garage/search-unit, /garage/search-operator, /garage/get-trailer/{id}, /garage/get-unit/{id}, /garage/get-tire/{id}, POST /garage/assign-tire, POST /garage/quit-tire/{tire}, POST /garage/export, comentarios de unit/trailer |
routes/tires/inspections.php |
/tires |
Grupo inspections: GET /, GET /{inspection}, GET /search, POST /store, PUT /update/{inspection}, DELETE {inspection}, PATCH {inspection} (restore), PATCH {inspection}/approve, POST /detail/store, /detail/save-photo, /detail/remove-photo, POST /export, GET /find-by-unit/{unit}, más subsistema completo de alertas de inspección (/alert/store, /alert/{id} delete, /alert/update/{id}, /alert/end/{id}, /alert/change-request/{id} + /approve, /alert/tire-to-change/assign|remove/{id}, /alert/confirm-change/{id}, /alert/tire-destination/request|confirm-trash|confirm-express-tire/{id}, /alert/approve-new-position/{id}) |
routes/tires/inspection-files.php, routes/tires/inspection-alert-files.php, routes/tires/inspection-alert-approval.php |
/tires |
archivos adjuntos y aprobación de alertas (no leídos en detalle) |
routes/control-data/tires.php, routes/control-data/tires-kpi1.php |
módulo ControlData | dashboards KPI de llantas (fuera del alcance de esta ficha) |
Rutas efectivas confirmadas por los tests: POST /tires/garage/assign-tire, POST /tires/garage/quit-tire/{tire}, GET /tires/inspections, POST /tires/inspections/store, GET /tires/inspections/search, PUT /tires/inspections/update/{id}, DELETE /tires/inspections/{id}, PATCH /tires/inspections/{id} (restore).
Frontend (Vue 2)
Sección titulada «Frontend (Vue 2)»Rutas SPA (resources/js/routes.js, todas con guard can('view-tire')):
| Ruta | Componente |
|---|---|
/llantas |
resources/js/components/Tires/TiresPage.vue |
/llantas/garaje |
resources/js/components/Tires/Garage/GaragePage.vue |
/llantas/inspecciones |
resources/js/components/Tires/Inspections/InspectionsPage.vue |
Estructura de componentes bajo resources/js/components/Tires/:
- Listado principal:
TiresPage.vue,TireRow.vue,TireForm.vue; formularios seccionados enFormSections/:TireGeneralForm.vue,TireAssignmentForm.vue,InspectionsForm.vue. - Garage (
Garage/):GaragePage.vue,GarageRow.vue,GarageForm.vue, yFormSections/:AssignTireForm.vue,AssignedTiresForm.vue,TirePositionSelector.vue,TirePositionsForm.vue,TireRowItem.vue. - Inspecciones (
Inspections/):InspectionsPage.vue,InspectionRow.vue,InspectionForm.vue, yFormSections/:HourSelector.vue,InspectionFilesForm.vue,InspectionGeneralForm.vue,InspectionPhotoForm.vue,InspectionTireRow.vue,InspectionTiresForm.vue,InspectionAlertForm.vue,InspectionDetailGeneralForm.vue. - Logs:
Logs/InspectionLogs.vue,Logs/TireLogs.vue. - Dashboards KPI (módulo distinto,
resources/js/components/ControlData/Tires/):ControlData.vue,kpi1/*,kpi2/*— corresponde a/control-data/llantas, fuera del alcance directo de “Llantas/Inspecciones” pero comparte modelos.
No se leyó el contenido interno de estos .vue (fuera del alcance de tiempo de esta pasada) — por verificar: estructura exacta de la tabla/filtros del listado, nombres de props/eventos, y si usan algún data-testid.
Permisos (Laratrust)
Sección titulada «Permisos (Laratrust)»Constantes en app/Core/Security/PermissionConst.php relevantes al módulo:
view-tire(guard de las 3 rutas SPA/llantas*)create-tire,update-tire,delete-tire,read-tireassign-tire(usado enPOST /tires/garage/assign-tire, verificado en tests)edit-catalogs-tire(catálogos: marca/tipo/tamaño/almacén/origen)assign-tire-inspection-alert,remove-tire-inspection-alert,confirm-express-tire-inspection-alert(subsistema de alertas de inspección)
Por verificar: permiso exacto detrás de POST /tires/garage/quit-tire/{tire} y de las acciones de InspectionController/InspectionAlertController (no se abrieron los FormRequests/Controllers para confirmar cada uno).
Selectores usados por el tester (riesgo de fragilidad)
Sección titulada «Selectores usados por el tester (riesgo de fragilidad)»No existen specs de Playwright para este módulo todavía (a diferencia de Remolques, que ya tiene tests/remolques/expand-trailer-detail.spec.ts). Todo lo de esta sección es observación de código/QA, no verificado contra selectores reales de Playwright.
- No hay
data-testiden la app (no se modificacontrolgmg-v2), consistente con lo encontrado en Remolques. - El listado de 9,309 registros es la tabla más grande del sitio — cualquier estrategia de selector por texto (
getByText) corre alto riesgo de “strict mode violation” (múltiples matches) si no se escopea bien a una fila/columna; considerar filtrar primero (p. ej. por “No. Económico” único) antes de intentar localizar una fila específica. - Al igual que en Remolques, es probable que el link “Llantas” del sidebar quede fuera del viewport — recomendable navegar directo por URL (
/llantas,/llantas/garaje,/llantas/inspecciones) en vez de clickear el menú, hasta confirmar lo contrario connpm run codegen:gmg. - Los 3 sub-módulos (listado, garaje, inspecciones) son SPAs separadas bajo el mismo guard de permiso (
view-tire) — verificar si comparten header/filtros de layout o son totalmente independientes antes de escribir specs.
Datos observados en QA (2026-08-12)
Sección titulada «Datos observados en QA (2026-08-12)»URL: https://qa.controlgmg.com/llantas — 9,309 registros, el módulo más grande del sitio.
Columnas de la tabla: N. Económico, DOT, Marca, Medida, Tipo, Origen, Fecha almacén (+ “piso” en mm visible en alguna fila/detalle).
Filas de ejemplo observadas:
- Código interno
2070123110, número interno0623. - Marcas vistas: MICHELIN, GOODYEAR, BFGOODRICH, BRIDGESTONE, FIRESTONE.
- Medida vista:
295/75R22.5. - Tipos vistos: GALLO, ORIGINAL DIRECCION, RENOVADO BANDA RECUPERADA.
- Almacenes (origen/ubicación) vistos: EXPRESS TIRE RENOVADA POR ENTREGAR, MEX STOCK LISTA USARSE, GDL STOCK LISTA USARSE.
- “Piso” expresado en mm (profundidad de banda, presumiblemente ligado a
flooren el modeloTire).
Filtros disponibles: No. Económico, Marca, Tipo, Medida, Tipo de Unidad, Almacén, Origen, Asignación, checkboxes “Basura” y “Express Tire”, rango de Fecha de creación.
Botones de acción: “Registrar Llanta” (→ POST /tires/store), “Borrado por almacén” (→ DELETE /tires/delete-by-warehouse/{warehouse}), “Exportador” (→ POST /tires/export).
Tests backend relevantes (PHPUnit, no Playwright)
Sección titulada «Tests backend relevantes (PHPUnit, no Playwright)»tests/Feature/Tires/AssignTireTest.phpytests/Feature/Garage/TireAssignTest.phpson prácticamente idénticos (mismo cuerpo, distinto namespace/trait de import paraFakeUserHelper— uno usaTests\FakeUserHelper, el otroTests\Traits\FakeUserHelper). Ambos cubren el endpointPOST /tires/garage/assign-tire:- Sin sesión → redirect a
/login. - Usuario sin team → redirect a
/teams/missing. - Usuario con team pero sin permiso
assign-tire→ 403 Forbidden. - Usuario con permiso pero payload vacío → redirect con errores de validación en español:
tire_id(“La llanta es requerida”),assignable_id/assignable_type(“El tractor o remolque es requerido”),position(“La posición de la llanta es requerida”). - Caso feliz: crea una
Tiresin asignar (Tire::factory()->unassigned()) y unTrailer, asigna con una posición aleatoria deTrailer::POSITIONS, y verifica: (a) la respuesta JSON coincide con la llanta recargada +commonWith(), (b) sin errores de sesión, (c) en BD la filatirestieneassignable_id/assignable_type(=Trailer::class, el FQCN) ypositioncorrectos, (d) se crea un registro enlogscontype = Tire::ASSIGNED,loggable_type = 'tire',loggable_id= id de la llanta.
- Sin sesión → redirect a
- Esto confirma en código: la relación polimórfica usa el FQCN completo como
assignable_type(no un alias corto), y que el flujo de asignación pasa por autenticación + team + permisoassign-tire+ validación, en ese orden — el mismo patrón de guardas usado en Remolques.