Desempeño
Resumen
Sección titulada «Resumen»El módulo cubre el flujo de autoservicio de evaluación de desempeño del usuario autenticado: ver las campañas activas en las que participa (como autoevaluado, jefe, par, cliente interno o evaluador de un colaborador), responder los formularios que le corresponden, guardar avances parciales, enviar la evaluación final, y consultar su historial de evaluaciones junto con el informe de resultados y el feedback recibido.
Acciones cubiertas:
- Ver campañas de evaluación activas en las que el usuario participa (como evaluado o como evaluador).
- Ver el detalle de una campaña (secciones habilitadas, tipo de evaluación).
- Ver la lista de personas que el usuario debe evaluar dentro de una campaña (autoevaluación, jefe, pares, colaboradores a cargo, cliente interno, según cómo esté diseñada la campaña).
- Responder el formulario de evaluación (funciones, competencias, condiciones organizacionales, filosofía, valores, potencial, comentarios) y guardarlo de forma parcial o final.
- Ver el historial de evaluaciones propias, filtrado por año.
- Ver el informe final de una evaluación terminada y el feedback publicado por el jefe.
- Descargar el informe final como PDF (resumen ejecutivo o informe completo, con secciones opcionales, en español o inglés).
- Acusar recibo del feedback recibido.
Prerrequisitos:
- El usuario debe estar autenticado y el
x-tenant-iddebe coincidir con su tenant (ver Convenciones). - Guardar progreso (
PATCH .../save), ver historial y ver el informe requieren que el tenant tenga configurados sus parámetros generales — el backend rechaza estas llamadas si el tenant no los tiene (RequiresTenantParameters). - Las campañas tipo
EM(Evaluación de MRL) quedan excluidas del listado de personas a evaluar (GET /me/evaluation/{id}/type/{abbreviation}), sin importar quéabbreviationse pida — el backend las filtra explícitamente. Si una campañaEMaparece enGET /me/evaluation, la app no podrá construir su roster con este flujo.
sequenceDiagram
participant App as App móvil
participant API as th-core-api
Note over App,API: Lista de campañas activas
App->>API: GET /me/evaluation
API-->>App: campañas activas del usuario
Note over App,API: Usuario abre una campaña
App->>API: GET /me/evaluation/:id/details
API-->>App: detalle + evaluationType.abbreviation + secciones habilitadas
Note over App: abbreviation = ED (Desempeño) o similar -> ir al roster<br/>abbreviation = OB (Objetivos) -> flujo fuera de alcance
App->>API: GET /me/evaluation/:id/type/:abbreviation
API-->>App: personas a evaluar (autoevaluación, jefe, pares, colaboradores) con estado y accion requerida
Note over App,API: Usuario elige a quién evaluar y empieza el formulario
App->>API: GET /me/evaluation/content/all
API-->>App: contenido dinámico del formulario según la configuración de la campaña
Note over App,API: El usuario guarda avance en cualquier momento
App->>API: PATCH /me/evaluation/:evaluationId/username/:usernameEvaluated/save
Note over App: metadata.isFinal = false -> guardado parcial
Note over App,API: Al completar todas las secciones, enviar
App->>API: PATCH /me/evaluation/:evaluationId/username/:usernameEvaluated/save
Note over App: metadata.isFinal = true -> envío final
App->>API: GET /me/evaluation/:id/type/:abbreviation
API-->>App: refresco del roster
Note over App,API: Historial y resultados
App->>API: GET /me/evaluation/history/years
API-->>App: años con evaluaciones
App->>API: GET /me/evaluation/history
API-->>App: historial de evaluaciones propias
Note over App,API: Si la evaluación terminada permite ver informe
App->>API: GET /me/evaluation/:evaluationId/report
API-->>App: informe final, incluye feedback del jefe si fue publicado
Note over App,API: Descargar el informe como PDF (opcional)
App->>API: GET /me/evaluation/:evaluationId/report/pdf?mode=&hide=&lang=
Note over API: arma el HTML con los datos del informe<br/>y lo renderiza el Worker th-pdfs
API-->>App: binario application/pdf (no JSON)
Note over App,API: Si el jefe publicó feedback y aún no fue confirmado
App->>API: PATCH /me/evaluation/:evaluationId/report/feedback/acknowledge
API-->>App: confirmación
Endpoints
Sección titulada «Endpoints»GET /me/evaluation
Sección titulada «GET /me/evaluation»Campañas de evaluación activas en las que participa el usuario autenticado (como evaluado o como evaluador de alguien más).
Query params:
| Param | Tipo | Obligatorio | Enum | Descripción |
|---|---|---|---|---|
abbreviation |
string | No | EvaluationTypeAbbreviationEnum: PP, ED, PO, OB, EM |
Filtra a un solo tipo de evaluación. |
Response — array de:
| Campo | Tipo | Descripción |
|---|---|---|
id |
number | Id de la campaña de evaluación. |
name |
string | Nombre de la campaña. |
evaluationTypeId |
number | Id del tipo de evaluación. |
evaluationType |
array | [{ id, abbreviation }] — ver enum de tipos en Lógica de cliente. |
periodId / periodMonth / periodYear |
number | Período al que pertenece la campaña. |
startDate / endDate |
date | Vigencia de la campaña. |
form |
string | Código de formato interno ('1' horizontal, '2' vertical, '3' escala por conducta) — no confundir con el format de texto (HORIZONTAL/VERTICAL) que trae el informe final. |
status |
enum EvaluationStatusEnum |
Estado de la campaña (ver tabla en Lógica de cliente). |
maxScaleValue |
number | Valor máximo de la escala de calificación. |
instructions |
string | null | Instrucciones HTML de la campaña, si las tiene. |
hasResponsibilitiesConfigured |
boolean | El tenant tiene habilitado el cumplimiento de responsabilidades para esta campaña. |
hasFunction, hasObjective, hasCompetency, hasCondition, hasPhilosophy, hasValue, hasPotential, hasQuestions, hasResponses |
enum 'S' | 'N' |
Qué secciones tiene habilitadas la campaña. La app no necesita usarlos para armar el formulario — GET /me/evaluation/content/all ya devuelve sólo las secciones que aplican — pero sirven para mostrar un resumen rápido en la card. |
weightCompetency, weightCondition, weightFunction, weightObjective, weightPhilosophy, weightValue, weightPotential, weightQuestions, weightResponses |
number | Peso de cada sección en el puntaje final. Uso informativo; el cálculo lo hace el backend. |
[ { "id": 4102, "name": "Evaluación de Desempeño 2026 - Semestre 1", "evaluationTypeId": 2, "evaluationType": [{ "id": 2, "abbreviation": "ED" }], "periodId": 18, "periodMonth": 6, "periodYear": 2026, "startDate": "2026-06-01T00:00:00.000Z", "endDate": "2026-07-31T23:59:59.000Z", "form": "1", "status": "A", "maxScaleValue": 5, "instructions": null, "hasResponsibilitiesConfigured": false, "hasFunction": "S", "hasObjective": "S", "hasCompetency": "S", "hasCondition": "N", "hasPhilosophy": "N", "hasValue": "N", "hasPotential": "N", "hasQuestions": "N", "hasResponses": "N" }]Sin errores específicos adicionales a los comunes.
GET /me/evaluation/{id}/details
Sección titulada «GET /me/evaluation/{id}/details»Detalle completo de una campaña puntual — se usa principalmente para leer evaluationType.abbreviation y decidir a qué pantalla navegar, y para conocer los flags de configuración antes de entrar al roster.
Path param: id (number).
Response: mismos campos que GET /me/evaluation, más:
| Campo | Tipo | Descripción |
|---|---|---|
evaluationUsers |
array | Todas las filas de evaluación asociadas a la campaña (evaluado, evaluador, relación, estado). En autoservicio normalmente no se recorre esta lista directamente — para eso está el endpoint paginado GET /me/evaluation/{id}/type/{abbreviation} — pero el campo viene presente. |
hasStrategicObjective |
boolean | true si la campaña tiene un objetivo estratégico asociado (strategicObjectivePeriodId > -1). |
Sin errores específicos adicionales a los comunes.
GET /me/evaluation/{id}/type/{abbreviation}
Sección titulada «GET /me/evaluation/{id}/type/{abbreviation}»Lista paginada de las personas que el usuario autenticado debe evaluar dentro de la campaña, con su estado y qué acción falta. abbreviation es el evaluationType.abbreviation de la propia campaña (obtenido en /details), no un valor libre.
Query params:
| Param | Tipo | Obligatorio | Descripción |
|---|---|---|---|
page |
number | No | Página (default 1). |
limit |
number | No | Ítems por página (default 10). |
includeAutoEvaluations |
boolean | No (default false) |
Si es false, oculta la fila donde el evaluador es igual al evaluado (tu propia autoevaluación). El frontend de referencia siempre envía true para mostrarla junto al resto. |
Response:
{ "items": [ /* ver tabla de campos abajo */ ], "meta": { "totalItems": 4, "itemCount": 4, "itemsPerPage": 10, "totalPages": 1, "currentPage": 1, "pendingCount": 2 }}Campos de cada item (entidad casi cruda — este endpoint no tiene un DTO de respuesta formal, a diferencia del resto del módulo):
| Campo | Tipo | Descripción |
|---|---|---|
username |
string | Usuario evaluado en esta fila. |
evaluator |
string | Usuario evaluador (el autenticado, salvo casos administrativos). |
evaluationId |
number | Id de la campaña. |
relationId |
number | Relación del evaluador respecto al evaluado — ver tabla RelationEnum en Lógica de cliente. |
relation |
object | { id, name } — nombre legible de la relación. |
status |
enum EvaluationUserStatusEnum |
Estado de esta evaluación puntual (ver tabla en Lógica de cliente). |
user |
object | { username, name, lastName, avatar, position: { id, name } } — datos del evaluado. |
userAutoevaluationStatus |
enum | null | Sólo presente si relationId es 1 (Jefe) o 6 (Gerente Funcional): estado de la autoevaluación del evaluado, para saber si ya puede evaluarlo. |
actionRequired |
object | null | { key, defaultLabel } si hay una acción bloqueante antes de poder calificar — ver tabla en Lógica de cliente. null si puede evaluar directamente. |
{ "items": [ { "username": "jperez", "evaluator": "jperez", "evaluationId": 4102, "relationId": 5, "relation": { "id": 5, "name": "Autoevaluación" }, "status": "F", "user": { "username": "jperez", "name": "Juan", "lastName": "Pérez", "avatar": "jperez.png", "position": { "id": 31, "name": "Analista de Nómina" } }, "userAutoevaluationStatus": null, "actionRequired": null }, { "username": "mrojas", "evaluator": "jperez", "evaluationId": 4102, "relationId": 3, "relation": { "id": 3, "name": "Pares" }, "status": "A", "user": { "username": "mrojas", "name": "María", "lastName": "Rojas", "avatar": "mrojas.png", "position": { "id": 40, "name": "Analista de Nómina" } }, "userAutoevaluationStatus": null, "actionRequired": null } ], "meta": { "totalItems": 2, "itemCount": 2, "itemsPerPage": 10, "totalPages": 1, "currentPage": 1, "pendingCount": 1 }}Sin errores específicos adicionales a los comunes.
GET /me/evaluation/{id}/type/{abbreviation}/user
Sección titulada «GET /me/evaluation/{id}/type/{abbreviation}/user»Variante singular del endpoint anterior: devuelve la fila de evaluación del propio usuario autenticado (evaluado = evaluador = usuario actual) para esa campaña y tipo, sin paginar.
Path params: id (number), abbreviation (EvaluationTypeAbbreviationEnum).
Response: un solo objeto con la misma forma que un item de GET /me/evaluation/{id}/type/{abbreviation}.
Errores específicos:
| Código | Cuándo ocurre |
|---|---|
404 |
La evaluación no existe o el usuario no está autorizado a verla. |
GET /me/evaluation/content/all
Sección titulada «GET /me/evaluation/content/all»Devuelve el contenido dinámico del formulario de evaluación — funciones, objetivos, competencias, condiciones organizacionales, filosofía, valores, potencial, preguntas, responsabilidades — ya filtrado según qué secciones tiene habilitadas la campaña y la relación del evaluador. Es el endpoint que carga la pantalla de responder evaluación: una llamada por cada par evaluador → evaluado que el usuario abre desde el roster.
Query params:
Todos los params son opcionales para el framework (el backend no devuelve 400 por omitirlos), pero funcionalmente debes enviarlos siempre: el contenido se arma con ellos y, si faltan, el resultado es un error o datos incorrectos. Todos salen de la fila del roster que el usuario eligió (GET /me/evaluation/{id}/type/{abbreviation}).
| Param | Tipo | Obligatorio | Para qué / cuándo usarlo |
|---|---|---|---|
evaluationId |
number | Funcionalmente sí | Campaña que se va a responder. Úsalo con el evaluationId de la fila del roster. |
username |
string | Funcionalmente sí | Usuario evaluado (no el autenticado, salvo en autoevaluación). Úsalo con el username de la fila. |
positionId |
number | Funcionalmente sí | Cargo del evaluado; determina qué funciones, competencias y objetivos se cargan. Tómalo de user.position.id de la fila. |
relationship |
number | Funcionalmente sí | relationId de la fila (ver RelationEnum). Define qué secciones aplican a quien evalúa (por ejemplo, potencial sólo para 1). |
userAutoevaluationStatus |
string | No | Estado de la autoevaluación del evaluado, tal cual viene en la fila del roster (A, P, F, V, C, N, R — ver Estados de la evaluación del usuario). Sólo tiene efecto con el valor F. Envíalo cuando el roster lo traiga (jefe y gerente funcional); omítelo si es null. |
El único valor que cambia la respuesta es userAutoevaluationStatus = F, y sólo si además la campaña tiene activeAutomaticVerification = 'S' y relationship != 5: entonces se rellenan los resultAutoevaluation de cada ítem y general.observationAutoevaluation / secondObservationAutoevaluation. Con cualquier otro valor esos campos no vienen.
Estados de la evaluación del usuario (EvaluationUserStatusEnum):
| Valor | Significado |
|---|---|
A |
Activa: asignada, sin finalizar (también es el estado de un guardado parcial). |
P |
En proceso. |
F |
Finalizada: el evaluador ya envió su evaluación. |
V |
En validación. |
C |
En calibración. |
N |
Rechazada. |
R |
Registrada: en el flujo de objetivos, el registro ya fue hecho y espera la evaluación. |
Response. La forma completa es extensa y varía según qué secciones tenga la campaña. Se describe por objeto, siempre con la misma pregunta: ¿para qué sirve y cuándo lo uso?
evaluation — qué campaña es y cómo se comporta
Sección titulada «evaluation — qué campaña es y cómo se comporta»| Campo | Tipo | Para qué / cuándo usarlo |
|---|---|---|
id / name |
number / string | Identifican la campaña. Usa name como título de la pantalla. |
evaluationType |
object | { id, abbreviation } (PP, ED, PO…). Úsalo para ajustar textos según el tipo (por ejemplo, el período de prueba tiene recomendación de contrato). |
evaluation.configuration — banderas de la campaña. Los 'S'/'N' son cadenas, no booleanos:
| Campo | Tipo | Para qué / cuándo usarlo |
|---|---|---|
hasFunction / hasObjective / hasCompetency / hasCondition / hasPhilosophy / hasValue / hasPotential / hasQuestions / hasQuestionsPerformance / hasResponses |
'S' | 'N' |
La campaña incluye esa sección. Que sea 'S' no garantiza que haya ítems: la relación del evaluador puede no tenerla. Para decidir qué pintar usa la longitud del array en components; para el cálculo de pasos y pesos usa estas banderas. |
activeisNotObserved |
'S' | 'N' |
Si es 'S', ofrece la opción “No observado” en las escalas. Si es 'N', no la muestres: el backend ignora un -1 enviado en ese caso. |
activeFlowControl |
'S' | 'N' |
Control de flujo secuencial: con 'S' el jefe/pares no pueden evaluar hasta que el evaluado termine su autoevaluación. El roster ya lo refleja en actionRequired; aquí sólo úsalo como contexto (el backend revalida y responde 400). |
activeAutomaticVerification |
'S' | 'N' |
Si es 'S' (y se cumple la regla de Referencia de autoevaluación), puedes mostrar junto a cada ítem lo que el evaluado se calificó. |
activeObjectiveScale |
'S' | 'N' |
Indica que la campaña usa una escala propia para objetivos (scales.objectiveScale). Úsala, y no mainScale, para calificar objetivos cuando sea 'S'. |
activeAttachedObjectiveFile |
'S' | 'N' |
Si es 'S', permite adjuntar un archivo de evidencia por objetivo (objectives[].file). Muestra u oculta el control de adjuntos. |
activePeriodicObjective |
'S' | 'N' |
La campaña maneja objetivos periódicos. El backend lo expone como bandera; la app de referencia no cambia el formulario por él, y la gestión de objetivos queda fuera de este documento. |
registeredUserConfig |
'U' | 'J' | 'A' |
Quién registra los objetivos/responsabilidades: U colaborador, J jefe inmediato, A ambos. Explica por qué la sección de objetivos o responsabilidades puede llegar vacía (ver Cuándo una sección llega vacía). |
format |
'1' | '2' | '3' |
Cómo se califican competencias y condiciones: 1 horizontal (nivel por conducta), 2 vertical (se elige una conducta por competencia), 3 escala por conducta. Define qué formato de save usar. |
philosophyValuesVertical |
boolean | true si Filosofía y Valores se califican en modo vertical (sólo ocurre con format = '2' y un parámetro del cliente activo). Úsalo, y no format, para decidir cómo renderizar y guardar Filosofía y Valores: con false se califican por escala aunque la campaña sea vertical. |
scales — opciones de calificación
Sección titulada «scales — opciones de calificación»| Campo | Tipo | Para qué / cuándo usarlo |
|---|---|---|
mainScale |
array | Escala de funciones, competencias, condiciones, filosofía, valores y preguntas. |
objectiveScale |
array | Escala de objetivos; sólo se usa si activeObjectiveScale = 'S'. |
strategyScale |
array | Escala de estrategias; no hay una sección de estrategias en components, así que normalmente no se usa. |
Cada valor es { id, idScale, valueName, shortValueName, value, valueOrder, status }: pinta valueName (o shortValueName en botones compactos), ordena por valueOrder y envía value al guardar. El backend devuelve todos los niveles activos, incluido uno con value = 0 (por ejemplo “No cumple”): es una calificación válida, no “sin responder”. “No observado” no es un nivel de la escala: es una opción aparte que la app agrega sólo si activeisNotObserved = 'S'.
components — el contenido a calificar
Sección titulada «components — el contenido a calificar»Cada propiedad es un array. Una sección que llega [] no aplica para esta relación (o la campaña no la tiene): ocúltala. Esquema por sección:
| Sección | Ítem (campos relevantes) | Para qué / cuándo usarlo |
|---|---|---|
functions |
id, name, description, weight, result, items, resultAutoevaluation |
Funciones del cargo. result es la calificación ya guardada (promedio) y items cuántas calificaciones la forman; sirven para precargar la pantalla. weight es informativo. Guarda con { id, obtainedLevel }. |
objectives |
id, name, description, indicator, periodRelations[] (meta, línea base, unidad), obtainedLevel, complianceValue, observation, file, fileCounterpart, approvalStatus, approverObservation, resultAutoevaluation, observationAutoevaluation |
indicator y periodRelations describen qué se mide y la meta; muéstralos como contexto. obtainedLevel / complianceValue / observation / file son lo ya guardado por este evaluador. fileCounterpart es el adjunto del otro registrante (sólo lectura). approvalStatus y approverObservation aplican al flujo de aprobación de objetivos del colaborador (rechazo del jefe con su motivo). Los objetivos usan obtainedLevel, no result. |
responsibilities |
id, name, description, weight, obtainedLevel, complianceValue, observation, resultAutoevaluation, approvalStatus, registeredByCollaborator |
Responsabilidades del período. registeredByCollaborator = true indica que las registró el colaborador (relevante con registeredUserConfig = 'U'/'A'). El resto funciona como en objetivos. Guarda con { id, obtainedLevel, observation, approvalStatus }. |
competencies |
competency { id, name, description, macroCompetency }, competencyBehaviors[] (cada una con id, name, scaleId, result, items, resultAutoevaluation), level |
Competencias específicas del cargo. Cada conducta es lo que se califica. Ver Contrato de result. |
behaviors |
competencyId, competencyName, competencyDescription, competencyBehaviors[] (id, name, levelId, result, items, resultAutoevaluation) |
Competencias organizacionales (condiciones). Misma forma y mismo contrato de result que philosophies y values. |
philosophies / values |
igual que behaviors |
Filosofía y Valores. Su modo (vertical o por escala) lo marca configuration.philosophyValuesVertical. |
questions / questionsPerformance |
id, name, type, status, result, resultAutoevaluation |
Preguntas generales y de desempeño que se califican con la escala principal. result es la respuesta guardada. |
potential |
igual que questions |
Preguntas de potencial. Sólo viene poblada cuando relationship = 1 (jefe inmediato); para cualquier otra relación llega []. |
Contrato de result en conductas (evaluaciones verticales)
Sección titulada «Contrato de result en conductas (evaluaciones verticales)»En formato vertical se elige una sola conducta por competencia, y el backend repite el mismo result en cada conducta de esa competencia. Interpretación:
result |
Significa | Qué hacer en la app |
|---|---|---|
0 |
Sin respuesta: no hay nada guardado para la competencia. | No marques ninguna opción. No lo envíes al guardar. |
-1 |
“No observado” elegido explícitamente. | Marca “No observado” (sólo se ofrece si activeisNotObserved = 'S'). |
> 0 |
Es el id de la conducta elegida. |
Marca la conducta cuyo id coincide con result. |
-1 no es un nivel de la escala. Antes de la corrección de octubre de 2026 la lectura devolvía -1 para “sin responder”; hoy -1 sólo significa “No observado” y lo sin responder es 0. En formato horizontal, result de cada conducta es el nivel (valor de escala) ya guardado para esa conducta.
metadata — contexto de esta evaluación
Sección titulada «metadata — contexto de esta evaluación»| Campo | Tipo | Para qué / cuándo usarlo |
|---|---|---|
evaluator / evaluated |
string | Eco de quién evalúa y a quién. Úsalos para el encabezado y para armar la ruta del PATCH save (usernameEvaluated). |
positionId / relationship |
number | Eco de los params. Reenvía positionId en metadata.positionId al guardar. |
status |
string | Estado de la campaña. |
userAutoevaluationStatus |
string | null | Eco del estado de autoevaluación del evaluado (ver tabla de estados). Úsalo para decidir si mostrar la referencia de autoevaluación. |
managerObjectivesFinished |
boolean | true cuando el jefe ya finalizó su evaluación de objetivos. Sólo se calcula en la autoevaluación con registeredUserConfig = 'U'; en cualquier otro caso es false. Úsalo para resaltar o bloquear los objetivos aprobados/negados por el jefe sólo cuando es true; antes de eso el estado “aprobado” es sólo un valor por defecto y no debe bloquear el borrador del colaborador. |
requiresContractRecommendation |
boolean | true en evaluaciones de período de prueba (PP) cuando la relación de quien evalúa debe responder la recomendación. Si es true, muestra y exige la pregunta “¿El contrato debe continuar? Sí/No” y envíala en general.isRecommended. Si es false, no la muestres. |
totalItems |
object | { functions, objectives, competencies, behaviors, philosophies, values, potential, responsibilities }: cantidad de ítems calificables por sección (en vertical cuenta una por competencia; en horizontal, una por conducta). Úsalo como denominador del progreso. |
general — comentarios y recomendación
Sección titulada «general — comentarios y recomendación»| Campo | Tipo | Para qué / cuándo usarlo |
|---|---|---|
observation / secondObservation |
string | Comentarios finales ya guardados (por ejemplo logros y áreas de mejora). Precarga los campos de texto y reenvíalos en el save. |
isRecommended |
'S' | 'N' |
Respuesta guardada de “¿El contrato debe continuar?”. Sólo úsala si metadata.requiresContractRecommendation = true. |
observationAutoevaluation / secondObservationAutoevaluation |
string | Comentarios que escribió el evaluado en su autoevaluación. Sólo vienen con la regla de autoevaluación cumplida y si el evaluado escribió algo; muéstralos como referencia de sólo lectura. |
weights — pesos de cada sección
Sección titulada «weights — pesos de cada sección»objectives, functions, specificCompetencies, organizationalCompetencies, philosophy, values, potential, finalComments, responsibilities — número o null. Indican cuánto pesa cada sección en el puntaje final de esta campaña. Son informativos: el cálculo lo hace el backend; úsalos, si quieres, para mostrar la ponderación junto al título de cada sección. null indica que no hay peso configurado para esa sección.
Cuándo una sección llega vacía
Sección titulada «Cuándo una sección llega vacía»Una sección con la bandera 'S' puede llegar [] por reglas de la relación; no lo trates como error:
| Sección | Llega vacía cuando… |
|---|---|
| Cualquiera | La relación del evaluador no tiene configurada esa sección en la campaña. |
potential |
relationship no es 1 (sólo el jefe inmediato evalúa potencial). |
objectives |
Con registeredUserConfig = 'A': el evaluador no es jefe (1) ni autoevaluación (5). Con 'U': es autoevaluación → ve objetivos; es el jefe → los ve sólo cuando el colaborador ya los registró (estado R); cualquier otra relación → vacía. Con 'J': el jefe siempre los ve; en autoevaluación sólo cuando el jefe ya los registró (R); otra relación → vacía. |
responsibilities |
Igual que objetivos, salvo que con registeredUserConfig = 'A' siempre se muestran, sin importar la relación. |
Ejemplo
Sección titulada «Ejemplo»{ "evaluation": { "id": 4102, "name": "Evaluación de Desempeño 2026 - Semestre 1", "evaluationType": { "id": 2, "abbreviation": "ED" }, "configuration": { "hasFunction": "S", "hasObjective": "N", "hasCompetency": "S", "hasCondition": "N", "hasPhilosophy": "N", "hasValue": "N", "hasPotential": "N", "hasQuestions": "N", "hasQuestionsPerformance": "N", "hasResponses": "N", "activeisNotObserved": "S", "activeFlowControl": "S", "activeAutomaticVerification": "S", "activeObjectiveScale": "N", "activeAttachedObjectiveFile": "N", "activePeriodicObjective": "N", "registeredUserConfig": "U", "format": "2", "philosophyValuesVertical": false } }, "scales": { "mainScale": [ { "id": 1, "idScale": 3, "valueName": "Insuficiente", "shortValueName": "1", "value": 1, "valueOrder": 1, "status": "A" }, { "id": 5, "idScale": 3, "valueName": "Sobresaliente", "shortValueName": "5", "value": 5, "valueOrder": 5, "status": "A" } ], "objectiveScale": [], "strategyScale": [] }, "components": { "functions": [ { "id": 812, "name": "Procesar nómina quincenal", "description": "", "weight": 40, "result": 4, "items": 1, "resultAutoevaluation": 4 } ], "objectives": [], "competencies": [ { "competency": { "id": 22, "name": "Orientación al detalle", "description": "Revisa su trabajo antes de entregarlo." }, "competencyBehaviors": [ { "id": 443, "name": "Revisa ocasionalmente", "result": 444, "items": 1 }, { "id": 444, "name": "Revisa siempre y detecta errores ajenos", "result": 444, "items": 1 } ] }, { "competency": { "id": 28, "name": "Trabajo en equipo", "description": "Colabora con otras áreas." }, "competencyBehaviors": [ { "id": 251, "name": "Colabora si se le pide", "result": 0, "items": 0 }, { "id": 252, "name": "Colabora de forma proactiva", "result": 0, "items": 0 } ] } ], "behaviors": [], "philosophies": [], "values": [], "questions": [], "questionsPerformance": [], "potential": [], "responsibilities": [] }, "metadata": { "evaluator": "mrojas", "evaluated": "jperez", "positionId": 31, "relationship": 3, "status": "A", "userAutoevaluationStatus": "F", "managerObjectivesFinished": false, "requiresContractRecommendation": false, "totalItems": { "functions": 1, "objectives": 0, "competencies": 2, "behaviors": 0, "philosophies": 0, "values": 0, "potential": 0, "responsibilities": 0 } }, "general": { "observation": "", "secondObservation": "" }, "weights": { "objectives": null, "functions": 40, "specificCompetencies": 60, "organizationalCompetencies": null, "philosophy": null, "values": null, "potential": null, "finalComments": null, "responsibilities": null }}En el ejemplo, la competencia 22 ya tiene la conducta 444 elegida (result: 444) y la 28 está sin responder (result: 0).
Errores:
| Código | Cuándo ocurre |
|---|---|
400 |
This evaluation is not available due to flow control rules: '<etiqueta>'. El control de flujo (activeFlowControl) bloquea esta evaluación ahora: la autoevaluación del evaluado sigue activa (A) y quien evalúa es el jefe inmediato en una evaluación de objetivos, o cualquier otra relación (distinta de autoevaluación) cuya evaluación aún no está finalizada. <etiqueta> es el mismo texto de actionRequired del roster (Autoevaluación pendiente / Pendiente Jefe Inmediato). No debería ocurrir si la app ya ocultó el botón, pero el backend lo revalida. |
404 |
Evaluations for evaluated user not found. No existe una fila de evaluación para ese username evaluado con el usuario autenticado como evaluador en esa campaña. Ocurre con un evaluationId inexistente, un username equivocado o cuando el usuario no es evaluador de ese par. |
Tanto 400 como 404 son errores reales (no un 200 con datos vacíos): muestra un estado de error de carga con opción de volver al roster. Por defensa, verifica igualmente que evaluation?.id venga presente antes de pintar el formulario.
PATCH /me/evaluation/{evaluationId}/username/{usernameEvaluated}/save
Sección titulada «PATCH /me/evaluation/{evaluationId}/username/{usernameEvaluated}/save»Guarda el avance de una evaluación puntual (parcial) o la envía como final.
Path params: evaluationId (number), usernameEvaluated (string — el evaluado, no necesariamente el usuario autenticado).
Body (SaveEvaluationProgressDto):
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
general.observation |
string | No | Comentario general. |
general.secondObservation |
string | No | Áreas de mejora / segundo comentario. |
general.isRecommended |
'S' | 'N' |
No | Recomendación de aprobación (uso administrativo). |
metadata.positionId |
number | Sí | Cargo del evaluado. |
metadata.isFinal |
boolean | No (default false) |
false = guardado parcial (la fila queda en A/Activa). true = envío final (la fila pasa a F/Finalizada). |
components.functions / .objectives / .responsibilities / .questions / .questionsPerformance / .potential |
array | No | Formato completo: [{ id, obtainedLevel }] (objetivos y responsabilidades además admiten observation, file, approvalStatus). |
components.competencies / .behaviors / .philosophies / .values |
array | No | Formato completo (horizontal): [{ competency: { id, competencyBehaviors: [{ id, obtainedLevel }] } }]. |
components.competenciesRatings / .behaviorsRatings / .philosophiesRatings / .valuesRatings |
object | No | Formato simplificado: un mapa por sección que el backend transforma a la forma completa. La clave y el valor dependen del modo de la sección: en vertical es { [competencyId]: behaviorId } (la conducta elegida, no un nivel) y en horizontal es { [behaviorId]: obtainedLevel }. Competencias y condiciones son verticales con format = '2'; Filosofía y Valores con philosophyValuesVertical = true (ver content/all). Usa el formato simplificado o el completo para una misma sección, nunca ambos (si envías el array completo, el mapa de esa sección se ignora). |
components.potentialRatings |
object | No | Mapa { [questionId]: obtainedLevel } para las preguntas de potencial. |
{ "general": { "observation": "Buen desempeño en el período, cumplió los objetivos principales.", "secondObservation": "Mejorar tiempos de respuesta en solicitudes urgentes." }, "components": { "functions": [ { "id": 812, "obtainedLevel": 4 } ], "objectives": [], "responsibilities": [], "questions": [], "questionsPerformance": [] }, "metadata": { "positionId": 31, "isFinal": true }}Qué enviar en secciones verticales (competencias, condiciones, filosofía y valores). Lee el result de content/all (Contrato de result) y sigue esta regla para cada competencia del mapa:
| Estado en la app | Qué poner en el mapa |
|---|---|
| El usuario eligió una conducta | { [competencyId]: behaviorId }. |
| El usuario eligió “No observado” | { [competencyId]: -1 }. Sólo se guarda si activeisNotObserved = 'S'; en caso contrario el backend lo ignora y la competencia queda sin responder. |
Sin responder (result = 0) |
Omite la clave. |
Response: 200, sin cuerpo relevante (confirmación de guardado).
Errores específicos:
| Código | Cuándo ocurre |
|---|---|
400 |
Falta evaluator, evaluationId, evaluated o el body data. |
400 |
Todas las filas de evaluación de este usernameEvaluated/evaluador ya están en estado F (Finalizada) — no se puede reenviar. |
404 |
La campaña (evaluationId) no existe. |
404 |
No hay filas de evaluación para ese usernameEvaluated con este evaluador en esta campaña. |
GET /me/evaluation/history
Sección titulada «GET /me/evaluation/history»Historial de evaluaciones (de todo tipo) del usuario autenticado, con métricas de progreso y resultado.
Query params:
| Param | Tipo | Obligatorio | Descripción |
|---|---|---|---|
periodId |
number | No | Filtra por período. |
evaluationTypeId |
number | No | Filtra por tipo de evaluación. |
year |
number | No | Filtra por año del período. |
username |
string | No | Omitir en self-service — sólo se usa en la ficha de administración de un colaborador. Sin él, se consulta el usuario autenticado. |
Response — array de:
| Campo | Tipo | Descripción |
|---|---|---|
evaluationId / evaluationName |
— | Identificador y nombre de la campaña. |
periodId / periodYear |
— | Período. |
evaluationType |
object | { id, name, abbreviation }. |
startDate / endDate |
date | Vigencia. |
progress |
number | Porcentaje de avance (0-100). |
completedCount / totalCount |
number | Evaluadores que ya terminaron vs. total asignado. |
score |
number | null | Puntaje final. null si aún no hay puntaje evaluable o si es una evaluación de Líder Multiplicador (isLeadershipMultiplier). |
developmentLevel |
object | null | { id, name, color } — nivel de desarrollo (semáforo). |
status |
string | Estado de la campaña. |
totalObjectives / evaluatedObjectives |
number | Objetivos asignados vs. evaluados (fuera de alcance detallar el flujo de objetivos, pero el dato viene en el historial). |
objectiveCompliance |
number | null | % de cumplimiento promedio de los objetivos evaluados. |
isPublished |
boolean | El registro de evaluación del usuario está marcado como publicado. |
isFinished |
boolean | La evaluación llegó a 100% de avance. |
canViewReport |
boolean | Habilita el botón “Ver informe” — sólo mostrarlo si isFinished && canViewReport. |
canViewObjectives |
boolean | Fuera de alcance (módulo de objetivos). |
isLeadershipMultiplier |
boolean | Evaluación de Líder Multiplicador — no tiene score/developmentLevel global, esas columnas se muestran vacías. |
[ { "evaluationId": 3980, "evaluationName": "Evaluación de Desempeño 2025 - Semestre 2", "periodId": 17, "periodYear": 2025, "evaluationType": { "id": 2, "name": "Evaluación de Desempeño", "abbreviation": "ED" }, "startDate": "2025-12-01T00:00:00.000Z", "endDate": "2026-01-15T23:59:59.000Z", "progress": 100, "completedCount": 4, "totalCount": 4, "score": 86.5, "developmentLevel": { "id": 3, "name": "Cumple lo esperado", "color": "#22C55E" }, "status": "F", "totalObjectives": 3, "evaluatedObjectives": 3, "objectiveCompliance": 92.1, "isPublished": true, "isFinished": true, "canViewReport": true, "canViewObjectives": true, "isLeadershipMultiplier": false }]Sin errores específicos adicionales a los comunes.
GET /me/evaluation/history/years
Sección titulada «GET /me/evaluation/history/years»Años disponibles en el historial del usuario, para armar el filtro de año.
Sin query params en self-service (el parámetro username existe pero es sólo para la ficha de administración — omitirlo).
Response: number[], por ejemplo [2024, 2025, 2026].
Sin errores específicos adicionales a los comunes.
GET /me/evaluation/{evaluationId}/report
Sección titulada «GET /me/evaluation/{evaluationId}/report»Informe final de una evaluación terminada: puntajes por sección, resultado consolidado, nivel de desarrollo y feedback.
Path param: evaluationId (number).
Query params:
| Param | Tipo | Obligatorio | Descripción |
|---|---|---|---|
username |
string | No | Omitir en self-service — sin él, se devuelve el informe del usuario autenticado. |
showByEvaluator |
boolean | No (default false) |
Incluye el desglose de puntajes por tipo de evaluador (autoevaluación, jefe, pares, etc.) en cada sección. |
Response (resumen — la forma completa es extensa y varía según qué secciones tenga habilitada la evaluación):
| Campo | Tipo | Descripción |
|---|---|---|
evaluation |
object | Metadata: { id, name, type, period, startDate, endDate, format, isAnonymous }. format acá sí viene como texto ('HORIZONTAL' / 'VERTICAL'), a diferencia del código numérico de content/all. |
evaluatedUser |
object | { username, fullName, identification, position, department, avatar }. |
configuration |
object | Qué secciones están habilitadas (hasFunction, hasCompetency, hasBehavior, hasObjective, hasResponsibility, hasEnterpriseObjective, hasLeadershipMultiplier, hasPhilosophy, hasValue, hasPotential) y sus pesos. |
keyMetrics.items |
array | Métricas para el resumen ejecutivo: { key, name, percentage, developmentLevel, weight, isEnabled } por sección. |
sections.* |
object | Una clave por sección habilitada (functions, competencies, behaviors, objectives, responsibilities, enterpriseObjectives, specificCompetencies, organizationalCompetencies, leadershipMultiplier), cada una con su consolidated (average, percentage, items[]) y, si se pidió showByEvaluator=true, byEvaluator[]. |
finalResult |
object | { score, scaleValue, pointsScore?, developmentLevel?, developmentScale?, isAccepted, acceptedDate? } — resultado consolidado. |
feedback |
object | Ver tabla de feedback más abajo. |
evaluators |
array | { evaluator, evaluatorName, relationId, relationName, status, evaluationDate, avatar } — quiénes participaron como evaluadores. |
feedback (relevante para el flujo de acuse de recibo):
| Campo | Tipo | Descripción |
|---|---|---|
sentFeedback |
string | Feedback que el jefe escribió/publicó para el evaluado. |
receivedFeedback |
string | Comentario del evaluado en respuesta (queda vacío hasta que acusa recibo). |
isFeedbackPublished |
boolean | El jefe publicó el feedback. Mostrar el bloque de feedback sólo si es true. |
isFeedbackAcknowledged |
boolean | El evaluado ya acusó recibo. Mientras sea false y isFeedbackPublished sea true, mostrar el formulario de acuse. |
byEvaluator |
array | { relationId, relationName, comments: string[] } — observaciones de cada evaluador, agrupadas por relación. |
{ "evaluation": { "id": 3980, "name": "Evaluación de Desempeño 2025 - Semestre 2", "type": { "id": 2, "name": "Evaluación de Desempeño", "abbreviation": "ED" }, "period": { "id": 17, "year": 2025 }, "startDate": "2025-12-01T00:00:00.000Z", "endDate": "2026-01-15T23:59:59.000Z", "format": "HORIZONTAL", "isAnonymous": false }, "evaluatedUser": { "username": "jperez", "fullName": "Juan Pérez", "identification": "0102030405", "position": { "id": 31, "name": "Analista de Nómina" }, "department": { "id": 6, "name": "Recursos Humanos" }, "avatar": "jperez.png" }, "finalResult": { "score": 86.5, "scaleValue": 4, "developmentLevel": { "id": 3, "name": "Cumple lo esperado", "color": "#22C55E" }, "isAccepted": true, "acceptedDate": "2026-01-16T10:05:00.000Z" }, "feedback": { "sentFeedback": "Excelente cierre de período, sigue así en el control de tiempos.", "receivedFeedback": "", "isFeedbackPublished": true, "isFeedbackAcknowledged": false }, "evaluators": [ { "evaluator": "jperez", "evaluatorName": "Juan Pérez", "relationId": 5, "relationName": "Autoevaluación", "status": "F", "evaluationDate": "2026-01-10T09:00:00.000Z", "avatar": "jperez.png" } ]}Errores específicos:
| Código | Cuándo ocurre |
|---|---|
404 |
La evaluación no existe o el usuario autenticado no está autorizado a verla. |
PATCH /me/evaluation/{evaluationId}/report/feedback/acknowledge
Sección titulada «PATCH /me/evaluation/{evaluationId}/report/feedback/acknowledge»El empleado acusa recibo del feedback publicado por su jefe.
Path param: evaluationId (number).
Body (AcknowledgeFeedbackDto):
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
comment |
string | No | Comentario opcional del empleado sobre el feedback recibido. |
{ "comment": "Gracias por el feedback, voy a trabajar en los tiempos de respuesta."}Response: { "success": true }.
Errores específicos:
| Código | Cuándo ocurre |
|---|---|
400 |
No hay feedback publicado para acusar recibo todavía (isFeedbackPublished es false). |
GET /me/evaluation/{evaluationId}/report/pdf
Sección titulada «GET /me/evaluation/{evaluationId}/report/pdf»Descarga el informe final de una evaluación como archivo PDF. El documento no se arma en el cliente: el backend toma los mismos datos que devuelve GET /me/evaluation/{evaluationId}/report, genera el HTML y lo renderiza el Worker th-pdfs (el mismo servicio que usa el Centro de Plantillas). Web y móvil obtienen el mismo archivo pidiendo la misma URL — la app no tiene que reimplementar ninguna sección del informe.
Path param: evaluationId (number).
Query params (EvaluationReportPdfQueryDto):
| Param | Tipo | Obligatorio | Enum / default | Descripción |
|---|---|---|---|---|
username |
string | No | Default: usuario autenticado | Omitir en self-service. Sólo se usa para descargar el informe de otro colaborador (jefe, evaluador o perfil de Desempeño); la autorización la resuelve el mismo control que protege el JSON del informe. |
mode |
string | No | executive | full — default full |
executive imprime únicamente el resumen ejecutivo (primera página); full imprime el informe completo. |
hide |
string (CSV) | No | Ver claves abajo | Secciones a excluir, separadas por coma. Sólo tiene efecto en mode=full. Las claves desconocidas se descartan sin error. |
lang |
string | No | Default: español | Idioma del documento (es-ES, en-US…). Se normaliza por prefijo: cualquier valor que empiece por en imprime en inglés; todo lo demás, en español. No afecta el idioma de los mensajes de error. |
Claves válidas de hide:
| Clave | Sección que oculta |
|---|---|
objectives |
Objetivos |
enterpriseObjectives |
Objetivos empresariales |
responsibilities |
Responsabilidades |
competencies |
Competencias (también oculta la página de conductas, que depende de ella) |
functions |
Funciones |
philosophies |
Filosofías |
values |
Valores |
leadershipMultiplier |
Líder multiplicador vs minimizador |
Request:
GET /me/evaluation/3980/report/pdf?mode=full&hide=values,philosophies&lang=es-ES HTTP/1.1Authorization: Bearer <jwt>x-tenant-id: <tenant>Response 200 — binario, no JSON. El cuerpo es el PDF crudo, sin la envoltura de las respuestas JSON.
| Header | Valor |
|---|---|
Content-Type |
application/pdf |
Content-Disposition |
inline; filename="<slug>.pdf" — el nombre se arma con el nombre de la evaluación y del evaluado, en minúsculas, sin tildes y con guiones (ej. evaluacion-de-desempeno-2026-nombre-apellido.pdf). |
Cache-Control |
no-store — el documento se regenera en cada descarga. |
Qué contiene el documento. La primera página es siempre el resumen ejecutivo. En mode=full se agregan, en este orden, las páginas de: objetivos, objetivos empresariales, responsabilidades, competencias, conductas, funciones, filosofías, valores, líder multiplicador y feedback detallado. Cada página sólo se imprime si la sección está habilitada en la evaluación y trae datos, por lo que el número de páginas varía según la campaña. Reglas que conviene conocer:
- La página de feedback detallado no se puede ocultar con
hidey no se imprime en modo ejecutivo. Se imprime sólo si existe algún comentario: observaciones por evaluador, autoevaluación u observación del jefe, feedback publicado (isFeedbackPublished) o comentario de acuse ya confirmado (isFeedbackAcknowledged). - En una evaluación de líder multiplicador no se imprime la página de filosofías (esas conductas viajan dentro de la de liderazgo).
- En evaluaciones 360° anónimas se hereda la redacción de evaluadores del informe JSON y se oculta el bloque de evaluadores.
- El pie de cada página lleva la numeración (
Página X de Y) y el crédito de TwiinsHRM; el encabezado lleva el logo del tenant (o su nombre, si no tiene logo).
Errores específicos:
| Código | Cuándo ocurre |
|---|---|
400 |
mode con un valor distinto de executive / full. El message llega como array de strings. |
403 |
username es otro colaborador y el solicitante no es su evaluador, su jefe en la jerarquía ni tiene el perfil de seguridad de Desempeño. Mensaje: No tiene permisos para ver este reporte de evaluación. No ocurre al descargar el informe propio. |
404 |
La evaluación no existe (Evaluation {id} not found). |
503 |
El servicio de generación no está disponible. El message está redactado para mostrarse al usuario tal cual y distingue: Hay muchos documentos generándose en este momento… (límite del Worker), La generación del documento tardó demasiado… (timeout), El servicio de generación de documentos no está configurado. (el entorno no tiene el token del Worker) y No se pudo generar el documento en este momento. (cualquier otro fallo). |
GET /me/evaluation/fulldata
Sección titulada «GET /me/evaluation/fulldata»Variante de GET /me/evaluation que devuelve, además de los mismos campos, evaluationUsers[] embebido en cada campaña (igual forma que en /details) y hasStrategicObjective.
Sin query params.
Lógica de cliente
Sección titulada «Lógica de cliente»Estados de campaña (EvaluationStatusEnum)
Sección titulada «Estados de campaña (EvaluationStatusEnum)»| Valor | Significado | Texto sugerido |
|---|---|---|
A |
Activa | Activa |
I |
Inactiva | Inactiva |
R |
Registrada | Registrada |
F |
Finalizada | Finalizada |
X |
Eliminada | — (excluir de listados) |
La card de campaña en el listado usa este status para el badge Activa/Inactiva, pero además calcula en el cliente si endDate < fecha actual para mostrar “Finalizada” superpuesto al badge — no es un valor que devuelva el backend, es una regla del cliente sobre endDate.
Estados de una fila de evaluación (EvaluationUserStatusEnum)
Sección titulada «Estados de una fila de evaluación (EvaluationUserStatusEnum)»| Valor | Significado backend | Texto sugerido en el roster |
|---|---|---|
A |
Activa (pendiente de calificar) | Pendiente |
P |
En proceso | Pendiente |
F |
Finalizada | Completada |
V |
En validación | Pendiente |
C |
En calibración | Pendiente |
N |
Rechazada | Pendiente |
R |
Registrada | Pendiente |
El roster simplifica todo lo que no sea F a “Pendiente” con el botón “Empezar evaluación”, salvo que actionRequired esté presente (ver siguiente tabla), en cuyo caso se reemplaza el botón por el texto de la acción bloqueante.
Acción requerida antes de poder calificar (actionRequired.key)
Sección titulada «Acción requerida antes de poder calificar (actionRequired.key)»| Valor | Texto sugerido | Cuándo aparece |
|---|---|---|
selfEvaluationPending |
Autoevaluación pendiente | El control de flujo de la campaña está activo y el evaluado todavía no terminó su propia autoevaluación. |
immediateManagerPending |
Pendiente Jefe Inmediato | Evaluación de tipo Objetivo donde falta la acción del jefe inmediato antes de continuar. |
Si actionRequired es null, el botón “Empezar evaluación” está habilitado.
Relación evaluador → evaluado (RelationEnum)
Sección titulada «Relación evaluador → evaluado (RelationEnum)»Valor (relationId) |
Significado |
|---|---|
1 |
Jefe Inmediato |
2 |
Colaborador |
3 |
Pares |
4 |
Cliente Interno |
5 |
Autoevaluación |
6 |
Gerente Funcional |
Tipos de evaluación (EvaluationTypeAbbreviationEnum)
Sección titulada «Tipos de evaluación (EvaluationTypeAbbreviationEnum)»| Valor | Significado | Navegación desde la card |
|---|---|---|
PP |
Evaluación de Periodo de Prueba | /performance/:id (genérica) |
ED |
Evaluación de Desempeño | /performance/:id/evaluations (roster de este documento) |
PO |
Evaluación de Potencial de Liderazgo | /performance/:id (genérica) |
OB |
Registro y Aprobación de Objetivos | Fuera de alcance — ruta distinta según registeredUserConfig de la campaña. |
EM |
Evaluación de MRL | Excluida del roster (ver Prerrequisitos); no navegable con este flujo. |
Secciones dinámicas del formulario
Sección titulada «Secciones dinámicas del formulario»GET /me/evaluation/content/all sólo devuelve pobladas las secciones habilitadas para la relación del evaluador actual; el resto llega como arrays vacíos. La app debe ocultar cualquier sección cuyo array venga vacío, sin asumir que todas las evaluaciones tienen todas las secciones.
- Formato vertical (
configuration.format === '2'para competencias y condiciones;configuration.philosophyValuesVertical === truepara Filosofía y Valores): el usuario elige una conducta por competencia; guarda{ competencyId: behaviorId }en el mapa simplificado correspondiente. - Formato horizontal (
'1'o'3', o Filosofía/Valores conphilosophyValuesVertical === false): cada conducta lleva su nivel; guarda{ behaviorId: obtainedLevel }o las estructuras completas (competencyBehaviorsanidados). - En vertical, el
resultleído tiene tres estados:0sin responder (no marques nada y no lo reenvíes),-1“No observado” explícito y> 0elidde la conducta elegida.-1no es un nivel de la escala: ofrece “No observado” sólo siconfiguration.activeisNotObserved === 'S'. Las escalas incluyen todos los niveles activos, también uno convalue = 0, que es una calificación válida. - La escala (
scales.mainScale) puede ser numérica o de texto — si todos losvalueNameson numéricos, tratarla como escala numérica; si no, como escala de opciones con etiqueta.
Referencia de autoevaluación
Sección titulada «Referencia de autoevaluación»Mostrar el valor de autoevaluación (resultAutoevaluation en cada item, o general.observationAutoevaluation para comentarios) sólo cuando se cumplen las tres condiciones:
configuration.activeAutomaticVerification === 'S'.- La relación actual no es autoevaluación (
relationship !== 5). metadata.userAutoevaluationStatus === 'F'(el evaluado ya finalizó su propia autoevaluación).
Progreso y envío final
Sección titulada «Progreso y envío final»- El progreso se calcula como ítems calificados /
metadata.totalItems(suma de todas las secciones con datos). - El botón “Enviar” (envío final,
isFinal: true) sólo se habilita cuando todas las secciones con ítems están completas — no sólo la sección visible en pantalla. - El botón “Guardar avance” (
isFinal: false) está disponible en cualquier momento, aunque falten secciones. - Después de un envío final exitoso, volver al roster (
GET /me/evaluation/{id}/type/{abbreviation}) para reflejar el nuevo estado. - Si el usuario reintenta guardar sobre una evaluación donde todas sus filas ya están
F, el backend responde400— la app debería haber deshabilitado la acción antes de llegar a este punto (roster constatus = F).
Historial: filtro por año y nivel de desarrollo
Sección titulada «Historial: filtro por año y nivel de desarrollo»- Poblar el selector de año con
GET /me/evaluation/history/years; si el año actual no tiene datos, seleccionar por defecto el año más reciente que sí tenga (no forzar el año calendario actual). - El filtro “Nivel de desarrollo” (Cumple lo esperado / Por debajo de lo esperado / No evaluado) es una convención del cliente sobre
score, no un campo del backend:score === null→ “No evaluado”;score >= 70→ “Cumple lo esperado”; en cualquier otro caso → “Por debajo de lo esperado”. El umbral (70) es del cliente de referencia, no algo que confirme el backend — validar contra el criterio real de negocio antes de reutilizarlo.
Ver informe y acuse de feedback
Sección titulada «Ver informe y acuse de feedback»- Mostrar la acción “Ver informe” en el historial sólo si
isFinished && canViewReportdel item correspondiente. - Dentro del informe, mostrar el bloque de feedback sólo si
feedback.isFeedbackPublished. - Si
isFeedbackPublished && !isFeedbackAcknowledged, mostrar el formulario de acuse (comentario opcional + confirmación); trasPATCH .../acknowledgeexitoso, refrescar el informe para queisFeedbackAcknowledgedpase atruey el bloque quede de solo lectura.
Descargar el informe en PDF
Sección titulada «Descargar el informe en PDF»Cuándo mostrar la acción. El backend no valida que la evaluación esté terminada ni canViewReport al generar el PDF — ese control es sólo del historial. Aplica en el cliente la misma regla de “Ver informe”: mostrar “Descargar informe” únicamente si isFinished && canViewReport del item del historial.
Opciones de descarga (comportamiento del portal de referencia). El portal ofrece un selector de alcance y un checklist de secciones, y los traduce a query params:
| Opción en la UI | Param | Regla |
|---|---|---|
| Resumen ejecutivo / Informe completo | mode=executive / mode=full |
Un solo valor a la vez. |
| Secciones desmarcadas | hide=<claves separadas por coma> |
Enviar sólo en mode=full; en ejecutivo omitir hide. Si no se apagó ninguna sección, omitir el param. |
| Idioma activo de la app | lang |
Enviar el locale de la interfaz (es-ES / en-US) para que el documento salga en el mismo idioma que el usuario está viendo. |
| — (informe propio) | username |
Omitir. |
Manejo de la respuesta.
- Tratar el éxito como bytes, no como JSON, y verificar
Content-Type: application/pdfantes de abrirlo. En error, leer el cuerpo como JSON para mostrarmessage(ver aviso en el endpoint). - Mostrar el
messagedel backend tal cual (viene redactado para el usuario). Si el cuerpo no se puede parsear —por ejemplo, el504de API Gateway— mostrar un texto genérico localizado en la app, no un literal del backend. - El backend responde en español los errores independientemente de
lang: ese parámetro sólo cambia el documento.
Estado de carga y reintentos.
- Deshabilitar el botón y mostrar estado de carga mientras la petición está en vuelo (el portal lo hace con
:loading). Un arranque en frío puede tardar ~17 s; configura un timeout del cliente mayor a 30 s para no cortar una generación válida antes que el servidor. - No lances descargas en paralelo, no pre-generes el PDF “por si acaso” ni reintentes en bucle: cada descarga dispara un render en un servicio compartido con límite de uso (su rechazo es el
503de “muchos documentos generándose”). Ante ese503, dejar que el usuario reintente manualmente. - No cachear el archivo como fuente de verdad:
Cache-Control: no-storeindica que el contenido cambia con los datos vigentes de la evaluación (por ejemplo, tras publicar o acusar recibo de feedback). Si se guarda una copia local para compartirla, tratarla como temporal.
Estados vacíos y casos borde
Sección titulada «Estados vacíos y casos borde»- Sin campañas activas:
GET /me/evaluationdevuelve[]— mostrar estado vacío (“No tienes evaluaciones activas”). - Roster vacío:
GET /me/evaluation/{id}/type/{abbreviation}sin ítems — no debería ocurrir si la campaña apareció en el listado, pero si pasa, tratar como estado vacío, no como error. - Sin historial:
GET /me/evaluation/historydevuelve[]— mostrar estado vacío, ocultando los filtros de año/nivel salvo que existan años disponibles. content/allcon404(Evaluations for evaluated user not found): el par evaluador → evaluado no existe para esa campaña; tratar como error de carga y volver al roster. Con400(control de flujo) mostrar el motivo del mensaje. Por defensa, si llegara un200sinevaluation?.id, tratarlo también como error de carga y no como campaña sin secciones.