Historial de Fichajes (API v2)¶
Endpoints para consultar el historial de fichajes de empleados. Soporta filtrado por curso escolar para ver datos históricos.
✅ Obtener Resumen Mensual de Empleados¶
Método/Ruta: GET /api/v2/timerecord/history/summary
Auth: Bearer JWT (rol: Superadmin)
Descripción: Obtiene un resumen mensual de fichajes por empleado con métricas agregadas. Ideal para la vista de tarjetas del dashboard de historial. Solo muestra empleados activos que tengan registros en el período consultado.
Params¶
| Param | Tipo | Requerido | Descripción | Ejemplo |
|---|---|---|---|---|
month |
number | ✅ | Mes (1-12) | 1 |
year |
number | ✅ | Año | 2026 |
schoolCourseId |
number | ❌ | ID del curso escolar. Si no se envía, usa el curso activo | 5 |
departmentId |
number | ❌ | Filtrar por departamento | 2 |
search |
string | ❌ | Búsqueda por nombre, apellido o email | García |
page |
number | ❌ | Página (default: 1) | 1 |
limit |
number | ❌ | Registros por página (default: 20) | 20 |
cURL¶
# Curso activo (por defecto)
curl -X GET "http://localhost:7001/api/v2/timerecord/history/summary?month=1&year=2026&page=1&limit=20" \
-H "Authorization: Bearer <TOKEN>"
# Curso específico (histórico)
curl -X GET "http://localhost:7001/api/v2/timerecord/history/summary?month=6&year=2025&schoolCourseId=3&page=1&limit=20" \
-H "Authorization: Bearer <TOKEN>"
# Con búsqueda
curl -X GET "http://localhost:7001/api/v2/timerecord/history/summary?month=1&year=2026&search=García&page=1&limit=20" \
-H "Authorization: Bearer <TOKEN>"
Response 200¶
{
"status": "success",
"summary": {
"totalActiveEmployees": 45,
"totalHoursAll": 3520.5,
"totalHoursAllFormatted": "3520h 30m",
"totalOpenIncidents": 8
},
"data": [
{
"employeeId": 15,
"fullName": "García López Juan",
"workedDays": 18,
"totalHours": 142.5,
"totalHoursFormatted": "142h 30m",
"incidents": 2
},
{
"employeeId": 23,
"fullName": "Martínez Ruiz Ana",
"workedDays": 22,
"totalHours": 176.0,
"totalHoursFormatted": "176h",
"incidents": 0
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
}
}
Campos de respuesta¶
Summary (métricas globales del dashboard):
| Campo | Tipo | Descripción |
|---|---|---|
totalActiveEmployees |
number | Total de empleados activos |
totalHoursAll |
number | Horas totales trabajadas (decimal) |
totalHoursAllFormatted |
string | Horas totales en texto ("4h 30m") |
totalOpenIncidents |
number | Incidencias abiertas sin resolver |
Data (por empleado):
| Campo | Tipo | Descripción |
|---|---|---|
employeeId |
number | ID del empleado |
fullName |
string | Nombre completo (Apellido Nombre) |
workedDays |
number | Días únicos con fichajes (registros abiertos) |
totalHours |
number | Suma de horas trabajadas (decimal) |
totalHoursFormatted |
string | Horas en texto ("4h 30m") |
incidents |
number | Incidencias abiertas sin resolver del empleado |
Errors¶
| Código | Error | Descripción |
|---|---|---|
400 |
VALIDATION_ERROR |
Parámetros inválidos (month/year requeridos) |
401 |
UNAUTHORIZED |
Token inválido o expirado |
403 |
FORBIDDEN |
Usuario sin permisos de Superadmin |
404 |
NO_ACTIVE_SCHOOL_COURSE |
No hay curso escolar activo configurado |
404 |
SCHOOL_COURSE_NOT_FOUND |
El schoolCourseId proporcionado no existe |
Notas¶
- El ordenamiento es alfabético por apellido, luego por nombre
- El
compliancese calcula sobre 22 días laborables por mes (aproximado) - Si no se envía
schoolCourseId, el sistema resuelve automáticamente el curso activo
✅ Obtener Detalle de Fichajes por Empleado¶
Método/Ruta: GET /api/v2/timerecord/history/details
Auth: Bearer JWT (rol: Superadmin)
Descripción: Obtiene el listado detallado de fichajes de un empleado específico. Ideal para la vista de detalle al hacer clic en una tarjeta.
Params¶
| Param | Tipo | Requerido | Descripción | Ejemplo |
|---|---|---|---|---|
employeeId |
number | ✅ | ID del empleado | 15 |
schoolCourseId |
number | ❌ | ID del curso escolar. Si no se envía, usa el curso activo | 5 |
startDate |
string (ISO) | ❌ | Fecha inicio. Default: inicio del mes actual | 2026-01-01 |
endDate |
string (ISO) | ❌ | Fecha fin. Default: fin del mes actual | 2026-01-31 |
page |
number | ❌ | Página (default: 1) | 1 |
limit |
number | ❌ | Registros por página (default: 20) | 20 |
cURL¶
# Mes actual, curso activo
curl -X GET "http://localhost:7001/api/v2/timerecord/history/details?employeeId=15&page=1&limit=20" \
-H "Authorization: Bearer <TOKEN>"
# Rango de fechas específico
curl -X GET "http://localhost:7001/api/v2/timerecord/history/details?employeeId=15&startDate=2026-01-01&endDate=2026-01-31&page=1&limit=20" \
-H "Authorization: Bearer <TOKEN>"
# Curso histórico
curl -X GET "http://localhost:7001/api/v2/timerecord/history/details?employeeId=15&schoolCourseId=3&startDate=2025-06-01&endDate=2025-06-30" \
-H "Authorization: Bearer <TOKEN>"
Response 200¶
{
"status": "success",
"data": [
{
"date": "2026-01-28T08:00:00.000Z",
"startTime": "2026-01-28T08:00:00.000Z",
"endTime": "2026-01-28T14:30:00.000Z",
"startSource": "App",
"endSource": "App",
"duration": 390,
"status": "closed"
},
{
"date": "2026-01-27T08:15:00.000Z",
"startTime": "2026-01-27T08:15:00.000Z",
"endTime": null,
"startSource": "Web",
"endSource": null,
"duration": null,
"status": "open"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 18,
"totalPages": 1
}
}
Campos de respuesta¶
| Campo | Tipo | Descripción |
|---|---|---|
date |
string (ISO) | Fecha del fichaje |
startTime |
string (ISO) | null | Hora de entrada |
endTime |
string (ISO) | null | Hora de salida |
startSource |
string | null | Origen del fichaje de entrada (Web/App) |
endSource |
string | null | Origen del fichaje de salida (Web/App) |
duration |
number | null | Duración en minutos (null si está abierto) |
status |
string | Estado: open o closed |
Errors¶
| Código | Error | Descripción |
|---|---|---|
400 |
EMPLOYEE_ID_REQUIRED |
El employeeId es obligatorio |
400 |
VALIDATION_ERROR |
Parámetros inválidos |
401 |
UNAUTHORIZED |
Token inválido o expirado |
403 |
FORBIDDEN |
Usuario sin permisos de Superadmin |
404 |
NO_ACTIVE_SCHOOL_COURSE |
No hay curso escolar activo configurado |
404 |
SCHOOL_COURSE_NOT_FOUND |
El schoolCourseId proporcionado no existe |
Notas¶
- El ordenamiento es cronológico descendente (más reciente primero)
- Si no se envían
startDate/endDate, se asume el mes actual - La
durationestá en minutos para facilitar cálculos en el frontend