tango-app-api-trax 3.9.38 → 3.9.39

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.
Files changed (55) hide show
  1. package/index.js +1 -2
  2. package/package.json +2 -2
  3. package/src/controllers/internalTrax.controller.js +99 -221
  4. package/src/controllers/mobileTrax.controller.js +29 -69
  5. package/src/controllers/teaxFlag.controller.js +953 -15
  6. package/src/controllers/trax.controller.js +2 -3
  7. package/src/controllers/traxDashboard.controllers.js +55 -69
  8. package/src/hbs/flag.hbs +1 -1
  9. package/src/hbs/login-otp.hbs +943 -943
  10. package/src/hbs/template.hbs +0 -7
  11. package/src/hbs/visit-checklist.hbs +91 -71
  12. package/src/routes/internalTraxApi.router.js +0 -1
  13. package/src/routes/trax.routes.js +3 -0
  14. package/src/routes/traxFlag.router.js +24 -0
  15. package/src/services/app.service.js +9 -15
  16. package/src/services/approver.service.js +15 -23
  17. package/src/services/authentication.service.js +3 -9
  18. package/src/services/camera.service.js +13 -19
  19. package/src/services/checklist.service.js +27 -35
  20. package/src/services/checklistAssign.service.js +38 -43
  21. package/src/services/checklistQuestion.service.js +34 -39
  22. package/src/services/checklistlog.service.js +34 -39
  23. package/src/services/clientRequest.service.js +2 -9
  24. package/src/services/clients.services.js +18 -23
  25. package/src/services/cluster.service.js +23 -31
  26. package/src/services/domain.service.js +18 -23
  27. package/src/services/download.services.js +25 -35
  28. package/src/services/group.service.js +17 -23
  29. package/src/services/lenskartEmployeeMapping.service.js +10 -15
  30. package/src/services/locus.service.js +28 -35
  31. package/src/services/notification.service.js +26 -35
  32. package/src/services/otp.service.js +13 -20
  33. package/src/services/planogram.service.js +2 -9
  34. package/src/services/processedTaskConfig.service.js +27 -35
  35. package/src/services/processedTaskList.service.js +26 -32
  36. package/src/services/processedchecklist.services.js +47 -55
  37. package/src/services/processedchecklistconfig.services.js +34 -39
  38. package/src/services/recurringFlagTracker.service.js +32 -39
  39. package/src/services/runAIFeatures.services.js +27 -32
  40. package/src/services/runAIRequest.services.js +38 -43
  41. package/src/services/store.service.js +27 -32
  42. package/src/services/tagging.service.js +2 -9
  43. package/src/services/taskConfig.service.js +27 -35
  44. package/src/services/teams.service.js +24 -35
  45. package/src/services/ticket.service.js +10 -15
  46. package/src/services/user.service.js +20 -27
  47. package/src/services/userAssignedstores.service.js +5 -12
  48. package/src/utils/visitChecklistPdf.utils.js +21 -449
  49. package/src/logging/activityLogFlusher.js +0 -59
  50. package/src/logging/activityLogMiddleware.js +0 -45
  51. package/src/logging/activityLogStore.js +0 -91
  52. package/src/logging/compressBatches.js +0 -83
  53. package/src/logging/config.js +0 -24
  54. package/src/logging/createLoggableService.js +0 -46
  55. package/src/logging/logExternalCall.js +0 -37
package/index.js CHANGED
@@ -9,6 +9,5 @@ import { mobileRouter } from './src/routes/mobileTrax.routes.js';
9
9
  import { internalTraxRouter } from './src/routes/internalTraxApi.router.js';
10
10
  import { locusOrderRouter } from './src/routes/locus.router.js';
11
11
  import { traxActivityLogRouter } from './src/routes/activityLog.router.js';
12
- import traxActivityLogMiddleware from './src/logging/activityLogMiddleware.js';
13
12
 
