> ## Documentation Index
> Fetch the complete documentation index at: https://docs.filexpress.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Horarios

> Crear horarios de trabajo con sus turnos y asignarlos a empleados en lote

| Operación | Permiso |
| - | - |
| Consultar horarios y asignaciones | `hr.schedules` |
| Crear, editar y eliminar horarios; asignar y quitar asignaciones | `hr.schedules.write` |

Ver [Horarios](/docs/rrhh/horarios) en el manual.

## Listar horarios

```http theme={null}
GET /hr/work-schedules
```

**Permiso requerido:** `hr.schedules`

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "uuid",
      "business_id": "uuid",
      "name": "Administrativo",
      "code": "ADM",
      "description": "Lunes a viernes",
      "color": "#1890ff",
      "is_active": true,
      "shifts": [
        { "id": "uuid", "weekday": 1, "start_time": "08:00", "end_time": "17:00", "break_minutes": 60, "break_paid": null }
      ],
      "weekly_minutes": 2400,
      "days": [
        { "weekday": 1, "minutes": 480, "shift_kind": "day", "warnings": [] },
        { "weekday": 7, "minutes": 0, "shift_kind": null, "warnings": [] }
      ],
      "warnings": []
    }
  ]
}
```

| Campo | Descripción |
| - | - |
| `shifts[].weekday` | Día ISO: 1 = lunes … 7 = domingo. Un día sin turnos es de descanso. |
| `shifts[].break_paid` | `true`/`false` o `null` (usa lo definido en las reglas laborales). |
| `weekly_minutes` | Minutos semanales programados (sin descansos no pagados). |
| `days` | Resumen de los 7 días: minutos, clase de jornada (`day`, `night`, `mixed` o `null` si es descanso) y avisos. |
| `warnings` | Avisos legales: `daily_max_exceeded` (en `days`), `weekly_max_exceeded`. Cada aviso es `{ "code", "message" }`. |

Los horarios eliminados no se listan.

## Consultar horario

```http theme={null}
GET /hr/work-schedules/{id}
```

**Permiso requerido:** `hr.schedules`

Devuelve el mismo objeto.

## Crear horario

```http theme={null}
POST /hr/work-schedules
```

**Permiso requerido:** `hr.schedules.write`

```json theme={null}
{
  "name": "Nocturno",
  "code": "NOCHE",
  "description": "Vigilancia",
  "color": "#2f54eb",
  "shifts": [
    { "weekday": 1, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 },
    { "weekday": 2, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 },
    { "weekday": 3, "start_time": "22:00", "end_time": "05:00", "break_minutes": 0 }
  ]
}
```

| Campo | Requerido | Descripción |
| - | - | - |
| `name` | Sí | Nombre (máx. 255). |
| `code` | Sí | Código único en la empresa (máx. 50). |
| `description` | No | Descripción. |
| `color` | No | Color para la interfaz (máx. 20). |
| `is_active` | No | Por defecto `true`. |
| `shifts` | Sí | Lista de turnos (puede ser vacía; máx. 50). |
| `shifts[].weekday` | Sí | 1 = lunes … 7 = domingo. |
| `shifts[].start_time`, `shifts[].end_time` | Sí | `HH:MM`. Si la salida es menor o igual que la entrada, el turno termina **al día siguiente**. |
| `shifts[].break_minutes` | No | Minutos de descanso; debe ser menor que la duración del turno. |
| `shifts[].break_paid` | No | Si se omite, aplica lo de las reglas laborales. |

**Comportamiento**

* Un día puede tener varios turnos, pero no pueden solaparse.
* Los avisos legales (`warnings`) no impiden guardar.

**Respuesta `201`**: el horario creado.

| Código | Causa |
| - | - |
| `422` | Validación, turnos solapados, descanso mayor que el turno o código duplicado. |

## Editar horario

```http theme={null}
PUT /hr/work-schedules/{id}
```

**Permiso requerido:** `hr.schedules.write`

Envía solo los campos que cambian. Si envías `shifts`, **reemplaza** todos los turnos.

**Respuesta `200`**: el horario con `recalculation_queued` (`true` si se encoló el recálculo de la asistencia de los empleados que lo tienen asignado).

## Eliminar horario

```http theme={null}
DELETE /hr/work-schedules/{id}
```

**Permiso requerido:** `hr.schedules.write`

```json theme={null}
{ "status": true, "data": { "id": "uuid", "deleted": true } }
```

Responde `409` si el horario tiene asignaciones vigentes (sin fecha final o con fecha final de hoy en adelante): termínalas o cámbialas primero.

## Consultar asignaciones

```http theme={null}
GET /hr/schedule-assignments?employee_id=uuid&date=2026-10-01
```

**Permiso requerido:** `hr.schedules`

| Parámetro | Requerido | Descripción |
| - | - | - |
| `employee_id` | No | Filtra por empleado (historial completo). |
| `date` | No | Solo las asignaciones vigentes en esa fecha. |

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "uuid",
      "business_id": "uuid",
      "employee_id": "uuid",
      "employee_name": "María López",
      "work_schedule_id": "uuid",
      "work_schedule_name": "Administrativo",
      "work_schedule_code": "ADM",
      "start_date": "2026-10-01",
      "end_date": null,
      "notes": null
    }
  ]
}
```

`end_date: null` = asignación indefinida.

## Asignar horario en lote

```http theme={null}
POST /hr/schedule-assignments
```

**Permiso requerido:** `hr.schedules.write`

Asigna un horario a varios empleados con la misma vigencia.

```json theme={null}
{
  "work_schedule_id": "uuid",
  "document_numbers": ["01234567-8", "98765432-1"],
  "start_date": "2026-11-01",
  "end_date": null,
  "close_previous": true,
  "notes": "Cambio de turno"
}
```

| Campo | Requerido | Descripción |
| - | - | - |
| `work_schedule_id` | Sí | Horario de la empresa. |
| `employee_ids` | Uno de los dos | IDs de empleados (máx. 500). |
| `document_numbers` | Uno de los dos | Números de documento (máx. 500; se ignoran guiones y espacios). Se pueden combinar con `employee_ids`. |
| `start_date` | Sí | Inicio (`YYYY-MM-DD`). |
| `end_date` | No | Fin (igual o posterior al inicio). `null` = indefinida. |
| `close_previous` | No | Si es `true`, la asignación vigente anterior del empleado termina el día previo a `start_date`. |
| `notes` | No | Notas (máx. 1000). |

**Comportamiento**

* **Idempotente**: si el empleado ya tiene una asignación que empieza en `start_date`, se actualiza en lugar de duplicarse.
* Una asignación que se solapa con otra del empleado se rechaza (salvo que `close_previous` la cierre).
* Cada empleado se procesa por separado: un error no detiene el resto.

**Respuesta `200`**

```json theme={null}
{
  "status": true,
  "data": {
    "created": 1,
    "updated": 0,
    "errors": [
      { "employee_id": null, "document_number": "98765432-1", "message": "Empleado no encontrado." }
    ],
    "recalculation_queued": true
  }
}
```

Si **ningún** empleado se asignó y hay errores, la respuesta tiene código `422`. Si el horario no existe, `404`.

<Note>
  Los mensajes de error de empleados ya resueltos (por ejemplo, solapes) vienen en español, con `employee_id` informado.
</Note>

## Eliminar asignación

```http theme={null}
DELETE /hr/schedule-assignments/{id}
```

**Permiso requerido:** `hr.schedules.write`

```json theme={null}
{ "status": true, "data": { "id": "uuid", "deleted": true, "recalculation_queued": true } }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.