diff --git a/config/default.js b/config/default.js index 3298fb10..cb589290 100644 --- a/config/default.js +++ b/config/default.js @@ -214,5 +214,17 @@ module.exports = { FIX_DELAY_STEP_ASSIGN_MEMBER: parseInt(process.env.PAYMENT_PROCESSING_FIX_DELAY_STEP_ASSIGN_MEMBER || process.env.PAYMENT_PROCESSING_FIX_DELAY_STEP || 500), // the fix delay after step of activate challenge, unit: ms FIX_DELAY_STEP_ACTIVATE_CHALLENGE: parseInt(process.env.PAYMENT_PROCESSING_FIX_DELAY_STEP_ACTIVATE_CHALLENGE || process.env.PAYMENT_PROCESSING_FIX_DELAY_STEP || 500) + }, + // if a job reach this critier, system will automatically withdrawn other job applications. + JOBS_HOUR_PER_WEEK: 20, + // the mapping includes the status transformation when auto-withdrawn feature is performed on job candidates. + WITHDRAWN_STATUS_CHANGE_MAPPING: { + applied: 'withdrawn-prescreen', + 'skills-test': 'withdrawn-prescreen', + 'phone-screen': 'withdrawn-prescreen', + open: 'withdrawn', + interview: 'withdrawn', + selected: 'withdrawn', + offered: 'withdrawn' } } diff --git a/src/eventHandlers/JobCandidateEventHandler.js b/src/eventHandlers/JobCandidateEventHandler.js index 8780ab08..2ad9005b 100644 --- a/src/eventHandlers/JobCandidateEventHandler.js +++ b/src/eventHandlers/JobCandidateEventHandler.js @@ -1,11 +1,14 @@ /* * Handle events for JobCandidate. */ - +const { Op } = require('sequelize') +const _ = require('lodash') +const config = require('config') const models = require('../models') const logger = require('../common/logger') const helper = require('../common/helper') const JobService = require('../services/JobService') +const JobCandidateService = require('../services/JobCandidateService') /** * Once we create at least one JobCandidate for a Job, the Job status should be changed to in-review. @@ -44,6 +47,84 @@ async function inReviewJob (payload) { } } +/** + * Actual Update Job Candidates + * + * @param {*} statuses the source status we'll update + * @param {*} userId the userID + */ +async function updateJobCandidates (statuses, userId) { + logger.info({ + component: 'JobCandidateEventHandler', + context: 'updateJobCandidates', + message: `Update jobCandidates for user ${userId}` + }) + const filter = { [Op.and]: [] } + filter[Op.and].push({ status: statuses }) + filter[Op.and].push({ userId: userId }) + const candidates = await models.JobCandidate.findAll({ + where: filter + }) + if (candidates && candidates.length > 0) { + _.each(candidates, async (candidate) => { + logger.info({ + component: 'JobCandidateEventHandler', + context: 'updateJobCandidates', + message: `Begin update id: ${candidate.id}' candidate with ${candidate.status} status into ${config.WITHDRAWN_STATUS_CHANGE_MAPPING[candidate.status]} for userId: ${userId}` + }) + await JobCandidateService.partiallyUpdateJobCandidate( + helper.getAuditM2Muser(), + candidate.id, + { status: config.WITHDRAWN_STATUS_CHANGE_MAPPING[candidate.status] } + ).then(result => { + logger.info({ + component: 'JobCandidateEventHandler', + context: 'updateJobCandidates', + message: `Finish update id: ${result.id}' candidate into ${result.status} status for userId: ${userId}` + }) + }) + }) + } else { + logger.info({ + component: 'JobCandidateEventHandler', + context: 'updateJobCandidates', + message: `There are not jobCandidates for user ${userId} that required to be updated.` + }) + } +} + +/** + * Update Job Candidates based on business rules + * + * @param {*} payload the updated jobCandidate info + */ +async function withDrawnJobCandidates (payload) { + const jobCandidate = payload.value + if (jobCandidate.status === 'placed') { + const job = await models.Job.findById(payload.value.jobId) + if (job.hoursPerWeek > config.JOBS_HOUR_PER_WEEK) { + // find all these user's open job Candidate and mark the status as withdrawn or withdrawn-prescreen + logger.info({ + component: 'JobCandidateEventHandler', + context: 'withDrawnJobCandidates', + message: `Begin update jobCandidates as ${payload.value.id} candidate's new gig is requiring more than 20 hrs per week` + }) + await updateJobCandidates(['applied', 'skills-test', 'phone-screen', 'open', 'interview', 'selected', 'offered'], payload.value.userId) + logger.info({ + component: 'JobCandidateEventHandler', + context: 'withDrawnJobCandidates', + message: `Finish update jobCandidates as ${payload.value.id} candidate` + }) + } else { + logger.debug({ + component: 'JobCandidateEventHandler', + context: 'withDrawnJobCandidates', + message: `id: ${payload.value.id} candidate is not placing on a gig requiring 20 hrs per week` + }) + } + } +} + /** * Process job candidate create event. * @@ -52,6 +133,9 @@ async function inReviewJob (payload) { */ async function processCreate (payload) { await inReviewJob(payload) + if (payload.value.status === 'placed') { + await withDrawnJobCandidates(payload) + } } /** @@ -62,6 +146,9 @@ async function processCreate (payload) { */ async function processUpdate (payload) { await inReviewJob(payload) + if (payload.value.status === 'placed' && payload.options.oldValue.status !== 'placed') { + await withDrawnJobCandidates(payload) + } } module.exports = { diff --git a/src/services/JobCandidateService.js b/src/services/JobCandidateService.js index b08922e4..8bdd2b60 100644 --- a/src/services/JobCandidateService.js +++ b/src/services/JobCandidateService.js @@ -144,6 +144,7 @@ createJobCandidate.schema = Joi.object().keys({ */ async function updateJobCandidate (currentUser, id, data) { const jobCandidate = await JobCandidate.findById(id) + const oldValue = jobCandidate.toJSON() const userId = await helper.getUserId(currentUser.userId) // check user permission @@ -155,7 +156,7 @@ async function updateJobCandidate (currentUser, id, data) { data.updatedBy = userId const updated = await jobCandidate.update(data) - await helper.postEvent(config.TAAS_JOB_CANDIDATE_UPDATE_TOPIC, updated.toJSON()) + await helper.postEvent(config.TAAS_JOB_CANDIDATE_UPDATE_TOPIC, updated.toJSON(), { oldValue: oldValue }) const result = _.assign(jobCandidate.dataValues, data) return result }