2022-12-06 15:47:52 +00:00
|
|
|
import {
|
|
|
|
BadRequestException,
|
|
|
|
Injectable,
|
|
|
|
NotFoundException,
|
|
|
|
} from '@nestjs/common';
|
2022-12-06 12:26:39 +00:00
|
|
|
import { InjectModel } from '@nestjs/mongoose';
|
|
|
|
import { Model } from 'mongoose';
|
2022-12-06 15:47:52 +00:00
|
|
|
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';
|
2022-12-06 12:26:39 +00:00
|
|
|
import {
|
|
|
|
IndustryChangeApplication,
|
|
|
|
IndustryChangeApplicationDocument,
|
|
|
|
} from './schemas/IndustryChangeApplication.schema';
|
|
|
|
|
2022-12-06 15:47:52 +00:00
|
|
|
const requestedFields = [
|
|
|
|
'industry',
|
|
|
|
'willWorkInPhysicalJurisdiction',
|
|
|
|
'regulatoryElection',
|
|
|
|
'regulatoryElectionSub',
|
|
|
|
];
|
|
|
|
|
|
|
|
const fieldsToExpose = [
|
|
|
|
'id',
|
|
|
|
'residentSub',
|
|
|
|
'current',
|
|
|
|
'requested',
|
|
|
|
'status',
|
|
|
|
'submittedAt',
|
|
|
|
'decision',
|
|
|
|
'objectStatus',
|
|
|
|
];
|
|
|
|
|
2022-12-06 12:26:39 +00:00
|
|
|
@Injectable()
|
|
|
|
export class IndustryChangeApplicationService {
|
|
|
|
constructor(
|
|
|
|
@InjectModel(IndustryChangeApplication.name)
|
|
|
|
private applicationModel: Model<IndustryChangeApplicationDocument>,
|
2022-12-06 15:47:52 +00:00
|
|
|
private resident: ResidentService,
|
2022-12-06 12:26:39 +00:00
|
|
|
) {}
|
|
|
|
|
2022-12-06 15:47:52 +00:00
|
|
|
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),
|
|
|
|
},
|
2022-12-07 06:54:34 +00:00
|
|
|
objectStatus: ObjectStatus.CURRENT,
|
2022-12-06 15:47:52 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async getById(id: string) {
|
2022-12-07 06:54:34 +00:00
|
|
|
const find = await this.applicationModel.findById(id);
|
|
|
|
if (!find) {
|
|
|
|
throw new NotFoundException('The application was not found');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (find.objectStatus !== ObjectStatus.CURRENT) {
|
|
|
|
throw new BadRequestException('This application has been deleted.');
|
|
|
|
}
|
|
|
|
|
|
|
|
return find;
|
2022-12-06 15:47:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-12-07 06:54:34 +00:00
|
|
|
decision:
|
|
|
|
status === ApplicationStatus.APPROVED
|
|
|
|
? {
|
|
|
|
decidedAt: new Date(),
|
|
|
|
decidedBy: 'Automatic',
|
|
|
|
rejectionReason: null,
|
|
|
|
}
|
|
|
|
: undefined,
|
2022-12-06 15:47:52 +00:00
|
|
|
submittedAt: new Date(),
|
|
|
|
createdBy: token ?? 'no token provided for testing',
|
|
|
|
});
|
|
|
|
|
2022-12-06 16:29:27 +00:00
|
|
|
if (status === ApplicationStatus.APPROVED) {
|
|
|
|
getResident.willWorkInPhysicalJurisdiction =
|
|
|
|
data.willWorkInPhysicalJurisdiction;
|
|
|
|
getResident.industry = data.industry ?? null;
|
|
|
|
getResident.regulatoryElection = data.regulatoryElection ?? null;
|
|
|
|
getResident.regulatoryElectionSub = data.regulatoryElectionSub ?? null;
|
|
|
|
await this.resident.save(getResident);
|
|
|
|
}
|
|
|
|
|
2022-12-06 15:47:52 +00:00
|
|
|
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);
|
2022-12-06 12:26:39 +00:00
|
|
|
}
|
|
|
|
}
|