14
- export { traxDashboardRouter, traxFlagRouter, traxRouter, galleryRouter, downloadRouter, mobileRouter, internalTraxRouter, locusOrderRouter, traxActivityLogRouter, traxActivityLogMiddleware };
13
+ export { traxDashboardRouter, traxFlagRouter, traxRouter, galleryRouter, downloadRouter, mobileRouter, internalTraxRouter, locusOrderRouter, traxActivityLogRouter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.38",
3
+ "version": "3.9.39",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "path": "^0.12.7",
31
31
  "puppeteer": "^24.39.1",
32
32
  "swagger-ui-express": "^5.0.1",
33
- "tango-api-schema": "^2.6.20",
33
+ "tango-api-schema": "^2.6.17",
34
34
  "tango-app-api-middleware": "^3.5.2",
35
35
  "url": "^0.11.4",
36
36
  "winston": "^3.13.1",
@@ -18,7 +18,7 @@ import timeZone from 'dayjs/plugin/timezone.js';
18
18
  import utc from 'dayjs/plugin/utc.js';
19
19
  import { logger } from 'tango-app-api-middleware';
20
20
  import mongoose from 'mongoose';
21
- import { sendPushNotification, sendAiPushNotification, sendEmailWithSES, signedUrl, fileUpload } from 'tango-app-api-middleware';
21
+ import { sendPushNotification, sendAiPushNotification, sendEmailWithSES, signedUrl, fileUpload, getOpenSearchData } from 'tango-app-api-middleware';
22
22
  // import * as planoService from '../services/planogram.service.js';
23
23
  import * as clusterServices from '../services/cluster.service.js';
24
24
  import * as teamsServices from '../services/teams.service.js';
@@ -624,7 +624,6 @@ export async function PCLconfigCreation( req, res ) {
624
624
  element4.rawImageUpload = getCLconfig?.rawImageUpload || false;
625
625
  element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
626
626
  element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
627
- element4.userVerification = getCLconfig?.userVerification;
628
627
  }
629
628
  if ( userIdList.length ) {
630
629
  allQuestion = allQuestion.filter( ( item ) => typeof item._id == 'undefined' );
@@ -931,7 +930,7 @@ async function insertData( requestData ) {
931
930
  },
932
931
  } );
933
932
  let getSections = await CLquestions.aggregate( sectionQuery );
