@tiledesk/tiledesk-server 2.13.49 → 2.13.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -243,270 +243,152 @@ class RequestService {
243
243
 
244
244
  // TODO changePreflightByRequestId se un agente entra in request freflight true disabilitare add agente e reassing ma mettere un bottone removePreflight???
245
245
  // usalo no_populate
246
- route(request_id, departmentid, id_project, nobot, no_populate) {
247
- var that = this;
246
+ async route(request_id, departmentid, id_project, nobot, no_populate) {
248
247
 
249
- return new Promise(function (resolve, reject) {
248
+ try {
250
249
  winston.debug("request_id:" + request_id);
251
250
  winston.debug("departmentid:" + departmentid);
252
251
  winston.debug("id_project:" + id_project);
253
252
  winston.debug("nobot:" + nobot);
253
+ winston.info("main_flow_cache_3 route");
254
254
 
255
- let q = Request
256
- .findOne({ request_id: request_id, id_project: id_project });
257
-
258
- // if (cacheEnabler.request) { //(node:60837) UnhandledPromiseRejectionWarning: VersionError: No matching document found for id "633efe246a6cc0eda5732684" version 0 modifiedPaths "status, participants, participantsAgents, department, assigned_at, snapshot, snapshot.department, snapshot.department.updatedAt, snapshot.agents"
259
- // q.cache(cacheUtil.defaultTTL, id_project+":requests:request_id:"+request_id+":simple") //request_cache
260
- // winston.debug('request cache enabled');
261
- // }
262
- return q.exec(function (err, request) {
263
-
264
- if (err) {
265
- winston.error(err);
266
- return reject(err);
267
- }
268
-
269
- winston.debug('request return', request);
270
-
271
- // cambia var in let
272
-
273
- //it is important to clone here
274
- var requestBeforeRoute = Object.assign({}, request.toObject());
275
- winston.debug("requestBeforeRoute", requestBeforeRoute);
276
-
277
- var beforeParticipants = requestBeforeRoute.participants;
278
- winston.debug("beforeParticipants: ", beforeParticipants);
279
-
280
- return that.routeInternal(request, departmentid, id_project, nobot).then( async function (routedRequest) {
281
-
282
- winston.debug("after routeInternal", routedRequest);
283
- winston.debug("requestBeforeRoute.status:" + requestBeforeRoute.status);
284
- winston.debug("routedRequest.status:" + routedRequest.status);
285
-
286
- let beforeDepartmentId;
287
- if (requestBeforeRoute.department) { //requestBeforeRoute.department can be empty for internal ticket
288
- beforeDepartmentId = requestBeforeRoute.department.toString();
289
- winston.debug("beforeDepartmentId:" + beforeDepartmentId);
290
- }
291
-
292
-
293
- let afterDepartmentId;
294
- if (routedRequest.department) {
295
- afterDepartmentId = routedRequest.department.toString();
296
- winston.debug("afterDepartmentId:" + afterDepartmentId);
297
- }
298
-
299
- winston.debug("requestBefore status: ", requestBeforeRoute.status)
300
- winston.debug("routedRequest status: ", routedRequest.status)
301
- /**
302
- * Case 1
303
- * After internal routing:
304
- * - same STATUS
305
- * - same DEPARTMENT
306
- * - same PARTICIPANTS
307
- */
308
- if (requestBeforeRoute.status === routedRequest.status &&
309
- beforeDepartmentId === afterDepartmentId &&
310
- requestUtil.arraysEqual(beforeParticipants, routedRequest.participants)) {
311
-
312
- winston.verbose("Request " + request.request_id + " contains already the same participants at the same request status. Routed to the same participants");
313
-
314
- if (routedRequest.attributes && routedRequest.attributes.fully_abandoned && routedRequest.attributes.fully_abandoned === true) {
315
- request.status = RequestConstants.ABANDONED;
316
- request.attributes.fully_abandoned = true;
317
- request.markModified('status');
318
- request.markModified('attributes');
319
- request.save((err, savedRequest) => {
320
- if (err) {
321
- winston.error("Error updating request with status ABANDONED ", err);
322
- } else {
323
- winston.verbose("Status modified in ABANDONED for request: " + savedRequest._id);
324
- }
325
- })
326
- }
327
-
328
- /**
329
- * TODO: Restore proper functioning
330
- * Option commented on 03/12/2025 by Johnny in order to allows clients to receive the request populated
331
- * in such case of Abandoned Request (Status 150)
332
- */
333
- // if (no_populate === "true" || no_populate === true) {
334
- // winston.debug("no_populate is true");
335
- // requestEvent.emit('request.update', request);
336
- // return resolve(request);
337
- // }
338
-
339
- return request
340
- .populate('lead')
341
- .populate('department')
342
- .populate('participatingBots')
343
- .populate('participatingAgents')
344
- .populate({ path: 'requester', populate: { path: 'id_user' } })
345
- .execPopulate(function (err, requestComplete) {
346
- winston.debug("requestComplete", requestComplete);
347
-
348
- var oldParticipants = beforeParticipants;
349
- winston.debug("oldParticipants ", oldParticipants);
350
-
351
- let newParticipants = requestComplete.participants;
352
- winston.debug("newParticipants ", newParticipants);
353
-
354
- var removedParticipants = oldParticipants.filter(d => !newParticipants.includes(d));
355
- winston.debug("removedParticipants ", removedParticipants);
356
-
357
- var addedParticipants = newParticipants.filter(d => !oldParticipants.includes(d));
358
- winston.debug("addedParticipants ", addedParticipants);
359
-
360
- requestEvent.emit('request.update', requestComplete);
361
- requestEvent.emit("request.update.comment", { comment: "REROUTE", request: requestComplete });//Deprecated
362
- requestEvent.emit("request.updated", { comment: "REROUTE", request: requestComplete, patch: { removedParticipants: removedParticipants, addedParticipants: addedParticipants } });
363
-
364
- requestEvent.emit('request.participants.update', {
365
- beforeRequest: request,
366
- removedParticipants: removedParticipants,
367
- addedParticipants: addedParticipants,
368
- request: requestComplete
369
- });
370
-
371
- return resolve(requestComplete);
372
- });
373
- }
374
-
375
- let project = await projectService.getCachedProject(id_project).catch((err) => {
376
- winston.warn("Error getting cached project. Skip conversation quota check.")
377
- winston.warn("Getting cached project error: ", err)
378
- })
379
-
380
-
381
- let isTestConversation = false;
382
- let isVoiceConversation = false;
383
- let isStandardConversation = false;
384
-
385
- let payload = {
386
- project: project,
387
- request: request
388
- }
389
-
390
- if (request.attributes && request.attributes.sourcePage && (request.attributes.sourcePage.indexOf("td_draft=true") > -1)) {
391
- winston.verbose("is a test conversation --> skip quote availability check")
392
- isTestConversation = true;
393
- }
394
- else if (request.channel && (request.channel.name === 'voice-vxml')) {
395
- winston.verbose("is a voice conversation --> skip quote availability check")
396
- isVoiceConversation = true;
397
- }
398
- else {
399
- isStandardConversation = true;
400
- let available = await qm.checkQuote(project, request, 'requests');
401
- if (available === false) {
402
- winston.info("Requests limits reached for project " + project._id)
403
- return reject("Requests limits reached for project " + project._id);
404
- }
405
- }
255
+ // Find request
256
+ let query = Request.findOne({ request_id, id_project });
257
+ if (cacheEnabler.request) {
258
+ query = query.cache(cacheUtil.defaultTTL, id_project + ":requests:request_id:" + request_id + ":simple");
259
+ winston.debug('request cache enabled');
260
+ }
261
+
262
+ const request = await query.exec();
406
263
 
407
- /**
408
- * Case 2 - Leaving TEMP status
409
- * After internal routing:
410
- * - STATUS changed from 50 to 100 or 200
411
- */
412
- if (requestBeforeRoute.status === RequestConstants.TEMP && (routedRequest.status === RequestConstants.ASSIGNED || routedRequest.status === RequestConstants.UNASSIGNED)) {
413
- // console.log("Case 2 - Leaving TEMP status")
414
- if (isStandardConversation) {
415
- requestEvent.emit('request.create.quote', payload);
416
- }
417
- }
264
+ if (!request) {
265
+ throw new Error(`Request not found: ${request_id}`);
266
+ }
418
267
 
419
- /**
420
- * Case 3 - Conversation opened through proactive message
421
- * After internal routing:
422
- * - STATUS changed from undefined to 100
423
- */
424
- if ((!requestBeforeRoute.status || requestBeforeRoute.status === undefined) && routedRequest.status === RequestConstants.ASSIGNED) {
425
- // console.log("Case 3 - 'Proactive' request")
426
- if (isStandardConversation) {
427
- requestEvent.emit('request.create.quote', payload);
428
- }
268
+ // Clone before route
269
+ const requestBeforeRoute = request.toObject();
270
+ const beforeParticipants = requestBeforeRoute.participants;
271
+ const beforeDepartmentId = requestBeforeRoute.department?.toString();
272
+
273
+ // Route internal
274
+ const routedRequest = await this.routeInternal(request, departmentid, id_project, nobot);
275
+
276
+ const afterDepartmentId = routedRequest.department?.toString();
277
+
278
+ // Case 1 - No changes
279
+ if (
280
+ requestBeforeRoute.status === routedRequest.status &&
281
+ beforeDepartmentId === afterDepartmentId &&
282
+ requestUtil.arraysEqual(beforeParticipants, routedRequest.participants)
283
+ ) {
284
+
285
+ winston.verbose(`Request ${request.request_id} routed to same status/participants`);
286
+
287
+ if (routedRequest.attributes?.fully_abandoned === true) {
288
+ request.status = RequestConstants.ABANDONED;
289
+ request.attributes.fully_abandoned = true;
290
+ request.markModified("status");
291
+ request.markModified("attributes");
292
+
293
+ try {
294
+ await request.save();
295
+ winston.verbose(`Status set to ABANDONED for request ${request._id}`);
296
+ } catch (err) {
297
+ winston.error("Error updating request to ABANDONED", err);
429
298
  }
299
+ }
430
300
 
431
- //cacheinvalidation
432
- return routedRequest.save(function (err, savedRequest) {
433
- // https://stackoverflow.com/questions/54792749/mongoose-versionerror-no-matching-document-found-for-id-when-document-is-being
434
- //return routedRequest.update(function(err, savedRequest) {
435
- if (err) {
436
- winston.error('Error saving the request. ', { err: err, routedRequest: routedRequest });
437
- return reject(err);
438
- }
439
-
440
- winston.debug("after save savedRequest", savedRequest);
441
-
442
- return savedRequest
443
- .populate('lead')
444
- .populate('department')
445
- .populate('participatingBots')
446
- .populate('participatingAgents')
447
- .populate({ path: 'requester', populate: { path: 'id_user' } })
448
- .execPopulate(function (err, requestComplete) {
449
-
450
- // return Request //to populate correctly i must re-exec the query
451
- // .findById(savedRequest.id)
452
- // .populate('lead')
453
- // .populate('department')
454
- // .populate('participatingBots')
455
- // .populate('participatingAgents')
456
- // .populate({path:'requester',populate:{path:'id_user'}})
457
- // .exec( function(err, requestComplete) {
458
-
459
-
460
- if (err) {
461
- winston.error('Error populating the request.', err);
462
- return reject(err);
463
- }
464
-
465
- winston.verbose("Request routed", requestComplete.toObject());
466
-
467
-
468
- var oldParticipants = beforeParticipants;
469
- winston.debug("oldParticipants ", oldParticipants);
470
-
471
- let newParticipants = requestComplete.participants;
472
- winston.debug("newParticipants ", newParticipants);
473
-
474
- var removedParticipants = oldParticipants.filter(d => !newParticipants.includes(d));
475
- winston.debug("removedParticipants ", removedParticipants);
476
-
477
- var addedParticipants = newParticipants.filter(d => !oldParticipants.includes(d));
478
- winston.debug("addedParticipants ", addedParticipants);
301
+ /**
302
+ * TODO: Restore proper functioning
303
+ * Option commented on 03/12/2025 by Johnny in order to allows clients to receive the request populated
304
+ * in such case of Abandoned Request (Status 150)
305
+ */
306
+ // if (no_populate === "true" || no_populate === true) {
307
+ // winston.debug("no_populate is true");
308
+ // requestEvent.emit('request.update', request);
309
+ // return request;
310
+ // }
311
+
312
+ const requestComplete = await request
313
+ .populate("lead")
314
+ .populate("department")
315
+ .populate("participatingBots")
316
+ .populate("participatingAgents")
317
+ .populate({ path: "requester", populate: { path: "id_user" } })
318
+ .execPopulate();
319
+
320
+ this.emitParticipantsEvents(
321
+ request,
322
+ requestComplete,
323
+ beforeParticipants
324
+ );
325
+
326
+ return requestComplete;
327
+ }
479
328
 
329
+
330
+ // Quota check
331
+ let project;
332
+ try {
333
+ project = await projectService.getCachedProject(id_project);
334
+ } catch (err) {
335
+ winston.warn("Error getting cached project, skip quota check", err);
336
+ }
480
337
 
481
- requestEvent.emit('request.update', requestComplete);
482
- requestEvent.emit("request.update.comment", { comment: "REROUTE", request: requestComplete });//Deprecated
483
- requestEvent.emit("request.updated", { comment: "REROUTE", request: requestComplete, patch: { removedParticipants: removedParticipants, addedParticipants: addedParticipants } });
338
+ const isTestConversation = request.attributes?.sourcePage?.includes("td_draft=true");
339
+ const isVoiceConversation = request.channel?.name === "voice-vxml";
340
+ const isStandardConversation = !isTestConversation && !isVoiceConversation;
484
341
 
485
- requestEvent.emit('request.participants.update', {
486
- beforeRequest: request,
487
- removedParticipants: removedParticipants,
488
- addedParticipants: addedParticipants,
489
- request: requestComplete
490
- });
342
+ if (isStandardConversation) {
343
+ const available = await qm.checkQuote(project, request, "requests");
344
+ if (!available) {
345
+ throw new Error(`Requests limits reached for project ${project._id}`);
346
+ }
347
+ }
491
348
 
492
- requestEvent.emit('request.department.update', requestComplete); //se req ha bot manda messaggio \welcome
349
+ // Case 2 - Leaving TEMP status. After internal routing: STATUS changed from 50 to 100 or 200
350
+ if (requestBeforeRoute.status === RequestConstants.TEMP &&
351
+ [RequestConstants.ASSIGNED, RequestConstants.UNASSIGNED].includes(routedRequest.status) &&
352
+ isStandardConversation
353
+ ) {
354
+ requestEvent.emit("request.create.quote", { project, request });
355
+ }
493
356
 
494
- winston.debug("here end");
357
+ // Case 3 - Conversation opened through proactive message. After internal routing: STATUS changed from undefined to 100
358
+ if (
359
+ !requestBeforeRoute.status &&
360
+ routedRequest.status === RequestConstants.ASSIGNED &&
361
+ isStandardConversation
362
+ ) {
363
+ requestEvent.emit("request.create.quote", { project, request });
364
+ }
495
365
 
496
- return resolve(requestComplete);
497
- });
366
+ // Save and populate
367
+ const savedRequest = await routedRequest.save();
498
368
 
369
+ const requestComplete = await savedRequest
370
+ .populate("lead")
371
+ .populate("department")
372
+ .populate("participatingBots")
373
+ .populate("participatingAgents")
374
+ .populate({ path: "requester", populate: { path: "id_user" } })
375
+ .execPopulate();
499
376
 
500
377
 
501
- });
378
+ this.emitParticipantsEvents(
379
+ request,
380
+ requestComplete,
381
+ beforeParticipants
382
+ );
502
383
 
503
- }).catch(function (err) {
504
- return reject(err);
505
- });
384
+ requestEvent.emit("request.department.update", requestComplete);
506
385
 
386
+ return requestComplete;
507
387
 
508
- });
509
- });
388
+ } catch (error) {
389
+ winston.error("Route error", { error, request_id, id_project });
390
+ throw error;
391
+ }
510
392
  }
511
393
 
512
394
 
@@ -579,558 +461,249 @@ class RequestService {
579
461
  };
580
462
 
581
463
  async create(request) {
582
-
583
- if (!request.createdAt) {
584
- request.createdAt = new Date();
585
- }
586
-
587
- var request_id = request.request_id;
588
- var project_user_id = request.project_user_id;
589
- var lead_id = request.lead_id;
590
- var id_project = request.id_project;
591
- var first_text = request.first_text;
592
- var departmentid = request.departmentid;
593
- var sourcePage = request.sourcePage;
594
- var language = request.language;
595
- var userAgent = request.userAgent;
596
- var status = request.status;
597
- var createdBy = request.createdBy;
598
- var attributes = request.attributes;
599
- var subject = request.subject;
600
- var preflight = request.preflight;
601
- var channel = request.channel;
602
- var location = request.location;
603
- var participants = request.participants || [];
604
- var tags = request.tags;
605
- var notes = request.notes;
606
- var priority = request.priority;
607
- var auto_close = request.auto_close;
608
- var followers = request.followers;
609
- let createdAt = request.createdAt;
610
-
611
- if (!departmentid) {
612
- departmentid = 'default';
613
- }
614
-
615
- if (!createdBy) {
616
- if (project_user_id) {
617
- createdBy = project_user_id;
618
- } else {
619
- createdBy = "system";
620
- }
621
- }
622
-
623
- // Utils
464
+ const createdAt = request.createdAt || new Date();
465
+
466
+ let {
467
+ request_id,
468
+ project_user_id,
469
+ lead_id,
470
+ id_project,
471
+ first_text,
472
+ sourcePage,
473
+ language,
474
+ userAgent,
475
+ status,
476
+ attributes,
477
+ subject,
478
+ preflight,
479
+ channel,
480
+ location,
481
+ participants = [],
482
+ tags,
483
+ notes,
484
+ priority,
485
+ auto_close,
486
+ followers
487
+ } = request;
488
+
489
+ let departmentid = request.departmentid || 'default';
490
+ let createdBy = request.createdBy || project_user_id || "system";
491
+
492
+ // Utils and flags
624
493
  let payload;
625
494
  let isTestConversation = false;
626
495
  let isVoiceConversation = false;
627
496
  let isStandardConversation = false;
628
- var that = this;
629
497
 
630
- return new Promise( async (resolve, reject) => {
631
- var context = {
632
- request: {
633
- request_id: request_id, project_user_id: project_user_id, lead_id: lead_id, id_project: id_project,
634
- first_text: first_text, departmentid: departmentid, sourcePage: sourcePage, language: language, userAgent: userAgent, status: status,
635
- createdBy: createdBy, attributes: attributes, subject: subject, preflight: preflight, channel: channel, location: location,
636
- participants: participants, tags: tags, notes: notes,
637
- priority: priority, auto_close: auto_close, followers: followers
638
- }
639
- };
640
- winston.debug("context", context);
498
+ const context = {
499
+ request: {
500
+ request_id, project_user_id, lead_id, id_project,
501
+ first_text, departmentid, sourcePage, language, userAgent, status,
502
+ createdBy, attributes, subject, preflight, channel, location,
503
+ participants,tags,notes,priority,auto_close,followers
504
+ }
505
+ };
641
506
 
642
- var participantsAgents = [];
643
- var participantsBots = [];
644
- var hasBot = false;
645
- var dep_id = undefined;
646
- var assigned_at = undefined;
647
- var agents = [];
648
- var snapshot = {};
507
+ winston.debug("context: ", context);
508
+
509
+ // Initial assignment
510
+ let agents = [];
511
+ let participantsAgents = [];
512
+ let participantsBots = [];
513
+ let hasBot = false;
514
+ let dep_id;
515
+ let assigned_at;
516
+ let snapshot = {};
517
+
518
+ // Getting operators
519
+ let result;
520
+ try {
521
+ result = await departmentService.getOperators(departmentid, id_project, false, undefined, context);
522
+ winston.debug("Get operators result: ", result);
523
+ } catch (err) {
524
+ throw new Error("Error getting operators", { cause: err });
525
+ }
649
526
 
650
- try {
651
- // (method) DepartmentService.getOperators(departmentid: any, projectid: any, nobot: any, disableWebHookCall: any, context: any): Promise<any>
652
- var result = await departmentService.getOperators(departmentid, id_project, false, undefined, context);
653
- winston.debug("getOperators", result);
527
+ agents = result.agents;
654
528
 
529
+ // Status and quote management
530
+ if (status === RequestConstants.TEMP) {
531
+ // Skip assignment
532
+ if (participants.length === 0) {
533
+ dep_id = result.department._id;
534
+ }
535
+ }
536
+ else {
537
+
538
+ let project;
539
+ try {
540
+ project = await projectService.getCachedProject(id_project);
655
541
  } catch (err) {
656
- return reject(err);
542
+ throw new Error("Error getting project", { cause: err });
657
543
  }
658
544
 
659
- agents = result.agents;
545
+ if (!project) {
546
+ winston.error(`Project not found for id_project: ${id_project}`);
547
+ throw new Error(`Project not found for id_project: ${id_project}`);
548
+ }
660
549
 
661
- if (status == 50) {
662
- // skip assignment
663
- if (participants.length == 0) {
664
- dep_id = result.department._id;
665
- }
666
- } else {
550
+ payload = { project, request };
667
551
 
668
- let project = await projectService.getCachedProject(id_project).catch((err) => {
669
- winston.warn("Error getting cached project. Skip conversation quota check.")
670
- winston.warn("Getting cached project error: ", err)
671
- })
552
+ // Test conversation
553
+ if (attributes?.sourcePage?.includes("td_draft=true")) {
554
+ winston.verbose("is a test conversation --> skip quote availability check");
555
+ isTestConversation = true;
556
+ }
672
557
 
673
- payload = {
674
- project: project,
675
- request: request
558
+ // Voice conversation
559
+ else if (channel?.name === "voice-vxml") {
560
+ isVoiceConversation = true;
561
+ const available = await qm.checkQuote(project, request, "voice_duration");
562
+ if (!available) {
563
+ winston.info(`Voice duration limits reached for project ${id_project}`);
564
+ throw new Error(`Voice duration limits reached for project ${id_project}`);
676
565
  }
566
+ }
677
567
 
678
- if (attributes && attributes.sourcePage && (attributes.sourcePage.indexOf("td_draft=true") > -1)) {
679
- winston.verbose("is a test conversation --> skip quote availability check")
680
- isTestConversation = true;
681
- }
682
- else if (channel && (channel.name === 'voice-vxml')) {
683
- isVoiceConversation = true;
684
- let available = await qm.checkQuote(project, request, 'voice_duration');
685
- if (available === false) {
686
- winston.info("Voice duration limits reached for project " + project._id);
687
- return reject("Voice duration limits reached for project " + project._id);
688
- }
568
+ // Standard conversation
569
+ else {
570
+ isStandardConversation = true;
571
+ const available = await qm.checkQuote(project, request, "requests");
572
+ if (!available) {
573
+ winston.info(`Requests limits reached for project ${project._id}`);
574
+ throw new Error(`Requests limits reached for project ${project._id}`);
689
575
  }
690
- else {
691
- isStandardConversation = true;
692
- let available = await qm.checkQuote(project, request, 'requests');
693
- if (available === false) {
694
- winston.info("Requests limits reached for project " + project._id)
695
- return reject("Requests limits reached for project " + project._id);
696
- }
697
- }
698
-
576
+ }
699
577
 
700
- if (participants.length == 0) {
701
- if (result.operators && result.operators.length > 0) {
702
- participants.push(result.operators[0].id_user.toString());
703
- }
704
- // for preflight it is important to save agents in req for trigger. try to optimize it
705
- dep_id = result.department._id;
578
+ // Assignment
579
+ if (participants.length === 0) {
580
+ if (result.operators?.length > 0) {
581
+ participants.push(result.operators[0].id_user.toString());
706
582
  }
583
+ dep_id = result.department._id;
584
+ }
707
585
 
708
- if (participants.length > 0) {
709
- status = RequestConstants.ASSIGNED;
710
- // botprefix
711
- if (participants[0].startsWith("bot_")) {
712
-
713
- hasBot = true;
714
- winston.debug("hasBot:" + hasBot);
715
-
716
- // botprefix
717
- var assigned_operator_idStringBot = participants[0].replace("bot_", "");
718
- winston.debug("assigned_operator_idStringBot:" + assigned_operator_idStringBot);
719
-
720
- participantsBots.push(assigned_operator_idStringBot);
721
-
722
- } else {
723
-
724
- participantsAgents.push(participants[0]);
725
-
726
- }
586
+ if (participants.length > 0) {
587
+ status = RequestConstants.ASSIGNED;
727
588
 
728
- assigned_at = Date.now();
589
+ const firstParticipant = participants[0];
729
590
 
591
+ if (firstParticipant.startsWith("bot_")) {
592
+ hasBot = true;
593
+ const botId = firstParticipant.replace("bot_", "");
594
+ participantsBots.push(botId);
730
595
  } else {
731
- status = RequestConstants.UNASSIGNED;
596
+ participantsAgents.push(firstParticipant);
732
597
  }
733
- }
734
-
735
- if (dep_id) {
736
- snapshot.department = result.department;
737
- }
738
-
739
- snapshot.agents = agents;
740
- snapshot.availableAgentsCount = that.getAvailableAgentsCount(agents);
741
-
742
- if (request.requester) {
743
- snapshot.requester = request.requester;
744
- }
745
- if (request.lead) {
746
- snapshot.lead = request.lead;
747
- }
748
598
 
749
- var newRequest = new Request({
750
- request_id: request_id,
751
- requester: project_user_id,
752
- lead: lead_id,
753
- first_text: first_text,
754
- subject: subject,
755
- status: status,
756
- participants: participants,
757
- participantsAgents: participantsAgents,
758
- participantsBots: participantsBots,
759
- hasBot: hasBot,
760
- department: dep_id,
761
- // agents: agents,
762
- //others
763
- sourcePage: sourcePage,
764
- language: language,
765
- userAgent: userAgent,
766
- assigned_at: assigned_at,
767
- attributes: attributes,
768
- //standard
769
- id_project: id_project,
770
- createdBy: createdBy,
771
- updatedBy: createdBy,
772
- preflight: preflight,
773
- channel: channel,
774
- location: location,
775
- snapshot: snapshot,
776
- tags: tags,
777
- notes: notes,
778
- priority: priority,
779
- auto_close: auto_close,
780
- followers: followers,
781
- createdAt: createdAt
782
- });
783
-
784
- if (isTestConversation) {
785
- newRequest.draft = true;
599
+ assigned_at = Date.now();
600
+ } else {
601
+ status = RequestConstants.UNASSIGNED;
786
602
  }
603
+ }
787
604
 
788
- winston.debug('newRequest.', newRequest);
789
-
790
- //cacheinvalidation
791
- return newRequest.save( async function (err, savedRequest) {
792
-
793
- if (err) {
794
- winston.error('RequestService error for method createWithIdAndRequester for newRequest' + JSON.stringify(newRequest), err);
795
- return reject(err);
796
- }
797
- winston.debug("Request created", savedRequest.toObject());
798
-
799
- requestEvent.emit('request.create.simple', savedRequest);
800
-
801
- if (isStandardConversation) {
802
- requestEvent.emit('request.create.quote', payload);;
803
- }
605
+ // Snapshot
606
+ if (dep_id) {
607
+ snapshot.department = result.department;
608
+ }
609
+ snapshot.agents = agents;
804
610
 
805
- return resolve(savedRequest);
611
+ if (request.requester) {
612
+ snapshot.requester = request.requester;
613
+ }
614
+ if (request.lead) {
615
+ snapshot.lead = request.lead;
616
+ }
806
617
 
807
- });
618
+ // Create request
619
+ const newRequest = new Request({
620
+ request_id,
621
+ requester: project_user_id,
622
+ lead: lead_id,
623
+ first_text,
624
+ subject,
625
+ status,
626
+ participants,
627
+ participantsAgents,
628
+ participantsBots,
629
+ hasBot,
630
+ department: dep_id,
631
+ sourcePage,
632
+ language,
633
+ userAgent,
634
+ assigned_at,
635
+ attributes,
636
+ id_project,
637
+ createdBy,
638
+ updatedBy: createdBy,
639
+ preflight,
640
+ channel,
641
+ location,
642
+ tags,
643
+ notes,
644
+ priority,
645
+ auto_close,
646
+ followers,
647
+ createdAt
808
648
  })
809
- }
810
-
811
- // DEPRECATED
812
- // async _create(request) {
813
-
814
- // var startDate = new Date();
815
-
816
- // if (!request.createdAt) {
817
- // request.createdAt = new Date();
818
- // }
819
-
820
-
821
- // var request_id = request.request_id;
822
- // var project_user_id = request.project_user_id;
823
- // var lead_id = request.lead_id;
824
- // var id_project = request.id_project;
825
-
826
- // var first_text = request.first_text;
827
-
828
- // //removed for ticket
829
- // // // lascia che sia nico a fare il replace...certo tu devi fare il test che tutto sia ok quindi dopo demo
830
- // // var first_text;
831
- // // if (request.first_text) { //first_text can be empty for type image
832
- // // first_text = request.first_text.replace(/[\n\r]+/g, ' '); //replace new line with space
833
- // // }
834
-
835
- // var departmentid = request.departmentid;
836
- // var sourcePage = request.sourcePage;
837
- // var language = request.language;
838
- // var userAgent = request.userAgent;
839
- // var status = request.status;
840
- // var createdBy = request.createdBy;
841
- // var attributes = request.attributes;
842
- // var subject = request.subject;
843
- // var preflight = request.preflight;
844
- // var channel = request.channel;
845
- // var location = request.location;
846
- // var participants = request.participants || [];
847
-
848
- // var tags = request.tags;
849
- // var notes = request.notes;
850
- // var priority = request.priority;
851
-
852
- // var auto_close = request.auto_close;
853
-
854
- // var followers = request.followers;
855
- // let createdAt = request.createdAt;
856
-
857
-
858
- // if (!departmentid) {
859
- // departmentid = 'default';
860
- // }
861
-
862
- // if (!createdBy) {
863
- // if (project_user_id) {
864
- // createdBy = project_user_id;
865
- // } else {
866
- // createdBy = "system";
867
- // }
868
-
869
- // }
870
-
871
- // let isTestConversation = false;
872
- // let isVoiceConversation = false;
873
-
874
- // var that = this;
875
-
876
- // return new Promise(async (resolve, reject) => {
877
-
878
- // let q = Project.findOne({ _id: request.id_project, status: 100 });
879
- // if (cacheEnabler.project) {
880
- // q.cache(cacheUtil.longTTL, "projects:id:" + request.id_project) //project_cache
881
- // winston.debug('project cache enabled for /project detail');
882
- // }
883
- // q.exec(async function (err, p) {
884
- // if (err) {
885
- // winston.error('Error getting project ', err);
886
- // }
887
- // if (!p) {
888
- // winston.warn('Project not found ');
889
- // }
890
-
891
-
892
- // let payload = {
893
- // project: p,
894
- // request: request
895
- // }
896
-
897
- // if (attributes && attributes.sourcePage && (attributes.sourcePage.indexOf("td_draft=true") > -1)) {
898
- // winston.verbose("is a test conversation --> skip quote availability check")
899
- // isTestConversation = true;
900
- // }
901
- // else if (channel && (channel.name === 'voice-vxml')) {
902
- // winston.verbose("is a voice conversation --> skip quote availability check")
903
- // isVoiceConversation = true;
904
- // }
905
- // else {
906
- // // console.log("! check quota moved")
907
- // // let available = await qm.checkQuote(p, request, 'requests');
908
- // // if (available === false) {
909
- // // winston.info("Requests limits reached for project " + p._id)
910
- // // return false;
911
- // // }
912
- // }
913
-
914
-
915
- // var context = {
916
- // request: {
917
- // request_id: request_id, project_user_id: project_user_id, lead_id: lead_id, id_project: id_project,
918
- // first_text: first_text, departmentid: departmentid, sourcePage: sourcePage, language: language, userAgent: userAgent, status: status,
919
- // createdBy: createdBy, attributes: attributes, subject: subject, preflight: preflight, channel: channel, location: location,
920
- // participants: participants, tags: tags, notes: notes,
921
- // priority: priority, auto_close: auto_close, followers: followers
922
- // }
923
- // };
924
-
925
- // winston.debug("context", context);
926
-
927
- // var participantsAgents = [];
928
- // var participantsBots = [];
929
- // var hasBot = false;
930
-
931
- // var dep_id = undefined;
932
-
933
- // var assigned_at = undefined;
934
-
935
- // var agents = [];
936
-
937
- // var snapshot = {};
938
-
939
- // try {
940
- // // getOperators(departmentid, projectid, nobot, disableWebHookCall, context) {
941
- // var result = await departmentService.getOperators(departmentid, id_project, false, undefined, context);
942
- // // console.log("************* after get operator: "+new Date().toISOString());
943
-
944
- // winston.debug("getOperators", result);
945
- // } catch (err) {
946
- // return reject(err);
947
- // }
948
-
949
-
950
-
951
- // agents = result.agents;
952
-
953
- // if (status == 50) {
954
- // // skip assignment
955
- // if (participants.length == 0) {
956
- // dep_id = result.department._id;
957
- // }
958
- // } else {
959
-
960
- // if (participants.length == 0) {
961
- // if (result.operators && result.operators.length > 0) {
962
- // participants.push(result.operators[0].id_user.toString());
963
- // }
964
- // // for preflight it is important to save agents in req for trigger. try to optimize it
965
- // dep_id = result.department._id;
966
-
967
- // }
968
-
969
- // if (participants.length > 0) {
970
-
971
- // status = RequestConstants.ASSIGNED;
972
-
973
- // /**
974
- // * QUOTAS - START!!!
975
- // */
976
- // if (!isTestConversation && !isVoiceConversation) {
977
- // requestEvent.emit('request.create.quote', payload);
978
- // }
979
- // /**
980
- // * QUOTAS - END!!!
981
- // */
982
-
983
-
984
-
985
- // // botprefix
986
- // if (participants[0].startsWith("bot_")) {
987
-
988
- // hasBot = true;
989
- // winston.debug("hasBot:" + hasBot);
990
-
991
- // // botprefix
992
- // var assigned_operator_idStringBot = participants[0].replace("bot_", "");
993
- // winston.debug("assigned_operator_idStringBot:" + assigned_operator_idStringBot);
994
-
995
- // participantsBots.push(assigned_operator_idStringBot);
996
-
997
- // } else {
998
-
999
- // participantsAgents.push(participants[0]);
1000
-
1001
- // }
1002
-
1003
- // assigned_at = Date.now();
1004
-
1005
- // } else {
1006
-
1007
- // status = RequestConstants.UNASSIGNED;
1008
-
1009
- // }
1010
649
 
1011
- // }
1012
-
1013
-
1014
-
1015
-
1016
- // if (dep_id) {
1017
- // snapshot.department = result.department;
1018
- // }
1019
-
1020
- // // console.log("result.agents",result.agents);
1021
- // snapshot.agents = agents;
1022
- // snapshot.availableAgentsCount = that.getAvailableAgentsCount(agents);
1023
-
1024
- // if (request.requester) { //.toObject()????
1025
- // snapshot.requester = request.requester;
1026
- // }
1027
- // if (request.lead) {
1028
- // snapshot.lead = request.lead;
1029
- // }
1030
-
1031
- // // winston.debug("assigned_operator_id", assigned_operator_id);
1032
- // // winston.debug("req status", status);
1033
-
1034
- // var newRequest = new Request({
1035
- // request_id: request_id,
1036
- // requester: project_user_id,
1037
- // lead: lead_id,
1038
- // first_text: first_text,
1039
- // subject: subject,
1040
- // status: status,
1041
- // participants: participants,
1042
- // participantsAgents: participantsAgents,
1043
- // participantsBots: participantsBots,
1044
- // hasBot: hasBot,
1045
- // department: dep_id,
1046
- // // agents: agents,
1047
-
1048
- // //others
1049
- // sourcePage: sourcePage,
1050
- // language: language,
1051
- // userAgent: userAgent,
1052
- // assigned_at: assigned_at,
1053
-
1054
- // attributes: attributes,
1055
- // //standard
1056
- // id_project: id_project,
1057
- // createdBy: createdBy,
1058
- // updatedBy: createdBy,
1059
- // preflight: preflight,
1060
- // channel: channel,
1061
- // location: location,
1062
- // snapshot: snapshot,
1063
- // tags: tags,
1064
- // notes: notes,
1065
- // priority: priority,
1066
- // auto_close: auto_close,
1067
- // followers: followers,
1068
- // createdAt: createdAt
1069
- // });
1070
-
1071
- // if (isTestConversation) {
1072
- // newRequest.draft = true;
1073
- // }
1074
-
1075
- // winston.debug('newRequest.', newRequest);
1076
-
1077
-
1078
- // //cacheinvalidation
1079
- // return newRequest.save( async function (err, savedRequest) {
650
+ if (isTestConversation) {
651
+ newRequest.draft = true;
652
+ }
1080
653
 
1081
- // if (err) {
1082
- // winston.error('RequestService error for method createWithIdAndRequester for newRequest' + JSON.stringify(newRequest), err);
1083
- // return reject(err);
1084
- // }
654
+ winston.debug('newRequest.', newRequest);
655
+ winston.info("main_flow_cache_ requestService create");
1085
656
 
657
+ // Save request
658
+ try {
659
+ const savedRequest = await newRequest.save();
660
+ winston.debug("Request created", savedRequest.toObject());
1086
661
 
1087
- // winston.debug("Request created", savedRequest.toObject());
662
+ /**
663
+ * Se non faccio findOne usando la cache adesso, quando verrà eseguita la query in reroute() la request non ha il department.
664
+ */
665
+ let q = Request.findOne({ request_id, id_project });
666
+ if (cacheEnabler.request) {
667
+ q.cache(cacheUtil.defaultTTL, id_project + ":requests:request_id:" + request_id + ":simple");
668
+ winston.debug('request cache enabled');
669
+ }
670
+ await q.exec();
1088
671
 
1089
- // var endDate = new Date();
1090
- // winston.verbose("Performance Request created in millis: " + endDate - startDate);
672
+ requestEvent.emit("request.create.simple", savedRequest);
1091
673
 
1092
- // requestEvent.emit('request.create.simple', savedRequest);
674
+ if (isStandardConversation) {
675
+ requestEvent.emit("request.create.quote", payload);
676
+ }
1093
677
 
1094
- // if (!isTestConversation && !isVoiceConversation) {
1095
- // // requestEvent.emit('request.create.quote', payload);;
1096
- // }
678
+ // Emit event to update snapshot in queue
679
+ if (Object.keys(snapshot).length > 0) {
680
+ requestEvent.emit("request.snapshot.update", {
681
+ request: savedRequest,
682
+ snapshot: snapshot
683
+ });
684
+ }
1097
685
 
1098
- // return resolve(savedRequest);
686
+ return savedRequest;
1099
687
 
1100
- // });
1101
- // // }).catch(function(err){
1102
- // // return reject(err);
1103
- // // });
688
+ } catch (err) {
689
+ winston.error(`RequestService error on save request ${newRequest.request_id}`, err);
690
+ throw new Error("RequestService error on save request", { cause: err });
691
+ }
1104
692
 
1105
- // })
1106
- // });
1107
-
1108
- // }
693
+ }
1109
694
 
1110
695
  // DEPRECATED
1111
- // async __create(request) {
1112
-
1113
- // var startDate = new Date();
696
+ // async create(request) {
1114
697
 
1115
- // if (!request.createdAt) {
698
+ // if (!request.createdAt) {
1116
699
  // request.createdAt = new Date();
1117
700
  // }
1118
701
 
1119
-
1120
702
  // var request_id = request.request_id;
1121
703
  // var project_user_id = request.project_user_id;
1122
704
  // var lead_id = request.lead_id;
1123
705
  // var id_project = request.id_project;
1124
-
1125
706
  // var first_text = request.first_text;
1126
-
1127
- // //removed for ticket
1128
- // // // lascia che sia nico a fare il replace...certo tu devi fare il test che tutto sia ok quindi dopo demo
1129
- // // var first_text;
1130
- // // if (request.first_text) { //first_text can be empty for type image
1131
- // // first_text = request.first_text.replace(/[\n\r]+/g, ' '); //replace new line with space
1132
- // // }
1133
-
1134
707
  // var departmentid = request.departmentid;
1135
708
  // var sourcePage = request.sourcePage;
1136
709
  // var language = request.language;
@@ -1143,13 +716,10 @@ class RequestService {
1143
716
  // var channel = request.channel;
1144
717
  // var location = request.location;
1145
718
  // var participants = request.participants || [];
1146
-
1147
719
  // var tags = request.tags;
1148
720
  // var notes = request.notes;
1149
721
  // var priority = request.priority;
1150
-
1151
722
  // var auto_close = request.auto_close;
1152
-
1153
723
  // var followers = request.followers;
1154
724
  // let createdAt = request.createdAt;
1155
725
 
@@ -1163,13 +733,16 @@ class RequestService {
1163
733
  // } else {
1164
734
  // createdBy = "system";
1165
735
  // }
1166
-
1167
736
  // }
1168
737
 
738
+ // // Utils
739
+ // let payload;
740
+ // let isTestConversation = false;
741
+ // let isVoiceConversation = false;
742
+ // let isStandardConversation = false;
1169
743
  // var that = this;
1170
744
 
1171
- // return new Promise(async (resolve, reject) => {
1172
-
745
+ // return new Promise( async (resolve, reject) => {
1173
746
  // var context = {
1174
747
  // request: {
1175
748
  // request_id: request_id, project_user_id: project_user_id, lead_id: lead_id, id_project: id_project,
@@ -1179,33 +752,25 @@ class RequestService {
1179
752
  // priority: priority, auto_close: auto_close, followers: followers
1180
753
  // }
1181
754
  // };
1182
-
1183
755
  // winston.debug("context", context);
1184
756
 
1185
757
  // var participantsAgents = [];
1186
758
  // var participantsBots = [];
1187
759
  // var hasBot = false;
1188
-
1189
760
  // var dep_id = undefined;
1190
-
1191
761
  // var assigned_at = undefined;
1192
-
1193
762
  // var agents = [];
1194
-
1195
763
  // var snapshot = {};
1196
764
 
1197
765
  // try {
1198
- // // getOperators(departmentid, projectid, nobot, disableWebHookCall, context) {
766
+ // // (method) DepartmentService.getOperators(departmentid: any, projectid: any, nobot: any, disableWebHookCall: any, context: any): Promise<any>
1199
767
  // var result = await departmentService.getOperators(departmentid, id_project, false, undefined, context);
1200
- // // console.log("************* after get operator: "+new Date().toISOString());
1201
-
1202
768
  // winston.debug("getOperators", result);
769
+
1203
770
  // } catch (err) {
1204
771
  // return reject(err);
1205
772
  // }
1206
773
 
1207
-
1208
-
1209
774
  // agents = result.agents;
1210
775
 
1211
776
  // if (status == 50) {
@@ -1215,19 +780,48 @@ class RequestService {
1215
780
  // }
1216
781
  // } else {
1217
782
 
783
+ // let project = await projectService.getCachedProject(id_project).catch((err) => {
784
+ // winston.warn("Error getting cached project. Skip conversation quota check.")
785
+ // winston.warn("Getting cached project error: ", err)
786
+ // })
787
+
788
+ // payload = {
789
+ // project: project,
790
+ // request: request
791
+ // }
792
+
793
+ // if (attributes && attributes.sourcePage && (attributes.sourcePage.indexOf("td_draft=true") > -1)) {
794
+ // winston.verbose("is a test conversation --> skip quote availability check")
795
+ // isTestConversation = true;
796
+ // }
797
+ // else if (channel && (channel.name === 'voice-vxml')) {
798
+ // isVoiceConversation = true;
799
+ // let available = await qm.checkQuote(project, request, 'voice_duration');
800
+ // if (available === false) {
801
+ // winston.info("Voice duration limits reached for project " + project._id);
802
+ // return reject("Voice duration limits reached for project " + project._id);
803
+ // }
804
+ // }
805
+ // else {
806
+ // isStandardConversation = true;
807
+ // let available = await qm.checkQuote(project, request, 'requests');
808
+ // if (available === false) {
809
+ // winston.info("Requests limits reached for project " + project._id)
810
+ // return reject("Requests limits reached for project " + project._id);
811
+ // }
812
+ // }
813
+
814
+
1218
815
  // if (participants.length == 0) {
1219
816
  // if (result.operators && result.operators.length > 0) {
1220
817
  // participants.push(result.operators[0].id_user.toString());
1221
818
  // }
1222
819
  // // for preflight it is important to save agents in req for trigger. try to optimize it
1223
820
  // dep_id = result.department._id;
1224
-
1225
821
  // }
1226
822
 
1227
823
  // if (participants.length > 0) {
1228
-
1229
824
  // status = RequestConstants.ASSIGNED;
1230
-
1231
825
  // // botprefix
1232
826
  // if (participants[0].startsWith("bot_")) {
1233
827
 
@@ -1249,34 +843,24 @@ class RequestService {
1249
843
  // assigned_at = Date.now();
1250
844
 
1251
845
  // } else {
1252
-
1253
846
  // status = RequestConstants.UNASSIGNED;
1254
-
1255
847
  // }
1256
-
1257
848
  // }
1258
849
 
1259
-
1260
-
1261
-
1262
850
  // if (dep_id) {
1263
851
  // snapshot.department = result.department;
1264
852
  // }
1265
853
 
1266
- // // console.log("result.agents",result.agents);
1267
854
  // snapshot.agents = agents;
1268
855
  // snapshot.availableAgentsCount = that.getAvailableAgentsCount(agents);
1269
856
 
1270
- // if (request.requester) { //.toObject()????
857
+ // if (request.requester) {
1271
858
  // snapshot.requester = request.requester;
1272
859
  // }
1273
860
  // if (request.lead) {
1274
861
  // snapshot.lead = request.lead;
1275
862
  // }
1276
863
 
1277
- // // winston.debug("assigned_operator_id", assigned_operator_id);
1278
- // // winston.debug("req status", status);
1279
-
1280
864
  // var newRequest = new Request({
1281
865
  // request_id: request_id,
1282
866
  // requester: project_user_id,
@@ -1290,13 +874,11 @@ class RequestService {
1290
874
  // hasBot: hasBot,
1291
875
  // department: dep_id,
1292
876
  // // agents: agents,
1293
-
1294
877
  // //others
1295
878
  // sourcePage: sourcePage,
1296
879
  // language: language,
1297
880
  // userAgent: userAgent,
1298
881
  // assigned_at: assigned_at,
1299
-
1300
882
  // attributes: attributes,
1301
883
  // //standard
1302
884
  // id_project: id_project,
@@ -1305,7 +887,7 @@ class RequestService {
1305
887
  // preflight: preflight,
1306
888
  // channel: channel,
1307
889
  // location: location,
1308
- // snapshot: snapshot,
890
+ // //snapshot: snapshot,
1309
891
  // tags: tags,
1310
892
  // notes: notes,
1311
893
  // priority: priority,
@@ -1314,62 +896,34 @@ class RequestService {
1314
896
  // createdAt: createdAt
1315
897
  // });
1316
898
 
1317
- // winston.debug('newRequest.', newRequest);
899
+ // if (isTestConversation) {
900
+ // newRequest.draft = true;
901
+ // }
1318
902
 
903
+ // winston.debug('newRequest.', newRequest);
1319
904
 
1320
905
  // //cacheinvalidation
1321
- // return newRequest.save(function (err, savedRequest) {
906
+ // return newRequest.save( async function (err, savedRequest) {
1322
907
 
1323
908
  // if (err) {
1324
909
  // winston.error('RequestService error for method createWithIdAndRequester for newRequest' + JSON.stringify(newRequest), err);
1325
910
  // return reject(err);
1326
911
  // }
1327
-
1328
-
1329
912
  // winston.debug("Request created", savedRequest.toObject());
1330
913
 
1331
- // var endDate = new Date();
1332
- // winston.verbose("Performance Request created in millis: " + endDate - startDate);
1333
-
1334
914
  // requestEvent.emit('request.create.simple', savedRequest);
1335
915
 
1336
- // let q = Project.findOne({ _id: request.id_project, status: 100 });
1337
- // if (cacheEnabler.project) {
1338
- // q.cache(cacheUtil.longTTL, "projects:id:" + request.id_project) //project_cache
1339
- // winston.debug('project cache enabled for /project detail');
1340
- // }
1341
- // q.exec(async function (err, p) {
1342
- // if (err) {
1343
- // winston.error('Error getting project ', err);
1344
- // }
1345
- // if (!p) {
1346
- // winston.warn('Project not found ');
1347
- // }
1348
- // //TODO REMOVE settings from project
1349
- // let payload = {
1350
- // project: p,
1351
- // request: request
1352
- // }
1353
-
916
+ // if (isStandardConversation) {
1354
917
  // requestEvent.emit('request.create.quote', payload);;
1355
-
1356
- // });
918
+ // }
1357
919
 
1358
920
  // return resolve(savedRequest);
1359
921
 
1360
922
  // });
1361
- // // }).catch(function(err){
1362
- // // return reject(err);
1363
- // // });
1364
-
1365
-
1366
- // });
923
+ // })
1367
924
  // }
1368
925
 
1369
-
1370
-
1371
-
1372
-
926
+
1373
927
  //DEPRECATED. USED ONLY IN SAME TESTS
1374
928
  createWithId(request_id, requester_id, id_project, first_text, departmentid, sourcePage, language, userAgent, status, createdBy, attributes) {
1375
929
 
@@ -2921,6 +2475,31 @@ class RequestService {
2921
2475
  })
2922
2476
  }
2923
2477
 
2478
+ emitParticipantsEvents(beforeRequest, requestComplete, oldParticipants) {
2479
+ const newParticipants = requestComplete.participants;
2480
+
2481
+ const removedParticipants = oldParticipants.filter(
2482
+ p => !newParticipants.includes(p)
2483
+ );
2484
+ const addedParticipants = newParticipants.filter(
2485
+ p => !oldParticipants.includes(p)
2486
+ );
2487
+
2488
+ requestEvent.emit("request.update", requestComplete);
2489
+ requestEvent.emit("request.updated", {
2490
+ comment: "REROUTE",
2491
+ request: requestComplete,
2492
+ patch: { removedParticipants, addedParticipants }
2493
+ });
2494
+
2495
+ requestEvent.emit("request.participants.update", {
2496
+ beforeRequest,
2497
+ removedParticipants,
2498
+ addedParticipants,
2499
+ request: requestComplete
2500
+ });
2501
+ }
2502
+
2924
2503
 
2925
2504
  }
2926
2505