Ir al contenido

Expediente Clientes

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

  • Modelo: App\Core\Clients\Client (app/Core/Clients/Client.php), tabla clients (inferida por convención — Reliese Model, no se localizó la migración de creación explícita). Extiende App\BaseModel. Usa SoftDeletes (deleted_at).
  • Columnas clave (via $fillable/$casts/uso en el modelo): name, abbreviation, rfc, code, company_id (int), city_id (int), credit_days (int), sync_status, affect_accounting (usada en scopeAffectAccounting, valores 1/2). name y abbreviation pasan por iconv('ISO-8859-1','UTF-8', ...) en sus mutators — posible fuente de datos con encoding raro si se prueba con acentos/ñ.
  • Traits relevantes: ApprovableTrait (flujo de aprobación, ver abajo), FileableTrait (archivos adjuntos, getFileBasePath() → 'client'), SynchronizableTrait + As400RelatedModelTrait (sincronización con AS400/DB2 — hay ClientSyncJob, ClientSyncService en app/Jobs/AS400 y app/AS400/Ein00dat/AS400Sync), SearchableTrait (búsqueda full-text-like sobre name, abbreviation, rfc, code, más joins a cities, companies, contracts y file_downloads), MessageCenterNotifiableTrait/UserNotifiableTrait (notificaciones), BusinessRuleLockableTrait/BusinessRuleAppendsTrait (reglas de negocio configurables, ver app/Core/BusinessRules/Modules/Clients/), HasModule → getModule() retorna Module::expediente_clientes (así se llama el módulo internamente, coincide con el nombre visible en UI).
  • $with eager-loaded siempre: company, city.
  • $appends: edit_url, action_url (→ /expediente-cliente/?id={id}, usado por notificaciones/links directos), name_with_code ("{complete_code} - {name}"), morph_class, complete_code ("{companyCode} {cityCode} {code}").
  • Relaciones clave (confirma el contexto de Trailer/Waybill dado):
    • waybills() → hasMany(Waybill) (dueño por client_id), sender_waybills() (sender_client_id), receiver_waybills() (receiver_client_id).
    • trailers() → hasMany(Trailer) (por client_id), receiver_trailers() (receiver_client_id), sender_trailers() (sender_client_id) — confirma la relación triple con Trailer mencionada en el contexto de este dominio.
    • contracts() → hasMany(Contract) (modelo Contract, no explorado a fondo en esta pasada — vive en el mismo namespace App\Core\Clients, probablemente Contract.php), con variante contracts_with_valid_trips().
    • company() → belongsTo(Company)::withTrashed(), city() → belongsTo(App\Core\LegacyCatalogs\City).
  • Controladores: App\Http\Controllers\Clients\ClientController (CRUD + getPaginated, sync, restore, getDownloadHistory), ClientApprovalController (approve/disapprove/notify), ClientFileController (archivos), ClientCommentController (comentarios). También Clients\ReportsController (reportes), Clients\GetTravelReportController/GenerateTravelReportController, Clients\KilometerHistory\GetClientKilometerHistoryController.
  • Identidad compuesta: scopeWhereKeys($companyCode, $code, $cityCode) / findByKeys(...) — un cliente se identifica de forma legible por la combinación empresa+ciudad+código (ligado a complete_code), no solo por id. Útil si el tester necesita “buscar el cliente X” por datos visibles en vez de por id interno.
  • Export: app/Exports/Clients/ClientsReportExport.php.

Todas bajo prefijo global client (routes/web.php línea ~296, dentro del grupo auth:api + password_expired), repartidas en varios archivos de routes/client/:

Archivo Método Ruta (bajo /client/...) Acción
client.php GET `` (raíz) listado (get)
client.php GET paginated listado paginado
client.php POST `` (raíz) crear
client.php GET/POST travel-report reporte de viajes
client.php PUT {client} actualizar
client.php DELETE {client} eliminar (soft delete)
client.php GET {client}/sync forzar sync AS400
client.php PATCH {client} restaurar
client.php GET {client}/download-history historial de descargas
client.php GET {client}/kilometer-history historial de kilometraje
client.php GET /{client} detalle (show)
comments.php POST {client}/comment agregar comentario
files.php POST {client}/files subir archivo
files.php GET/PUT/DELETE files/{file} descargar/actualizar/borrar archivo
approvals.php POST {client}/approve, {client}/disapprove, {client}/notify flujo de aprobación
reports.php GET reports/get-clients, reports/get-client-contracts, reports/get-contract-waybills, reports/get-export-waybills, reports/get-chart-info, reports/get-waybills-in-months datos para el reporte “Clientes con viajes”
reports.php POST reports/export exportar reporte

No se detectó middleware permission: a nivel de ruta explícito en estos archivos — la autorización probablemente vive en los controladores/FormRequests (patrón similar a Liquidaciones/Remolques), no verificado línea por línea en esta pasada.