934
- if ( getSections.length || [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring', 'activitymonitoring' ].includes( getCLconfig.checkListType ) ) {
933
+ if ( getSections.length || [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring' ].includes( getCLconfig.checkListType ) ) {
935
934
  if ( getSections.length ) {
936
935
  for ( let element3 of getSections ) {
937
936
  let collectQuestions = {};
@@ -1226,11 +1225,11 @@ async function insertData( requestData ) {
1226
1225
  // }
1227
1226
  }
1228
1227
  } else {
1229
- if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring', 'activitymonitoring' ].includes( getCLconfig.checkListType ) ) {
1228
+ if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring' ].includes( getCLconfig.checkListType ) ) {
1230
1229
  let storeNameList = allQuestion.map( ( item ) => item.store_id );
1231
- let storeDetails = await storeService.find( { clientId: getCLconfig.client_id, status: 'active', ...( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring', 'activitymonitoring' ].includes( getCLconfig.checkListType ) ) ? { storeId: { $in: storeNameList } } : {} }, { storeId: 1 } );
1230
+ let storeDetails = await storeService.find( { clientId: getCLconfig.client_id, status: 'active', ...( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring' ].includes( getCLconfig.checkListType ) ) ? { storeId: { $in: storeNameList } } : {} }, { storeId: 1 } );
1232
1231
  let storeList = storeDetails.map( ( store ) => store.storeId );
1233
- if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring', 'activitymonitoring' ].includes( getCLconfig.checkListType ) ) {
1232
+ if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'employeemonitoring' ].includes( getCLconfig.checkListType ) ) {
1234
1233
  allQuestion = allQuestion.filter( ( ele ) => storeList.includes( ele?.store_id ) );
1235
1234
  } else {
1236
1235
  allQuestion = storeDetails.map( ( item ) => {
@@ -1384,40 +1383,40 @@ export async function insertTimeDelayFlags( req, res ) {
1384
1383
  let updatedDetails = await processedchecklist.updateMany( { _id: { $in: checklistId } }, { timeFlag: 1 } );
1385
1384
 
1386
1385
 
1387
- // let notifyStores = await processedchecklist.find( { _id: { $in: checklistId }, notify: { $exists: false } } );
1388
-
1389
- // if ( notifyStores.length ) {
1390
- // Promise.all( notifyStores.map( async ( store ) => {
1391
- // let checklistDetails = await CLconfig.findOne( { _id: store.sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
1392
- // if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
1393
- // let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
1394
- // emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
1395
- // let data = {
1396
- // storeName: store.storeName,
1397
- // flagCount: 1,
1398
- // checklistName: store.checkListName?.trim(),
1399
- // submittedBy: '--',
1400
- // time: '--',
1401
- // domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
1402
- // type: 'delay',
1403
- // };
1404
- // const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
1405
- // const htmlContent = handlebars.compile( fileContent );
1406
- // const html = htmlContent( { data: data } );
1407
- // emailList.forEach( ( email ) => {
1408
- // let params = {
1409
- // toEmail: email,
1410
- // mailSubject: 'TangoEye | Checklist Flag',
1411
- // htmlBody: html,
1412
- // attachment: '',
1413
- // sourceEmail: JSON.parse( process.env.SES ).adminEmail,
1414
- // };
1415
- // sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1416
- // } );
1417
- // }
1418
- // await processedchecklist.updateOne( { _id: store._id }, { notify: true } );
1419
- // } ) );
1420
- // }
1386
+ let notifyStores = await processedchecklist.find( { _id: { $in: checklistId }, notify: { $exists: false } } );
1387
+
1388
+ if ( notifyStores.length ) {
1389
+ Promise.all( notifyStores.map( async ( store ) => {
1390
+ let checklistDetails = await CLconfig.findOne( { _id: store.sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
1391
+ if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
1392
+ let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
1393
+ emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
1394
+ let data = {
1395
+ storeName: store.storeName,
1396
+ flagCount: 1,
1397
+ checklistName: store.checkListName?.trim(),
1398
+ submittedBy: '--',
1399
+ time: '--',
1400
+ domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
1401
+ type: 'delay',
1402
+ };
1403
+ const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
1404
+ const htmlContent = handlebars.compile( fileContent );
1405
+ const html = htmlContent( { data: data } );
1406
+ emailList.forEach( ( email ) => {
1407
+ let params = {
1408
+ toEmail: email,
1409
+ mailSubject: 'TangoEye | Checklist Flag',
1410
+ htmlBody: html,
1411
+ attachment: '',
1412
+ sourceEmail: JSON.parse( process.env.SES ).adminEmail,
1413
+ };
1414
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1415
+ } );
1416
+ }
1417
+ await processedchecklist.updateOne( { _id: store._id }, { notify: true } );
1418
+ } ) );
1419
+ }
1421
1420
 
1422
1421
  if ( updatedDetails ) {
1423
1422
  return res.sendSuccess( { message: 'Time Delay Updated Successfully' } );
@@ -1703,85 +1702,6 @@ export async function saleUpdateCollection( req, res ) {
1703
1702
  }
1704
1703
  };
1705
1704
 
1706
- /**
1707
- * One-off migration: in checklistquestionsconfigs, when a question's answerType
1708
- * is 'multiplechoicesingle' and one of its answers has validationType
1709
- * 'Capture Image', replace it with 'Capture Multiple Image with description'.
1710
- *
1711
- * Document shape: { question: [ { answerType, answers: [ { validationType } ] } ] }
1712
- * Defaults to a dry run; pass apply:true (body) or ?apply=true to write changes.
1713
- * @param {Object} req Express request — body.apply / query.apply toggles writing
1714
- * @param {Object} res Express response
1715
- * @return {Promise<void>}
1716
- */
1717
- export async function updateMultipleChoiceSingleValidationType( req, res ) {
1718
- try {
1719
- const TARGET_ANSWER_TYPE = 'multiplechoicesingle';
1720
- const FROM_VALIDATION_TYPE = 'Capture Image';
1721
- const TO_VALIDATION_TYPE = 'Capture Multiple Image with description';
1722
-
1723
- const apply = req.body?.apply === true || req.query?.apply === 'true';
1724
-
1725
- // Only load docs that actually contain a matching question + answer.
1726
- const query = {
1727
- question: {
1728
- $elemMatch: {
1729
- answerType: TARGET_ANSWER_TYPE,
1730
- answers: { $elemMatch: { validationType: FROM_VALIDATION_TYPE } },
1731
- },
1732
- },
1733
- };
1734
-
1735
- const docs = await CLquestions.find( query );
1736
-
1737
- let documentsUpdated = 0;
1738
- let answersUpdated = 0;
1739
- const updatedIds = [];
1740
-
1741
- for ( const doc of docs ) {
1742
- const questions = Array.isArray( doc.question ) ? doc.question : [];
1743
- let changedInDoc = 0;
1744
-
1745
- for ( const q of questions ) {
1746
- if ( !q || q.answerType !== TARGET_ANSWER_TYPE ) continue;
1747
-
1748
- const answers = Array.isArray( q.answers ) ? q.answers : [];
1749
- for ( const a of answers ) {
1750
- if ( a && a.validationType === FROM_VALIDATION_TYPE ) {
1751
- a.validationType = TO_VALIDATION_TYPE;
1752
- changedInDoc++;
1753
- }
1754
- }
1755
- }
1756
-
1757
- if ( changedInDoc > 0 ) {
1758
- documentsUpdated++;
1759
- answersUpdated += changedInDoc;
1760
- updatedIds.push( doc._id );
1761
-
1762
- // `question` is a Mixed array, so write the whole rebuilt array.
1763
- if ( apply ) {
1764
- await CLquestions.updateMany( { _id: doc._id }, { question: questions } );
1765
- }
1766
- }
1767
- }
1768
-
1769
- return res.sendSuccess( {
1770
- message: apply ?
1771
- 'Validation type updated successfully' :
1772
- 'Dry run — pass apply:true to write these changes',
1773
- apply,
1774
- matchedDocuments: docs.length,
1775
- documentsUpdated,
1776
- answersUpdated,
1777
- updatedIds,
1778
- } );
1779
- } catch ( error ) {
1780
- logger.error( { function: 'updateMultipleChoiceSingleValidationType', error: error } );
1781
- return res.sendError( error, 500 );
1782
- }
1783
- };
1784
-
1785
1705
  export async function getUserStoreList( req, res ) {
1786
1706
  try {
1787
1707
  let details = [];
@@ -2515,6 +2435,7 @@ export async function internalAISendPushNotification( req, res ) {
2515
2435
  storeId: req.body?.storeId,
2516
2436
  date: req.body?.date,
2517
2437
  clientId: req.body?.clientId,
2438
+ ...( req.body?.checklistType && { checklistType: req.body?.checklistType } ),
2518
2439
  };
2519
2440
  let responseData = await sendAiPushNotification( userData.fcmToken, custom, userData.loginFrom );
2520
2441
  if ( responseData ) {
@@ -2768,69 +2689,22 @@ export async function countUpdateRunAI( req, res ) {
2768
2689
  if ( !req.body.id ) {
2769
2690
  return res.sendError( 'Checklist id is required', 400 );
2770
2691
  }
2771
- // if ( !req.body.runAICount ) {
2772
- // return res.sendError( 'runAICount is required', 400 );
2773
- // }
2692
+ if ( !req.body.runAICount ) {
2693
+ return res.sendError( 'runAICount is required', 400 );
2694
+ }
2774
2695
  let getDetails = await processedchecklist.findOne( { _id: req.body.id } );
2775
2696
  if ( !getDetails ) {
2776
2697
  return res.sendError( 'No data found', 204 );
2777
2698
  }
2778
2699
 
2779
2700
  let updateData = {
2780
- runAIFlag: req.body?.runAICount ?? 0,
2701
+ runAIFlag: req.body.runAICount,
2781
2702
  };
2782
2703
 
2783
- // complianceCount is already populated for the other answer types; add the image/video scores on top.
2784
- let complianceCount = getDetails.userComplianceCount || 0;
2785
-
2786
- // Pull every "Matched/Not Matched" verdict out of a runAIData array, tolerating both shapes we store:
2787
- // 1) [ { answerImage, results: [ { featureName, value } ] } ] (verdict nested under results)
2788
- // 2) [ { featureName, value } ] (flat verdict)
2789
- const extractMatchValues = ( runAIData ) => {
2790
- if ( !Array.isArray( runAIData ) ) return [];
2791
- const values = [];
2792
- runAIData.forEach( ( entry ) => {
2793
- const results = Array.isArray( entry?.results ) ? entry.results : [ entry ];
2794
- results.forEach( ( r ) => {
2795
- if ( r?.featureName === 'Matched/Not Matched' ) values.push( r?.value );
2796
- } );
2797
- } );
2798
- return values;
2799
- };
2800
-
2801
- getDetails.questionAnswers.forEach( ( section ) => {
2802
- section.questions.forEach( ( question ) => {
2803
- if ( question.compliance && [ 'image', 'image/video' ].includes( question.answerType ) ) {
2804
- // For 'image/video' each userAnswer carries its own answerType and runAIData only lives on
2805
- // the image entries; a pure 'image' question keeps runAIData directly on every userAnswer.
2806
- const aiAnswers = question.answerType === 'image/video' ?
2807
- ( question.userAnswer || [] ).filter( ( ua ) => ua?.answerType === 'image' ) :
2808
- ( question.userAnswer || [] );
2809
-
2810
- // Compliance comes from the first configured answer: every "Matched/Not Matched" verdict
2811
- // across the image answers must be True to count as matched, otherwise it's not matched.
2812
- const matchValues = aiAnswers.flatMap( ( ua ) => extractMatchValues( ua?.runAIData ) );
2813
- const allMatched = matchValues.length > 0 && matchValues.every( ( v ) => v === 'True' || v === true );
2814
- const score = allMatched ?
2815
- ( question.answers?.[0]?.matchedCount ?? 0 ) :
2816
- ( question.answers?.[0]?.notMatchedCount ?? 0 );
2817
-
2818
- // Persist the derived score back onto each userAnswer so PDF/dashboard reads stay consistent.
2819
- ( question.userAnswer || [] ).forEach( ( ua ) => {
2820
- ua.complianceScore = score;
2821
- } );
2822
-
2823
- complianceCount += score;
2824
- }
2825
- } );
2826
- } );
2827
-
2828
- updateData.complianceCount = complianceCount;
2829
-
2830
2704
  await processedchecklist.updateOne( { _id: req.body.id }, updateData );
2831
2705
  return res.sendSuccess( 'RunAI Count updated successfully' );
2832
2706
  } catch ( e ) {
2833
- logger.error( { functionName: 'countUpdateRunAI', error: e } );
2707
+ logger.error( { functionName: 'updateRunAI', error: e } );
2834
2708
  return res.sendError( e, 500 );
2835
2709
  }
2836
2710
  }
@@ -3667,38 +3541,47 @@ export async function runAIFlag( req, res ) {
3667
3541
  ],
3668
3542
  },
3669
3543
  }, { _id: 1, notifyFlags: 1, approver: 1 } );
3670
- let date = dayjs().format( 'YYYY-MM-DD' );
3544
+ let date = dayjs().subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
3671
3545
  if ( checklistDetails.length ) {
3672
3546
  await Promise.all( checklistDetails.map( async ( ele ) => {
3673
- let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, date_string: date, $or: [ { timeFlag: { $gt: 0 } }, { questionFlag: { $gt: 0 } }, { runAIFlag: { $gt: 0 } } ] }, { storeName: 1, checkListName: 1, userName: 1, submitTime_string: 1, runAIFlag: 1, questionFlag: 1, checklistStatus: 1 } );
3674
- let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
3675
- emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
3547
+ let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, checklistStatus: 'submit', date_string: date }, { storeName: 1, checkListName: 1, questionAnswers: 1 } );
3676
3548
  await Promise.all( submitDetails.map( ( store ) => {
3677
- let data = {
3678
- storeName: store.storeName,
3679
- flagCount: store.timeFlag ? 1 : store?.runAIFlag + store?.questionFlag,
3680
- runAIFlag: store?.runAIFlag,
3681
- questionFlag: store?.questionFlag,
3682
- checklistName: store.checkListName?.trim(),
3683
- submittedBy: store.timeFlag ? '--' : store?.userName,
3684
- time: store?.submitTime_string ?? '--',
3685
- domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
3686
- status: store.checklistStatus,
3687
- };
3688
- const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
3689
- const htmlContent = handlebars.compile( fileContent );
3690
- const html = htmlContent( { data: data } );
3691
- emailList.forEach( ( email ) => {
3692
- console.log( email );
3693
- let params = {
3694
- toEmail: email,
3695
- mailSubject: 'TangoEye | Checklist Flag',
3696
- htmlBody: html,
3697
- attachment: '',
3698
- sourceEmail: JSON.parse( process.env.SES ).adminEmail,
3699
- };
3700
- sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
3701
- } );
3549
+ if ( store.questionAnswers.length ) {
3550
+ let runAIFlag = 0;
3551
+ store.questionAnswers.forEach( ( section ) => {
3552
+ section.questions.forEach( ( question ) => {
3553
+ if ( question.answerType == 'image' && ( question?.userAnswer?.[0]?.runAIData?.value == 'False' || !question?.userAnswer?.[0]?.runAIData?.value ) ) {
3554
+ runAIFlag++;
3555
+ }
3556
+ } );
3557
+ } );
3558
+ if ( runAIFlag ) {
3559
+ let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
3560
+ emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
3561
+ let data = {
3562
+ storeName: store.storeName,
3563
+ flagCount: 1,
3564
+ checklistName: store.checkListName?.trim(),
3565
+ submittedBy: '--',
3566
+ time: '--',
3567
+ domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
3568
+ type: 'delay',
3569
+ };
3570
+ const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
3571
+ const htmlContent = handlebars.compile( fileContent );
3572
+ const html = htmlContent( { data: data } );
3573
+ emailList.forEach( ( email ) => {
3574
+ let params = {
3575
+ toEmail: email,
3576
+ mailSubject: 'TangoEye | Checklist Flag',
3577
+ htmlBody: html,
3578
+ attachment: '',
3579
+ sourceEmail: JSON.parse( process.env.SES ).adminEmail,
3580
+ };
3581
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
3582
+ } );
3583
+ }
3584
+ }
3702
3585
  } ) );
3703
3586
  } ) );
3704
3587
  }
@@ -3746,36 +3629,31 @@ export const downloadInsertPdf = async ( req, res ) => {
3746
3629
  const safeName = ( str ) =>
3747
3630
  ( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
3748
3631
 
3749
- // const query = {
3750
- // query: {
3751
- // bool: {
3752
- // must: [
3753
- // {
3754
- // term: {
3755
- // _id: req.body.checklistId,
3756
- // },
3757
- // },
3758
- // ],
3759
- // },
3760
- // },
3761
- // };
3762
-
3632
+ const query = {
3633
+ query: {
3634
+ bool: {
3635
+ must: [
3636
+ {
3637
+ term: {
3638
+ _id: req.body.checklistId,
3639
+ },
3640
+ },
3641
+ ],
3642
+ },
3643
+ },
3644
+ };
3763
3645
 
3764
3646
  // 1) Launch browser page + fetch OpenSearch data in parallel
3765
3647
  const [ browser, aiDetails ] = await Promise.all( [
3766
3648
  getBrowserInstance(),
3767
- // getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3768
- processedchecklist.findOne( { _id: req.body.checklistId } ),
3649
+ getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3769
3650
  ] );
3770
- console.log( aiDetails );
3771
3651
 
3772
- if ( !aiDetails ) {
3652
+ if ( aiDetails?.statusCode != 200 || !aiDetails?.body?.hits?.hits.length ) {
3773
3653
  return res.sendError( 'Checklist not found', 404 );
3774
3654
  }
3775
3655
 
3776
- const doc = { ...aiDetails?.toObject() };
3777
-
3778
- console.log( doc );
3656
+ const doc = { ...aiDetails.body.hits.hits[0]._source };
3779
3657
 
3780
3658
  // 2) Fetch brandInfo + compliance data in parallel
3781
3659
  const complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
@@ -1765,7 +1765,6 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1765
1765
  } else {
1766
1766
  if ( qaans[k].validationType == 'Capture Multiple Image with description' ) {
1767
1767
  qaans[k]['validationImage']=[];
1768
- qaans[k]['validationVideo']=[];
1769
1768
  if ( requestSection[i].validationImage.length ) {
1770
1769
  for ( let image of requestSection[i].validationImage ) {
1771
1770
  let validationAnswer = decodeURIComponent( image.split( '?' )[0] );
@@ -1778,18 +1777,6 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1778
1777
  };
1779
1778
  }
1780
1779
  }
1781
- if ( requestSection[i]?.validationVideo?.length ) {
1782
- for ( let video of requestSection[i].validationVideo ) {
1783
- let validationAnswer = decodeURIComponent( video.split( '?' )[0] );
1784
- if ( validationAnswer.length ) {
1785
- let splitImgUrl = validationAnswer.split( '/' );
1786
- if ( splitImgUrl.length > 1 ) {
1787
- splitImgUrl.splice( 0, 3 );
1788
- qaans[k].validationVideo.push( splitImgUrl.join( '/' ) || '' );
1789
- }
1790
- };
1791
- }
1792
- }
1793
1780
  }
1794
1781
  // qaans[k].descriptivetype = qaAnswers[j].descriptivetype || '';
1795
1782
  qaans[k].validationAnswer = requestSection[i].validationAnswer || '';
@@ -2140,18 +2127,12 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2140
2127
  logger.error( { message: requestData.questionAnswers, error: 'QuestionanswersPayload' } );
2141
2128
 
2142
2129
  if ( requestData.submittype == 'submit' ) {
2143
- requestData.userComplianceCount = 0;
2144
2130
  for ( let section of sectionFormat ) {
2145
2131
  for ( let question of section.questions ) {
2146
2132
  let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
2147
2133
  if ( mustValidate && ( !question.userAnswer || !question.userAnswer.length ) ) {
2148
2134
  return res.sendError( 'Please Fill All Fields', 400 );
2149
2135
  }
2150
- if ( question.compliance ) {
2151
- const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
2152
- let score = scores.length ? Math.max( ...scores ) : 0;
2153
- requestData.userComplianceCount += score;
2154
- }
2155
2136
  }
2156
2137
  }
2157
2138
  }
@@ -2795,15 +2776,6 @@ export async function submitChecklist( req, res ) {
2795
2776
  updateData.checklistStatus = 'submit';
2796
2777
  updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
2797
2778
  updateData.submitMobileTime = requestData?.currentTime;
2798
- updateData.userComplianceCount = requestData?.userComplianceCount;
2799
- updateData.userSignature = requestData?.userSignature;
2800
- if ( requestData?.userImage ) {
2801
- let splitImgUrl = requestData?.userImage.split( '/' );
2802
- if ( splitImgUrl.length > 1 ) {
2803
- splitImgUrl.splice( 0, 3 );
2804
- updateData.userImage = splitImgUrl.join( '/' ) || '';
2805
- }
2806
- }
2807
2779
  }
2808
2780
  // updateData.questionAnswers.forEach( ( section ) => {
2809
2781
  // section.questions.forEach( ( question ) => {
@@ -2904,34 +2876,34 @@ export async function submitChecklist( req, res ) {
2904
2876
  userType: req.user.userType,
2905
2877
  };
2906
2878
  insertOpenSearchData( openSearch.traxActivityLog, inserttraxlogs );
2907
- // if ( updateData.questionFlag ) {
2908
- // let checklistDetails = await checklistService.findOne( { _id: getchecklist[0].sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
2909
- // if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
2910
- // let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
2911
- // emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
2912
- // let data = {
2913
- // storeName: getchecklist[0].storeName,
2914
- // flagCount: updateData.questionFlag,
2915
- // checklistName: getchecklist[0].checkListName,
2916
- // submittedBy: getchecklist[0].userName,
2917
- // time: updateData.submitTime_string,
2918
- // domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
2919
- // };
2920
- // const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
2921
- // const htmlContent = handlebars.compile( fileContent );
2922
- // const html = htmlContent( { data: data } );
2923
- // emailList.forEach( ( email ) => {
2924
- // let params = {
2925
- // toEmail: email,
2926
- // mailSubject: 'TangoEye | Checklist Flag',
2927
- // htmlBody: html,
2928
- // attachment: '',
2929
- // sourceEmail: JSON.parse( process.env.SES ).adminEmail,
2930
- // };
2931
- // sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2932
- // } );
2933
- // }
2934
- // }
2879
+ if ( updateData.questionFlag ) {
2880
+ let checklistDetails = await checklistService.findOne( { _id: getchecklist[0].sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
2881
+ if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
2882
+ let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
2883
+ emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
2884
+ let data = {
2885
+ storeName: getchecklist[0].storeName,
2886
+ flagCount: updateData.questionFlag,
2887
+ checklistName: getchecklist[0].checkListName,
2888
+ submittedBy: getchecklist[0].userName,
2889
+ time: updateData.submitTime_string,
2890
+ domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
2891
+ };
2892
+ const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
2893
+ const htmlContent = handlebars.compile( fileContent );
2894
+ const html = htmlContent( { data: data } );
2895
+ emailList.forEach( ( email ) => {
2896
+ let params = {
2897
+ toEmail: email,
2898
+ mailSubject: 'TangoEye | Checklist Flag',
2899
+ htmlBody: html,
2900
+ attachment: '',
2901
+ sourceEmail: JSON.parse( process.env.SES ).adminEmail,
2902
+ };
2903
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
2904
+ } );
2905
+ }
2906
+ }
2935
2907
  }
2936
2908
  if ( getchecklist?.[0]?.redoEdit?.toString() != requestData?.redoEdit ) {
2937
2909
  return res.sendSuccess( 'New Questions Added So,Checklist moved back to In Progress. Please complete the updated checklist.' );
@@ -4205,9 +4177,6 @@ export async function questionList( req, res ) {
4205
4177
  streamId: { $ifNull: [ '$streamId', '' ] },
4206
4178
  export: { $ifNull: [ '$export', false ] },
4207
4179
  redoEdit: 1,
4208
- userVerification: 1,
4209
- userSignature: 1,
4210
- userImage: 1,
4211
4180
  },
4212
4181
  } );
4213
4182
 
@@ -4218,7 +4187,6 @@ export async function questionList( req, res ) {
4218
4187
  logger.info( `v5 => Checklist Continue => store Name: ${getchecklist[0].storeName}, User Email: ${getchecklist[0].userEmail}, Checklist Name: ${getchecklist[0].checkListName}` );
4219
4188
  // let bucket = JSON.parse( process.env.BUCKET );
4220
4189
  let cdnurl = JSON.parse( process.env.CDNURL );
4221
- getchecklist[0].userImage = `${cdnurl}/${getchecklist[0].userImage}`;
4222
4190
  for ( let [ secIndex, section ] of getchecklist[0].questionAnswers.entries() ) {
4223
4191
  for ( let [ questionIndex, question ] of section.questions.entries() ) {
4224
4192
  let Multianswer = [];
@@ -4274,15 +4242,10 @@ export async function questionList( req, res ) {
4274
4242
  }
4275
4243
  if ( userAns.validationType == 'Capture Multiple Image with description' ) {
4276
4244
  let imageAnswers = userAns.validationImage;
4277
- getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationImage = [];
4245
+ userAns.validationImage = [];
4278
4246
  for ( let image of imageAnswers ) {
4279
4247
  getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationImage.push( `${cdnurl.TraxAnswerCDN}${image}` );
4280
4248
  }
4281
- let videoAnswer = userAns.validationVideo;
4282
- getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationVideo = [];
4283
- for ( let image of videoAnswer ) {
4284
- getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationVideo.push( `${cdnurl.TraxAnswerCDN}${image}` );
4285
- }
4286
4249
  }
4287
4250
  if ( [ 'image', 'descriptiveImage', 'video' ].includes( question.answerType ) && userAns.answer != '' ) {
4288
4251
  getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].answer =`${cdnurl.TraxAnswerCDN}${userAns.answer}`;
@@ -5527,9 +5490,6 @@ export async function downloadChecklist( req, res ) {
5527
5490
  if ( [ 'Capture Multiple Image with description' ].includes( answer.validationType ) && answer?.validationImage?.length ) {
5528
5491
  answer.validationImage = answer?.validationImage?.map( ( ele ) => JSON.parse( process.env.CDNURL )?.TraxAnswerCDN+ele );
5529
5492
  }
5530
- if ( [ 'Capture Multiple Image with description' ].includes( answer.validationType ) && answer?.validationVideo?.length ) {
5531
- answer.validationVideo = answer?.validationVideo?.map( ( ele ) => JSON.parse( process.env.CDNURL )?.TraxAnswerCDN+ele );
5532
- }
5533
5493
  if ( answer?.answer?.trim() && [ 'image', 'descriptiveImage', 'multipleImage', 'image/video', 'video' ].includes( question.answerType ) ) {
5534
5494
  answer.answer = JSON.parse( process.env.CDNURL )?.TraxAnswerCDN+answer.answer;
5535
5495
  }