pcpuma_unam_api/src/prestamo/prestamo.service.ts
2022-07-10 21:53:56 -05:00

688 lines
24 KiB
TypeScript

import * as moment from 'moment';
import {
ConflictException,
forwardRef,
Inject,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AppGateway } from '../app.gateway';
import { Institucion } from '../institucion/entity/institucion.entity';
import { Operador } from '../operador/entity/operador.entity';
import { Prestamo } from './entity/prestamo.entity';
import { EquipoService } from '../equipo/equipo.service';
import { InstitucionService } from '../institucion/institucion.service';
import { InstitucionDiaService } from '../institucion-dia/institucion-dia.service';
import { InstitucionProgramaService } from '../institucion-programa/institucion-programa.service';
import { InstitucionTipoCarritoService } from '../institucion-tipo-carrito/institucion-tipo-carrito.service';
import { InstitucionTipoEntradaService } from '../institucion-tipo-entrada/institucion-tipo-entrada.service';
import { ModuloService } from '../modulo/modulo.service';
import { MultaService } from '../multa/multa.service';
import { OperadorService } from '../operador/operador.service';
import { TipoUsuarioService } from '../tipo-usuario/tipo-usuario.service';
import { UsuarioService } from '../usuario/usuario.service';
import { InstitucionUsuario } from '../institucion-usuario/entity/institucion-usuario.entity';
import { Modulo } from '../modulo/entity/modulo.entity';
@Injectable()
export class PrestamoService {
constructor(
@InjectRepository(Prestamo) private repository: Repository<Prestamo>,
private appGateway: AppGateway,
private equipoService: EquipoService,
private institucionService: InstitucionService,
private institucionDiaService: InstitucionDiaService,
private institucionProgramaService: InstitucionProgramaService,
private institucionTipoCarritoService: InstitucionTipoCarritoService,
private institucionTipoEntradaService: InstitucionTipoEntradaService,
private moduloService: ModuloService,
@Inject(forwardRef(() => MultaService))
private multaService: MultaService,
private operadorService: OperadorService,
private tipoUsuarioService: TipoUsuarioService,
private usuarioService: UsuarioService,
) {}
async cancelarOperador(
id_prestamo: number,
id_operador: number,
motivo: string,
) {
const ahora = moment();
const operadorRegreso = await this.operadorService.findById(id_operador);
const prestamo = await this.findById(id_prestamo);
this.validacionBasicaPrestamo(prestamo, operadorRegreso);
prestamo.activo = false;
prestamo.fecha_entrega = ahora.toDate();
prestamo.cancelado_operador = true;
prestamo.operadorRegreso = operadorRegreso;
return this.equipoService
.update(prestamo.equipo, null, 6, operadorRegreso, motivo)
.then((_) => this.repository.save(prestamo))
.then((_) => {
this.appGateway.actualizarUsuario(prestamo.usuario.id_usuario);
return {
message: 'Se canceló correctamente este préstamo.',
};
});
}
async cancelarUsuario(id_prestamo: number) {
const ahora = moment();
const prestamo = await this.findById(id_prestamo);
this.validacionBasicaPrestamo(prestamo);
if (prestamo.equipo.status.id_status === 3)
throw new ConflictException(
'No puedes cancelar este préstamo una vez se te entregó el equipo.',
);
prestamo.activo = false;
prestamo.fecha_entrega = ahora.toDate();
prestamo.cancelado_usuario = true;
return this.equipoService
.update(prestamo.equipo, null, 1)
.then((_) => this.repository.save(prestamo))
.then((_) => {
this.appGateway.actualizarOperador(
prestamo.equipo.carrito.modulo.institucion.id_institucion,
);
return {
message: 'Se canceló correctamente este préstamo.',
};
});
}
async create(
id_usuario: number,
id_modulo: number,
id_tipo_carrito: number,
id_programa?: number,
id_tipo_entrada?: number,
) {
const ahora = moment().add(1, 'd');
const sistema = await this.operadorService.findById(1);
const modulo = await this.moduloService.findById(id_modulo);
const usuario = await this.usuarioService.findById(id_usuario, true, true);
const tipoCarrito =
await this.institucionTipoCarritoService.findTipoCarritoById(
id_tipo_carrito,
);
const institucionDia = await this.institucionDiaService.findInstitucionDia(
modulo.institucion,
ahora.weekday(),
);
const horaMax = institucionDia
? moment(`${ahora.format('YYYY-MM-DD')} ${institucionDia.hora_fin}`)
: null;
const horaMin = institucionDia
? moment(`${ahora.format('YYYY-MM-DD')} ${institucionDia.hora_inicio}`)
: null;
const programa = id_programa
? await this.institucionProgramaService.findProgramaById(id_programa)
: null;
const tipoEntrada = id_tipo_entrada
? await this.institucionTipoEntradaService.findTipoEntradaById(
id_tipo_entrada,
)
: null;
if (!usuario.activo)
throw new ConflictException(
`${this.mensajeNoPoderPedir()} tu cuenta esta desactivada.`,
);
this.validarInstitucionUsuario(usuario.instituciones, modulo);
if (ahora.weekday() === 0 || ahora.weekday() === 6)
throw new ConflictException('No hay servicio los días sábado y domingo.');
if (!institucionDia.activo)
throw new ConflictException(
'El día de hoy no hay servicio de préstamo de equipos.',
);
if (ahora < horaMin)
throw new ConflictException(
`El servicio inicia a las ${institucionDia.hora_inicio} el día de hoy.`,
);
if (ahora > horaMax)
throw new ConflictException(
`El servicio terminó a las ${institucionDia.hora_fin} el día de hoy.`,
);
for (let i = 0; i < institucionDia.horasExcepcion.length; i++) {
const horaFin = moment(
`${ahora.format('YYYY-MM-DD')} ${
institucionDia.horasExcepcion[i].hora_fin
}`,
);
const horaInicio = moment(
`${ahora.format('YYYY-MM-DD')} ${
institucionDia.horasExcepcion[i].hora_inicio
}`,
);
if (ahora > horaInicio && ahora < horaFin)
throw new ConflictException(
`Se suspendió temporalmente el servicio de ${institucionDia.horasExcepcion[i].hora_inicio} a ${institucionDia.horasExcepcion[i].hora_fin}.`,
);
}
return this.repository
.findOne({ activo: true, usuario })
.then((existePrestamo) => {
if (existePrestamo)
throw new ConflictException(
'Este usuario ya tiene un préstamo activo.',
);
return this.equipoService.findEquipo(
modulo,
tipoCarrito,
programa,
tipoEntrada,
);
})
.then(async (equipo) => {
if (!equipo)
throw new ConflictException(
'No hay un equipo de cómputo que cumpla con las caracteríasticas solicitadas o ya no hay equipos disponibles en este momento. Intenta más tarde o cambia las características.',
);
return this.equipoService.update(equipo, null, 2);
})
.then(({ equipo }) =>
this.repository.save(
this.repository.create({
equipo,
fecha_inicio: ahora.toDate(),
hora_max_recoger: ahora
.add(modulo.institucion.tiempo_recoger, 'm')
.toDate(),
operadorEntrega: sistema,
operadorRegreso: sistema,
usuario,
}),
),
)
.then((prestamo) => {
this.appGateway.actualizarOperador(modulo.institucion.id_institucion);
return prestamo;
});
}
async desactivarPrestamos() {
const ahora = moment();
return this.repository
.find({
join: { alias: 'p', innerJoin: { e: 'p.equipo', s: 'e.status' } },
where: { activo: true, equipo: { status: { id_status: 2 } } },
})
.then(async (prestamos) => {
for (let i = 0; i < prestamos.length; i++)
if (ahora.diff(moment(prestamos[i].hora_max_recoger)) > 0) {
prestamos[i].activo = false;
prestamos[i].cancelado_operador = true;
await this.repository
.save(prestamos[i])
.then((_) =>
this.equipoService.update(prestamos[i].equipo, null, 1),
)
.then((_) => {
this.appGateway.actualizarOperador(
prestamos[i].equipo.carrito.modulo.institucion.id_institucion,
);
this.appGateway.actualizarUsuario(
prestamos[i].usuario.id_usuario,
);
});
}
});
}
async entregar(id_prestamo: number, id_operador: number) {
const ahora = moment();
const operadorEntrega = await this.operadorService.findById(id_operador);
const prestamo = await this.findById(id_prestamo);
this.validacionBasicaPrestamo(prestamo, operadorEntrega);
if (prestamo.equipo.status.id_status === 3)
throw new ConflictException(
'Ya se entregó el equipo de cómputo al usuario.',
);
prestamo.hora_inicio = ahora.toDate();
prestamo.hora_fin = ahora
.add(operadorEntrega.institucion.tiempo_prestamo, 'm')
.toDate();
prestamo.operadorEntrega = operadorEntrega;
prestamo.equipo.prestado = true;
return this.repository
.save(prestamo)
.then((_) => this.equipoService.update(prestamo.equipo, null, 3))
.then((_) => {
this.appGateway.actualizarUsuario(prestamo.usuario.id_usuario);
return {
message: 'Se entregó el equipo de cómputo al usuario.',
equipo: prestamo.equipo,
};
});
}
async findAll(filtros: {
pagina: string;
activo?: string | boolean;
carrito?: string;
equipo?: string;
fechaFin?: string;
fechaInicio?: string;
id_institucion?: string;
id_modulo?: string;
id_operador_entrega?: string;
id_operador_regreso?: string;
id_prestamo?: string;
id_tipo_carrito?: string;
id_tipo_usuario?: string;
usuario?: string;
}) {
const query = this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e')
.innerJoinAndSelect('p.operadorEntrega', 'oe')
.innerJoinAndSelect('p.operadorRegreso', 'or')
.innerJoinAndSelect('p.usuario', 'u')
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('oe.tipoUsuario', 'tuoe')
.innerJoinAndSelect('or.tipoUsuario', 'tuor')
.innerJoinAndSelect('u.instituciones', 'is')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('is.institucionCarrera', 'ic')
.innerJoinAndSelect('m.institucion', 'i')
.innerJoinAndSelect('ic.carrera', 'ca')
.innerJoinAndSelect('ic.institucion', 'in')
.innerJoinAndSelect('ca.nivel', 'n')
.orderBy('i.institucion')
.addOrderBy('m.modulo')
.addOrderBy('tc.tipo_carrito')
.addOrderBy('c.carrito')
.addOrderBy('e.equipo')
.addOrderBy('p.id_prestamo', 'DESC')
.skip((parseInt(filtros.pagina) - 1) * 25)
.take(25);
const institucion = filtros.id_institucion
? await this.institucionService.findById(parseInt(filtros.id_institucion))
: null;
const modulo = filtros.id_modulo
? await this.moduloService.findById(parseInt(filtros.id_modulo))
: null;
const operadorEntrega = filtros.id_operador_entrega
? await this.operadorService.findById(
parseInt(filtros.id_operador_entrega),
)
: null;
const operadorRegreso = filtros.id_operador_regreso
? await this.operadorService.findById(
parseInt(filtros.id_operador_regreso),
)
: null;
const tipoCarrito = filtros.id_tipo_carrito
? await this.institucionTipoCarritoService.findTipoCarritoById(
parseInt(filtros.id_tipo_carrito),
)
: null;
const tipoUsuario = filtros.id_tipo_usuario
? await this.tipoUsuarioService.findById(
parseInt(filtros.id_tipo_usuario),
)
: null;
if (filtros.activo) {
if (typeof filtros.activo === 'boolean') {
query
.innerJoinAndSelect('e.status', 's')
.andWhere('p.activo = :activo', { activo: filtros.activo });
} else
query.andWhere('p.activo = :activo', {
activo: filtros.activo === 'true',
});
}
if (filtros.id_prestamo)
query.andWhere('p.id_prestamo LIKE :id_prestamo', {
id_prestamo: `%${filtros.id_prestamo}%`,
});
if (filtros.carrito)
query.andWhere('c.carrito LIKE :carrito', {
carrito: `%${filtros.carrito}%`,
});
if (filtros.equipo)
query.andWhere('e.equipo LIKE :equipo', {
equipo: `%${filtros.equipo}%`,
});
if (filtros.usuario)
query.andWhere('u.usuario LIKE :usuario', {
usuario: `%${filtros.usuario}%`,
});
if (institucion)
query.andWhere('i.id_institucion = :id_institucion', {
id_institucion: institucion.id_institucion,
});
if (modulo)
query.andWhere('m.id_modulo = :id_modulo', {
id_modulo: modulo.id_modulo,
});
if (tipoCarrito)
query.andWhere('tc.id_tipo_carrito = :id_tipo_carrito', {
id_tipo_carrito: tipoCarrito.id_tipo_carrito,
});
if (tipoUsuario)
query.andWhere('tu.id_tipo_usuario = :id_tipo_usuario', {
id_tipo_usuario: tipoUsuario.id_tipo_usuario,
});
if (operadorEntrega)
query.andWhere('p.id_operador_entrega = :id_operador_entrega', {
id_operador_entrega: operadorEntrega.id_operador,
});
if (operadorRegreso)
query.andWhere('p.id_operador_regreso = :id_operador_regreso', {
id_operador_regreso: operadorRegreso.id_operador,
});
return query.getManyAndCount();
}
findAllByIdUsuario(id_usuario: number, pagina: number) {
return this.usuarioService
.findById(id_usuario, true, true)
.then((usuario) =>
this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e')
.innerJoinAndSelect('p.operadorEntrega', 'oe')
.innerJoinAndSelect('p.operadorRegreso', 'or')
.innerJoinAndSelect('p.usuario', 'u', 'u.id_usuario = :id_usuario', {
id_usuario: usuario.id_usuario,
})
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('e.status', 's')
.innerJoinAndSelect('oe.tipoUsuario', 'tuoe')
.innerJoinAndSelect('or.tipoUsuario', 'tuor')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('m.institucion', 'i')
.skip((pagina - 1) * 25)
.take(25)
.getManyAndCount(),
);
}
async findAllByIdEquipo(id_equipo: number, pagina: number) {
return this.equipoService.findById(id_equipo).then((equipo) =>
this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e', 'e.id_equipo = :id_equipo', {
id_equipo: equipo.id_equipo,
})
.innerJoinAndSelect('p.operadorEntrega', 'oe')
.innerJoinAndSelect('p.operadorRegreso', 'or')
.innerJoinAndSelect('p.usuario', 'u')
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('e.status', 's')
.innerJoinAndSelect('oe.tipoUsuario', 'tuoe')
.innerJoinAndSelect('or.tipoUsuario', 'tuor')
.innerJoinAndSelect('u.instituciones', 'is')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('is.institucionCarrera', 'ic')
.innerJoinAndSelect('m.institucion', 'i')
.innerJoinAndSelect('ic.carrera', 'ca')
.innerJoinAndSelect('ic.institucion', 'in')
.innerJoinAndSelect('ca.nivel', 'n')
.orderBy('p.id_prestamo', 'DESC')
.skip((pagina - 1) * 25)
.take(25)
.getManyAndCount(),
);
}
findById(id_prestamo: number) {
return this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e')
.innerJoinAndSelect('p.usuario', 'u')
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('e.programas', 'ps')
.innerJoinAndSelect('e.status', 's')
.innerJoinAndSelect('e.tiposEntradas', 'tes')
.innerJoinAndSelect('u.instituciones', 'is')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('ps.programa', 'pr')
.innerJoinAndSelect('tes.tipoEntrada', 'te')
.innerJoinAndSelect('is.institucionCarrera', 'ic')
.innerJoinAndSelect('ic.carrera', 'ca')
.innerJoinAndSelect('ic.institucion', 'in')
.innerJoinAndSelect('ca.nivel', 'n')
.innerJoinAndSelect('m.institucion', 'i')
.where('p.id_prestamo = :id_prestamo', {
id_prestamo,
})
.getOne()
.then((prestamo) => {
if (!prestamo) throw new NotFoundException('No existe este préstamo.');
return prestamo;
});
}
findByIdUsuario(id_usuario: number) {
return this.usuarioService
.findById(id_usuario, true, true)
.then((usuario) =>
this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e')
.innerJoinAndSelect('p.usuario', 'u', 'u.id_usuario = :id_usuario', {
id_usuario: usuario.id_usuario,
})
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('e.programas', 'ps')
.innerJoinAndSelect('e.status', 's')
.innerJoinAndSelect('e.tiposEntradas', 'tes')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('ps.programa', 'pr')
.innerJoinAndSelect('tes.tipoEntrada', 'te')
.innerJoinAndSelect('m.institucion', 'i')
.where('p.activo = 1')
.getOne(),
)
.then((prestamo) => {
if (!prestamo)
throw new NotFoundException(
'Este usuario no tiene un préstamo activo.',
);
return prestamo;
});
}
async findByNumeroInventario(
id_institucion: number | Institucion,
numero_inventario: string,
) {
const institucion =
typeof id_institucion === 'number'
? await this.institucionService.findById(id_institucion)
: id_institucion;
return this.equipoService
.findByNumeroInventario(institucion, numero_inventario)
.then((equipo) =>
this.repository
.createQueryBuilder('p')
.innerJoinAndSelect('p.equipo', 'e', 'e.id_equipo = :id_equipo', {
id_equipo: equipo.id_equipo,
})
.innerJoinAndSelect('p.usuario', 'u')
.innerJoinAndSelect('e.carrito', 'c')
.innerJoinAndSelect('e.status', 's')
.innerJoinAndSelect('u.instituciones', 'is')
.innerJoinAndSelect('c.modulo', 'm')
.innerJoinAndSelect('c.tipoCarrito', 'tc')
.innerJoinAndSelect('is.institucionCarrera', 'ic')
.innerJoinAndSelect('m.institucion', 'i')
.innerJoinAndSelect('ic.carrera', 'ca')
.innerJoinAndSelect('ic.institucion', 'in')
.innerJoinAndSelect('ca.nivel', 'n')
.where('p.activo = 1')
.getOne(),
)
.then((prestamo) => {
if (!prestamo)
throw new NotFoundException(
'No existe un préstamo activo con este equipo de cómputo.',
);
return prestamo;
});
}
mensajeNoPoderPedir() {
return 'No puedes pedir equipos de cómputo porque:';
}
async regresar(
prestamo: Prestamo,
operadorRegreso: Operador,
descripcion?: string,
id_institucion_infraccion?: number,
) {
const ahora = moment();
const tardanza = Math.trunc(ahora.diff(moment(prestamo.hora_fin)) / 60000);
const semanasCastigo = Math.trunc(
tardanza / operadorRegreso.institucion.tiempo_entrega,
);
this.validacionBasicaPrestamo(prestamo, operadorRegreso);
if (prestamo.equipo.status.id_status === 2)
throw new ConflictException(
'Aún no se ha entregado el equipo de cómputo al usuario.',
);
if (id_institucion_infraccion && !descripcion)
throw new ConflictException('No se mandó la descripción de lo ocurrido.');
prestamo.activo = false;
prestamo.fecha_entrega = ahora.toDate();
prestamo.operadorRegreso = operadorRegreso;
if (semanasCastigo > 0) {
const mensajeTardanza = `El usaurio se tardó: ${tardanza} minutos en entregar el equipo de cómputo.`;
if (id_institucion_infraccion)
await this.multaService.create(
prestamo,
operadorRegreso,
`${mensajeTardanza} ${descripcion}`,
semanasCastigo,
id_institucion_infraccion,
);
else
await this.multaService.create(
prestamo,
operadorRegreso,
mensajeTardanza,
semanasCastigo,
);
} else if (id_institucion_infraccion)
await this.multaService.create(
prestamo,
operadorRegreso,
descripcion,
null,
id_institucion_infraccion,
);
return this.repository
.save(prestamo)
.then((_) => this.equipoService.update(prestamo.equipo, null, 1))
.then((_) => {
this.appGateway.actualizarUsuario(prestamo.usuario.id_usuario);
return {
message: 'Se regresó el equipo de cómputo.',
};
});
}
async regresarIdPrestamo(
id_operador: number,
id_prestamo: number,
descripcion?: string,
id_institucion_infraccion?: number,
) {
const operador = await this.operadorService.findById(id_operador);
const prestamo = await this.findById(id_prestamo);
return this.regresar(
prestamo,
operador,
descripcion,
id_institucion_infraccion,
);
}
async regresarNumeroInventario(
id_operador: number,
numero_inventario: string,
descripcion?: string,
id_institucion_infraccion?: number,
) {
const operador = await this.operadorService.findById(id_operador);
const prestamo = await this.findByNumeroInventario(
operador.institucion,
numero_inventario,
);
return this.regresar(
prestamo,
operador,
descripcion,
id_institucion_infraccion,
);
}
validacionBasicaPrestamo(prestamo: Prestamo, operador?: Operador) {
if (prestamo.cancelado_usuario)
throw new ConflictException(
'Este préstamo fue cancelado por el usuario.',
);
if (prestamo.cancelado_operador)
throw new ConflictException(
'Este préstamo fue cancelado por un operador.',
);
if (!prestamo.activo)
throw new ConflictException('Este préstamo ya no se encuentra activo.');
if (
operador &&
operador.institucion.id_institucion !=
prestamo.equipo.carrito.modulo.institucion.id_institucion
) {
throw new ConflictException(
'Este préstamo no pertenece a esta institución.',
);
}
}
validarInstitucionUsuario(
instituciones: InstitucionUsuario[],
modulo: Modulo,
) {
for (let i = 0; i < instituciones.length; i++) {
const institucion = instituciones[i].institucionCarrera.institucion;
if (institucion.id_institucion === modulo.institucion.id_institucion) {
if (!instituciones[i].activo)
throw new ConflictException(
`${this.mensajeNoPoderPedir()} tu cuenta esta desactivada en esta institución.`,
);
if (instituciones[i].multa)
throw new ConflictException(
`${this.mensajeNoPoderPedir()} tienes una multa activa.`,
);
return;
}
}
throw new ConflictException(
`${this.mensajeNoPoderPedir()} no perteneces a esta institución.`,
);
}
}