Skip to main content

Group Management API

The Groups (Grupos) API provides endpoints for managing work groups, assigning operational roles to personnel, and controlling group membership. This module handles both operational groups (production floor teams organized by shifts) and administrative groups, with strict validation rules to prevent cross-type assignments.

Permissions

All group endpoints require authentication and specific permissions:
  • VIEW_STAFF - Required for viewing groups and operational roles
  • MANAGE_STAFF - Required for creating groups, managing memberships, and assigning operational roles

Get All Groups

curl -X GET https://api.prod-sys.com/api/grupos \
  -H "Authorization: Bearer YOUR_TOKEN"
GET /api/grupos
{
  "success": true,
  "data": [
    {
      "id": 1,
      "nombre": "Grupo A - Turno 1",
      "tipo": "operativo",
      "turno_actual": "T1",
      "activo": 1,
      "created_at": "2024-01-10T08:00:00Z",
      "updated_at": "2024-01-10T08:00:00Z"
    },
    {
      "id": 2,
      "nombre": "Grupo B - Turno 2",
      "tipo": "operativo",
      "turno_actual": "T2",
      "activo": 1,
      "created_at": "2024-01-10T08:00:00Z",
      "updated_at": "2024-01-10T08:00:00Z"
    },
    {
      "id": 5,
      "nombre": "Administrativos",
      "tipo": "administrativo",
      "turno_actual": "T4",
      "activo": 1,
      "created_at": "2024-01-10T08:00:00Z",
      "updated_at": "2024-01-10T08:00:00Z"
    }
  ]
}

Authorization

Required Permission: VIEW_STAFF

Response Schema

Returns a list of all active groups:
success
boolean
required
Indicates if the request was successful
data
array
required
Array of group objects
id
integer
Unique group identifier
nombre
string
Group name
tipo
string
Group type: operativo or administrativo
turno_actual
string
Current shift assignment (e.g., “T1”, “T2”, “T3”, “T4”). Administrative groups are fixed at “T4”.
activo
integer
Active status (1 = active, 0 = inactive)
created_at
string
Group creation timestamp
updated_at
string
Last update timestamp

Get Group Details

curl -X GET https://api.prod-sys.com/api/grupos/1 \
  -H "Authorization: Bearer YOUR_TOKEN"
GET /api/grupos/:id
{
  "success": true,
  "data": {
    "id": 1,
    "nombre": "Grupo A - Turno 1",
    "tipo": "operativo",
    "turno_actual": "T1",
    "activo": 1,
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-03-15T14:30:00Z",
    "integrantes": [
      {
        "id": 3,
        "grupo_id": 1,
        "persona_id": 15,
        "nombre": "Juan",
        "apellido": "Pérez",
        "codigo_interno": "EMP015",
        "rol_organizacional": "Operador de Máquina",
        "rol_operativo": "Operador Especializado",
        "fecha_desde": "2024-01-20T00:00:00Z",
        "fecha_hasta": null,
        "motivo": "Asignación operativa estándar",
        "asignado_por": 2
      },
      {
        "id": 7,
        "grupo_id": 1,
        "persona_id": 22,
        "nombre": "Ana",
        "apellido": "García",
        "codigo_interno": "EMP022",
        "rol_organizacional": "Técnico de Línea",
        "rol_operativo": "Técnico",
        "fecha_desde": "2024-02-01T00:00:00Z",
        "fecha_hasta": null,
        "motivo": "Integración al grupo operativo",
        "asignado_por": 2
      }
    ],
    "historial": [
      {
        "id": 2,
        "grupo_id": 1,
        "persona_id": 18,
        "nombre": "Carlos",
        "apellido": "Ramírez",
        "fecha_desde": "2024-01-20T00:00:00Z",
        "fecha_hasta": "2024-02-28T00:00:00Z",
        "motivo": "Remoción por cambio de turno",
        "asignado_por": 2
      }
    ]
  }
}

Parameters

id
integer
required
Group ID

Authorization

Required Permission: VIEW_STAFF

Response Schema

Returns comprehensive group details including current members and historical changes:
success
boolean
required
Request success indicator
data
object
required
Detailed group information
integrantes
array
Current active group members
id
integer
Membership record ID
persona_id
integer
Personnel ID
nombre
string
First name
apellido
string
Last name
codigo_interno
string
Employee code
rol_organizacional
string
Organizational role title
rol_operativo
string
Current operational role name
fecha_desde
string
Membership start timestamp
fecha_hasta
string
Membership end timestamp (null for active members)
motivo
string
Reason for assignment
asignado_por
integer
ID of user who assigned this member
historial
array
Complete membership history including removed members

Add Group Member