También existen rutas de Contratos (routes/contract/contract.php, files.php, comments.php) — dominio relacionado (Contract belongsTo Client) pero fuera del alcance de esta ficha; documentar aparte si se testea.

  • Ruta raíz /expediente-cliente/ → resources/js/components/Clients/ClientsPage.vue (guard beforeEnter: can('view-client'), definido en resources/js/routes.js ~línea 165).
  • /expediente-cliente/reportes/clientes-con-viajes → Clients/Reports/WaybillsByClient/WaybillsByClientKpi.vue (guard can('view-client-reports')).
  • Componentes principales en resources/js/components/Clients/:
    • ClientsPage.vue — página contenedora del listado.
    • ClientRow.vue — fila individual del listado.
    • ClientForm.vue — formulario de alta/edición, con subcomponentes en FormSections/ (ClientGeneralForm.vue, ClientDataForm.vue, ClientFilesForm.vue, ClientOtherFilesForm.vue) y FormElements/ (ClientSelector.vue, ClientSelectorRemote.vue — selector reutilizado en otros módulos, p.ej. para elegir cliente en Remolques al asignar sender_client_id/receiver_client_id).
    • ClientApproval.vue — UI del flujo de aprobación (ApprovableTrait en backend).
    • File/ClientFileUpload.vue — subida de archivos del expediente.
    • Contracts/ — submódulo de Contratos del cliente: ContractList.vue, ContractRow.vue, ContractForm.vue, con FormSections/ (ContractDetail.vue, ContractFilesForm.vue, ContractOtherFilesForm.vue) y FormElements/ContractSelector.vue.
    • Reports/ — ClientKilometerHistory.vue/ClientKilometerHistoryDialog.vue (historial de km), y Reports/WaybillsByClient/ con varios componentes de KPI/tabla/gráfica (WaybillsByClientKpi.vue, WaybillsByClientKpiChartReport.vue, WaybillsByClientKpiTableList.vue, WaybillsByClientKpiSubTable.vue y sus Row, WaybillsDialog.vue, WaybillsInMonthsDialog.vue/Table.vue).
  • Servicio JS: no se localizó/leyó el archivo de servicio explícitamente en esta pasada (esperado en resources/js/services/clients/ por convención del proyecto, análogo a operations/operation-service.js) — por verificar.
  • Menú lateral: resources/js/components/Nav/menuOptions.js línea ~163, item name: 'Expediente Clientes', module: 'client', canView: 'view-client', path: '/expediente-cliente', ícono /img/exp-cliente.svg. Submenú incluye el propio “Expediente Clientes” (path: '/expediente-cliente') y “Clientes con viajes” (path: '/expediente-cliente/reportes/clientes-con-viajes', canView: 'view-client-reports'). El ícono /img/exp-cliente.svg también aparece referenciado en otras secciones del menú (líneas ~545+, posiblemente reportes cruzados de otros módulos que enlazan a Clientes) — no explorado en detalle.

Fuente: config/laratrust_seeder.php, resource client: 'v,c,r,u,d,co,a,n,cf,uf,df' (visto en al menos dos bloques de roles con el mismo string, ver también variantes reducidas por rol: vendedor → v,c,r,u,co,n,cf; directorGeneralAdjunto → v,r,co,a; directorComercial → v,c,u,r,co,cf,uf,df) → generadas en App\Core\Security\PermissionConst:

  • view-client, create-client, read-client, update-client, delete-client
  • comment-client (co), approve-client (a), notify-client (n)
  • create-file-client (cf), update-file-client (uf), delete-file-client (df)
  • read-client-reports, view-client-reports (gobierna /expediente-cliente/reportes/*)

No se localizó en esta pasada la lista completa de roles que tienen acceso a client (se vieron ejemplos sueltos: vendedor, directorGeneralAdjunto, directorComercial, más los bloques genéricos que probablemente correspondan a admin/owner) — por verificar un listado completo si se necesita para matriz de permisos de testing.

Selectores usados por el tester (riesgo de fragilidad)

Sección titulada «Selectores usados por el tester (riesgo de fragilidad)»

No hay specs de Playwright para este módulo todavía — a diferencia de Remolques, que sí tiene tests/remolques/expand-trailer-detail.spec.ts en este mismo repo tester. Expediente Clientes no ha sido explorado con Playwright/codegen contra QA real; todo lo de esta sección es inferencia de código, no verificación en vivo:

  • No hay data-testid en la app (mismo patrón que el resto de controlgmg-v2; no se modifica el repo para agregarlos).
  • El item “Expediente Clientes” en el sidebar aparece con ícono propio (exp-cliente.svg) — su posición exacta dentro del menuOptions array (y por tanto si queda visible sin scroll) no fue determinada en esta pasada; comparar contra el patrón ya visto en Liquidaciones (primer ítem, visible) y Remolques (fuera de viewport).
  • El listado probablemente use el mismo patrón de filas basadas en div (no tabla HTML) visto en Remolques/Liquidaciones (ClientRow.vue), pero su estructura interna no fue inspeccionada — no asumir selectores de columna sin verificar.
  • El formulario ClientForm.vue está dividido en secciones (FormSections/*) más complejas que Remolques/Liquidaciones (datos generales, datos adicionales, archivos, otros archivos) — al ser multi-sección, es más probable que use tabs o acordeón; no asumir que todos los campos están visibles simultáneamente sin verificar en vivo.
  • Existe un flujo de aprobación (ClientApproval.vue, botones aprobar/desaprobar/notificar) que no existe en Remolques ni Liquidaciones tal cual — sus selectores y estados visuales son completamente desconocidos, hay que explorarlos manualmente.
  • El selector remoto de cliente (ClientSelectorRemote.vue) se reutiliza en otros módulos (p.ej. Remolques, al asignar sender_client_id/receiver_client_id) — si un test en Remolques necesita elegir un cliente, este es el componente involucrado; probablemente un el-select con búsqueda remota (autocomplete), típicamente frágil para getByLabel/getByRole sin verificar antes con codegen.

Antes de escribir cualquier spec para este módulo, correr npm run codegen:gmg contra QA real para verificar selectores, igual que se hizo para Remolques.

La página carga en https://qa.controlgmg.com/expediente-cliente bajo el título “Expediente Cliente”. No se inspeccionó el contenido a mayor detalle visual.