24 KiB
US-HR-005: Exportación IMSS/INFONAVIT
Epic: MAI-007 - RRHH, Asistencias y Nómina RF: RF-HR-004, RF-HR-005 ET: ET-HR-004, ET-HR-005 Tipo: Historia de Usuario Prioridad: Crítica (Cumplimiento Legal) 🔴 Story Points: 12 Sprint: 11-12 (2 sprints) Estado: 📋 Pendiente Última actualización: 2025-11-17
📖 Historia de Usuario
Como Gerente de RRHH o Contador Quiero gestionar automáticamente altas/bajas en IMSS, generar archivos SUA y calcular aportaciones INFONAVIT Para cumplir con obligaciones legales, evitar multas y sanciones, y tener trazabilidad completa de movimientos ante instituciones gubernamentales
🎯 Criterios de Aceptación
CA-1: Alta de Trabajador ante IMSS 🆕
Dado que registro un nuevo empleado en el sistema Cuando el empleado pasa validación de datos fiscales Entonces puedo registrarlo ante el IMSS:
-
Validación Previa:
- ✅ CURP válido (18 caracteres)
- ✅ NSS válido (11 dígitos)
- ✅ RFC válido (13 caracteres)
- ✅ Fecha de nacimiento < hoy
- ✅ Salario ≥ $248.93 (salario mínimo 2025)
- ❌ Si falta algún dato: error "Complete datos fiscales antes del alta IMSS"
-
Pantalla de Alta IMSS:
- Ver modal "Registrar Alta ante IMSS"
- Datos pre-llenados del empleado:
- Nombre completo: Juan Pérez García
- CURP: BADD110313HCMLNS09
- NSS: 12345678901
- RFC: BADD110313AB1
- Fecha de nacimiento: 13/03/1990
- Género: Masculino
- Campos editables:
- Salario Base de Cotización (SBC): $500.00 (puede ser diferente al salario diario)
- Fecha de alta: 2025-11-17 (default: hoy, editable)
- Tipo de trabajador:
- ○ Permanente (default)
- ○ Eventual
- ○ Por obra determinada
- Tipo de salario:
- ○ Fijo (default)
- ○ Variable
- ○ Mixto
-
Enviar Alta (Sandbox Mode):
- Hacer clic en "Enviar Alta a IMSS"
- Spinner: "Enviando a IMSS..."
- En modo sandbox: Simular respuesta después de 2 segundos
- Response mock:
{ "success": true, "folio": "IMSS-2025-11-17-00123", "message": "Alta registrada exitosamente", "fechaAlta": "2025-11-17", "nss": "12345678901" } - Guardar log en base de datos:
{ "employeeId": "uuid", "operationType": "alta", "requestPayload": {...}, "responsePayload": {...}, "status": "success", "imssFolio": "IMSS-2025-11-17-00123", "createdAt": "2025-11-17T10:30:00Z" }
-
Confirmación:
- Toast: "✓ Alta registrada en IMSS con folio IMSS-2025-11-17-00123"
- Badge en perfil del empleado: "IMSS: Registrado ✓"
- Link para ver comprobante
Y el sistema debe recordar que el empleado ya está dado de alta (no permitir duplicados)
CA-2: Baja de Trabajador ante IMSS 🚫
Dado que un empleado termina su relación laboral Cuando registro su baja en el sistema Entonces debo dar de baja en IMSS:
-
Trigger Automático:
- Al dar de baja empleado en sistema (US-HR-001)
- Modal aparece automáticamente: "¿Registrar baja ante IMSS?"
- Opciones:
- "Sí, registrar ahora" (recomendado)
- "Recordarme después"
- "No registrar (requiere justificación)"
-
Formulario de Baja:
- Fecha de baja: 2025-11-17 (default: fecha de terminación, no editable)
- Motivo de baja (obligatorio):
- ○ Renuncia voluntaria
- ○ Despido sin responsabilidad del trabajador
- ○ Despido con responsabilidad del trabajador
- ○ Término de contrato
- ○ Defunción
- ○ Otro
- Si "Otro": campo de texto obligatorio
- Advertencia: ⚠️ "Tienes 5 días hábiles para reportar la baja ante IMSS"
-
Enviar Baja:
- Validar que fecha de baja ≥ fecha de alta
- Enviar request a API IMSS (o sandbox)
- Response:
{ "success": true, "folio": "IMSS-2025-11-17-00456", "message": "Baja registrada exitosamente", "fechaBaja": "2025-11-17", "nss": "12345678901" }
-
Confirmación:
- Toast: "✓ Baja registrada en IMSS"
- Badge en empleado: "IMSS: Dado de baja 🔴"
- Empleado ya no aparece en archivo SUA mensual
CA-3: Modificación Salarial ante IMSS 💰
Dado que cambio el salario de un empleado Cuando el cambio es > 5% o > 1 UMA (~$103 MXN) Entonces debo notificar a IMSS:
-
Detección Automática:
- Empleado con salario actual: $500.00
- Se modifica a: $550.00
- Cambio: $50.00 (10%)
- Sistema detecta que > 5% → requiere notificación IMSS
- Modal aparece: "Este cambio requiere notificación al IMSS"
-
Formulario de Modificación:
- Salario anterior: $500.00 (no editable)
- Nuevo SBC: $550.00 (editable)
- Fecha efectiva: 2025-12-01 (editable, no puede ser pasada)
- Tipo de modificación: "Modificación salarial"
-
Enviar Modificación:
- Request a API IMSS
- Response:
{ "success": true, "folio": "IMSS-2025-11-17-00789", "fechaEfectiva": "2025-12-01" }
-
Manejo de Cambios Menores:
- Si cambio < 5% y < 1 UMA:
- Mensaje: "ℹ️ Cambio menor al 5%. No requiere notificación inmediata al IMSS"
- Se acumulan para reporte mensual en SUA
CA-4: Generación de Archivo SUA Mensual 📁
Dado que necesito presentar declaración mensual al IMSS Cuando accedo a "IMSS" > "Generar Archivo SUA" Entonces puedo:
-
Seleccionar Periodo:
- Mes: Noviembre
- Año: 2025
- Mostrar: "Noviembre 2025 (01/11/2025 - 30/11/2025)"
-
Vista Previa de Empleados:
- Tabla con empleados que se incluirán:
NSS Nombre Días Cotizados SBC Cuotas IMSS 12345678901 Juan Pérez 30 $500 $4,050 98765432109 María López 28 $450 $3,402 ... ... ... ... ... - Totales:
- Empleados: 52
- Total cuotas: $187,400
-
Validaciones Previas:
- ✅ Todos los empleados tienen NSS
- ✅ Todos los empleados dados de alta en IMSS
- ⚠️ 2 empleados con ausencias > 5 días (se marcan pero se incluyen)
- ❌ 1 empleado sin fecha de alta IMSS (bloquea generación)
-
Generar Archivo:
- Hacer clic en "Generar Archivo SUA"
- Formato del archivo: Layout SUA estándar (80 caracteres por línea)
- Contenido ejemplo:
*************112025A1234567890 A1234567890CONSTRUCCIONES SA DE CV 12345678901PEREZ GARCIA JUAN 30500.00 98765432109LOPEZ MARTINEZ MARIA 28450.00 ... 99999999999TOTALES 0052187400.00
-
Descarga:
- Archivo generado:
SUA_202511_A1234567890.txt - Tamaño: ~5 KB
- Encoding: ASCII (requerido por IMSS)
- Toast: "✓ Archivo SUA generado exitosamente"
- Archivo generado:
-
Guardar Registro:
- Guardar en tabla
sua_files:{ "periodMonth": 11, "periodYear": 2025, "fileContent": "...", "fileHash": "sha256-hash", "totalEmployees": 52, "totalContributions": 187400.00, "generatedBy": "user-uuid", "createdAt": "2025-11-17T15:00:00Z" }
- Guardar en tabla
Y poder regenerar el archivo en cualquier momento (con los datos actuales)
CA-5: Cálculo de Aportaciones Bimestrales INFONAVIT 🏠
Dado que debo pagar aportaciones INFONAVIT bimestralmente Cuando accedo a "INFONAVIT" > "Calcular Aportaciones" Entonces puedo:
-
Seleccionar Bimestre:
- Bimestre (dropdown):
- Bimestre 1: Enero-Febrero
- Bimestre 2: Marzo-Abril
- Bimestre 3: Mayo-Junio
- Bimestre 4: Julio-Agosto
- Bimestre 5: Septiembre-Octubre
- Bimestre 6: Noviembre-Diciembre
- Año: 2025
- Bimestre (dropdown):
-
Cálculo Automático:
- Sistema calcula aportaciones del 5% del SBC
- Fórmula:
Aportación = SBC × Días Cotizados × 5% - Ejemplo:
- Empleado: Juan Pérez
- SBC: $500
- Días cotizados (bimestre): 60
- Aportación: $500 × 60 × 0.05 = $1,500
-
Vista Previa:
NSS Nombre SBC Días Aportación 5% 12345678901 Juan Pérez $500 60 $1,500 98765432109 María López $450 60 $1,350 ... ... ... ... ... - Total aportaciones: $78,000
-
Incluir Descuentos por Créditos:
- Si empleado tiene crédito INFONAVIT activo:
- Mostrar columna adicional: "Descuento Crédito"
- Ejemplo:
- Empleado con crédito de 2.5 VSM
- Descuento bimestral: $14,934 (2 meses × 30 días × $248.93 × 2.5)
- Total descuentos: $45,000
- Monto total a pagar: $78,000 (aportaciones) + $45,000 (descuentos) = $123,000
CA-6: Generación de Archivo de Pago INFONAVIT 📄
Dado que tengo el cálculo de aportaciones bimestrales Cuando hago clic en "Generar Archivo de Pago" Entonces:
-
Archivo Generado:
- Formato: Layout INFONAVIT estándar
- Contenido:
HEADER|A1234567890|BIMESTRE 5-2025 APORTACION|12345678901|1500.00|60 APORTACION|98765432109|1350.00|60 DESCUENTO|12345678901|1234567890123|14934.00 TOTAL|52|78000.00|45000.00
-
Línea de Captura Bancaria:
- Generar línea de captura para pago en banco
- Formato:
INFONAVIT[registro][periodo][monto] - Ejemplo:
INFONAVITA123456789020255000000123000 - Mostrar en pantalla para copiar:
┌──────────────────────────────────────┐ │ Línea de Captura Bancaria: │ │ │ │ INFONAVITA123456789020255000000123000│ │ │ │ Monto: $123,000.00 │ │ Vencimiento: 17/12/2025 │ │ [Copiar] │ └──────────────────────────────────────┘
-
Descarga:
- Archivo:
INFONAVIT_Bim5_2025.txt - Hash SHA-256 para validación de integridad
- Guardar registro en DB
- Archivo:
CA-7: Consulta de Acreditados INFONAVIT 🔍
Dado que necesito saber qué empleados tienen crédito INFONAVIT Cuando accedo a "INFONAVIT" > "Consultar Acreditados" Entonces:
-
Consulta Automática (Sandbox):
- Hacer clic en "Actualizar desde INFONAVIT"
- Spinner: "Consultando acreditados..."
- Simular respuesta de API:
{ "acreditados": [ { "nss": "12345678901", "numero_credito": "1234567890123", "tipo_descuento": "VSM", "descuento_mensual": 2.5, "saldo_pendiente": 285000.00, "fecha_inicio": "2020-05-15" } ] }
-
Actualizar Registros:
- Buscar empleado por NSS
- Crear o actualizar registro de crédito:
- Número de crédito
- Tipo de descuento: VSM (Veces Salario Mínimo)
- Valor de descuento: 2.5
- Saldo pendiente: $285,000
- Toast: "✓ 1 crédito actualizado"
-
Ver Lista de Acreditados:
Empleado NSS Núm. Crédito Tipo Descuento Saldo Juan Pérez 123...901 1234...123 VSM 2.5 $285,000 -
Cálculo de Descuento:
- Al generar nómina o archivo INFONAVIT
- Calcular descuento automáticamente:
- Tipo VSM: $248.93 × 2.5 × 30 días = $18,669.75/mes
- Validar que no exceda 30% del salario bruto
- Si excede: ajustar a 30% máximo
CA-8: Historial de Movimientos IMSS 📜
Dado que necesito trazabilidad de movimientos ante IMSS Cuando accedo a un empleado > "Historial IMSS" Entonces veo:
-
Timeline de Movimientos:
📅 17/11/2025 - 10:30 AM ✅ ALTA IMSS Folio: IMSS-2025-11-17-00123 SBC: $500.00 Usuario: Juan Rodríguez (RRHH) 📅 01/12/2025 - 14:15 PM 💰 MODIFICACIÓN SALARIAL Folio: IMSS-2025-12-01-00789 SBC anterior: $500.00 → Nuevo: $550.00 Usuario: María González (RRHH) 📅 15/03/2026 - 09:00 AM 🚫 BAJA IMSS Folio: IMSS-2026-03-15-00456 Motivo: Renuncia voluntaria Usuario: Juan Rodríguez (RRHH) -
Filtros:
- Por tipo: Alta, Baja, Modificación
- Por fecha: Últimos 30 días, 6 meses, 1 año, Todo
-
Descargar Comprobante:
- Hacer clic en un movimiento
- Ver modal con detalles completos
- Botón "Descargar comprobante PDF"
CA-9: Dashboard de Cumplimiento 📊
Dado que soy Gerente de RRHH Cuando accedo a "RRHH" > "Dashboard de Cumplimiento" Entonces veo:
-
Métricas de IMSS:
┌─────────────────────────────────┐ │ IMSS - Estado de Cumplimiento │ ├─────────────────────────────────┤ │ ✅ Empleados dados de alta: 52 │ │ ⚠️ Altas pendientes: 3 │ │ ⚠️ Bajas pendientes: 1 │ │ ✅ Archivo SUA mes actual: ✓ │ │ Última actualización: Hoy 10:30 │ └─────────────────────────────────┘ -
Métricas de INFONAVIT:
┌─────────────────────────────────┐ │ INFONAVIT - Estado │ ├─────────────────────────────────┤ │ Empleados con crédito: 8 │ │ Total descuentos mes: $149,358 │ │ ⚠️ Pago bimestral vence: 17/12 │ │ (faltan 30 días) │ │ Último archivo generado: ✓ │ └─────────────────────────────────┘ -
Alertas:
- 🔴 Urgente: 1 baja de empleado pendiente de reportar (día 4 de 5)
- 🟡 Advertencia: Pago INFONAVIT vence en 30 días
- 🟢 Ok: Todos los archivos SUA generados a tiempo
CA-10: Permisos por Rol 🔐
Roles y Permisos:
| Acción | Director | Engineer | Resident | HR | Finance |
|---|---|---|---|---|---|
| Alta/baja/mod IMSS | ✅ | ❌ | ❌ | ✅ | ❌ |
| Generar archivo SUA | ✅ | ❌ | ❌ | ✅ | ✅ |
| Calcular aportaciones INFONAVIT | ✅ | ❌ | ❌ | ✅ | ✅ |
| Consultar acreditados | ✅ | ❌ | ❌ | ✅ | ❌ |
| Ver historial de movimientos | ✅ | ❌ | ❌ | ✅ | ✅ |
| Descargar comprobantes | ✅ | ❌ | ❌ | ✅ | ✅ |
🔧 Detalles Técnicos
Integración IMSS (Sandbox)
// imss-integration.service.ts
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import * as fs from 'fs';
import * as https from 'https';
@Injectable()
export class IMSSIntegrationService {
private readonly apiUrl: string;
private readonly certificatePath: string;
private readonly privateKeyPath: string;
constructor(
private httpService: HttpService,
private configService: ConfigService,
) {
this.apiUrl = configService.get('IMSS_API_URL');
this.certificatePath = configService.get('IMSS_CERTIFICATE_PATH');
this.privateKeyPath = configService.get('IMSS_PRIVATE_KEY_PATH');
}
/**
* Alta de trabajador ante IMSS
*/
async registrarTrabajador(employeeId: string): Promise<any> {
const employee = await this.employeeRepo.findOne({ where: { id: employeeId } });
const payload = {
registro_patronal: this.registroPatronal,
nss: employee.nss,
curp: employee.curp,
nombre: employee.firstName,
apellido_paterno: employee.lastName.split(' ')[0],
apellido_materno: employee.lastName.split(' ')[1] || '',
fecha_nacimiento: employee.dateOfBirth,
sexo: employee.gender === 'male' ? 'H' : 'M',
salario_base_cotizacion: employee.currentSalary,
fecha_alta: employee.hireDate,
tipo_trabajador: employee.contractType === 'permanent' ? '1' : '2',
};
try {
const response = await this.makeIMSSRequest('/afiliacion/alta', payload);
await this.saveLog(employeeId, 'alta', payload, response, 'success', response.folio);
return response;
} catch (error) {
await this.saveLog(employeeId, 'alta', payload, null, 'failed', null, error.message);
throw error;
}
}
/**
* Generar archivo SUA mensual
*/
async generarArchivoSUA(
constructoraId: string,
periodMonth: number,
periodYear: number,
): Promise<string> {
const employees = await this.getActiveEmployeesForMonth(
constructoraId,
periodMonth,
periodYear,
);
let suaContent = '';
// Header
suaContent += `*************${periodMonth.toString().padStart(2, '0')}${periodYear}${this.registroPatronal}\n`;
// Registro patronal
suaContent += `${this.registroPatronal}CONSTRUCCIONES SA DE CV\n`;
// Trabajadores
let totalContributions = 0;
employees.forEach((emp) => {
const line = this.buildEmployeeLine(emp, periodMonth, periodYear);
suaContent += line + '\n';
totalContributions += this.calculateIMSSContributions(emp.currentSalary, 30);
});
// Totales
suaContent += `99999999999TOTALES${' '.repeat(30)}${employees.length.toString().padStart(5, '0')}${totalContributions.toFixed(2).padStart(15, '0')}\n`;
return suaContent;
}
/**
* Request HTTP a API IMSS con certificado digital
*/
private async makeIMSSRequest(endpoint: string, payload: any): Promise<any> {
const cert = fs.readFileSync(this.certificatePath);
const key = fs.readFileSync(this.privateKeyPath);
const config = {
headers: { 'Content-Type': 'application/json' },
httpsAgent: new https.Agent({
cert,
key,
rejectUnauthorized: false, // Solo para sandbox
}),
};
const response = await firstValueFrom(
this.httpService.post(this.apiUrl + endpoint, payload, config),
);
return response.data;
}
}
Integración INFONAVIT
// infonavit-integration.service.ts
@Injectable()
export class INFONAVITIntegrationService {
/**
* Calcular aportaciones bimestrales (5% del SBC)
*/
async calcularAportaciones(
constructoraId: string,
periodNumber: number, // 1-6 (bimestre)
periodYear: number,
): Promise<{ totalAportaciones: number; empleados: any[] }> {
const { startDate, endDate } = this.getBimestreDates(periodNumber, periodYear);
const employees = await this.getActiveEmployeesForPeriod(
constructoraId,
startDate,
endDate,
);
let totalAportaciones = 0;
const detalles = [];
for (const employee of employees) {
const diasCotizados = 60; // Simplificado: 60 días por bimestre
const aportacion = employee.currentSalary * diasCotizados * 0.05;
totalAportaciones += aportacion;
detalles.push({
employeeId: employee.id,
nss: employee.nss,
nombre: employee.fullName,
sbc: employee.currentSalary,
diasCotizados,
aportacion,
});
}
return { totalAportaciones, empleados: detalles };
}
/**
* Calcular descuento por crédito INFONAVIT
*/
async calcularDescuentoCredito(
employeeId: string,
salarioBruto: number,
): Promise<number> {
const credit = await this.creditRepo.findOne({
where: { employeeId, isActive: true },
});
if (!credit) return 0;
let descuento = 0;
switch (credit.discountType) {
case 'VSM': {
const salarioMinimo = 248.93; // 2025
descuento = salarioMinimo * credit.discountValue * 30;
break;
}
case 'percentage': {
descuento = salarioBruto * (credit.discountValue / 100);
break;
}
case 'fixed': {
descuento = credit.discountValue;
break;
}
}
// Validar que no exceda 30% del salario bruto
const maxDescuento = salarioBruto * 0.3;
if (descuento > maxDescuento) {
descuento = maxDescuento;
}
return descuento;
}
}
🧪 Casos de Prueba
TC-IMSS-001: Alta de Trabajador ✅
Precondiciones:
- Empleado con datos completos (CURP, NSS, RFC)
- No dado de alta previamente en IMSS
Pasos:
- Ir a empleado > "Registrar en IMSS"
- Verificar datos pre-llenados
- Confirmar SBC: $500
- Hacer clic en "Enviar Alta"
Resultado esperado:
- Request enviado a API IMSS
- Response 200 OK con folio
- Log guardado con status "success"
- Badge "IMSS: Registrado ✓" visible
- Toast: "✓ Alta registrada con folio IMSS-2025-11-17-00123"
TC-IMSS-002: Generación de Archivo SUA ✅
Precondiciones:
- 52 empleados activos en noviembre 2025
- Todos dados de alta en IMSS
Pasos:
- Ir a "IMSS" > "Generar Archivo SUA"
- Seleccionar noviembre 2025
- Ver preview de 52 empleados
- Hacer clic en "Generar"
Resultado esperado:
- Archivo descargado:
SUA_202511_A1234567890.txt - Formato correcto (80 caracteres por línea)
- Header:
*************112025A1234567890 - 52 líneas de empleados
- Línea de totales al final
- Registro guardado en DB
TC-INFONAVIT-001: Cálculo de Aportaciones ✅
Precondiciones:
- Bimestre 5 (Sep-Oct) 2025
- 52 empleados con SBC promedio $500
Pasos:
- Ir a "INFONAVIT" > "Calcular Aportaciones"
- Seleccionar Bimestre 5, 2025
- Ver cálculo automático
Resultado esperado:
- Empleado Juan Pérez:
- SBC: $500
- Días: 60
- Aportación: $500 × 60 × 0.05 = $1,500 ✓
- Total 52 empleados: ~$78,000
TC-INFONAVIT-002: Descuento por Crédito ✅
Precondiciones:
- Empleado con crédito tipo VSM: 2.5
- Salario bruto mensual: $10,500
Pasos:
- Calcular descuento automáticamente
Resultado esperado:
- Descuento VSM: $248.93 × 2.5 × 30 = $18,669.75
- Validar 30% máximo: $10,500 × 0.3 = $3,150
- Descuento aplicado: $3,150 (limitado a 30%) ✓
📦 Dependencias
Dependencias de Otros US
- ✅ US-HR-001: Empleados con datos fiscales completos
- ⏳ US-HR-003: Cálculo de SBC
Configuración Requerida
# .env.production
IMSS_API_URL=https://api.imss.gob.mx/v1
IMSS_REGISTRO_PATRONAL=A1234567890
IMSS_CERTIFICATE_PATH=/secrets/imss-cert.cer
IMSS_PRIVATE_KEY_PATH=/secrets/imss-key.key
INFONAVIT_API_URL=https://api.infonavit.org.mx/v1
INFONAVIT_REGISTRO_PATRONAL=1234567890
INFONAVIT_API_KEY=your-api-key
INFONAVIT_ACCESS_TOKEN=your-oauth-token
⚠️ Riesgos
R-1: Cambios en API Gubernamentales
Descripción: IMSS/INFONAVIT pueden cambiar APIs sin aviso Impacto: Crítico Probabilidad: Media Mitigación:
- Modo sandbox para desarrollo
- Monitoreo de errores 24/7
- Contacto directo con soporte técnico gubernamental
R-2: Certificados Digitales Expirados
Descripción: Certificados .cer/.key expiran anualmente Impacto: Alto Probabilidad: Alta Mitigación:
- Alertas 30 días antes de expiración
- Proceso documentado de renovación
- Respaldo de certificados en vault seguro
📊 Métricas de Éxito
- ✅ 100% de altas/bajas reportadas dentro de plazo legal (5 días)
- ✅ 0 multas o sanciones de IMSS/INFONAVIT
- ✅ Archivos SUA generados antes del día 17 de cada mes
- ✅ 100% de créditos INFONAVIT rastreados correctamente
📋 Checklist de Implementación
Backend
- Implementar IMSSIntegrationService
- Implementar INFONAVITIntegrationService
- Crear endpoints de alta/baja/modificación
- Implementar generación de archivo SUA
- Implementar cálculo de aportaciones
- Crear sistema de logs de integración
- Configurar certificados digitales
Frontend
- Crear pantalla de alta IMSS
- Crear generador de archivo SUA
- Crear calculadora de aportaciones INFONAVIT
- Crear dashboard de cumplimiento
- Implementar timeline de movimientos
Testing
- Tests de cálculos (5%, 30% límite)
- Tests de generación de archivos
- Tests de integración con sandbox
Fecha de creación: 2025-11-17 Versión: 1.0