curl -X POST https://api.prod-sys.com/api/grupos/1/integrantes \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "personaId": 25,
    "motivo": "Integración al grupo operativo por necesidad de producción",
    "es_correccion": false
  }'
{
  "personaId": 25,
  "motivo": "Integración al grupo operativo por necesidad de producción",
  "es_correccion": false
}
{
  "success": true,
  "data": {
    "message": "Integrante añadido con éxito"
  }
}

Parameters

id
integer
required
Group ID

Authorization

Required Permission: MANAGE_STAFF

Request Body

personaId
integer
required
Personnel ID to add to the group
motivo
string
Reason for adding the member. Defaults to “Asignación operativa estándar” if not provided.
es_correccion
boolean
default:"false"
Mark this assignment as a correction. If true, “[CORRECCIÓN]” prefix is added to the audit log.

Business Logic

Validation Rules

  1. Group Existence: Group must exist in the system
  2. Personnel Existence: Person must exist in the system
  3. Terminal State Protection: Cannot add personnel with estado_laboral = "Baja"
  4. Type Segregation:
    • Personnel from “Administración” area can only join administrativo groups
    • Personnel from other areas (e.g., “Producción”) can only join operativo groups
    • Violation returns error: “Un colaborador administrativo no puede pertenecer a un grupo operativo” or vice versa
  5. Duplicate Prevention: Personnel cannot be added if they’re already an active member
  6. Transaction Safety: All operations run within a database transaction
  7. Audit Trail: Logged with action GRUPO_ADD_INTEGRANTE including member details and group info

Error Responses

{
  "success": false,
  "error": "Grupo no encontrado"
}

Remove Group Member

curl -X POST https://api.prod-sys.com/api/grupos/1/integrantes/25/remove \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "motivo": "Remoción por rotación de turno",
    "es_correccion": false
  }'
{
  "motivo": "Remoción por rotación de turno",
  "es_correccion": false
}
{
  "success": true,
  "data": {
    "message": "Integrante removido con éxito"
  }
}

Parameters

id
integer
required
Group ID
personaId
integer
required
Personnel ID to remove from the group

Authorization

Required Permission: MANAGE_STAFF

Request Body

motivo
string
Reason for removal. Defaults to “Remoción operativa estándar” if not provided.
es_correccion
boolean
default:"false"
Mark this removal as a correction. If true, “[CORRECCIÓN]” prefix is added to the audit log.

Business Logic

  1. Membership Validation: Verifies that the person is currently an active member of the group
  2. Soft Delete: Sets fecha_hasta = CURRENT_TIMESTAMP rather than deleting the record
  3. Historical Preservation: Membership history is preserved for audit and reporting
  4. Transaction Safety: Runs within a database transaction
  5. Audit Trail: Logged with action GRUPO_REMOVE_INTEGRANTE including person and group details

Error Responses

{
  "success": false,
  "error": "Grupo no encontrado"
}

Rotate Group Shift

curl -X PUT https://api.prod-sys.com/api/grupos/1/turno \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nuevoTurno": "T2"
  }'
{
  "nuevoTurno": "T2"
}
{
  "success": true,
  "data": {
    "message": "Turno actualizado con éxito"
  }
}

Parameters

id
integer
required
Group ID

Authorization

Required Permission: MANAGE_STAFF

Request Body

nuevoTurno
string
required
New shift identifier (e.g., “T1”, “T2”, “T3”)

Business Logic

  1. Group Validation: Group must exist
  2. Type Restriction: Administrative groups (tipo = "administrativo") have a fixed shift (“T4”) and cannot be rotated
    • Returns error: “El turno del grupo administrativo es fijo (T4)”
  3. Transaction Safety: Runs within a database transaction
  4. Audit Trail: Logged with action GRUPO_ROTAR_TURNO showing previous and new shift values
  5. Timestamp Update: The updated_at field is automatically updated
Shift rotation is typically used for operational groups that follow rotating shift schedules (e.g., weekly rotation between T1, T2, and T3).

Error Responses

{
  "success": false,
  "error": "Grupo no encontrado"
}

Get Operational Roles

curl -X GET https://api.prod-sys.com/api/grupos/roles-operativos \
  -H "Authorization: Bearer YOUR_TOKEN"
GET /api/grupos/roles-operativos
{
  "success": true,
  "data": [
    {
      "id": 1,
      "nombre": "Auxiliar",
      "descripcion": "Personal de apoyo operativo",
      "activo": 1
    },
    {
      "id": 2,
      "nombre": "Operador Especializado",
      "descripcion": "Operador certificado en procesos específicos",
      "activo": 1
    },
    {
      "id": 3,
      "nombre": "Técnico",
      "descripcion": "Técnico de línea con competencias avanzadas",
      "activo": 1
    },
    {
      "id": 4,
      "nombre": "Líder de Línea",
      "descripcion": "Responsable de coordinar la línea de producción",
      "activo": 1
    }
  ]
}

