Skip to content

Commit

Permalink
Merge pull request #6420 from NMDSdevopsServiceAdm/featureBranch/qual…
Browse files Browse the repository at this point in the history
…sCertificate

Feature branch/quals certificate
  • Loading branch information
duncanc19 authored Nov 22, 2024
2 parents 2ccc66f + 7916e3a commit 1347b67
Show file tree
Hide file tree
Showing 68 changed files with 5,257 additions and 1,455 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
return queryInterface.createTable(
'QualificationCertificates',
{
ID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
UID: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.literal('uuid_generate_v4()'),
allowNull: false,
unique: true,
},
WorkerQualificationsFK: {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
references: {
model: {
tableName: 'WorkerQualifications',
schema: 'cqc',
},
key: 'ID',
},
},
WorkerFK: {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
references: {
model: {
tableName: 'Worker',
schema: 'cqc',
},
key: 'ID',
},
},
FileName: {
type: Sequelize.DataTypes.TEXT,
allowNull: false,
},
UploadDate: {
type: Sequelize.DataTypes.DATE,
allowNull: true,
},
Key: {
type: Sequelize.DataTypes.TEXT,
allowNull: false,
},
},
{ schema: 'cqc' },
);
},

async down(queryInterface) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
return queryInterface.dropTable({
tableName: 'QualificationCertificates',
schema: 'cqc',
});
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const { compact } = require('lodash');

const models = require('../../index');
const WorkerCertificateService = require('../../../routes/establishments/workerCertificate/workerCertificateService');

class BulkUploadQualificationHelper {
constructor({ workerId, workerUid, establishmentId, savedBy, externalTransaction }) {
this.workerId = workerId;
this.workerUid = workerUid;
this.establishmentId = establishmentId;
this.savedBy = savedBy;
this.bulkUploaded = true;
this.externalTransaction = externalTransaction;
this.qualificationCertificateService = WorkerCertificateService.initialiseQualifications();
}

async processQualificationsEntities(qualificationsEntities) {
const promisesToReturn = [];

const allQualificationRecords = await models.workerQualifications.findAll({
where: {
workerFk: this.workerId,
},
});

for (const bulkUploadEntity of qualificationsEntities) {
const currentQualificationId = bulkUploadEntity?._qualification?.id;
const existingQualification = allQualificationRecords.find(
(record) => record.qualificationFk === currentQualificationId,
);

if (existingQualification) {
promisesToReturn.push(this.updateQualification(existingQualification, bulkUploadEntity));
} else {
promisesToReturn.push(this.createNewQualification(bulkUploadEntity));
}
}

const bulkUploadQualificationFks = compact(qualificationsEntities.map((entity) => entity?._qualification?.id));
const qualificationsToDelete = allQualificationRecords.filter(
(qualification) => !bulkUploadQualificationFks.includes(qualification.qualificationFk),
);
for (const qualification of qualificationsToDelete) {
promisesToReturn.push(this.deleteQualification(qualification));
}

return promisesToReturn;
}

createNewQualification(entityFromBulkUpload) {
entityFromBulkUpload.workerId = this.workerId;
entityFromBulkUpload.workerUid = this.workerUid;
entityFromBulkUpload.establishmentId = this.establishmentId;

return entityFromBulkUpload.save(this.savedBy, this.bulkUploaded, 0, this.externalTransaction);
}

updateQualification(existingRecord, entityFromBulkUpload) {
const fieldsToUpdate = {
source: 'Bulk',
updatedBy: this.savedBy.toLowerCase(),
notes: entityFromBulkUpload.notes ?? existingRecord.notes,
year: entityFromBulkUpload.year ?? existingRecord.year,
};

existingRecord.set(fieldsToUpdate);

return existingRecord.save({ transaction: this.externalTransaction });
}

async deleteQualification(existingRecord) {
const certificatesFound = await existingRecord.getQualificationCertificates();
if (certificatesFound?.length) {
await this.qualificationCertificateService.deleteCertificatesWithTransaction(
certificatesFound,
this.externalTransaction,
);
}

return existingRecord.destroy({ transaction: this.externalTransaction });
}
}

