Ir al contenido

Roles de Pago

El módulo cubre la visualización de roles de pago del usuario autenticado y su firma electrónica: consultar el rol del período actual, revisar el historial de totales por rango de fechas, descargar el comprobante en PDF y firmar el rol (conforme o no conforme) mediante aceptación simple o mediante código QR/OTP enviado por correo. También incluye una vista resumida de bonos anuales (décimo tercero, décimo cuarto y utilidades).

Acciones cubiertas:

  • Ver el rol de pago de un mes/año específico, con el detalle de ingresos, egresos y provisiones.
  • Ver el historial de totales de nómina por rango de fechas.
  • Ver y descargar roles precargados como archivo (tenants tipo Argentina, donde el rol no se calcula sino que se sube como PDF).
  • Descargar el comprobante en PDF del rol de un período.
  • Firmar el rol de pago: aceptación simple (SA) o firma con código QR/OTP (HD), incluyendo la variante “no conforme”.
  • Ver el resumen de bonos anuales (décimo tercero, décimo cuarto, utilidades) y descargar su comprobante.

Prerrequisitos:

  • El tipo de sistema de nómina del tenant (Payroll_System_Type, expuesto por backend vía TenantParameters) determina qué vista aplica:

    • calculated, integration-table e integration-ws → vista estructurada (ingresos/egresos/provisiones), usan GET /me/payroll y GET /me/payroll/history.
    • uploaded → vista de archivos cargados (roles precargados como PDF), usa GET /me/payroll/uploaded y GET /me/payroll/uploaded/history.

    Un mismo tenant usa un solo tipo; la app debe pedir este dato a través del endpoint de parámetros de tenant (fuera de alcance de este documento) y renderizar la vista correspondiente.

  • La firma está habilitada o no por tenant (GET /me/payroll/signature-config). Si enabled = false, no se muestra ninguna acción de firma y la descarga usa directamente la URL del archivo (url/fileUrl de las respuestas), sin pasar por generate.

  • El tipo de firma (SignatureConfigDto.type) puede ser SA, HD, VD, VDBIO, M-VD, M-VDBIO o VDSTAMP. Este documento cubre en detalle sólo SA (aceptación simple) y HD (código QR/OTP por correo), que son los únicos tipos con flujo de referencia implementado en el portal web. Los demás valores existen en el contrato pero no tienen flujo de cliente verificado — tratarlos como no soportados hasta confirmar con backend.

sequenceDiagram
    participant App as App móvil
    participant API as th-core-api

    Note over App,API: Carga inicial de la pantalla de Roles de Pago
    App->>API: GET /me/payroll/signature-config
    API-->>App: enabled + type del tenant
    App->>API: GET /me/payroll?month=:month&year=:year
    API-->>App: rol del período seleccionado (o 404 si no hay rol cerrado)
    App->>API: GET /me/payroll/history?startDate=:startDate&endDate=:endDate
    API-->>App: totales mensuales del rango + signatureEnabled
    Note over App: Sólo si Payroll_System_Type = uploaded
    App->>API: GET /me/payroll/uploaded/history?startDate=:startDate&endDate=:endDate
    API-->>App: archivos cargados del rango, con isSigned por archivo

    Note over App,API: Descargar comprobante PDF
    App->>API: GET /me/payroll/generate?year=:year&month=:month
    API-->>App: url firmada del PDF (S3, expira en 1 hora)

    Note over App,API: Firmar - aceptación simple (SA)
    App->>API: POST /me/payroll/:year/:month/sign
    API-->>App: 201 confirmacion de firma
    App->>API: GET /me/payroll?month=:month&year=:year
    API-->>App: refresco con signatureStatus = SIGNED o NOT_CONFORM

    Note over App,API: Firmar - código QR/OTP (HD)
    App->>API: POST /me/payroll/:year/:month/qr-sign/init
    API-->>App: status V, código de 6 digitos enviado por correo
    App->>API: POST /me/payroll/:year/:month/qr-sign/verify
    API-->>App: status CS si el codigo es correcto, la firma queda registrada
    App->>API: GET /me/payroll?month=:month&year=:year
    API-->>App: refresco con signatureStatus actualizado

    Note over App,API: Firmar no conforme sobre un archivo cargado, sin OTP
    App->>API: POST /me/payroll/:year/:month/qr-sign/non-conform
    API-->>App: status CS, firma no conforme registrada de una vez

    Note over App,API: Bonos anuales, opcional
    App->>API: GET /me/payroll/bonuses?year=:year
    API-->>App: totales de decimo tercero, decimo cuarto y utilidades
    App->>API: GET /me/payroll/generate/bonuses/:bonusType?year=:year
    API-->>App: url del comprobante del bono

