UNPKG

@universis/candidates

Version:

Universis api server plugin for study program candidates, internship selection etc

220 lines (216 loc) 7.57 kB
import { DataError, DataNotFoundError } from '@themost/common'; import { DataObjectState, executeInUnattendedModeAsync } from '@themost/data'; import { Guid } from '@themost/common'; /** * @param {DataEventArgs} event */ async function beforeSaveAsync(event) { // operate only on insert if (event.state !== DataObjectState.Insert) { return; } if (!Object.prototype.hasOwnProperty.call(event.target, 'candidate')) { return; } const context = event.model.context; const candidate = event.target.candidate; // if candidate is set on insert, exit if ( typeof candidate === 'number' || typeof candidate === 'string' || (typeof candidate === 'object' && Object.prototype.hasOwnProperty.call(candidate, 'id')) ) { return; } // validate isAccessibleForFree property of event if (event.target.studyProgramEnrollmentEvent == null) { throw new DataError( 'E_ENROLLMENT_EVENT', 'The study program enrollment event cannot be empty at this context.' ); } const enrollmentEvent = await context .model('StudyProgramEnrollmentEvent') .where('id') .equal( event.target.studyProgramEnrollmentEvent.id || event.target.studyProgramEnrollmentEvent ) .select('id', 'isAccessibleForFree', 'studyProgram', 'inscriptionMode', 'inscriptionYear', 'inscriptionPeriod') .flatten() .silent() .getItem(); if (enrollmentEvent == null) { throw new DataNotFoundError( 'The specified enrollment event cannot be found' ); } if (!enrollmentEvent.isAccessibleForFree) { throw new DataError( 'E_ACCESS', 'The study program does not accept self-registration.' ); } if (enrollmentEvent.inscriptionMode == null) { throw new DataError( 'E_DEFAULT_INSCR_MODE', 'The default inscription mode is not set for the event.' ); } // force set candidate study program from enrollment event if (candidate.studyProgram == null) { candidate.studyProgram = enrollmentEvent.studyProgram; } // find study program and always validate it if (!candidate.studyProgram) { throw new DataError( 'E_STUDY_PROGRAM', 'The candidate study program cannot be empty at this context' ); } const studyProgram = await context .model('StudyProgram') .where('id') .equal(candidate.studyProgram.id || candidate.studyProgram) .and('isActive') .equal(true) .expand('specialties') .getItem(); if (studyProgram == null) { throw new DataNotFoundError( 'The (active) study program cannot be found or is inaccessible.' ); } // validate that study program // indeed belongs to the event if (enrollmentEvent.studyProgram !== studyProgram.id) { throw new DataError( 'E_STUDY_PROGRAM', 'The provided study program is invalid.' ); } // if no specialization is set if (!candidate.specialtyId || !candidate.studyProgramSpecialty) { const specialtyId = candidate.specialtyId != null ? candidate.specialtyId : -1; // find study program specialty const studyProgramSpecialty = studyProgram.specialties.find( (specialization) => specialization.specialty === specialtyId ); if (studyProgramSpecialty == null) { throw new DataNotFoundError( 'The study program specialty cannot be found or is inaccessible.' ); } // assign data to candidate candidate.studyProgramSpecialty = studyProgramSpecialty; candidate.specialtyId = specialtyId; // also, append specialization to study program register action event.target.specialization = studyProgramSpecialty; } else { // if provided, always validate that study program and specialization match const trueSpecialty = studyProgram.specialties.find( (specialization) => specialization.specialty == candidate.specialtyId ); const studyProgramSpecialty = candidate.studyProgramSpecialty.id || candidate.studyProgramSpecialty; if (studyProgramSpecialty != trueSpecialty.id) { throw new DataError('E_SPECIALTY', 'The provided specialty is invalid.'); } } // check if the current user is a service agent and belongs to CandidateServices group const User = context.model('User').getDataObjectType(); /** * @type {import('@themost/data').DataQueryable} */ const q = await User.getMe(context); const user = await q.expand('groups').getItem(); const isCandidateService = user.groups.find((group) => group.alternateName === 'CandidateServices') != null; let alreadyEnrolled = 0; if (isCandidateService === false) { // check if candidate has already enrolled in this study program and specialty alreadyEnrolled = await context .model('StudyProgramRegisterAction') .where('studyProgram') .equal(enrollmentEvent.studyProgram) .and('actionStatus/alternateName') .notEqual('CancelledActionStatus') .and('owner') .equal(event.target.owner) .and('specialization') .equal(candidate.studyProgramSpecialty.id || candidate.studyProgramSpecialty) .select('id') .silent() .count(); } else { // try to find candidate by user name alreadyEnrolled = await context .model('StudyProgramRegisterAction') .where('studyProgram').equal(enrollmentEvent.studyProgram) .and('actionStatus/alternateName').notEqual('CancelledActionStatus') .and('candidate/user/name').equal(event.target.candidate?.user?.name) .and('candidate/user/name').notEqual(null) .and('specialization').equal(candidate.studyProgramSpecialty.id || candidate.studyProgramSpecialty) .select('id').silent().count(); } if (alreadyEnrolled) { throw new DataError( 'E_ALREADY_ENROLLED', 'The user has already enrolled (or has a pending enrollment request) in the specified study program and specialty.' ); } if (isCandidateService) { // if the candidate user given by the candidate service does not exist let candidateUser = await context.model('User').where('name').equal(event.target.candidate?.user?.name).silent().getItem(); if (candidateUser == null) { const group = await context.model('Group').where('alternateName').equal('Candidates').silent().getItem(); candidateUser = { name: event.target.candidate?.user?.name, description: `${event.target.candidate?.person?.familyName} ${event.target.candidate?.person?.givenName}`, groups: [ group ] }; await executeInUnattendedModeAsync(context, async () => { // create a new user as candidate await context.model('UserReference').insert(candidateUser); }); } Object.assign(candidate, { user: candidateUser }); } // assign extra attributes Object.assign(candidate, { // note: active studentStatus is forced when converting candidate to student studentStatus: { alternateName: 'candidate', }, // first semester and inscriptionSemester semester: 1, inscriptionSemester: 1, studyProgram: studyProgram.id, // force set the study program of the enrollment event department: studyProgram.department, // force set the department of the enrollment event inscriptionYear: enrollmentEvent.inscriptionYear, // force set inscription year inscriptionPeriod: enrollmentEvent.inscriptionPeriod, // force set inscription period // a guid for inscriptionNumber inscriptionNumber: Guid.newGuid().toString(), // and the event's default inscriptionMode inscriptionMode: enrollmentEvent.inscriptionMode, }); // and emulate nested attribute by saving candidate await context.model('CandidateStudent').save(candidate); } /** * @param {DataEventArgs} event * @param {Function} callback */ export function beforeSave(event, callback) { return beforeSaveAsync(event) .then(() => { return callback(); }) .catch((err) => { return callback(err); }); }