"use strict"; /** * EmployeeService - Servicio para gestión de empleados * * CRUD de empleados con gestión de estado y asignaciones a obras. * * @module HR */ Object.defineProperty(exports, "__esModule", { value: true }); exports.EmployeeService = void 0; class EmployeeService { employeeRepository; asignacionRepository; constructor(employeeRepository, asignacionRepository) { this.employeeRepository = employeeRepository; this.asignacionRepository = asignacionRepository; } async findWithFilters(ctx, filters = {}, page = 1, limit = 20) { const skip = (page - 1) * limit; const queryBuilder = this.employeeRepository .createQueryBuilder('employee') .leftJoinAndSelect('employee.puesto', 'puesto') .where('employee.tenant_id = :tenantId', { tenantId: ctx.tenantId }); if (filters.estado) { queryBuilder.andWhere('employee.estado = :estado', { estado: filters.estado }); } if (filters.puestoId) { queryBuilder.andWhere('employee.puesto_id = :puestoId', { puestoId: filters.puestoId }); } if (filters.departamento) { queryBuilder.andWhere('employee.departamento = :departamento', { departamento: filters.departamento }); } if (filters.fraccionamientoId) { queryBuilder .innerJoin('employee.asignaciones', 'asig') .andWhere('asig.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId: filters.fraccionamientoId }) .andWhere('asig.activo = true'); } if (filters.search) { queryBuilder.andWhere('(employee.codigo ILIKE :search OR employee.nombre ILIKE :search OR employee.apellido_paterno ILIKE :search OR employee.curp ILIKE :search)', { search: `%${filters.search}%` }); } queryBuilder .orderBy('employee.apellido_paterno', 'ASC') .addOrderBy('employee.nombre', 'ASC') .skip(skip) .take(limit); const [data, total] = await queryBuilder.getManyAndCount(); return { data, meta: { total, page, limit, totalPages: Math.ceil(total / limit), }, }; } async findById(ctx, id) { return this.employeeRepository.findOne({ where: { id, tenantId: ctx.tenantId, }, relations: ['puesto', 'asignaciones', 'asignaciones.fraccionamiento'], }); } async findByCodigo(ctx, codigo) { return this.employeeRepository.findOne({ where: { codigo, tenantId: ctx.tenantId, }, }); } async findByCurp(ctx, curp) { return this.employeeRepository.findOne({ where: { curp, tenantId: ctx.tenantId, }, }); } async create(ctx, dto) { // Validate unique codigo const existingCodigo = await this.findByCodigo(ctx, dto.codigo); if (existingCodigo) { throw new Error(`Employee with codigo ${dto.codigo} already exists`); } // Validate unique CURP if provided if (dto.curp) { const existingCurp = await this.findByCurp(ctx, dto.curp); if (existingCurp) { throw new Error(`Employee with CURP ${dto.curp} already exists`); } } const employee = this.employeeRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, estado: 'activo', ...dto, }); return this.employeeRepository.save(employee); } async update(ctx, id, dto) { const existing = await this.findById(ctx, id); if (!existing) { return null; } // Validate unique CURP if changed if (dto.curp && dto.curp !== existing.curp) { const existingCurp = await this.findByCurp(ctx, dto.curp); if (existingCurp) { throw new Error(`Employee with CURP ${dto.curp} already exists`); } } const updated = this.employeeRepository.merge(existing, dto); return this.employeeRepository.save(updated); } async changeStatus(ctx, id, estado, fechaBaja) { const existing = await this.findById(ctx, id); if (!existing) { return null; } existing.estado = estado; if (estado === 'baja' && fechaBaja) { existing.fechaBaja = fechaBaja; } return this.employeeRepository.save(existing); } async assignToFraccionamiento(ctx, employeeId, dto) { const employee = await this.findById(ctx, employeeId); if (!employee) { throw new Error('Employee not found'); } // Deactivate previous active assignment to same fraccionamiento await this.asignacionRepository.update({ tenantId: ctx.tenantId, employeeId, fraccionamientoId: dto.fraccionamientoId, activo: true, }, { activo: false, fechaFin: new Date() }); const asignacion = this.asignacionRepository.create({ tenantId: ctx.tenantId, employeeId, fraccionamientoId: dto.fraccionamientoId, fechaInicio: dto.fechaInicio, fechaFin: dto.fechaFin, rol: dto.rol, activo: true, }); return this.asignacionRepository.save(asignacion); } async removeFromFraccionamiento(ctx, employeeId, fraccionamientoId) { const result = await this.asignacionRepository.update({ tenantId: ctx.tenantId, employeeId, fraccionamientoId, activo: true, }, { activo: false, fechaFin: new Date() }); return (result.affected ?? 0) > 0; } async getEmployeesByFraccionamiento(ctx, fraccionamientoId) { const asignaciones = await this.asignacionRepository.find({ where: { tenantId: ctx.tenantId, fraccionamientoId, activo: true, }, relations: ['employee', 'employee.puesto'], }); return asignaciones.map(a => a.employee); } async getStats(ctx) { const [total, activos, inactivos, bajas] = await Promise.all([ this.employeeRepository.count({ where: { tenantId: ctx.tenantId }, }), this.employeeRepository.count({ where: { tenantId: ctx.tenantId, estado: 'activo' }, }), this.employeeRepository.count({ where: { tenantId: ctx.tenantId, estado: 'inactivo' }, }), this.employeeRepository.count({ where: { tenantId: ctx.tenantId, estado: 'baja' }, }), ]); const porDepartamento = await this.employeeRepository .createQueryBuilder('employee') .select('employee.departamento', 'departamento') .addSelect('COUNT(*)', 'count') .where('employee.tenant_id = :tenantId', { tenantId: ctx.tenantId }) .andWhere('employee.estado = :estado', { estado: 'activo' }) .groupBy('employee.departamento') .getRawMany(); return { total, activos, inactivos, bajas, porDepartamento: porDepartamento.map(p => ({ departamento: p.departamento || 'Sin departamento', count: parseInt(p.count, 10), })), }; } } exports.EmployeeService = EmployeeService; //# sourceMappingURL=employee.service.js.map