Rol de pago del usuario autenticado para un período específico. Sólo retorna datos de roles cerrados y publicados.

Query params:

Param Tipo Obligatorio Descripción
month number Mes del rol de pago (1-12).
year number Año del rol de pago.

Response:

Campo Tipo Descripción
username string Usuario del rol.
year, month number Período del rol.
periodId, payrollId number Identificadores internos del período y del rol.
income array Rubros de ingreso (id, name, value, additionalValue, observation, detailObservation).
expense array Rubros de egreso, misma forma que income.
provisions array Rubros de provisión (p. ej. décimo tercero prorrateado), misma forma que income.
incomeTotals, expenseTotals, provisionsTotals object { totalValue, totalAdditionalValue, itemCount } por categoría.
signatureStatus enum PayrollSignatureStatusEnum PENDING | SIGNED | NOT_CONFORM.
signatureDate date | null Fecha de la firma.
signatureType string | null SA, HD, VD, VDBIO, etc.
requiresSignature boolean Si el tenant exige firma para este rol.
observation string | null Observación del usuario al firmar (relevante sobre todo en NOT_CONFORM).
{
"username": "jperez",
"year": 2026,
"month": 6,
"periodId": 1,
"payrollId": 5,
"income": [
{ "id": 1, "name": "Sueldo Básico", "value": 500.0, "additionalValue": 0.0, "observation": null, "detailObservation": null }
],
"expense": [
{ "id": 15, "name": "IESS", "value": 47.25, "additionalValue": 0.0, "observation": null, "detailObservation": null }
],
"provisions": [
{ "id": 30, "name": "Décimo Tercer Sueldo", "value": 41.67, "additionalValue": 0.0, "observation": null, "detailObservation": null }
],
"incomeTotals": { "totalValue": 500.0, "totalAdditionalValue": 0.0, "itemCount": 1 },
"expenseTotals": { "totalValue": 47.25, "totalAdditionalValue": 0.0, "itemCount": 1 },
"provisionsTotals": { "totalValue": 41.67, "totalAdditionalValue": 0.0, "itemCount": 1 },
"signatureStatus": "PENDING",
"signatureDate": null,
"signatureType": null,
"requiresSignature": true,
"observation": null
}

Errores específicos:

Código Cuándo ocurre
404 No existe un rol de pago cerrado y publicado para ese mes/año. Tratar como estado vacío (“sin datos para este período”), no como error genérico.
400 Parámetros inválidos (month/year no numéricos).

Totales de nómina del usuario autenticado en un rango de fechas, desglosados mes a mes. Incluye información de firma por mes.

Query params:

Param Tipo Obligatorio Descripción
startDate string Fecha de inicio del rango (YYYY-MM-DD).
endDate string Fecha de fin del rango (YYYY-MM-DD).

Response:

Campo Tipo Descripción
username string Usuario consultado.
startDate, endDate string Rango consultado.
totalPayrollCount number Cantidad de roles encontrados en el rango.
monthlyTotals array Un ítem por mes: year, month, periodId, payrollId, payrollDate, incomeTotals, expenseTotals, provisionsTotals, signatureStatus, signatureDate, signatureType, requiresSignature, observation.
consolidatedTotals object { totalIncome, totalExpense, totalProvisions } de todo el rango.
signatureEnabled boolean Si la firma está habilitada para el tenant — úsalo para decidir si mostrar la columna/acción de firma en la tabla de historial.
{
"username": "jperez",
"startDate": "2026-01-01",
"endDate": "2026-06-30",
"totalPayrollCount": 6,
"monthlyTotals": [
{
"year": 2026,
"month": 6,
"periodId": 1,
"payrollId": 5,
"payrollDate": "2026-06-30",
"incomeTotals": { "totalValue": 500.0, "totalAdditionalValue": 0.0, "itemCount": 1 },
"expenseTotals": { "totalValue": 47.25, "totalAdditionalValue": 0.0, "itemCount": 1 },
"provisionsTotals": { "totalValue": 41.67, "totalAdditionalValue": 0.0, "itemCount": 1 },
"signatureStatus": "SIGNED",
"signatureDate": "2026-06-30T14:30:00.000Z",
"signatureType": "SA",
"requiresSignature": true,
"observation": "Conforme con el pago recibido"
}
],
"consolidatedTotals": { "totalIncome": 3000.0, "totalExpense": 283.5, "totalProvisions": 250.02 },
"signatureEnabled": true
}

Errores específicos:

Código Cuándo ocurre
404 No hay roles cerrados y publicados en el rango. Tratar como estado vacío.
400 startDate/endDate inválidos.

Roles de pago precargados como archivo para un período específico (tenants tipo Argentina, donde el rol no se calcula sino que se sube en PDF). Un mismo período puede tener varios archivos por tipo (SUELDO, AGUINALDO, quincenas, etc.).

Query params:

Param Tipo Obligatorio Descripción
month number Mes del rol (1-12).
year number Año del rol.

Response — array de:

Campo Tipo Enum Descripción
type string SUELDO | AGUINALDO | 1 QUINCENA | 2 QUINCENA | OTROS Tipo de rol cargado.
name string Identificador del empleado usado en el archivo legacy.
url string URL directa del PDF cargado (sin firma estampada).
year, month number Período del archivo.
isSigned boolean Si el archivo ya fue firmado/aceptado, calculado por backend.
signatureDate date | null Fecha de la firma.
[
{
"type": "SUELDO",
"name": "12345678",
"url": "https://twiins.twiinshrm.com/talento_upload/roles/202601/uuid-file-identifier.pdf",
"year": 2026,
"month": 1,
"isSigned": true,
"signatureDate": "2026-02-08T15:06:29.000Z"
}
]

Errores específicos:

Código Cuándo ocurre
404 No hay roles cargados para ese período. El portal de referencia lo trata como lista vacía, no como error visible al usuario.
400 Parámetros inválidos.

Historial completo de archivos de roles de pago cargados en un rango de fechas, con paginación y filtro opcional por estado de firma.

Query params:

Param Tipo Obligatorio Descripción
startDate string Fecha de inicio (YYYY-MM-DD).
endDate string Fecha de fin (YYYY-MM-DD).
signed boolean No true = sólo firmados, false = sólo sin firmar, omitir = todos.
page number No Página, base 1. Default 1.
limit number No Registros por página. Default 10.

Response:

{
"items": [
{
"type": "SUELDO",
"name": "12345678",
"url": "https://twiins.twiinshrm.com/talento_upload/roles/202601/uuid.pdf",
"year": 2026,
"month": 1,
"isSigned": true,
"signatureDate": "2026-02-08T15:06:29.000Z"
}
],
"meta": {
"totalItems": 24,
"itemCount": 10,
"itemsPerPage": 10,
"totalPages": 3,
"currentPage": 1
}
}

Ver la convención de paginación en Convenciones.

Errores específicos:

Código Cuándo ocurre
404 No hay archivos cargados en el rango.
400 Parámetros inválidos.

Genera y devuelve la URL del comprobante en PDF de un rol de pago, a través del servicio legacy. Sirve tanto para roles calculados (Ecuador, Perú) como para roles precargados (Argentina) — el backend resuelve internamente cuál de los dos escenarios aplica.

Query params:

Param Tipo Obligatorio Descripción
year number Año del rol.
month number Mes del rol (1-12).
cutoff string No Sólo aplica a roles precargados: SUELDO, AGUINALDO, 1 QUINCENA, 2 QUINCENA, OTROS. Se ignora en roles calculados. Sin cutoff, retorna SUELDO o el primer archivo disponible.
mode string No preview (sólo visualización, no exige firma previa) o download (default).

Response:

{ "url": "https://s3.us-east-1.amazonaws.com/bucket/roles/202606/file-identifier.pdf?X-Amz-Algorithm=..." }

url puede venir null si el servicio legacy no encontró el rol.

Si el rol precargado ya fue firmado con código QR (signatureType = HD), la URL retornada apunta al PDF con el QR ya estampado — no hace falta llamar download-qr aparte para obtenerlo.

Errores específicos:

Código Cuándo ocurre
400 Error del servicio legacy, o no se encontró el rol para el cutoff solicitado.
403 Rol precargado sin firmar, solicitado con mode=download y cutoff.
401 Token inválido.
500 Error interno.

Configuración de firma del tenant: si está habilitada, con qué tipo y si genera el PDF por el sistema propio.

Sin query params.

Response (SignatureConfigDto):

Campo Tipo Descripción
enabled boolean Si la firma de roles está habilitada para el tenant.
type enum SA (aceptación simple), HD (QR/OTP), VD, VDBIO, M-VD, M-VDBIO, VDSTAMP. Ver nota en Resumen sobre alcance.
generateTwiinsPdf boolean Si el PDF se genera por el sistema propio en vez del legacy.
deviceId string | null Sólo relevante para firmas biométricas.
{ "enabled": true, "type": "HD", "generateTwiinsPdf": true, "deviceId": null }

Sin errores específicos adicionales a los comunes.

Estado de firma de un rol de pago para el período indicado.

Path params: year (number), month (number, 1-12).

Response (PayrollSignatureStatusResponseDto):

{
"signatureStatus": "SIGNED",
"signatureDate": "2026-06-30T14:30:00.000Z",
"signatureType": "SA",
"requiresSignature": true,
"observation": "Conforme con el pago recibido"
}

Misma forma de campos que la tabla de GET /me/payroll (sección anterior).

Sin errores específicos adicionales a los comunes.

Firma el rol de pago con aceptación simple (SA).

Path params: year (number), month (number, 1-12).

Body (SignPayrollDto):

Campo Tipo Obligatorio Descripción
isConform boolean true = conforme, false = rechaza el rol.
observation string No (máx. 300) Comentario del usuario. El backend no lo exige aunque isConform sea false — es el cliente quien debe pedirlo obligatorio en ese caso (ver Lógica de cliente).
{ "isConform": false, "observation": "El valor de horas extra no coincide con lo trabajado" }

Response (201):

{ "message": "Rol de pago firmado exitosamente" }

Errores específicos:

Código Cuándo ocurre
400 El rol ya fue firmado, o datos inválidos.
404 No existe rol de pago para ese período.

POST /me/payroll/{year}/{month}/qr-sign/init

Sección titulada «POST /me/payroll/{year}/{month}/qr-sign/init»

Inicia la firma con código QR/OTP: envía un código de 6 dígitos al correo del usuario.

Path params: year (number), month (number, 1-12).

Body (InitQrSignatureDto):

Campo Tipo Obligatorio Descripción
isConform boolean true = conforme, false = rechaza el rol.
observation string No (máx. 300) Comentario. Requerido en la práctica cuando isConform = false (regla de cliente).
documentType string No (máx. 60) Tipo de documento a firmar (SUELDO, AGUINALDO, VACACIONES, BONOS Y GRATIFICACIONES, ANEXOS GANANCIAS, etc.). Si se omite, el backend asume SUELDO.
fileIdentifier string No (máx. 250) GUID del archivo cargado. Si viene, tiene prioridad sobre documentType.
{ "isConform": true, "observation": "" }

Response (201, QrSignatureResponseDto):

{ "status": "V", "message": "Se envió el código de verificación a su correo electrónico" }

Errores específicos:

Código Cuándo ocurre
400 El rol ya fue firmado, datos inválidos, o error del servicio legacy. Puede incluir status: "V" en el payload cuando el legacy ya había enviado un código antes — la app debe tratarlo como éxito informativo (ver Lógica de cliente), no como fallo.
404 No existe rol de pago para ese período.

POST /me/payroll/{year}/{month}/qr-sign/non-conform

Sección titulada «POST /me/payroll/{year}/{month}/qr-sign/non-conform»

Firma el rol como no conforme sin pedir código OTP. Aplica a roles precargados (HD) cuando ya se conoce el documentType/fileIdentifier del archivo.

Path params: year (number), month (number, 1-12).

Body (NonConformQrSignatureDto):

Campo Tipo Obligatorio Descripción
observation string Sí (máx. 300) Motivo de la disconformidad. A diferencia de sign e init, aquí el backend sí lo exige (@IsNotEmpty).
documentType string No (máx. 60) Igual que en qr-sign/init.
fileIdentifier string No (máx. 250) Igual que en qr-sign/init.
{ "observation": "No estoy conforme con los valores del rol de pagos", "documentType": "SUELDO" }

Response (201, QrSignatureResponseDto): misma forma que qr-sign/init.

Errores específicos:

Código Cuándo ocurre
400 El rol ya fue firmado, datos inválidos, o error del servicio legacy.
404 No existe rol de pago para ese período.

POST /me/payroll/{year}/{month}/qr-sign/verify

Sección titulada «POST /me/payroll/{year}/{month}/qr-sign/verify»

Verifica el código de 6 dígitos enviado por correo. Si es correcto, completa la firma.

Path params: year (number), month (number, 1-12).

Body (VerifyQrCodeDto):

Campo Tipo Obligatorio Descripción
code string Código de exactamente 6 dígitos.
documentType string No (máx. 60) Debe coincidir con el usado en qr-sign/init.
fileIdentifier string No (máx. 250) Debe coincidir con el usado en qr-sign/init.
{ "code": "482913" }

Response (200, QrSignatureResponseDto):

{ "status": "CS", "message": "Firma registrada exitosamente" }

En caso de código incorrecto:

{ "status": "CE", "message": "Código incorrecto", "attemptsRemaining": 2 }

Errores específicos:

Código Cuándo ocurre
400 Código inválido, o se alcanzó el máximo de 3 intentos (attemptsRemaining: 0).
404 No existe rol de pago para ese período, o no se inició el proceso de código.

Descarga el PDF del rol de pago con el código QR de firma embebido. Sólo retorna archivo si el rol ya fue firmado con HD.

Path params: year (number), month (number, 1-12).

Response: binario application/pdf (Content-Disposition: attachment; filename="rol-pago-{username}-{year}-{month}-qr.pdf").

Errores específicos:

Código Cuándo ocurre
400 El rol no está firmado, o no fue firmado con código QR.
404 No existe rol de pago para ese período.

Totales de los tres bonos anuales (décimo tercero, décimo cuarto, utilidades) del usuario autenticado.

Query params:

Param Tipo Obligatorio Descripción
year number Año de los bonos.

Response — array de:

Campo Tipo Enum Descripción
type string thirdBonus | fourthBonus | utilityBonus Tipo de bono.
workDays number Días trabajados considerados en el cálculo.
totalBonus number Monto total del bono.
observation string | null Observación del cálculo.
startDate, endDate date | null Rango considerado.
fileUrl string | null URL del comprobante si ya fue generado antes; si viene null, generarlo con el endpoint siguiente.
[
{ "type": "thirdBonus", "workDays": 365, "totalBonus": 500.0, "observation": "Décimo tercer sueldo", "startDate": "2025-01-01", "endDate": "2025-12-31", "fileUrl": null },
{ "type": "utilityBonus", "workDays": 365, "totalBonus": 320.5, "observation": null, "startDate": null, "endDate": null, "fileUrl": null }
]

Errores específicos:

Código Cuándo ocurre
404 No hay datos de bonos anuales para el año solicitado.
400 Parámetros inválidos.

GET /me/payroll/generate/bonuses/{bonusType}

Sección titulada «GET /me/payroll/generate/bonuses/{bonusType}»

