210 lines
8.3 KiB
JavaScript
210 lines
8.3 KiB
JavaScript
"use strict";
|
|
/**
|
|
* AsignacionService - Servicio de asignaciones de vivienda INFONAVIT
|
|
*
|
|
* Gestión de asignaciones de vivienda a derechohabientes.
|
|
*
|
|
* @module Infonavit
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.AsignacionService = void 0;
|
|
class AsignacionService {
|
|
asignacionRepository;
|
|
ofertaRepository;
|
|
derechohabienteRepository;
|
|
constructor(asignacionRepository, ofertaRepository, derechohabienteRepository) {
|
|
this.asignacionRepository = asignacionRepository;
|
|
this.ofertaRepository = ofertaRepository;
|
|
this.derechohabienteRepository = derechohabienteRepository;
|
|
}
|
|
generateAssignmentNumber() {
|
|
const now = new Date();
|
|
const year = now.getFullYear().toString().slice(-2);
|
|
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
|
const random = Math.random().toString(36).substring(2, 8).toUpperCase();
|
|
return `ASG-${year}${month}-${random}`;
|
|
}
|
|
async findWithFilters(ctx, filters = {}, page = 1, limit = 20) {
|
|
const skip = (page - 1) * limit;
|
|
const queryBuilder = this.asignacionRepository
|
|
.createQueryBuilder('asig')
|
|
.leftJoinAndSelect('asig.oferta', 'oferta')
|
|
.leftJoinAndSelect('asig.derechohabiente', 'derechohabiente')
|
|
.leftJoinAndSelect('asig.createdBy', 'createdBy')
|
|
.where('asig.tenant_id = :tenantId', { tenantId: ctx.tenantId });
|
|
if (filters.ofertaId) {
|
|
queryBuilder.andWhere('asig.oferta_id = :ofertaId', { ofertaId: filters.ofertaId });
|
|
}
|
|
if (filters.derechohabienteId) {
|
|
queryBuilder.andWhere('asig.derechohabiente_id = :derechohabienteId', {
|
|
derechohabienteId: filters.derechohabienteId,
|
|
});
|
|
}
|
|
if (filters.status) {
|
|
queryBuilder.andWhere('asig.status = :status', { status: filters.status });
|
|
}
|
|
if (filters.dateFrom) {
|
|
queryBuilder.andWhere('asig.assignment_date >= :dateFrom', { dateFrom: filters.dateFrom });
|
|
}
|
|
if (filters.dateTo) {
|
|
queryBuilder.andWhere('asig.assignment_date <= :dateTo', { dateTo: filters.dateTo });
|
|
}
|
|
queryBuilder
|
|
.orderBy('asig.assignment_date', 'DESC')
|
|
.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.asignacionRepository.findOne({
|
|
where: {
|
|
id,
|
|
tenantId: ctx.tenantId,
|
|
},
|
|
});
|
|
}
|
|
async findWithDetails(ctx, id) {
|
|
return this.asignacionRepository.findOne({
|
|
where: {
|
|
id,
|
|
tenantId: ctx.tenantId,
|
|
},
|
|
relations: ['oferta', 'oferta.registro', 'derechohabiente', 'createdBy'],
|
|
});
|
|
}
|
|
async create(ctx, dto) {
|
|
// Validate oferta exists and is available
|
|
const oferta = await this.ofertaRepository.findOne({
|
|
where: {
|
|
id: dto.ofertaId,
|
|
tenantId: ctx.tenantId,
|
|
},
|
|
});
|
|
if (!oferta) {
|
|
throw new Error('Housing offer not found');
|
|
}
|
|
if (oferta.status !== 'available' && oferta.status !== 'reserved') {
|
|
throw new Error('Housing offer is not available for assignment');
|
|
}
|
|
// Validate derechohabiente exists and is qualified
|
|
const derechohabiente = await this.derechohabienteRepository.findOne({
|
|
where: {
|
|
id: dto.derechohabienteId,
|
|
tenantId: ctx.tenantId,
|
|
deletedAt: null,
|
|
},
|
|
});
|
|
if (!derechohabiente) {
|
|
throw new Error('Worker not found');
|
|
}
|
|
if (derechohabiente.status !== 'qualified' && derechohabiente.status !== 'pre_qualified') {
|
|
throw new Error('Worker must be pre-qualified or qualified for assignment');
|
|
}
|
|
// Create assignment
|
|
const asignacion = this.asignacionRepository.create({
|
|
tenantId: ctx.tenantId,
|
|
createdById: ctx.userId,
|
|
ofertaId: dto.ofertaId,
|
|
derechohabienteId: dto.derechohabienteId,
|
|
assignmentNumber: this.generateAssignmentNumber(),
|
|
assignmentDate: dto.assignmentDate,
|
|
creditAmount: dto.creditAmount,
|
|
subsidyAmount: dto.subsidyAmount || 0,
|
|
downPayment: dto.downPayment || 0,
|
|
notes: dto.notes,
|
|
status: 'pending',
|
|
});
|
|
const saved = await this.asignacionRepository.save(asignacion);
|
|
// Update oferta status
|
|
oferta.status = 'reserved';
|
|
await this.ofertaRepository.save(oferta);
|
|
return saved;
|
|
}
|
|
async approve(ctx, id) {
|
|
const asignacion = await this.findWithDetails(ctx, id);
|
|
if (!asignacion) {
|
|
return null;
|
|
}
|
|
if (asignacion.status !== 'pending') {
|
|
throw new Error('Can only approve pending assignments');
|
|
}
|
|
asignacion.status = 'approved';
|
|
asignacion.approvalDate = new Date();
|
|
asignacion.updatedById = ctx.userId || '';
|
|
// Update oferta status
|
|
asignacion.oferta.status = 'assigned';
|
|
await this.ofertaRepository.save(asignacion.oferta);
|
|
// Update derechohabiente status
|
|
await this.derechohabienteRepository.update({ id: asignacion.derechohabienteId }, { status: 'assigned', updatedById: ctx.userId || '' });
|
|
return this.asignacionRepository.save(asignacion);
|
|
}
|
|
async formalize(ctx, id, dto) {
|
|
const asignacion = await this.findWithDetails(ctx, id);
|
|
if (!asignacion) {
|
|
return null;
|
|
}
|
|
if (asignacion.status !== 'approved') {
|
|
throw new Error('Can only formalize approved assignments');
|
|
}
|
|
asignacion.status = 'formalized';
|
|
asignacion.formalizationDate = dto.formalizationDate;
|
|
asignacion.notaryName = dto.notaryName;
|
|
asignacion.notaryNumber = dto.notaryNumber;
|
|
asignacion.deedNumber = dto.deedNumber;
|
|
asignacion.deedDate = dto.deedDate;
|
|
asignacion.updatedById = ctx.userId || '';
|
|
return this.asignacionRepository.save(asignacion);
|
|
}
|
|
async deliver(ctx, id, deliveryDate) {
|
|
const asignacion = await this.findWithDetails(ctx, id);
|
|
if (!asignacion) {
|
|
return null;
|
|
}
|
|
if (asignacion.status !== 'formalized') {
|
|
throw new Error('Can only deliver formalized assignments');
|
|
}
|
|
asignacion.status = 'delivered';
|
|
asignacion.deliveryDate = deliveryDate;
|
|
asignacion.updatedById = ctx.userId || '';
|
|
// Update oferta status
|
|
asignacion.oferta.status = 'delivered';
|
|
await this.ofertaRepository.save(asignacion.oferta);
|
|
return this.asignacionRepository.save(asignacion);
|
|
}
|
|
async cancel(ctx, id, reason) {
|
|
const asignacion = await this.findWithDetails(ctx, id);
|
|
if (!asignacion) {
|
|
return null;
|
|
}
|
|
if (asignacion.status === 'delivered') {
|
|
throw new Error('Cannot cancel delivered assignments');
|
|
}
|
|
const previousStatus = asignacion.status;
|
|
asignacion.status = 'cancelled';
|
|
asignacion.notes = `${asignacion.notes || ''}\n[CANCELLED] ${reason}`;
|
|
asignacion.updatedById = ctx.userId || '';
|
|
// Revert oferta status if it was assigned/reserved
|
|
if (previousStatus === 'approved' || previousStatus === 'formalized') {
|
|
asignacion.oferta.status = 'available';
|
|
await this.ofertaRepository.save(asignacion.oferta);
|
|
// Revert derechohabiente status
|
|
await this.derechohabienteRepository.update({ id: asignacion.derechohabienteId }, { status: 'qualified', updatedById: ctx.userId || '' });
|
|
}
|
|
else if (previousStatus === 'pending') {
|
|
asignacion.oferta.status = 'available';
|
|
await this.ofertaRepository.save(asignacion.oferta);
|
|
}
|
|
return this.asignacionRepository.save(asignacion);
|
|
}
|
|
}
|
|
exports.AsignacionService = AsignacionService;
|
|
//# sourceMappingURL=asignacion.service.js.map
|