import { BadRequestException, Injectable, NotFoundException, } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { ApplicationStatus, ObjectStatus, ResidentStatus, } from 'src/enums/status.enum'; import { TypeOfRegistration } from 'src/enums/type-of-registration.enums'; import { ResidentService } from 'src/resident/resident.service'; import { takeMongoObject, equate, take } from 'src/utility'; import { ListQueryDto } from './dtos/list-query.dto'; import { RegisterIndustryChangeApplicationDto } from './dtos/register-industry-change-application.dto'; import { IndustryChangeApplication, IndustryChangeApplicationDocument, } from './schemas/IndustryChangeApplication.schema'; const requestedFields = [ 'industry', 'willWorkInPhysicalJurisdiction', 'regulatoryElection', 'regulatoryElectionSub', ]; const fieldsToExpose = [ 'id', 'residentSub', 'current', 'requested', 'status', 'submittedAt', 'decision', 'objectStatus', ]; @Injectable() export class IndustryChangeApplicationService { constructor( @InjectModel(IndustryChangeApplication.name) private applicationModel: Model, private resident: ResidentService, ) {} async getAll(options: ListQueryDto) { const getResident = await this.resident.getResidentBySub( options.residentSub, ); if (!getResident) { throw new NotFoundException('Resident not found'); } return this.applicationModel.find({ residentSub: options.residentSub, status: { $in: options.statuses || Object.values(ApplicationStatus), }, }); } async getById(id: string) { return this.applicationModel.findById(id); } async create(data: RegisterIndustryChangeApplicationDto, token: string) { // This might be possible to turn into a custom validator for class-validator if ( data.willWorkInPhysicalJurisdiction === false && (!!data.industry || !!data.regulatoryElection || !!data.regulatoryElectionSub) ) { throw new BadRequestException( 'industry, regulatoryElection and regulatoryElectionSub are not allowed when willWorkInPhysicalJurisdiction is false', ); } const getResident = await this.resident.getResidentBySub(data.residentSub); if (!getResident) { throw new BadRequestException('Resident not found'); } if (getResident.status !== ResidentStatus.ACTIVE) { throw new BadRequestException('Resident must be active!'); } if ( ![TypeOfRegistration.E_RESIDENCY, TypeOfRegistration.RESIDENCY].includes( getResident.typeOfRegistration, ) ) { throw new BadRequestException( 'Resident must be either an E-resident or a resident', ); } if ( equate(data, getResident, requestedFields).length === requestedFields.length ) { throw new BadRequestException('Cannot request what is already the case.'); } const status = data.willWorkInPhysicalJurisdiction === true ? ApplicationStatus.IN_REVIEW : ApplicationStatus.APPROVED; const newApplication = new this.applicationModel({ residentSub: getResident.sub, current: take(takeMongoObject(getResident), requestedFields), requested: take(data, requestedFields), status, submittedAt: new Date(), createdBy: token ?? 'no token provided for testing', }); return newApplication.save(); } async markDeleted(id: string, token: string) { const findApplication = await this.applicationModel.findById(id); if (!findApplication) { throw new NotFoundException('Application was not found'); } if (findApplication.status !== ApplicationStatus.IN_REVIEW) { throw new BadRequestException( 'Only applications which are currently in review can be deleted.', ); } if (findApplication.objectStatus === ObjectStatus.DELETED) { throw new BadRequestException('The object has already been deleted.'); } findApplication.objectStatus = ObjectStatus.DELETED; findApplication.updatedBy = token || 'no token provided for testing'; return findApplication.save(); } // I wrote this because I could not for the life of me get class-transformer to // play along with mongo documents. I do not have experience with either, so this // was a last ditch effort. makeReadable(input: IndustryChangeApplication | IndustryChangeApplication[]) { return Array.isArray(input) ? input.map((object) => take(takeMongoObject(object), fieldsToExpose)) : take(takeMongoObject(input), fieldsToExpose); } }