module.exports = BulkUploadQualificationHelper;
47 changes: 45 additions & 2 deletions backend/server/models/classes/qualification.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Qualification extends EntityValidator {
this._qualification = null;
this._year = null;
this._notes = null;
this._qualificationCertificates = null;

// lifecycle properties
this._isNew = false;
Expand Down Expand Up @@ -105,7 +106,7 @@ class Qualification extends EntityValidator {
}

get workerId() {
return this._workerUid;
return this._workerId;
}
get workerUid() {
return this._workerUid;
Expand Down Expand Up @@ -139,6 +140,11 @@ class Qualification extends EntityValidator {
get created() {
return this._created;
}

get qualificationCertificates() {
return this._qualificationCertificates;
}

get updated() {
return this._updated;
}
Expand All @@ -156,9 +162,17 @@ class Qualification extends EntityValidator {
if (this._notes === null) return null;
return unescape(this._notes);
}

get qualificationCertificates() {
return this._qualificationCertificates;
}

set qualification(qualification) {
this._qualification = qualification;
}
set qualificationCertificates(qualificationCertificates) {
this._qualificationCertificates = qualificationCertificates;
}
set year(year) {
this._year = year;
}
Expand All @@ -171,6 +185,10 @@ class Qualification extends EntityValidator {
}
}

set qualificationCertificates(qualificationCertificates) {
this._qualificationCertificates = qualificationCertificates;
}

// used by save to initialise a new Qualification Record; returns true if having initialised this Qualification Record
_initialise() {
if (this._uid === null) {
Expand Down Expand Up @@ -542,6 +560,15 @@ class Qualification extends EntityValidator {
model: models.workerAvailableQualifications,
as: 'qualification',
},
{
model: models.qualificationCertificates,
as: 'qualificationCertificates',
attributes: ['uid', 'filename', 'uploadDate'],
order: [
[models.qualificationCertificates, 'uploadDate', 'DESC'],
[models.qualificationCertificates, 'filename', 'ASC'],
],
},
],
};

Expand All @@ -560,10 +587,12 @@ class Qualification extends EntityValidator {
};
this._year = fetchResults.year;
this._notes = fetchResults.notes !== null && fetchResults.notes.length === 0 ? null : fetchResults.notes;
this._qualificationCertificates = fetchResults.qualificationCertificates;

this._created = fetchResults.created;
this._updated = fetchResults.updated;
this._updatedBy = fetchResults.updatedBy;
this._qualificationCertificates = fetchResults.qualificationCertificates;

return true;
}
Expand All @@ -573,7 +602,7 @@ class Qualification extends EntityValidator {
// typically errors when making changes to model or database schema!
this._log(Qualification.LOG_ERROR, err);

throw new Error(`Failed to load Qualification record with uid (${this.uid})`);
throw new Error(`Failed to load Qualification record with uid (${uid})`);
}
}

