@@ -6,10 +6,11 @@ import config from 'config';
66import models from '../../models' ;
77import util from '../../util' ;
88import { PERMISSION } from '../../permissions/constants' ;
9- import { CONNECT_NOTIFICATION_EVENT , COPILOT_APPLICATION_STATUS , COPILOT_OPPORTUNITY_STATUS , COPILOT_REQUEST_STATUS , EVENT , INVITE_STATUS , PROJECT_MEMBER_ROLE , RESOURCES , TEMPLATE_IDS } from '../../constants' ;
9+ import { CONNECT_NOTIFICATION_EVENT , COPILOT_APPLICATION_STATUS , COPILOT_OPPORTUNITY_STATUS , COPILOT_REQUEST_STATUS , EVENT , INVITE_STATUS , PROJECT_MEMBER_ROLE , RESOURCES , TEMPLATE_IDS , USER_ROLE } from '../../constants' ;
1010import { getCopilotTypeLabel } from '../../utils/copilot' ;
1111import { createEvent } from '../../services/busApi' ;
1212import moment from 'moment' ;
13+ import { Op } from 'sequelize' ;
1314
1415const assignCopilotOpportunityValidations = {
1516 body : Joi . object ( ) . keys ( {
@@ -31,6 +32,34 @@ module.exports = [
3132 return next ( err ) ;
3233 }
3334
35+ const sendEmailToAllApplicants = async ( copilotRequest , allApplications ) => {
36+
37+ const userIds = allApplications . map ( item => item . userId ) ;
38+
39+ const users = await util . getMemberDetailsByUserIds ( userIds , req . log , req . id ) ;
40+
41+ users . forEach ( async ( user ) => {
42+ req . log . debug ( `Sending email notification to copilots who are not accepted` ) ;
43+ const emailEventType = CONNECT_NOTIFICATION_EVENT . EXTERNAL_ACTION_EMAIL ;
44+ const copilotPortalUrl = config . get ( 'copilotPortalUrl' ) ;
45+ const requestData = copilotRequest . data ;
46+ createEvent ( emailEventType , {
47+ data : {
48+ opportunity_details_url : copilotPortalUrl ,
49+ work_manager_url : config . get ( 'workManagerUrl' ) ,
50+ opportunity_title : requestData . opportunityTitle ,
51+ user_name : user ? user . handle : "" ,
52+ } ,
53+ sendgrid_template_id : TEMPLATE_IDS . COPILOT_OPPORTUNITY_COMPLETED ,
54+ recipients : [ user . email ] ,
55+ version : 'v3' ,
56+ } , req . log ) ;
57+
58+ req . log . debug ( `Email sent to copilots who are not accepted` ) ;
59+ } ) ;
60+
61+ } ;
62+
3463 return models . sequelize . transaction ( async ( t ) => {
3564 const opportunity = await models . CopilotOpportunity . findOne ( {
3665 where : { id : copilotOpportunityId } ,
@@ -172,51 +201,82 @@ module.exports = [
172201 return ;
173202 }
174203
175- const existingInvite = await models . ProjectMemberInvite . findAll ( {
176- where : {
177- userId,
178- projectId,
179- role : PROJECT_MEMBER_ROLE . COPILOT ,
180- status : INVITE_STATUS . PENDING ,
181- } ,
182- transaction : t ,
183- } ) ;
184-
185- if ( existingInvite && existingInvite . length ) {
186- const err = new Error ( `User already has an pending invite to the project` ) ;
187- err . status = 400 ;
188- throw err ;
189- }
190-
191- const invite = await models . ProjectMemberInvite . create ( {
192- status : INVITE_STATUS . PENDING ,
193- role : PROJECT_MEMBER_ROLE . COPILOT ,
194- userId,
204+ const member = {
195205 projectId,
196- applicationId : application . id ,
206+ role : USER_ROLE . TC_COPILOT ,
207+ userId,
197208 createdBy : req . authUser . userId ,
198- createdAt : new Date ( ) ,
199209 updatedBy : req . authUser . userId ,
200- updatedAt : new Date ( ) ,
210+ } ;
211+ req . context = req . context || { } ;
212+ req . context . currentProjectMembers = activeMembers ;
213+ await util . addUserToProject ( req , member , t )
214+
215+ await application . update ( {
216+ status : COPILOT_APPLICATION_STATUS . ACCEPTED ,
217+ } , {
218+ transaction : t ,
219+ } ) ;
220+
221+ await opportunity . update ( {
222+ status : COPILOT_OPPORTUNITY_STATUS . COMPLETED ,
201223 } , {
202224 transaction : t ,
203- } )
225+ } ) ;
204226
205- util . sendResourceToKafkaBus (
206- req ,
207- EVENT . ROUTING_KEY . PROJECT_MEMBER_INVITE_CREATED ,
208- RESOURCES . PROJECT_MEMBER_INVITE ,
209- Object . assign ( { } , invite . toJSON ( ) , {
210- source : 'copilot_portal' ,
211- } ) ,
212- ) ;
213227
214- await application . update ( {
215- status : COPILOT_APPLICATION_STATUS . INVITED ,
228+ await copilotRequest . update ( {
229+ status : COPILOT_REQUEST_STATUS . FULFILLED ,
216230 } , {
217231 transaction : t ,
218232 } ) ;
219233
234+ const sendEmailToCopilot = async ( ) => {
235+ const memberDetails = await util . getMemberDetailsByUserIds ( [ application . userId ] , req . log , req . id ) ;
236+ const member = memberDetails [ 0 ] ;
237+ req . log . debug ( `Sending email notification to accepted copilot` ) ;
238+ const emailEventType = CONNECT_NOTIFICATION_EVENT . EXTERNAL_ACTION_EMAIL ;
239+ const copilotPortalUrl = config . get ( 'copilotPortalUrl' ) ;
240+ const requestData = copilotRequest . data ;
241+ createEvent ( emailEventType , {
242+ data : {
243+ opportunity_details_url : `${ copilotPortalUrl } /opportunity/${ opportunity . id } ` ,
244+ opportunity_title : requestData . opportunityTitle ,
245+ start_date : moment . utc ( requestData . startDate ) . format ( 'DD-MM-YYYY' ) ,
246+ user_name : member ? member . handle : "" ,
247+ } ,
248+ sendgrid_template_id : TEMPLATE_IDS . COPILOT_APPLICATION_ACCEPTED ,
249+ recipients : [ member . email ] ,
250+ version : 'v3' ,
251+ } , req . log ) ;
252+
253+ req . log . debug ( `Email sent to copilot` ) ;
254+ } ;
255+
256+ await sendEmailToCopilot ( ) ;
257+
258+ // Cancel other applications
259+ const otherApplications = await models . CopilotApplication . findAll ( {
260+ where : {
261+ opportunityId : copilotOpportunityId ,
262+ id : {
263+ [ Op . notIn ] : [ applicationId ] ,
264+ } ,
265+ } ,
266+ transaction : t ,
267+ } ) ;
268+
269+ // Send email to all applicants about opportunity completion
270+ await sendEmailToAllApplicants ( copilotRequest , otherApplications ) ;
271+
272+ for ( const otherApplication of otherApplications ) {
273+ await otherApplication . update ( {
274+ status : COPILOT_APPLICATION_STATUS . CANCELED ,
275+ } , {
276+ transaction : t ,
277+ } ) ;
278+ }
279+
220280 res . status ( 200 ) . send ( { id : applicationId } ) ;
221281 } ) . catch ( err => next ( err ) ) ;
222282 } ,
0 commit comments