Convenciones
| Aspecto | Valor |
|---|---|
| Prefijo base | /api |
| Formato | JSON (application/json) |
| Autenticación | Authorization: Bearer <JWT> salvo endpoints marcados Anónimo |
| Enums | Serializados como string (p. ej. UsuarioEstado: Pendiente, Activo, Inhabilitado) |
| Fechas en filtros | String YYYYMMDD en query (desde, hasta) |
| Proxy desarrollo | Vite reenvía /api → http://127.0.0.1:5000 |
| Swagger | /swagger en la API en ejecución |
Seguridad y llamadas desde el frontend
El SPA (src/Web) nunca almacena credenciales de base de datos ni claves de mapas.
Toda operación protegida pasa por la API con JWT emitido tras login exitoso.
Flujo de autenticación
POST /api/auth/logincon usuario y clave → respuesta incluyetoken(JWT firmado en servidor).- El cliente guarda el token en
localStoragebajo la clavegotruck_token(auth.ts). - Las funciones
fetchJsonysendJsonañaden automáticamenteAuthorization: Bearer …si hay token. - Ante 401, el cliente borra el token y lanza
SessionExpiredErrorpara redirigir al login. POST /api/auth/registercrea cuenta en estadoPendientesin devolver JWT; un administrador debe habilitarla.
JWT (servidor)
| Claim / dato | Descripción |
|---|---|
sub / NameIdentifier | Id numérico del usuario |
unique_name / Name | Nombre de usuario |
admin | true si es administrador (menú Usuarios, endpoints /api/users) |
| Emisor / audiencia / firma | Configuración en appsettings.json → sección Jwt (clave ≥ 32 caracteres) |
| Expiración | Jwt:ExpiryMinutes |
El frontend decodifica el payload solo para UI (p. ej. readIsAdminFromToken()); la autorización real la valida la API en cada request.
Buenas prácticas implementadas
- Mismo origen en producción: nginx sirve el SPA y hace proxy de
/api/*al contenedor API (el navegador no expone otro host). - CORS restrictivo en API: orígenes explícitos (localhost Vite/nginx) en desarrollo.
- Login genérico: credenciales inválidas → 401 con mensaje único (Credenciales inválidas.).
- BCrypt para hash de contraseñas en
dbo.Usuarios. [AdminOnly]enUsuariosController: exige claimadmin=true.- Health anónimo solo para probes; no expone datos sensibles.
Ejemplo (cliente)
// auth.ts — petición autenticada
const token = localStorage.getItem("gotruck_token");
const res = await fetch("/api/catalogs/customers", {
headers: {
Accept: "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
});
Parámetros de consulta compartidos
Usados en reportes, tableros y mapas (ReporteFiltrosQuery):
| Query | Tipo | Descripción |
|---|---|---|
repartidorId | string? | Código/id del repartidor; omitir o vacío = todos |
numeroHoja | int? | Número de hoja de reparto |
desde | string? | Fecha inicio YYYYMMDD |
hasta | string? | Fecha fin YYYYMMDD |
tipo | string? | Filtro de actividad: ENTREGA, COBRANZAS, ENTREGA_COBRANZAS (u omitir = todas) |
canalId | string? | Canal de cliente (reporte estadístico) |
busqueda | string? | Texto libre (tablero) |
Mapas adicionales:
| Query | Tipo | Descripción |
|---|---|---|
includeWithoutGps | bool | Incluir actividades sin GPS (ubicación por dirección de entrega) |
includeWithoutActivity | bool | Incluir clientes sin actividad |
Autenticación — /api/auth
POST /api/auth/login Anónimo
Inicio de sesión. Usuarios Pendiente o Inhabilitado reciben 401 con mensaje específico.
Cuerpo
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
usuario | string | sí | Nombre de usuario |
clave | string | sí | Contraseña en claro (HTTPS en producción) |
Respuesta 200 — AuthTokenDto
| Campo | Tipo | Descripción |
|---|---|---|
token | string | JWT |
usuarioId | int | Id interno |
nombreUsuario | string | Login |
esAdministrador | bool | Rol administrador |
POST /api/auth/register Anónimo
Registro público. Crea usuario Pendiente, sin token.
Cuerpo
| Campo | Tipo | Descripción |
|---|---|---|
usuario | string | 3–64 caracteres |
clave | string | Mínimo 4 caracteres |
Respuesta 201 — RegistroPendienteDto
| Campo | Tipo |
|---|---|
mensaje | string |
GET /api/auth/me JWT
Respuesta 200 — UserMeDto
| Campo | Tipo |
|---|---|
id | int |
nombreUsuario | string |
estado | UsuarioEstado |
esAdministrador | bool |
Salud — /api/health
GET /api/health Anónimo
Respuesta 200 — HealthDto
| Campo | Tipo | Descripción |
|---|---|---|
servicio | string | Nombre del servicio |
utc | datetime | Marca de tiempo UTC |
baseDatosOk | bool | Conexión auth DB + datos operativos legacy |
baseDatos | string | Identificador lógico (ArcorGev5Reparto) |
Dashboard — /api/dashboard
GET /api/dashboard/summary JWT
Resumen KPI de la última jornada con actividad en E_ACTIVIDADES_REPARTIDOR.
Respuesta — DashboardResumenDto
| Campo | Tipo | Descripción |
|---|---|---|
fechaJornada | string? | YYYYMMDD de la jornada o null |
totalHojasReparto | int | |
visitasPlanificadas | int | |
visitasRealizadas | int | |
entregasOk | int | |
entregasRechazadas | int | |
montoCobrado | decimal | |
montoPendiente | decimal |
GET /api/dashboard/detail/{type} JWT
type: hojas | visitas | entregas | cobrado
Respuesta — DashboardDetalleDto
| Campo | Tipo |
|---|---|
tipo | string |
titulo | string |
fechaJornada | string? |
columnas | string[] |
filas | { celdas: string[] }[] |
Catálogos — /api/catalogs
Lectura de maestros I_* y catálogos dinámicos para filtros. Todos requieren JWT.
Repartidores (drivers)
GET /api/catalogs/drivers
Sin fechas: lista maestra RepartidorDto[]. Con desde + hasta: RepartidorConteoDto[] con conteos de repartos.
| Query | Descripción |
|---|---|
desde, hasta | Opcional; activan modo conteo |
tipo | Filtro de actividad (p. ej. ENTREGA_COBRANZAS) |
RepartidorDto
| Campo | Tipo |
|---|---|
id | int |
codigo | string |
nombre | string |
activo | bool |
RepartidorConteoDto (con fechas)
| Campo | Tipo |
|---|---|
id, codigo, nombre, activo | |
repartosRealizados | int |
repartosPlanificados | int |
GET /api/catalogs/drivers/{id}
Respuesta — RepartidorDetalleDto
| Campo | Tipo |
|---|---|
id | int |
campos | ArticuloCampoDto[] — todos los campos de I_REPARTIDORES (nombre + valor) |
Hojas para filtros de reportes
GET /api/catalogs/sheets
Query: desde, hasta, repartidorId → CatalogoItemDto[]
| Campo | Tipo |
|---|---|
codigo | string |
etiqueta | string |
Canales y categorías
GET /api/catalogs/channels · GET /api/catalogs/customer-categories → CatalogoItemDto[]
Productos (artículos)
GET /api/catalogs/products → ArticuloListItemDto[]
| Campo | Tipo |
|---|---|
id | int |
codigo | string |
descripcion | string |
unidadVenta | string |
GET /api/catalogs/products/{id} → ArticuloDetalleDto (id, campos[] de I_ARTICULOS)
Hojas de ruta
GET /api/catalogs/route-sheets → HojaRutaListItemDto[]
| Campo | Tipo | Descripción |
|---|---|---|
id | int | PK interna |
idHojaReparto | string | |
numeroHojaReparto | string | |
repartidorId | string? | |
repartidorNombre | string? | |
estadoRuta | string? | ABIERTA | CERRADA (registro en E_CIERRE_DE_RUTA para la hoja y FECHA_REGISTRACION) |
activo | bool | |
fechaRegistracion | string? | |
fechaUltimaModificacion | string? | |
procesadoQm | int? | |
routeIdQm | int? | |
cantidadClientesAVisitar | int | COUNT en I_CLIENTES |
GET /api/catalogs/route-sheets/{id} → HojaRutaDetalleDto
| Campo | Descripción |
|---|---|
hoja | HojaRutaListItemDto |
clientes[] | HojaRutaClienteVisitaDto: clienteId, codigo, razonSocial, ordenVisita, localidad |
Clientes
GET /api/catalogs/customers → ClienteListItemDto[]
| Campo | Tipo |
|---|---|
id | int |
codigo | string |
razonSocial | string |
canalId, canal | string? |
categoriaId, categoria | string? |
localidad | string? |
cantidadDirecciones | int |
GET /api/catalogs/customers/{id} → ClienteDetalleDto (campos[] de I_CLIENTES)
GET /api/catalogs/customers/{id}/addresses → ClienteDireccionesDto
| Campo | Tipo |
|---|---|
clienteId, codigo, razonSocial | |
direcciones[] | ClienteDireccionDto |
ClienteDireccionDto
| Campo | Tipo |
|---|---|
id | int |
texto | string |
localidad, provincia, codigoPostal, telefono, paisCodigo | string? |
latitud, longitud | double? |
esDireccionEntrega | bool |
tieneCoordenadasValidas | bool |
POST /api/catalogs/customers/addresses/{addressId}/geocode
Geocodifica vía Nominatim (OpenStreetMap). Respuesta ClienteDireccionGeocodificadaDto: id, latitud, longitud, consultaUsada.
Reportes — /api/reports
Todos usan ReporteFiltrosQuery y devuelven ReporteFilaDto[].
| Campo | Tipo | Descripción |
|---|---|---|
seleccionado | bool | Para UI de selección |
codigo | string | Código cliente |
razonSocial | string | |
detalle | string? | Texto auxiliar según reporte |
monto1, monto2 | decimal? | Métricas monetarias |
entero1, entero2 | int? | Contadores |
GET /api/reports/delivery-by-sheetGET /api/reports/returns-by-sheetGET /api/reports/collections-by-sheetGET /api/reports/collections-summaryGET /api/reports/performanceGET /api/reports/delivery-statistics(aceptacanalId)
Tableros — /api/boards
GET /api/boards/delivery-progress
Query: filtros compartidos + busqueda. Respuesta TableroAvanceDto[]:
| Campo | Tipo |
|---|---|
transportista | string |
repartidorId | string |
porcentajeAvanceVisita | decimal |
clientesVisitados, clientesAVisitar | int |
montoDeuda, montoCobrado, montoEntregadoCuentaCorriente | decimal |
repartosEntregados, repartosRechazados | int |
Mapas — /api/maps
GET /api/maps/delivery
Filtros + includeWithoutGps, includeWithoutActivity → MapaPuntoDto[]
| Campo | Tipo | |
|---|---|---|
idVisita | string | |
codigoCliente, razonSocial | string | |
latitud, longitud | decimal | |
estado | string | ENTREGA_COBRANZA, ENTREGA_SIN_COBRAR, PARCIAL, … |
repartidorId, numeroHoja | ||
gpsDesdeDireccionEntrega | bool |
GET /api/maps/delivery/activities → MapaActividadDetalleDto[] (metadatos por visita para leyenda y orden)
GET /api/maps/delivery/visit/{visitId} → MapaVisitaDetalleDto (entrega, cobranza, ítems, pagos)
Configuración — /api/settings
GET /api/settings/backoffice
ConfiguracionBackOfficeDto
| Campo | Tipo |
|---|---|
tituloAplicacion | string |
urlAplicacionExterna | string? |
motorBd | string? |
gpsDistribuidora | string? |
parametros[] | id, valor, descripcion |
Usuarios — /api/users Admin
Requiere JWT con claim admin=true.
GET /api/users→UsuarioListItemDto[]GET /api/users/{id}→UsuarioDetalleDtoPOST /api/users— cuerpoCrearUsuarioRequest→CrearUsuarioResponse(puede incluirclaveGenerada)PUT /api/users/{id}—ActualizarUsuarioRequestPOST /api/users/{id}/enable·POST /api/users/{id}/disableDELETE /api/users/{id}→ 204
| DTO | Campos principales |
|---|---|
| List / Detalle | id, nombreUsuario, estado, esAdministrador, creadoEnUtc |
| CrearUsuarioRequest | nombreUsuario, clave?, esAdministrador, estado? |
| ActualizarUsuarioRequest | nombreUsuario?, clave?, estado?, esAdministrador? |
Endpoints legacy (alias)
Misma implementación que catálogos, rutas alternativas:
GET /api/customersGET /api/driversGET /api/delivery-sheets→HojaRepartoDto[](métricas agregadas por hoja)
Errores
| Código | Uso |
|---|---|
| 400 | Validación (mensaje en mensaje o error) |
| 401 | No autenticado / token inválido / cuenta pendiente o inhabilitada |
| 404 | Recurso no encontrado |
| 409 | Conflicto (usuario duplicado) |