Expand Down Expand Up @@ -618,6 +647,7 @@ class Qualification extends EntityValidator {
this._qualification = null;
this._year = null;
this._notes = null;
this._qualificationCertificates = null;

this._created = null;
this._updated = null;
Expand Down Expand Up @@ -655,6 +685,11 @@ class Qualification extends EntityValidator {
as: 'qualification',
attributes: ['id', 'group', 'title', 'level'],
},
{
model: models.qualificationCertificates,
as: 'qualificationCertificates',
attributes: ['uid', 'filename', 'uploadDate'],
},
],
order: [
//['completed', 'DESC'],
Expand All @@ -674,6 +709,13 @@ class Qualification extends EntityValidator {
},
year: thisRecord.year != null ? thisRecord.year : undefined,
notes: thisRecord.notes !== null && thisRecord.notes.length > 0 ? unescape(thisRecord.notes) : undefined,
qualificationCertificates: thisRecord.qualificationCertificates?.map((certificate) => {
return {
uid: certificate.uid,
filename: certificate.filename,
uploadDate: certificate.uploadDate?.toISOString(),
};
}),
created: thisRecord.created.toISOString(),
updated: thisRecord.updated.toISOString(),
updatedBy: thisRecord.updatedBy,
Expand Down Expand Up @@ -712,6 +754,7 @@ class Qualification extends EntityValidator {
qualification: this.qualification,
year: this.year !== null ? this.year : undefined,
notes: this._notes !== null ? this.notes : undefined,
qualificationCertificates: this.qualificationCertificates ?? [],
};

return myDefaultJSON;
Expand Down
54 changes: 23 additions & 31 deletions backend/server/models/classes/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ const JSON_DOCUMENT_TYPE = require('./worker/workerProperties').JSON_DOCUMENT;
const SEQUELIZE_DOCUMENT_TYPE = require('./worker/workerProperties').SEQUELIZE_DOCUMENT;

const TrainingCertificateRoute = require('../../routes/establishments/workerCertificate/trainingCertificate');
const WorkerCertificateService = require('../../routes/establishments/workerCertificate/workerCertificateService');

// WDF Calculator
const WdfCalculator = require('./wdfCalculator').WdfCalculator;

const BulkUploadQualificationHelper = require('./helpers/bulkUploadQualificationHelper');

const STOP_VALIDATING_ON = ['UNCHECKED', 'DELETE', 'DELETED', 'NOCHANGE'];

class Worker extends EntityValidator {
Expand Down Expand Up @@ -473,7 +476,6 @@ class Worker extends EntityValidator {
// and qualifications records
this._qualificationsEntities = [];
if (document.qualifications && Array.isArray(document.qualifications)) {
// console.log("WA DEBUG - document.qualifications: ", document.qualifications)
document.qualifications.forEach((thisQualificationRecord) => {
const newQualificationRecord = new Qualification(null, null);

Expand Down Expand Up @@ -528,7 +530,7 @@ class Worker extends EntityValidator {
}

async saveAssociatedEntities(savedBy, bulkUploaded = false, externalTransaction) {
const newQualificationsPromises = [];
const qualificationChangePromises = [];
const newTrainingPromises = [];

try {
Expand All @@ -551,29 +553,22 @@ class Worker extends EntityValidator {
});
}

// there is no change audit on qualifications; simply delete all that is there and recreate
if (this._qualificationsEntities && this._qualificationsEntities.length > 0) {
// delete all existing training records for this worker
await models.workerQualifications.destroy({
where: {
workerFk: this._id,
},
transaction: externalTransaction,
});

// now create new training records
this._qualificationsEntities.forEach((currentQualificationRecord) => {
currentQualificationRecord.workerId = this._id;
currentQualificationRecord.workerUid = this._uid;
currentQualificationRecord.establishmentId = this._establishmentId;
newQualificationsPromises.push(
currentQualificationRecord.save(savedBy, bulkUploaded, 0, externalTransaction),
);
if (bulkUploaded && ['NEW', 'UPDATE', 'CHGSUB'].includes(this.status)) {
const qualificationHelper = new BulkUploadQualificationHelper({
workerId: this._id,
workerUid: this._uid,
establishmentId: this._establishmentId,
savedBy,
bulkUploaded,
externalTransaction,
});
const qualificationEntities = this._qualificationsEntities ? this._qualificationsEntities : [];
const promisesToPush = await qualificationHelper.processQualificationsEntities(qualificationEntities);
qualificationChangePromises.push(...promisesToPush);
}

await Promise.all(newTrainingPromises);
await Promise.all(newQualificationsPromises);
await Promise.all(qualificationChangePromises);
} catch (err) {
console.error('Worker::saveAssociatedEntities error: ', err);
// rethrow error to ensure the transaction is rolled back
Expand Down Expand Up @@ -1153,6 +1148,7 @@ class Worker extends EntityValidator {
}

await this.deleteAllTrainingCertificatesAssociatedWithWorker(thisTransaction);
await this.deleteAllQualificationCertificatesAssociatedWithWorker(thisTransaction);

// always recalculate WDF - if not bulk upload (this._status)
if (this._status === null) {
Expand Down Expand Up @@ -1900,17 +1896,13 @@ class Worker extends EntityValidator {
}

async deleteAllTrainingCertificatesAssociatedWithWorker(transaction) {
const trainingCertificates = await models.trainingCertificates.getAllTrainingCertificateRecordsForWorker(this._id);

if (!trainingCertificates.length) return;

const trainingCertificateUids = trainingCertificates.map((cert) => cert.uid);
const filesToDeleteFromS3 = trainingCertificates.map((cert) => {
return { Key: cert.key };
});
const workerTrainingCertificateService = WorkerCertificateService.initialiseTraining();
await workerTrainingCertificateService.deleteAllCertificates(this._id, transaction);
}

await models.trainingCertificates.deleteCertificate(trainingCertificateUids, transaction);
await TrainingCertificateRoute.deleteCertificatesFromS3(filesToDeleteFromS3);
async deleteAllQualificationCertificatesAssociatedWithWorker(transaction) {
const workerQualificationCertificateService = WorkerCertificateService.initialiseQualifications();
await workerQualificationCertificateService.deleteAllCertificates(this._id, transaction);
}
}

Expand Down
Loading

0 comments on commit 1347b67

Please sign in to comment.