Authorization

Required Permission: VIEW_STAFF

Response Schema

Returns the catalog of operational roles:
success
boolean
required
Request success indicator
data
array
required
List of active operational roles
id
integer
Operational role ID
nombre
string
Role name
descripcion
string
Role description
activo
integer
Active status (1 = active, 0 = inactive)

Usage

This endpoint provides the operational role catalog used for:
  • Populating dropdown menus when assigning operational roles
  • Validating rol_operativo_id in assignment operations
  • Displaying operational role capabilities and responsibilities

Assign Operational Role

curl -X POST https://api.prod-sys.com/api/grupos/persona/15/rol-operativo \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "rolOperativoId": 3,
    "motivo": "Promoción a técnico por certificación completada",
    "es_correccion": false
  }'
{
  "rolOperativoId": 3,
  "motivo": "Promoción a técnico por certificación completada",
  "es_correccion": false
}
{
  "success": true,
  "data": {
    "message": "Rol operativo asignado con éxito"
  }
}

Parameters

personaId
integer
required
Personnel ID

Authorization

Required Permission: MANAGE_STAFF

Request Body

rolOperativoId
integer
required
Operational role ID to assign. Must be a valid role from the roles_operativos catalog.
motivo
string
Reason for the role assignment. Defaults to “Cambio de rol operativo estándar” if not provided.
es_correccion
boolean
default:"false"
Mark this assignment as a correction. If true, “[CORRECCIÓN]” prefix is added to the audit log.

Business Logic

  1. Role Validation: Validates that the operational role exists and is active
  2. Historical Tracking:
    • Previous operational role assignment is closed by setting fecha_hasta = CURRENT_TIMESTAMP
    • New role assignment is inserted with fecha_hasta = NULL (indicating current/active)
  3. One Active Role: Each person can have only one active operational role at a time
  4. Transaction Safety: Runs within a database transaction
  5. Audit Trail: Logged with action ROL_OPERATIVO_CHANGE including the new role name
System Roles vs Operational Roles:
  • System Role (assigned via /api/personal/:id/rol): Controls application permissions (e.g., Administrador, Supervisor, Operario)
  • Operational Role (assigned via this endpoint): Defines production floor competencies (e.g., Auxiliar, Operador Especializado, Técnico)
A person has one system role and one operational role, managed independently.

Error Responses

{
  "success": false,
  "error": "Rol operativo no encontrado"
}

Get Personnel Operational History

curl -X GET https://api.prod-sys.com/api/grupos/persona/15/historial \
  -H "Authorization: Bearer YOUR_TOKEN"
GET /api/grupos/persona/:personaId/historial
{
  "success": true,
  "data": {
    "historial_roles_operativos": [
      {
        "id": 12,
        "persona_id": 15,
        "rol_operativo_id": 3,
        "rol_nombre": "Técnico",
        "fecha_desde": "2024-03-01T00:00:00Z",
        "fecha_hasta": null,
        "motivo": "Promoción a técnico por certificación completada",
        "asignado_por": 2
      },
      {
        "id": 8,
        "persona_id": 15,
        "rol_operativo_id": 2,
        "rol_nombre": "Operador Especializado",
        "fecha_desde": "2024-01-15T00:00:00Z",
        "fecha_hasta": "2024-02-29T23:59:59Z",
        "motivo": "Asignación inicial de rol operativo",
        "asignado_por": 2
      },
      {
        "id": 3,
        "persona_id": 15,
        "rol_operativo_id": 1,
        "rol_nombre": "Auxiliar",
        "fecha_desde": "2024-01-01T00:00:00Z",
        "fecha_hasta": "2024-01-14T23:59:59Z",
        "motivo": "Rol inicial de ingreso",
        "asignado_por": 2
      }
    ]
  }
}

Parameters

personaId
integer
required
Personnel ID

Authorization

Required Permission: VIEW_STAFF

Response Schema

Returns the complete operational role history for a person:
success
boolean
required
Request success indicator
data
object
required
historial_roles_operativos
array
Ordered list of operational role assignments (most recent first)
id
integer
Assignment record ID
persona_id
integer
Personnel ID
rol_operativo_id
integer
Operational role ID
rol_nombre
string
Operational role name
fecha_desde
string
Role start timestamp
fecha_hasta
string
Role end timestamp (null for current active role)
motivo
string
Reason for the assignment
asignado_por
integer
ID of user who made the assignment