Genera y devuelve la URL del comprobante en PDF de un bono anual específico.

Path param: bonusType (thirdBonus | fourthBonus | utilityBonus).

Query params:

Param Tipo Obligatorio Descripción
year number Año del bono.

Response:

{ "url": "https://s3.us-east-1.amazonaws.com/bucket/upload_twiins/tenant/roles/bono-utilidades-2025.pdf?..." }

Errores específicos:

Código Cuándo ocurre
400 Tipo de bono o año inválido.
404 No se encontró el reporte para esos parámetros.
500 Error interno.
  • El período por defecto al abrir la pantalla es el último mes cerrado: si el mes actual es enero, se preselecciona diciembre del año anterior; en cualquier otro caso, el mes anterior al actual del mismo año.
  • Las opciones de año van desde 2015 hasta el máximo año cerrado (igual regla que el mes). Las opciones de mes se limitan a los meses ya cerrados del año seleccionado — nunca se ofrece el mes en curso.
  • Cambiar año o mes dispara de nuevo GET /me/payroll (y GET /me/payroll/uploaded si el tenant es de tipo uploaded).
  • La app debe conocer el Payroll_System_Type del tenant antes de decidir qué endpoints llamar: calculated / integration-table / integration-ws → vista de ingresos/egresos (GET /me/payroll, GET /me/payroll/history); uploaded → vista de archivos (GET /me/payroll/uploaded, GET /me/payroll/uploaded/history).
  • En un tenant uploaded, si signatureEnabled/signature-config.enabled es false, la descarga usa directamente el campo url del archivo (abrir en el visor / navegador) y no se llama a generate.
  • Mostrar la acción sólo si signatureStatus === "PENDING" y signature-config.enabled === true. Con cualquier otro signatureStatus (SIGNED, NOT_CONFORM), la acción se reemplaza por el badge de estado.
  • El selector de método de firma (Simple vs. QR) sólo tiene sentido si el tenant expusiera más de un tipo disponible. Hoy signature-config siempre devuelve un único type por tenant, así que en la práctica no hay selección: la app aplica directamente el método indicado por type (SA → formulario simple, HD → formulario QR).

Badge de estado de firma (PayrollSignatureStatusEnum)

Sección titulada «Badge de estado de firma (PayrollSignatureStatusEnum)»
Valor Significado Texto sugerido Color sugerido
PENDING Pendiente de firma Pendiente warning / amarillo
SIGNED Firmado, conforme Firmado success / verde
NOT_CONFORM Firmado, no conforme No conforme error / rojo

Cuando signatureStatus === "NOT_CONFORM" y hay observation, mostrarla junto al badge (p. ej. en un UAlert de error) — es el motivo de disconformidad que el usuario registró al firmar.

Formulario de firma (conforme / no conforme)

Sección titulada «Formulario de firma (conforme / no conforme)»
  • Selección binaria: Conforme (default) o No conforme. El campo de observación se muestra siempre, pero sólo es obligatorio en cliente cuando se elige “No conforme” (mínimo un carácter no vacío) — el backend acepta observation vacío u omitido incluso con isConform: false, salvo en qr-sign/non-conform, donde sí lo exige.
  • Enviar observation: null (no cadena vacía) cuando es conforme y el usuario no escribió nada, para mantener consistencia con lo que ya persiste el backend.

Fases del flujo en el cliente: initverifysuccess / error.

  1. init: se pide conformidad + observación y se llama POST .../qr-sign/init. Si status === "V", pasar a verify. Si la respuesta de error trae status: "V" (el legacy ya había enviado un código antes), tratarlo igual que un envío exitoso y pasar a verify — no es un fallo.
  2. verify: el usuario ingresa el código de 6 dígitos y se llama POST .../qr-sign/verify. Máximo 3 intentos. Cada error trae attemptsRemaining; cuando llega a 0, deshabilitar el input y el botón “Verificar”, y mostrar sólo la opción “Reenviar código” (que vuelve a llamar qr-sign/init, reiniciando el contador a 3).
  3. success: status === "CS" en la verificación. Mostrar confirmación y refrescar los datos del rol (GET /me/payroll o GET /me/payroll/history según la vista activa) para reflejar el nuevo signatureStatus.
  4. error: errores críticos (red, 5xx) — mostrar el error y permitir reintentar desde init.

