Ir al contenido

Llantas / Inspecciones

Generada 2026-08-13. Fuente: exploración de código de controlgmg-v2 + observación manual del navegador (QA).

  • Modelo principal: App\Core\Tires\Tire (app/Core/Tires/Tire.php), tabla tires.
    • Nota: existen otros dos archivos llamados Tire.php en el repo (src/Tires/Main/Models/Tire.php y src/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ódulo ControlData (dashboards KPI) o para un boundary src/ en migración. Por verificar si se usan en algo relevante para el tester.
  • 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 variante assignable_with_trashed()). Los campos de la relación son assignable_id / assignable_type (visto en los tests: assignable_type se guarda como Trailer::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 (hasMany InspectionDetail), tire_movements (hasMany TireMovement).
  • 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í (SoftDeletes trait) — hay endpoint de restore.
  • getActionUrlAttribute() apunta a /llantas?id={id} — confirma la ruta frontend canónica.
  • getModule() usa Module::llantas (config/modules.php) — igual que Remolques, has_team_scope: false ya confirmado por investigación previa.
  • Inspecciones: modelo Inspection (con commonWith()) y InspectionDetail — no se leyó el archivo completo, pero Tire referencia first_inspection_id/last_inspection_id/first_inspection_detail_id/last_inspection_detail_id como columnas propias (desnormalización de “última/primera inspección” en la fila de la llanta). Por verificar: estructura completa de Inspection/InspectionDetail/InspectionAlert (hay un subsistema de alertas de inspección con flujo de aprobación — ver rutas abajo).
  • Garage: hay un controlador separado Tires\GarageController para la vista /llantas/garaje (asignar/quitar llantas de unidades y remolques), distinto de TireController (CRUD del catálogo de llantas) y de InspectionController.

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).

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 en FormSections/: TireGeneralForm.vue, TireAssignmentForm.vue, InspectionsForm.vue.
  • Garage (Garage/): GaragePage.vue, GarageRow.vue, GarageForm.vue, y FormSections/: AssignTireForm.vue, AssignedTiresForm.vue, TirePositionSelector.vue, TirePositionsForm.vue, TireRowItem.vue.
  • Inspecciones (Inspections/): InspectionsPage.vue, InspectionRow.vue, InspectionForm.vue, y FormSections/: 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.

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-tire
  • assign-tire (usado en POST /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-testid en la app (no se modifica controlgmg-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 con npm 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.

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 interno 0623.
  • 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 floor en el modelo Tire).

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.php y tests/Feature/Garage/TireAssignTest.php son prácticamente idénticos (mismo cuerpo, distinto namespace/trait de import para FakeUserHelper — uno usa Tests\FakeUserHelper, el otro Tests\Traits\FakeUserHelper). Ambos cubren el endpoint POST /tires/garage/assign-tire:
    1. Sin sesión → redirect a /login.
    2. Usuario sin team → redirect a /teams/missing.
    3. Usuario con team pero sin permiso assign-tire → 403 Forbidden.
    4. 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”).
    5. Caso feliz: crea una Tire sin asignar (Tire::factory()->unassigned()) y un Trailer, asigna con una posición aleatoria de Trailer::POSITIONS, y verifica: (a) la respuesta JSON coincide con la llanta recargada + commonWith(), (b) sin errores de sesión, (c) en BD la fila tires tiene assignable_id/assignable_type (= Trailer::class, el FQCN) y position correctos, (d) se crea un registro en logs con type = Tire::ASSIGNED, loggable_type = 'tire', loggable_id = id de la llanta.
  • 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 + permiso assign-tire + validación, en ese orden — el mismo patrón de guardas usado en Remolques.