# ET-HR-BACKEND - Especificacion Tecnica Backend HR ## METADATOS | Campo | Valor | |-------|-------| | **Modulo** | MGN-014 | | **Nombre** | Human Resources (HR) | | **Version** | 1.0.0 | | **Fecha** | 2026-01-10 | | **Ubicacion** | `backend/src/modules/hr/` | | **Schema BD** | `hr` | | **Estado** | Implementado | --- ## SERVICIOS ### Resumen de Servicios (7) | # | Servicio | Archivo | Descripcion | |---|----------|---------|-------------| | 1 | EmployeesService | `employees.service.ts` | Gestion de empleados | | 2 | DepartmentsService | `departments.service.ts` | Departamentos y puestos | | 3 | ContractsService | `contracts.service.ts` | Contratos laborales | | 4 | LeavesService | `leaves.service.ts` | Ausencias y vacaciones | | 5 | SkillsService | `skills.service.ts` | Competencias y habilidades | | 6 | ExpensesService | `expenses.service.ts` | Gastos de empleados | | 7 | PayslipsService | `payslips.service.ts` | Nominas y recibos | --- ### 1. EmployeesService **Archivo:** `employees.service.ts` #### Metodos | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAll` | `tenantId: string, filters: EmployeeFilters` | `Promise<{ data: Employee[]; total: number }>` | Lista empleados con paginacion y filtros | | `findById` | `id: string, tenantId: string` | `Promise` | Obtiene empleado por ID | | `create` | `dto: CreateEmployeeDto, tenantId: string, userId: string` | `Promise` | Crea nuevo empleado | | `update` | `id: string, dto: UpdateEmployeeDto, tenantId: string, userId: string` | `Promise` | Actualiza empleado | | `terminate` | `id: string, terminationDate: string, tenantId: string, userId: string` | `Promise` | Da de baja empleado | | `reactivate` | `id: string, tenantId: string, userId: string` | `Promise` | Reactiva empleado | | `delete` | `id: string, tenantId: string` | `Promise` | Elimina empleado | | `getSubordinates` | `id: string, tenantId: string` | `Promise` | Obtiene subordinados | #### Reglas de Negocio - El `employee_number` debe ser unico por tenant - No se puede eliminar un empleado con contratos asociados - No se puede eliminar un empleado que es manager de otros - Al terminar un empleado, se terminan sus contratos activos --- ### 2. DepartmentsService **Archivo:** `departments.service.ts` #### Metodos - Departamentos | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAll` | `tenantId: string, filters: DepartmentFilters` | `Promise<{ data: Department[]; total: number }>` | Lista departamentos | | `findById` | `id: string, tenantId: string` | `Promise` | Obtiene departamento por ID | | `create` | `dto: CreateDepartmentDto, tenantId: string, userId: string` | `Promise` | Crea departamento | | `update` | `id: string, dto: UpdateDepartmentDto, tenantId: string` | `Promise` | Actualiza departamento | | `delete` | `id: string, tenantId: string` | `Promise` | Elimina departamento | #### Metodos - Puestos de Trabajo | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `getJobPositions` | `tenantId: string, includeInactive?: boolean` | `Promise` | Lista puestos de trabajo | | `getJobPositionById` | `id: string, tenantId: string` | `Promise` | Obtiene puesto por ID | | `createJobPosition` | `dto: CreateJobPositionDto, tenantId: string` | `Promise` | Crea puesto | | `updateJobPosition` | `id: string, dto: UpdateJobPositionDto, tenantId: string` | `Promise` | Actualiza puesto | | `deleteJobPosition` | `id: string, tenantId: string` | `Promise` | Elimina puesto | #### Reglas de Negocio - El nombre del departamento debe ser unico por empresa - No se puede eliminar un departamento con empleados - No se puede eliminar un departamento con subdepartamentos - El nombre del puesto debe ser unico por tenant --- ### 3. ContractsService **Archivo:** `contracts.service.ts` #### Metodos | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAll` | `tenantId: string, filters: ContractFilters` | `Promise<{ data: Contract[]; total: number }>` | Lista contratos | | `findById` | `id: string, tenantId: string` | `Promise` | Obtiene contrato por ID | | `create` | `dto: CreateContractDto, tenantId: string, userId: string` | `Promise` | Crea contrato | | `update` | `id: string, dto: UpdateContractDto, tenantId: string, userId: string` | `Promise` | Actualiza contrato | | `activate` | `id: string, tenantId: string, userId: string` | `Promise` | Activa contrato | | `terminate` | `id: string, terminationDate: string, tenantId: string, userId: string` | `Promise` | Termina contrato | | `cancel` | `id: string, tenantId: string, userId: string` | `Promise` | Cancela contrato | | `delete` | `id: string, tenantId: string` | `Promise` | Elimina contrato | #### Flujo de Estados ``` draft -> active -> expired/terminated draft -> cancelled ``` #### Reglas de Negocio - Un empleado solo puede tener un contrato activo - Solo se pueden editar contratos en estado `draft` - Solo se pueden eliminar contratos en `draft` o `cancelled` - Al activar, actualiza el departamento y puesto del empleado --- ### 4. LeavesService **Archivo:** `leaves.service.ts` #### Metodos - Tipos de Ausencia | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `getLeaveTypes` | `tenantId: string, includeInactive?: boolean` | `Promise` | Lista tipos de ausencia | | `getLeaveTypeById` | `id: string, tenantId: string` | `Promise` | Obtiene tipo por ID | | `createLeaveType` | `dto: CreateLeaveTypeDto, tenantId: string` | `Promise` | Crea tipo de ausencia | | `updateLeaveType` | `id: string, dto: UpdateLeaveTypeDto, tenantId: string` | `Promise` | Actualiza tipo | | `deleteLeaveType` | `id: string, tenantId: string` | `Promise` | Elimina tipo | #### Metodos - Ausencias | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAll` | `tenantId: string, filters: LeaveFilters` | `Promise<{ data: Leave[]; total: number }>` | Lista ausencias | | `findById` | `id: string, tenantId: string` | `Promise` | Obtiene ausencia por ID | | `create` | `dto: CreateLeaveDto, tenantId: string, userId: string` | `Promise` | Crea ausencia | | `update` | `id: string, dto: UpdateLeaveDto, tenantId: string, userId: string` | `Promise` | Actualiza ausencia | | `submit` | `id: string, tenantId: string, userId: string` | `Promise` | Envia solicitud | | `approve` | `id: string, tenantId: string, userId: string` | `Promise` | Aprueba solicitud | | `reject` | `id: string, reason: string, tenantId: string, userId: string` | `Promise` | Rechaza solicitud | | `cancel` | `id: string, tenantId: string, userId: string` | `Promise` | Cancela solicitud | | `delete` | `id: string, tenantId: string` | `Promise` | Elimina ausencia | #### Flujo de Estados ``` draft -> submitted -> approved -> rejected draft/submitted/approved -> cancelled ``` #### Reglas de Negocio - Se calcula automaticamente el numero de dias - No se permiten ausencias solapadas - Se respeta el maximo de dias por tipo - Al aprobar, se actualiza el estado del empleado si corresponde --- ### 5. SkillsService **Archivo:** `skills.service.ts` #### Metodos - Tipos de Habilidad | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllSkillTypes` | `tenantId: string, filters: SkillTypeFilters` | `Promise<{ data: SkillType[]; total: number; page: number; limit: number }>` | Lista tipos | | `findSkillTypeById` | `id: string, tenantId: string` | `Promise` | Obtiene tipo por ID | | `createSkillType` | `tenantId: string, dto: CreateSkillTypeDto` | `Promise` | Crea tipo | | `updateSkillType` | `id: string, tenantId: string, dto: UpdateSkillTypeDto` | `Promise` | Actualiza tipo | | `deleteSkillType` | `id: string, tenantId: string` | `Promise` | Elimina tipo | #### Metodos - Habilidades | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllSkills` | `tenantId: string, filters: SkillFilters` | `Promise<{ data: Skill[]; total: number; page: number; limit: number }>` | Lista habilidades | | `findSkillById` | `id: string, tenantId: string` | `Promise` | Obtiene habilidad por ID | | `createSkill` | `tenantId: string, dto: CreateSkillDto` | `Promise` | Crea habilidad | | `updateSkill` | `id: string, tenantId: string, dto: UpdateSkillDto` | `Promise` | Actualiza habilidad | | `deleteSkill` | `id: string, tenantId: string` | `Promise` | Elimina habilidad | #### Metodos - Niveles de Habilidad | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllSkillLevels` | `tenantId: string, filters: SkillLevelFilters` | `Promise<{ data: SkillLevel[]; total: number; page: number; limit: number }>` | Lista niveles | | `findSkillLevelById` | `id: string, tenantId: string` | `Promise` | Obtiene nivel por ID | | `createSkillLevel` | `tenantId: string, dto: CreateSkillLevelDto` | `Promise` | Crea nivel | | `updateSkillLevel` | `id: string, tenantId: string, dto: UpdateSkillLevelDto` | `Promise` | Actualiza nivel | | `deleteSkillLevel` | `id: string, tenantId: string` | `Promise` | Elimina nivel | #### Metodos - Habilidades de Empleado | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllEmployeeSkills` | `tenantId: string, filters: EmployeeSkillFilters` | `Promise<{ data: EmployeeSkill[]; total: number; page: number; limit: number }>` | Lista habilidades de empleados | | `findEmployeeSkillById` | `id: string, tenantId: string` | `Promise` | Obtiene por ID | | `createEmployeeSkill` | `tenantId: string, dto: CreateEmployeeSkillDto` | `Promise` | Asigna habilidad | | `updateEmployeeSkill` | `id: string, tenantId: string, dto: UpdateEmployeeSkillDto` | `Promise` | Actualiza asignacion | | `deleteEmployeeSkill` | `id: string, tenantId: string` | `Promise` | Elimina asignacion | | `getSkillsByEmployee` | `employeeId: string, tenantId: string` | `Promise` | Obtiene habilidades de un empleado | --- ### 6. ExpensesService **Archivo:** `expenses.service.ts` #### Metodos - Hojas de Gastos | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllSheets` | `tenantId: string, filters: ExpenseSheetFilters` | `Promise<{ data: ExpenseSheet[]; total: number; page: number; limit: number }>` | Lista hojas | | `findSheetById` | `id: string, tenantId: string` | `Promise` | Obtiene hoja por ID | | `createSheet` | `tenantId: string, dto: CreateExpenseSheetDto, userId?: string` | `Promise` | Crea hoja | | `updateSheet` | `id: string, tenantId: string, dto: UpdateExpenseSheetDto` | `Promise` | Actualiza hoja | | `submitSheet` | `id: string, tenantId: string` | `Promise` | Envia hoja | | `approveSheet` | `id: string, tenantId: string, approvedBy: string` | `Promise` | Aprueba hoja | | `rejectSheet` | `id: string, tenantId: string` | `Promise` | Rechaza hoja | | `deleteSheet` | `id: string, tenantId: string` | `Promise` | Elimina hoja | #### Metodos - Gastos | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllExpenses` | `tenantId: string, filters: ExpenseFilters` | `Promise<{ data: Expense[]; total: number; page: number; limit: number }>` | Lista gastos | | `findExpenseById` | `id: string, tenantId: string` | `Promise` | Obtiene gasto por ID | | `createExpense` | `tenantId: string, dto: CreateExpenseDto, userId?: string` | `Promise` | Crea gasto | | `updateExpense` | `id: string, tenantId: string, dto: UpdateExpenseDto` | `Promise` | Actualiza gasto | | `deleteExpense` | `id: string, tenantId: string` | `Promise` | Elimina gasto | | `recalculateSheetTotals` | `sheetId: string, tenantId: string` | `Promise` | Recalcula totales | #### Flujo de Estados ``` draft -> submitted -> approved -> posted -> paid -> rejected ``` #### Reglas de Negocio - Solo se pueden modificar/eliminar gastos en `draft` - `total_amount` se calcula como `unit_amount * quantity` - Los totales de la hoja se recalculan automaticamente --- ### 7. PayslipsService **Archivo:** `payslips.service.ts` #### Metodos - Estructuras de Nomina | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllStructures` | `tenantId: string, filters: PayslipStructureFilters` | `Promise<{ data: PayslipStructure[]; total: number; page: number; limit: number }>` | Lista estructuras | | `findStructureById` | `id: string, tenantId: string` | `Promise` | Obtiene estructura por ID | | `createStructure` | `tenantId: string, dto: CreatePayslipStructureDto` | `Promise` | Crea estructura | | `updateStructure` | `id: string, tenantId: string, dto: UpdatePayslipStructureDto` | `Promise` | Actualiza estructura | | `deleteStructure` | `id: string, tenantId: string` | `Promise` | Elimina estructura | #### Metodos - Nominas | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `findAllPayslips` | `tenantId: string, filters: PayslipFilters` | `Promise<{ data: Payslip[]; total: number; page: number; limit: number }>` | Lista nominas | | `findPayslipById` | `id: string, tenantId: string` | `Promise` | Obtiene nomina por ID | | `createPayslip` | `tenantId: string, dto: CreatePayslipDto, userId?: string` | `Promise` | Crea nomina | | `updatePayslip` | `id: string, tenantId: string, dto: UpdatePayslipDto` | `Promise` | Actualiza nomina | | `verifyPayslip` | `id: string, tenantId: string` | `Promise` | Verifica nomina | | `confirmPayslip` | `id: string, tenantId: string` | `Promise` | Confirma nomina | | `cancelPayslip` | `id: string, tenantId: string` | `Promise` | Cancela nomina | | `deletePayslip` | `id: string, tenantId: string` | `Promise` | Elimina nomina | #### Metodos - Lineas de Nomina | Metodo | Parametros | Retorno | Descripcion | |--------|------------|---------|-------------| | `getPayslipLines` | `payslipId: string, tenantId: string` | `Promise` | Obtiene lineas | | `addPayslipLine` | `payslipId: string, tenantId: string, dto: CreatePayslipLineDto` | `Promise` | Agrega linea | | `updatePayslipLine` | `payslipId: string, lineId: string, tenantId: string, dto: UpdatePayslipLineDto` | `Promise` | Actualiza linea | | `removePayslipLine` | `payslipId: string, lineId: string, tenantId: string` | `Promise` | Elimina linea | #### Flujo de Estados ``` draft -> verify -> done draft/verify -> cancel ``` #### Reglas de Negocio - Solo se pueden modificar nominas en `draft` - Solo se pueden eliminar nominas en `draft` o `cancel` - Los totales se recalculan al modificar lineas - El numero de nomina se genera al confirmar --- ## ENTIDADES ### Employee ```typescript interface Employee { id: string; tenant_id: string; company_id: string; company_name?: string; employee_number: string; first_name: string; last_name: string; middle_name?: string; full_name?: string; user_id?: string; birth_date?: Date; gender?: string; marital_status?: string; nationality?: string; identification_id?: string; identification_type?: string; social_security_number?: string; tax_id?: string; email?: string; work_email?: string; phone?: string; work_phone?: string; mobile?: string; emergency_contact?: string; emergency_phone?: string; street?: string; city?: string; state?: string; zip?: string; country?: string; department_id?: string; department_name?: string; job_position_id?: string; job_position_name?: string; manager_id?: string; manager_name?: string; hire_date: Date; termination_date?: Date; status: EmployeeStatus; bank_name?: string; bank_account?: string; bank_clabe?: string; photo_url?: string; notes?: string; created_at: Date; } type EmployeeStatus = 'active' | 'inactive' | 'on_leave' | 'terminated'; ``` ### Department ```typescript interface Department { id: string; tenant_id: string; company_id: string; company_name?: string; name: string; code?: string; parent_id?: string; parent_name?: string; manager_id?: string; manager_name?: string; description?: string; color?: string; active: boolean; employee_count?: number; created_at: Date; } ``` ### JobPosition ```typescript interface JobPosition { id: string; tenant_id: string; name: string; department_id?: string; department_name?: string; description?: string; requirements?: string; responsibilities?: string; min_salary?: number; max_salary?: number; active: boolean; employee_count?: number; created_at: Date; } ``` ### Contract ```typescript interface Contract { id: string; tenant_id: string; company_id: string; company_name?: string; employee_id: string; employee_name?: string; employee_number?: string; name: string; reference?: string; contract_type: ContractType; status: ContractStatus; job_position_id?: string; job_position_name?: string; department_id?: string; department_name?: string; date_start: Date; date_end?: Date; trial_date_end?: Date; wage: number; wage_type: string; currency_id?: string; currency_code?: string; hours_per_week: number; vacation_days: number; christmas_bonus_days: number; document_url?: string; notes?: string; created_at: Date; } type ContractStatus = 'draft' | 'active' | 'expired' | 'terminated' | 'cancelled'; type ContractType = 'permanent' | 'temporary' | 'contractor' | 'internship' | 'part_time'; ``` ### Leave ```typescript interface Leave { id: string; tenant_id: string; company_id: string; company_name?: string; employee_id: string; employee_name?: string; employee_number?: string; leave_type_id: string; leave_type_name?: string; name?: string; date_from: Date; date_to: Date; number_of_days: number; status: LeaveStatus; description?: string; approved_by?: string; approved_by_name?: string; approved_at?: Date; rejection_reason?: string; created_at: Date; } type LeaveStatus = 'draft' | 'submitted' | 'approved' | 'rejected' | 'cancelled'; type LeaveType = 'vacation' | 'sick' | 'personal' | 'maternity' | 'paternity' | 'bereavement' | 'unpaid' | 'other'; ``` ### LeaveTypeConfig ```typescript interface LeaveTypeConfig { id: string; tenant_id: string; name: string; code?: string; leave_type: LeaveType; requires_approval: boolean; max_days?: number; is_paid: boolean; color?: string; active: boolean; created_at: Date; } ``` ### Skill, SkillType, SkillLevel, EmployeeSkill ```typescript interface SkillType { id: string; tenant_id: string; name: string; skill_levels: string; created_at: Date; } interface Skill { id: string; tenant_id: string; skill_type_id: string; skill_type_name?: string; name: string; created_at: Date; } interface SkillLevel { id: string; tenant_id: string; skill_type_id: string; skill_type_name?: string; name: string; level: number; created_at: Date; } interface EmployeeSkill { id: string; employee_id: string; employee_name?: string; skill_id: string; skill_name?: string; skill_level_id?: string; skill_level_name?: string; skill_type_id?: string; skill_type_name?: string; created_at: Date; } ``` ### ExpenseSheet, Expense ```typescript interface ExpenseSheet { id: string; tenant_id: string; company_id: string; employee_id: string; employee_name?: string; name: string; state: ExpenseStatus; total_amount: number; untaxed_amount: number; total_amount_taxes: number; journal_id?: string; account_move_id?: string; user_id?: string; user_name?: string; approved_by?: string; approved_by_name?: string; approved_date?: Date; accounting_date?: Date; created_at: Date; created_by?: string; updated_at?: Date; } interface Expense { id: string; tenant_id: string; company_id: string; employee_id: string; employee_name?: string; name: string; sheet_id?: string; sheet_name?: string; product_id?: string; product_name?: string; unit_amount: number; quantity: number; total_amount: number; untaxed_amount?: number; total_amount_taxes?: number; currency_id?: string; currency_code?: string; tax_ids?: string[]; date: Date; description?: string; reference?: string; analytic_account_id?: string; analytic_account_name?: string; state: ExpenseStatus; payment_mode: string; created_at: Date; created_by?: string; updated_at?: Date; } type ExpenseStatus = 'draft' | 'submitted' | 'approved' | 'posted' | 'paid' | 'rejected'; ``` ### Payslip, PayslipStructure, PayslipLine ```typescript interface PayslipStructure { id: string; tenant_id: string; name: string; code?: string; is_active: boolean; note?: string; created_at: Date; } interface Payslip { id: string; tenant_id: string; company_id: string; employee_id: string; employee_name?: string; employee_number?: string; contract_id?: string; contract_name?: string; name: string; number?: string; state: PayslipStatus; date_from: Date; date_to: Date; date?: Date; structure_id?: string; structure_name?: string; basic_wage?: number; gross_wage?: number; net_wage?: number; worked_days?: number; worked_hours?: number; journal_id?: string; move_id?: string; created_at: Date; created_by?: string; updated_at?: Date; } interface PayslipLine { id: string; payslip_id: string; name: string; code: string; sequence: number; category?: string; quantity: number; rate: number; amount: number; total: number; appears_on_payslip: boolean; created_at: Date; } type PayslipStatus = 'draft' | 'verify' | 'done' | 'cancel'; ``` --- ## DTOs ### Employees DTOs ```typescript interface CreateEmployeeDto { company_id: string; employee_number: string; first_name: string; last_name: string; middle_name?: string; user_id?: string; birth_date?: string; gender?: string; marital_status?: string; nationality?: string; identification_id?: string; identification_type?: string; social_security_number?: string; tax_id?: string; email?: string; work_email?: string; phone?: string; work_phone?: string; mobile?: string; emergency_contact?: string; emergency_phone?: string; street?: string; city?: string; state?: string; zip?: string; country?: string; department_id?: string; job_position_id?: string; manager_id?: string; hire_date: string; bank_name?: string; bank_account?: string; bank_clabe?: string; photo_url?: string; notes?: string; } interface UpdateEmployeeDto { first_name?: string; last_name?: string; middle_name?: string | null; user_id?: string | null; birth_date?: string | null; gender?: string | null; marital_status?: string | null; nationality?: string | null; identification_id?: string | null; identification_type?: string | null; social_security_number?: string | null; tax_id?: string | null; email?: string | null; work_email?: string | null; phone?: string | null; work_phone?: string | null; mobile?: string | null; emergency_contact?: string | null; emergency_phone?: string | null; street?: string | null; city?: string | null; state?: string | null; zip?: string | null; country?: string | null; department_id?: string | null; job_position_id?: string | null; manager_id?: string | null; bank_name?: string | null; bank_account?: string | null; bank_clabe?: string | null; photo_url?: string | null; notes?: string | null; } interface EmployeeFilters { company_id?: string; department_id?: string; status?: EmployeeStatus; manager_id?: string; search?: string; page?: number; limit?: number; } ``` ### Departments DTOs ```typescript interface CreateDepartmentDto { company_id: string; name: string; code?: string; parent_id?: string; manager_id?: string; description?: string; color?: string; } interface UpdateDepartmentDto { name?: string; code?: string | null; parent_id?: string | null; manager_id?: string | null; description?: string | null; color?: string | null; active?: boolean; } interface DepartmentFilters { company_id?: string; active?: boolean; search?: string; page?: number; limit?: number; } interface CreateJobPositionDto { name: string; department_id?: string; description?: string; requirements?: string; responsibilities?: string; min_salary?: number; max_salary?: number; } interface UpdateJobPositionDto { name?: string; department_id?: string | null; description?: string | null; requirements?: string | null; responsibilities?: string | null; min_salary?: number | null; max_salary?: number | null; active?: boolean; } ``` ### Contracts DTOs ```typescript interface CreateContractDto { company_id: string; employee_id: string; name: string; reference?: string; contract_type: ContractType; job_position_id?: string; department_id?: string; date_start: string; date_end?: string; trial_date_end?: string; wage: number; wage_type?: string; currency_id?: string; hours_per_week?: number; vacation_days?: number; christmas_bonus_days?: number; document_url?: string; notes?: string; } interface UpdateContractDto { reference?: string | null; job_position_id?: string | null; department_id?: string | null; date_end?: string | null; trial_date_end?: string | null; wage?: number; wage_type?: string; currency_id?: string | null; hours_per_week?: number; vacation_days?: number; christmas_bonus_days?: number; document_url?: string | null; notes?: string | null; } interface ContractFilters { company_id?: string; employee_id?: string; status?: ContractStatus; contract_type?: ContractType; search?: string; page?: number; limit?: number; } ``` ### Leaves DTOs ```typescript interface CreateLeaveTypeDto { name: string; code?: string; leave_type: LeaveType; requires_approval?: boolean; max_days?: number; is_paid?: boolean; color?: string; } interface UpdateLeaveTypeDto { name?: string; code?: string | null; requires_approval?: boolean; max_days?: number | null; is_paid?: boolean; color?: string | null; active?: boolean; } interface CreateLeaveDto { company_id: string; employee_id: string; leave_type_id: string; name?: string; date_from: string; date_to: string; description?: string; } interface UpdateLeaveDto { leave_type_id?: string; name?: string | null; date_from?: string; date_to?: string; description?: string | null; } interface LeaveFilters { company_id?: string; employee_id?: string; leave_type_id?: string; status?: LeaveStatus; date_from?: string; date_to?: string; search?: string; page?: number; limit?: number; } ``` ### Skills DTOs ```typescript interface CreateSkillTypeDto { name: string; skill_levels?: string; } interface UpdateSkillTypeDto { name?: string; skill_levels?: string; } interface CreateSkillDto { skill_type_id: string; name: string; } interface UpdateSkillDto { name?: string; skill_type_id?: string; } interface CreateSkillLevelDto { skill_type_id: string; name: string; level: number; } interface UpdateSkillLevelDto { name?: string; level?: number; } interface CreateEmployeeSkillDto { employee_id: string; skill_id: string; skill_level_id?: string; skill_type_id?: string; } interface UpdateEmployeeSkillDto { skill_level_id?: string | null; skill_type_id?: string | null; } ``` ### Expenses DTOs ```typescript interface CreateExpenseSheetDto { company_id: string; employee_id: string; name: string; user_id?: string; accounting_date?: string; } interface UpdateExpenseSheetDto { name?: string; user_id?: string | null; accounting_date?: string | null; } interface CreateExpenseDto { company_id: string; employee_id: string; name: string; sheet_id?: string; product_id?: string; unit_amount: number; quantity?: number; currency_id?: string; tax_ids?: string[]; date?: string; description?: string; reference?: string; analytic_account_id?: string; payment_mode?: string; } interface UpdateExpenseDto { name?: string; sheet_id?: string | null; product_id?: string | null; unit_amount?: number; quantity?: number; currency_id?: string | null; tax_ids?: string[]; date?: string; description?: string | null; reference?: string | null; analytic_account_id?: string | null; payment_mode?: string; } interface ExpenseSheetFilters { company_id?: string; employee_id?: string; state?: ExpenseStatus; date_from?: string; date_to?: string; search?: string; page?: number; limit?: number; } interface ExpenseFilters { company_id?: string; employee_id?: string; sheet_id?: string; state?: ExpenseStatus; date_from?: string; date_to?: string; search?: string; page?: number; limit?: number; } ``` ### Payslips DTOs ```typescript interface CreatePayslipStructureDto { name: string; code?: string; is_active?: boolean; note?: string; } interface UpdatePayslipStructureDto { name?: string; code?: string | null; is_active?: boolean; note?: string | null; } interface CreatePayslipDto { company_id: string; employee_id: string; contract_id?: string; name: string; date_from: string; date_to: string; date?: string; structure_id?: string; basic_wage?: number; worked_days?: number; worked_hours?: number; } interface UpdatePayslipDto { name?: string; date?: string | null; structure_id?: string | null; basic_wage?: number; gross_wage?: number; net_wage?: number; worked_days?: number; worked_hours?: number; } interface CreatePayslipLineDto { name: string; code: string; sequence?: number; category?: string; quantity?: number; rate?: number; amount: number; appears_on_payslip?: boolean; } interface UpdatePayslipLineDto { name?: string; code?: string; sequence?: number; category?: string; quantity?: number; rate?: number; amount?: number; appears_on_payslip?: boolean; } interface PayslipStructureFilters { is_active?: boolean; search?: string; page?: number; limit?: number; } interface PayslipFilters { company_id?: string; employee_id?: string; contract_id?: string; structure_id?: string; state?: PayslipStatus; date_from?: string; date_to?: string; search?: string; page?: number; limit?: number; } ``` --- ## ENDPOINTS ### Employees | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/employees` | Listar empleados | | GET | `/api/hr/employees/:id` | Obtener empleado | | POST | `/api/hr/employees` | Crear empleado | | PUT | `/api/hr/employees/:id` | Actualizar empleado | | DELETE | `/api/hr/employees/:id` | Eliminar empleado | | POST | `/api/hr/employees/:id/terminate` | Dar de baja | | POST | `/api/hr/employees/:id/reactivate` | Reactivar | | GET | `/api/hr/employees/:id/subordinates` | Obtener subordinados | ### Departments | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/departments` | Listar departamentos | | GET | `/api/hr/departments/:id` | Obtener departamento | | POST | `/api/hr/departments` | Crear departamento | | PUT | `/api/hr/departments/:id` | Actualizar departamento | | DELETE | `/api/hr/departments/:id` | Eliminar departamento | ### Job Positions | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/job-positions` | Listar puestos | | GET | `/api/hr/job-positions/:id` | Obtener puesto | | POST | `/api/hr/job-positions` | Crear puesto | | PUT | `/api/hr/job-positions/:id` | Actualizar puesto | | DELETE | `/api/hr/job-positions/:id` | Eliminar puesto | ### Contracts | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/contracts` | Listar contratos | | GET | `/api/hr/contracts/:id` | Obtener contrato | | POST | `/api/hr/contracts` | Crear contrato | | PUT | `/api/hr/contracts/:id` | Actualizar contrato | | DELETE | `/api/hr/contracts/:id` | Eliminar contrato | | POST | `/api/hr/contracts/:id/activate` | Activar contrato | | POST | `/api/hr/contracts/:id/terminate` | Terminar contrato | | POST | `/api/hr/contracts/:id/cancel` | Cancelar contrato | ### Leaves | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/leave-types` | Listar tipos de ausencia | | GET | `/api/hr/leave-types/:id` | Obtener tipo | | POST | `/api/hr/leave-types` | Crear tipo | | PUT | `/api/hr/leave-types/:id` | Actualizar tipo | | DELETE | `/api/hr/leave-types/:id` | Eliminar tipo | | GET | `/api/hr/leaves` | Listar ausencias | | GET | `/api/hr/leaves/:id` | Obtener ausencia | | POST | `/api/hr/leaves` | Crear ausencia | | PUT | `/api/hr/leaves/:id` | Actualizar ausencia | | DELETE | `/api/hr/leaves/:id` | Eliminar ausencia | | POST | `/api/hr/leaves/:id/submit` | Enviar solicitud | | POST | `/api/hr/leaves/:id/approve` | Aprobar solicitud | | POST | `/api/hr/leaves/:id/reject` | Rechazar solicitud | | POST | `/api/hr/leaves/:id/cancel` | Cancelar solicitud | ### Skills | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/skill-types` | Listar tipos de habilidad | | GET | `/api/hr/skill-types/:id` | Obtener tipo | | POST | `/api/hr/skill-types` | Crear tipo | | PUT | `/api/hr/skill-types/:id` | Actualizar tipo | | DELETE | `/api/hr/skill-types/:id` | Eliminar tipo | | GET | `/api/hr/skills` | Listar habilidades | | GET | `/api/hr/skills/:id` | Obtener habilidad | | POST | `/api/hr/skills` | Crear habilidad | | PUT | `/api/hr/skills/:id` | Actualizar habilidad | | DELETE | `/api/hr/skills/:id` | Eliminar habilidad | | GET | `/api/hr/skill-levels` | Listar niveles | | GET | `/api/hr/skill-levels/:id` | Obtener nivel | | POST | `/api/hr/skill-levels` | Crear nivel | | PUT | `/api/hr/skill-levels/:id` | Actualizar nivel | | DELETE | `/api/hr/skill-levels/:id` | Eliminar nivel | | GET | `/api/hr/employee-skills` | Listar habilidades de empleados | | GET | `/api/hr/employee-skills/:id` | Obtener asignacion | | POST | `/api/hr/employee-skills` | Asignar habilidad | | PUT | `/api/hr/employee-skills/:id` | Actualizar asignacion | | DELETE | `/api/hr/employee-skills/:id` | Eliminar asignacion | | GET | `/api/hr/employees/:id/skills` | Habilidades de empleado | ### Expenses | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/expense-sheets` | Listar hojas de gastos | | GET | `/api/hr/expense-sheets/:id` | Obtener hoja | | POST | `/api/hr/expense-sheets` | Crear hoja | | PUT | `/api/hr/expense-sheets/:id` | Actualizar hoja | | DELETE | `/api/hr/expense-sheets/:id` | Eliminar hoja | | POST | `/api/hr/expense-sheets/:id/submit` | Enviar hoja | | POST | `/api/hr/expense-sheets/:id/approve` | Aprobar hoja | | POST | `/api/hr/expense-sheets/:id/reject` | Rechazar hoja | | GET | `/api/hr/expenses` | Listar gastos | | GET | `/api/hr/expenses/:id` | Obtener gasto | | POST | `/api/hr/expenses` | Crear gasto | | PUT | `/api/hr/expenses/:id` | Actualizar gasto | | DELETE | `/api/hr/expenses/:id` | Eliminar gasto | ### Payslips | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | `/api/hr/payslip-structures` | Listar estructuras | | GET | `/api/hr/payslip-structures/:id` | Obtener estructura | | POST | `/api/hr/payslip-structures` | Crear estructura | | PUT | `/api/hr/payslip-structures/:id` | Actualizar estructura | | DELETE | `/api/hr/payslip-structures/:id` | Eliminar estructura | | GET | `/api/hr/payslips` | Listar nominas | | GET | `/api/hr/payslips/:id` | Obtener nomina | | POST | `/api/hr/payslips` | Crear nomina | | PUT | `/api/hr/payslips/:id` | Actualizar nomina | | DELETE | `/api/hr/payslips/:id` | Eliminar nomina | | POST | `/api/hr/payslips/:id/verify` | Verificar nomina | | POST | `/api/hr/payslips/:id/confirm` | Confirmar nomina | | POST | `/api/hr/payslips/:id/cancel` | Cancelar nomina | | GET | `/api/hr/payslips/:id/lines` | Obtener lineas | | POST | `/api/hr/payslips/:id/lines` | Agregar linea | | PUT | `/api/hr/payslips/:id/lines/:lineId` | Actualizar linea | | DELETE | `/api/hr/payslips/:id/lines/:lineId` | Eliminar linea | --- ## TESTS ### Ubicacion de Tests ``` backend/src/modules/hr/__tests__/ employees.test.ts departments.test.ts contracts.test.ts leaves.test.ts skills.test.ts expenses.test.ts payslips.test.ts ``` ### Casos de Prueba Requeridos #### EmployeesService - [ ] Crear empleado con datos validos - [ ] Error al crear empleado con numero duplicado - [ ] Obtener empleado existente - [ ] Error al obtener empleado inexistente - [ ] Listar empleados con filtros - [ ] Actualizar empleado - [ ] Dar de baja empleado - [ ] Error al dar de baja empleado ya dado de baja - [ ] Reactivar empleado - [ ] Error al eliminar empleado con contratos - [ ] Error al eliminar empleado que es manager - [ ] Obtener subordinados #### DepartmentsService - [ ] Crear departamento - [ ] Error al crear departamento con nombre duplicado - [ ] Listar departamentos - [ ] Actualizar departamento - [ ] Error al eliminar departamento con empleados - [ ] Error al eliminar departamento con subdepartamentos - [ ] CRUD de puestos de trabajo #### ContractsService - [ ] Crear contrato - [ ] Error al crear contrato con empleado que ya tiene uno activo - [ ] Activar contrato - [ ] Terminar contrato - [ ] Cancelar contrato - [ ] Error al editar contrato no borrador #### LeavesService - [ ] Crear tipo de ausencia - [ ] Crear solicitud de ausencia - [ ] Validar dias maximos por tipo - [ ] Error al crear ausencia solapada - [ ] Flujo completo: draft -> submitted -> approved - [ ] Rechazar solicitud con razon - [ ] Cancelar solicitud #### SkillsService - [ ] CRUD de tipos de habilidad - [ ] CRUD de habilidades - [ ] CRUD de niveles - [ ] Asignar habilidad a empleado - [ ] Obtener habilidades de empleado #### ExpensesService - [ ] Crear hoja de gastos - [ ] Agregar gastos a hoja - [ ] Recalculo automatico de totales - [ ] Flujo de aprobacion - [ ] Error al modificar gasto no borrador #### PayslipsService - [ ] Crear estructura de nomina - [ ] Crear nomina - [ ] Agregar lineas a nomina - [ ] Recalculo de gross/net - [ ] Flujo: draft -> verify -> done - [ ] Cancelar nomina - [ ] Error al modificar nomina confirmada --- ## DEPENDENCIAS ### Internas | Modulo | Descripcion | |--------|-------------| | `auth` | Empresas y usuarios | | `core` | Monedas | | `inventory` | Productos (para gastos) | | `analytics` | Cuentas analiticas | ### Tablas Referenciadas - `auth.companies` - Empresa del empleado/contrato - `auth.users` - Usuario asociado al empleado - `core.currencies` - Moneda del contrato/gasto - `inventory.products` - Producto del gasto - `analytics.analytic_accounts` - Cuenta analitica ### Externas ```json { "pg": "Base de datos PostgreSQL" } ``` --- ## ERRORES COMUNES | Codigo | Mensaje | Causa | |--------|---------|-------| | `NOT_FOUND` | Empleado no encontrado | ID invalido o no pertenece al tenant | | `CONFLICT` | Ya existe un empleado con ese numero | `employee_number` duplicado | | `CONFLICT` | No se puede eliminar un empleado con contratos | Tiene contratos asociados | | `CONFLICT` | No se puede eliminar un empleado que es manager | Es manager de otros empleados | | `VALIDATION` | El empleado ya tiene un contrato activo | Intento de crear segundo contrato activo | | `VALIDATION` | Solo se pueden editar contratos en estado borrador | Contrato no esta en `draft` | | `VALIDATION` | Ya existe una solicitud de ausencia para estas fechas | Ausencias solapadas | | `VALIDATION` | Este tipo de ausencia tiene un maximo de X dias | Excede dias permitidos | | `VALIDATION` | Solo se pueden modificar gastos en estado borrador | Gasto no esta en `draft` | | `VALIDATION` | Solo se pueden modificar nominas en estado borrador | Nomina no esta en `draft` | --- ## CHANGELOG | Version | Fecha | Cambios | |---------|-------|---------| | 1.0.0 | 2026-01-10 | Especificacion inicial con 7 servicios |