No conforme sin OTP (sólo roles precargados)

Sección titulada «No conforme sin OTP (sólo roles precargados)»

Regla no evidente y verificada contra el código del portal: cuando el usuario marca “No conforme” en el paso init y la app conoce el archivo específico que está firmando (tiene documentType o fileIdentifier — esto sólo ocurre al firmar un rol precargado, uploaded), el cliente omite el flujo de OTP por completo y llama directamente a POST .../qr-sign/non-conform. Para roles calculados (vista estructurada, sin documentType/fileIdentifier), la disconformidad pasa por el flujo normal de código (qr-sign/initqr-sign/verify), igual que la conformidad.

  • Con firma habilitada: usar siempre GET /me/payroll/generate (con mode=download por defecto). El backend ya decide si el PDF debe llevar el QR estampado según el signatureType registrado.
  • Con firma deshabilitada: usar directamente el campo url (vista estructurada) o fileUrl/url del archivo (vista de archivos cargados) — no llamar a generate.
  • Para previsualizar un rol precargado antes de firmarlo (p. ej. abrirlo en un visor dentro del flujo de firma), pedirlo con mode=preview y el cutoff correspondiente al type del archivo — con mode=download el backend rechaza la descarga de un archivo aún sin firmar (403).

Tipos de documento (documentType) en la firma QR

Sección titulada «Tipos de documento (documentType) en la firma QR»

Valores observados en las descripciones del DTO (campo de texto libre, máx. 60 caracteres, no es un enum cerrado en el backend): SUELDO, AGUINALDO, VACACIONES, BONOS Y GRATIFICACIONES, ANEXOS GANANCIAS. Si se omite, el backend asume SUELDO. Para roles precargados, usar el type del archivo (UploadedPayrollFileResponseDto.type); para roles calculados, omitir el campo.

Tipo de rol cargado (UploadedPayrollFileResponseDto.type)

Sección titulada «Tipo de rol cargado (UploadedPayrollFileResponseDto.type)»
Valor Significado
SUELDO Salario mensual.
AGUINALDO Bono anual complementario (SAC).
1 QUINCENA Primera quincena del mes.
2 QUINCENA Segunda quincena del mes.
OTROS Otros tipos de pago.

Estado de respuesta de la firma QR (QrSignatureResponseDto.status)

Sección titulada «Estado de respuesta de la firma QR (QrSignatureResponseDto.status)»
Valor Significado
V Verificado / código enviado — pasar a la pantalla de ingreso de código.
CS Firma correcta — proceso completado.
CE Código erróneo — mostrar error inline y descontar un intento.
NV Proceso no válido o expirado.
I Reinicializado (se alcanzó el máximo de intentos); en la práctica el backend reporta esto como attemptsRemaining: 0, no como un status distinto a manejar aparte.
  • Sin rol de pago para el período (GET /me/payroll404): mostrar tarjeta vacía (“No hay datos para este período”), no un error genérico.
  • Sin roles cargados para el período/rango (uploaded, uploaded/history404): tratar como lista vacía, no como error visible.
  • Firma deshabilitada para el tenant: ocultar toda acción de firma; la columna de estado de firma en las tablas de historial tampoco se muestra.
  • Intento de descarga de un rol precargado sin firmar con mode=download: 403 — la app debe haber ocultado o deshabilitado la acción de descarga directa para archivos no firmados y ofrecer en su lugar “Firmar” o, si necesita previsualizarlo, pedirlo con mode=preview.
  • Bonos sin comprobante generado todavía (fileUrl: null en GET /me/payroll/bonuses): mostrar el bono igual, con la acción “Descargar” apuntando a GET /me/payroll/generate/bonuses/{bonusType} bajo demanda.

Los errores genéricos (401, formato de paginación, etc.) siguen la convención documentada en Convenciones.