Usage

This endpoint is useful for:
  • Tracking personnel career progression
  • Auditing role changes
  • Generating competency reports
  • Identifying training and development paths

Data Models

Group Entity

Stored in the grupos table:
CREATE TABLE grupos (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  nombre TEXT NOT NULL,
  tipo TEXT NOT NULL CHECK(tipo IN ('operativo', 'administrativo')),
  turno_actual TEXT NOT NULL,
  activo INTEGER DEFAULT 1,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Group Membership Entity

Stored in the grupo_integrantes table:
CREATE TABLE grupo_integrantes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  grupo_id INTEGER NOT NULL,
  persona_id INTEGER NOT NULL,
  fecha_desde DATETIME DEFAULT CURRENT_TIMESTAMP,
  fecha_hasta DATETIME,
  motivo TEXT,
  asignado_por INTEGER,
  FOREIGN KEY (grupo_id) REFERENCES grupos(id),
  FOREIGN KEY (persona_id) REFERENCES personas(id)
);

Operational Role Assignment Entity

Stored in the persona_roles_operativos table:
CREATE TABLE persona_roles_operativos (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  persona_id INTEGER NOT NULL,
  rol_operativo_id INTEGER NOT NULL,
  fecha_desde DATETIME DEFAULT CURRENT_TIMESTAMP,
  fecha_hasta DATETIME,
  motivo TEXT,
  asignado_por INTEGER,
  FOREIGN KEY (persona_id) REFERENCES personas(id),
  FOREIGN KEY (rol_operativo_id) REFERENCES roles_operativos(id)
);

Operational Role Catalog

Stored in the roles_operativos table:
CREATE TABLE roles_operativos (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  nombre TEXT UNIQUE NOT NULL,
  descripcion TEXT,
  activo INTEGER DEFAULT 1
);

Business Rules Summary

Group Type Segregation

  1. Operational Groups (tipo = "operativo"):
    • Only for personnel from “Producción” and similar operational areas
    • Subject to shift rotation
    • Organized by production schedules
  2. Administrative Groups (tipo = "administrativo"):
    • Only for personnel from “Administración” area
    • Fixed shift (T4 - typically administrative hours)
    • Cannot be rotated

State Validation

  • Personnel with estado_laboral = "Baja" (terminated) cannot be added to groups
  • All group operations respect terminal state protection

Historical Integrity

  • Group memberships are soft-deleted (fecha_hasta is set, not deleted)
  • Operational role assignments maintain complete history
  • All changes are audited with reasons and timestamps

Audit Compliance

  • All mutations require a motivo (reason)
  • Corrections are explicitly marked with es_correccion flag
  • Audit logs include before/after states for all changes

Best Practices

Type Safety

  1. Validate area before assignment: Check personnel area before attempting to add to a group
const person = await fetch(`/api/personal/${personaId}`).then(r => r.json());
const group = await fetch(`/api/grupos/${grupoId}`).then(r => r.json());

if (person.data.area_nombre === 'Administración' && group.data.tipo !== 'administrativo') {
  alert('Cannot assign administrative personnel to operational group');
  return;
}
  1. Respect shift constraints: Don’t attempt to rotate administrative group shifts

Historical Queries

  1. Current vs Historical: Use fecha_hasta IS NULL to filter for current/active records:
-- Current group members only
SELECT * FROM grupo_integrantes 
WHERE grupo_id = ? AND fecha_hasta IS NULL;

-- Complete history
SELECT * FROM grupo_integrantes 
WHERE grupo_id = ? 
ORDER BY fecha_desde DESC;
  1. Role progression tracking: Query persona_roles_operativos ordered by fecha_desde DESC to see career progression

Performance

  1. Cache operational roles: The roles catalog is relatively static. Cache it client-side.
  2. Batch member additions: If adding multiple members to a group, make parallel requests:
const memberIds = [15, 22, 28, 31];
const promises = memberIds.map(id => 
  fetch(`/api/grupos/${grupoId}/integrantes`, {
    method: 'POST',
    body: JSON.stringify({ personaId: id, motivo: 'Batch assignment' })
  })
);
await Promise.all(promises);

Error Handling

async function addGroupMember(grupoId, personaId, motivo) {
  try {
    const response = await fetch(`/api/grupos/${grupoId}/integrantes`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ personaId, motivo })
    });
    
    const result = await response.json();
    
    if (!result.success) {
      // Business logic error (e.g., type mismatch, already member)
      console.error('Assignment failed:', result.error);
      return null;
    }
    
    return result.data;
  } catch (error) {
    // Network or server error
    console.error('Request failed:', error);
    throw error;
  }
}

Build docs developers (and LLMs) love