tango-app-api-trax 3.9.39 → 3.9.41

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 (51) hide show
  1. package/index.js +2 -1
  2. package/package.json +1 -1
  3. package/src/controllers/internalTrax.controller.js +225 -94
  4. package/src/controllers/mobileTrax.controller.js +69 -29
  5. package/src/controllers/trax.controller.js +3 -2
  6. package/src/controllers/traxDashboard.controllers.js +69 -55
  7. package/src/hbs/flag.hbs +1 -1
  8. package/src/hbs/template.hbs +7 -0
  9. package/src/hbs/visit-checklist.hbs +77 -93
  10. package/src/logging/activityLogFlusher.js +59 -0
  11. package/src/logging/activityLogMiddleware.js +45 -0
  12. package/src/logging/activityLogStore.js +91 -0
  13. package/src/logging/compressBatches.js +83 -0
  14. package/src/logging/config.js +24 -0
  15. package/src/logging/createLoggableService.js +46 -0
  16. package/src/logging/logExternalCall.js +37 -0
  17. package/src/routes/internalTraxApi.router.js +1 -0
  18. package/src/services/app.service.js +15 -9
  19. package/src/services/approver.service.js +23 -15
  20. package/src/services/authentication.service.js +9 -3
  21. package/src/services/camera.service.js +19 -13
  22. package/src/services/checklist.service.js +35 -27
  23. package/src/services/checklistAssign.service.js +43 -38
  24. package/src/services/checklistQuestion.service.js +39 -34
  25. package/src/services/checklistlog.service.js +39 -34
  26. package/src/services/clientRequest.service.js +9 -2
  27. package/src/services/clients.services.js +23 -18
  28. package/src/services/cluster.service.js +31 -23
  29. package/src/services/domain.service.js +23 -18
  30. package/src/services/download.services.js +35 -25
  31. package/src/services/group.service.js +23 -17
  32. package/src/services/lenskartEmployeeMapping.service.js +15 -10
  33. package/src/services/locus.service.js +35 -28
  34. package/src/services/notification.service.js +35 -26
  35. package/src/services/otp.service.js +20 -13
  36. package/src/services/planogram.service.js +9 -2
  37. package/src/services/processedTaskConfig.service.js +35 -27
  38. package/src/services/processedTaskList.service.js +32 -26
  39. package/src/services/processedchecklist.services.js +55 -47
  40. package/src/services/processedchecklistconfig.services.js +39 -34
  41. package/src/services/recurringFlagTracker.service.js +39 -32
  42. package/src/services/runAIFeatures.services.js +32 -27
  43. package/src/services/runAIRequest.services.js +43 -38
  44. package/src/services/store.service.js +32 -27
  45. package/src/services/tagging.service.js +9 -2
  46. package/src/services/taskConfig.service.js +35 -27
  47. package/src/services/teams.service.js +35 -24
  48. package/src/services/ticket.service.js +15 -10
  49. package/src/services/user.service.js +27 -20
  50. package/src/services/userAssignedstores.service.js +12 -5
  51. package/src/utils/visitChecklistPdf.utils.js +449 -21
package/index.js CHANGED
@@ -9,5 +9,6 @@ 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';
12
13
 
13
- export { traxDashboardRouter, traxFlagRouter, traxRouter, galleryRouter, downloadRouter, mobileRouter, internalTraxRouter, locusOrderRouter, traxActivityLogRouter };
14
+ export { traxDashboardRouter, traxFlagRouter, traxRouter, galleryRouter, downloadRouter, mobileRouter, internalTraxRouter, locusOrderRouter, traxActivityLogRouter, traxActivityLogMiddleware };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.39",
3
+ "version": "3.9.41",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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, getOpenSearchData } from 'tango-app-api-middleware';
21
+ import { sendPushNotification, sendAiPushNotification, sendEmailWithSES, signedUrl, fileUpload } 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,6 +624,7 @@ 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;
627
628
  }
628
629
  if ( userIdList.length ) {
629
630
  allQuestion = allQuestion.filter( ( item ) => typeof item._id == 'undefined' );
@@ -1333,6 +1334,7 @@ export async function insertTimeDelayFlags( req, res ) {
1333
1334
  checkListName: 1,
1334
1335
  client_id: 1,
1335
1336
  date_iso: 1,
1337
+ coverage: 1,
1336
1338
  },
1337
1339
  } );
1338
1340
 
@@ -1358,6 +1360,13 @@ export async function insertTimeDelayFlags( req, res ) {
1358
1360
  { storeId: data.store_id, clientId: data.client_id },
1359
1361
  { storeProfile: 1 },
1360
1362
  );
1363
+ if ( data.coverage == 'user' ) {
1364
+ getStoreZone = {
1365
+ storeProfile: {
1366
+ timeZone: 'Asia/Kolkata',
1367
+ },
1368
+ };
1369
+ }
1361
1370
  if ( getStoreZone ) {
1362
1371
  let checklistDate = dayjs.utc( data.scheduleEndTime_iso ).format();
1363
1372
  let date = dayjs.utc().tz( getStoreZone.storeProfile.timeZone ).format();
@@ -1383,40 +1392,40 @@ export async function insertTimeDelayFlags( req, res ) {
1383
1392
  let updatedDetails = await processedchecklist.updateMany( { _id: { $in: checklistId } }, { timeFlag: 1 } );
1384
1393
 
1385
1394
 
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
- }
1395
+ // let notifyStores = await processedchecklist.find( { _id: { $in: checklistId }, notify: { $exists: false } } );
1396
+
1397
+ // if ( notifyStores.length ) {
1398
+ // Promise.all( notifyStores.map( async ( store ) => {
1399
+ // let checklistDetails = await CLconfig.findOne( { _id: store.sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
1400
+ // if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
1401
+ // let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
1402
+ // emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
1403
+ // let data = {
1404
+ // storeName: store.storeName,
1405
+ // flagCount: 1,
1406
+ // checklistName: store.checkListName?.trim(),
1407
+ // submittedBy: '--',
1408
+ // time: '--',
1409
+ // domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
1410
+ // type: 'delay',
1411
+ // };
1412
+ // const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
1413
+ // const htmlContent = handlebars.compile( fileContent );
1414
+ // const html = htmlContent( { data: data } );
1415
+ // emailList.forEach( ( email ) => {
1416
+ // let params = {
1417
+ // toEmail: email,
1418
+ // mailSubject: 'TangoEye | Checklist Flag',
1419
+ // htmlBody: html,
1420
+ // attachment: '',
1421
+ // sourceEmail: JSON.parse( process.env.SES ).adminEmail,
1422
+ // };
1423
+ // sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
1424
+ // } );
1425
+ // }
1426
+ // await processedchecklist.updateOne( { _id: store._id }, { notify: true } );
1427
+ // } ) );
1428
+ // }
1420
1429
 
1421
1430
  if ( updatedDetails ) {
1422
1431
  return res.sendSuccess( { message: 'Time Delay Updated Successfully' } );
@@ -1702,6 +1711,85 @@ export async function saleUpdateCollection( req, res ) {
1702
1711
  }
1703
1712
  };
1704
1713
 
1714
+ /**
1715
+ * One-off migration: in checklistquestionsconfigs, when a question's answerType
1716
+ * is 'multiplechoicesingle' and one of its answers has validationType
1717
+ * 'Capture Image', replace it with 'Capture Multiple Image with description'.
1718
+ *
1719
+ * Document shape: { question: [ { answerType, answers: [ { validationType } ] } ] }
1720
+ * Defaults to a dry run; pass apply:true (body) or ?apply=true to write changes.
1721
+ * @param {Object} req Express request — body.apply / query.apply toggles writing
1722
+ * @param {Object} res Express response
1723
+ * @return {Promise<void>}
1724
+ */
1725
+ export async function updateMultipleChoiceSingleValidationType( req, res ) {
1726
+ try {
1727
+ const TARGET_ANSWER_TYPE = 'multiplechoicesingle';
1728
+ const FROM_VALIDATION_TYPE = 'Capture Image';
1729
+ const TO_VALIDATION_TYPE = 'Capture Multiple Image with description';
1730
+
1731
+ const apply = req.body?.apply === true || req.query?.apply === 'true';
1732
+
1733
+ // Only load docs that actually contain a matching question + answer.
1734
+ const query = {
1735
+ question: {
1736
+ $elemMatch: {
1737
+ answerType: TARGET_ANSWER_TYPE,
1738
+ answers: { $elemMatch: { validationType: FROM_VALIDATION_TYPE } },
1739
+ },
1740
+ },
1741
+ };
1742
+
1743
+ const docs = await CLquestions.find( query );
1744
+
1745
+ let documentsUpdated = 0;
1746
+ let answersUpdated = 0;
1747
+ const updatedIds = [];
1748
+
1749
+ for ( const doc of docs ) {
1750
+ const questions = Array.isArray( doc.question ) ? doc.question : [];
1751
+ let changedInDoc = 0;
1752
+
1753
+ for ( const q of questions ) {
1754
+ if ( !q || q.answerType !== TARGET_ANSWER_TYPE ) continue;
1755
+
1756
+ const answers = Array.isArray( q.answers ) ? q.answers : [];
1757
+ for ( const a of answers ) {
1758
+ if ( a && a.validationType === FROM_VALIDATION_TYPE ) {
1759
+ a.validationType = TO_VALIDATION_TYPE;
1760
+ changedInDoc++;
1761
+ }
1762
+ }
1763
+ }
1764
+
1765
+ if ( changedInDoc > 0 ) {
1766
+ documentsUpdated++;
1767
+ answersUpdated += changedInDoc;
1768
+ updatedIds.push( doc._id );
1769
+
1770
+ // `question` is a Mixed array, so write the whole rebuilt array.
1771
+ if ( apply ) {
1772
+ await CLquestions.updateMany( { _id: doc._id }, { question: questions } );
1773
+ }
1774
+ }
1775
+ }
1776
+
1777
+ return res.sendSuccess( {
1778
+ message: apply ?
1779
+ 'Validation type updated successfully' :
1780
+ 'Dry run — pass apply:true to write these changes',
1781
+ apply,
1782
+ matchedDocuments: docs.length,
1783
+ documentsUpdated,
1784
+ answersUpdated,
1785
+ updatedIds,
1786
+ } );
1787
+ } catch ( error ) {
1788
+ logger.error( { function: 'updateMultipleChoiceSingleValidationType', error: error } );
1789
+ return res.sendError( error, 500 );
1790
+ }
1791
+ };
1792
+
1705
1793
  export async function getUserStoreList( req, res ) {
1706
1794
  try {
1707
1795
  let details = [];
@@ -2689,22 +2777,69 @@ export async function countUpdateRunAI( req, res ) {
2689
2777
  if ( !req.body.id ) {
2690
2778
  return res.sendError( 'Checklist id is required', 400 );
2691
2779
  }
2692
- if ( !req.body.runAICount ) {
2693
- return res.sendError( 'runAICount is required', 400 );
2694
- }
2780
+ // if ( !req.body.runAICount ) {
2781
+ // return res.sendError( 'runAICount is required', 400 );
2782
+ // }
2695
2783
  let getDetails = await processedchecklist.findOne( { _id: req.body.id } );
2696
2784
  if ( !getDetails ) {
2697
2785
  return res.sendError( 'No data found', 204 );
2698
2786
  }
2699
2787
 
2700
2788
  let updateData = {
2701
- runAIFlag: req.body.runAICount,
2789
+ runAIFlag: req.body?.runAICount ?? 0,
2702
2790
  };
2703
2791
 
2792
+ // complianceCount is already populated for the other answer types; add the image/video scores on top.
2793
+ let userComplianceTotal = getDetails.userComplianceCount || 0;
2794
+
2795
+ // Pull every "Matched/Not Matched" verdict out of a runAIData array, tolerating both shapes we store:
2796
+ // 1) [ { answerImage, results: [ { featureName, value } ] } ] (verdict nested under results)
2797
+ // 2) [ { featureName, value } ] (flat verdict)
2798
+ const extractMatchValues = ( runAIData ) => {
2799
+ if ( !Array.isArray( runAIData ) ) return [];
2800
+ const values = [];
2801
+ runAIData.forEach( ( entry ) => {
2802
+ const results = Array.isArray( entry?.results ) ? entry.results : [ entry ];
2803
+ results.forEach( ( r ) => {
2804
+ if ( r?.featureName === 'Matched/Not Matched' ) values.push( r?.value );
2805
+ } );
2806
+ } );
2807
+ return values;
2808
+ };
2809
+
2810
+ getDetails.questionAnswers.forEach( ( section ) => {
2811
+ section.questions.forEach( ( question ) => {
2812
+ if ( question.compliance && [ 'image', 'image/video' ].includes( question.answerType ) && question?.answers?.[0]?.multiReferenceImage?.some( ( img ) => img.runAI ) ) {
2813
+ // For 'image/video' each userAnswer carries its own answerType and runAIData only lives on
2814
+ // the image entries; a pure 'image' question keeps runAIData directly on every userAnswer.
2815
+ const aiAnswers = question.answerType === 'image/video' ?
2816
+ ( question.userAnswer || [] ).filter( ( ua ) => ua?.answerType === 'image' ) :
2817
+ ( question.userAnswer || [] );
2818
+
2819
+ // Compliance comes from the first configured answer: every "Matched/Not Matched" verdict
2820
+ // across the image answers must be True to count as matched, otherwise it's not matched.
2821
+ const matchValues = aiAnswers.flatMap( ( ua ) => extractMatchValues( ua?.runAIData ) );
2822
+ const allMatched = matchValues.length > 0 && matchValues.every( ( v ) => v === 'True' || v === true );
2823
+ const score = allMatched ?
2824
+ ( question.answers?.[0]?.matchedCount ?? 0 ) :
2825
+ ( question.answers?.[0]?.notMatchedCount ?? 0 );
2826
+
2827
+ // Persist the derived score back onto each userAnswer so PDF/dashboard reads stay consistent.
2828
+ ( question.userAnswer || [] ).forEach( ( ua ) => {
2829
+ ua.complianceScore = score;
2830
+ } );
2831
+
2832
+ userComplianceTotal += score;
2833
+ }
2834
+ } );
2835
+ } );
2836
+
2837
+ updateData.userComplianceCount = userComplianceTotal;
2838
+
2704
2839
  await processedchecklist.updateOne( { _id: req.body.id }, updateData );
2705
2840
  return res.sendSuccess( 'RunAI Count updated successfully' );
2706
2841
  } catch ( e ) {
2707
- logger.error( { functionName: 'updateRunAI', error: e } );
2842
+ logger.error( { functionName: 'countUpdateRunAI', error: e } );
2708
2843
  return res.sendError( e, 500 );
2709
2844
  }
2710
2845
  }
@@ -3541,47 +3676,38 @@ export async function runAIFlag( req, res ) {
3541
3676
  ],
3542
3677
  },
3543
3678
  }, { _id: 1, notifyFlags: 1, approver: 1 } );
3544
- let date = dayjs().subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
3679
+ let date = dayjs().format( 'YYYY-MM-DD' );
3545
3680
  if ( checklistDetails.length ) {
3546
3681
  await Promise.all( checklistDetails.map( async ( ele ) => {
3547
- let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, checklistStatus: 'submit', date_string: date }, { storeName: 1, checkListName: 1, questionAnswers: 1 } );
3682
+ 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 } );
3683
+ let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
3684
+ emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
3548
3685
  await Promise.all( submitDetails.map( ( store ) => {
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
- }
3686
+ let data = {
3687
+ storeName: store.storeName,
3688
+ flagCount: store.timeFlag ? 1 : store?.runAIFlag + store?.questionFlag,
3689
+ runAIFlag: store?.runAIFlag,
3690
+ questionFlag: store?.questionFlag,
3691
+ checklistName: store.checkListName?.trim(),
3692
+ submittedBy: store.timeFlag ? '--' : store?.userName,
3693
+ time: store.timeFlag ? '--' : store?.submitTime_string,
3694
+ domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
3695
+ status: store.checklistStatus,
3696
+ };
3697
+ const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
3698
+ const htmlContent = handlebars.compile( fileContent );
3699
+ const html = htmlContent( { data: data } );
3700
+ emailList.forEach( ( email ) => {
3701
+ console.log( email );
3702
+ let params = {
3703
+ toEmail: email,
3704
+ mailSubject: 'TangoEye | Checklist Flag',
3705
+ htmlBody: html,
3706
+ attachment: '',
3707
+ sourceEmail: JSON.parse( process.env.SES ).adminEmail,
3708
+ };
3709
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
3710
+ } );
3585
3711
  } ) );
3586
3712
  } ) );
3587
3713
  }
@@ -3629,31 +3755,36 @@ export const downloadInsertPdf = async ( req, res ) => {
3629
3755
  const safeName = ( str ) =>
3630
3756
  ( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
3631
3757
 
3632
- const query = {
3633
- query: {
3634
- bool: {
3635
- must: [
3636
- {
3637
- term: {
3638
- _id: req.body.checklistId,
3639
- },
3640
- },
3641
- ],
3642
- },
3643
- },
3644
- };
3758
+ // const query = {
3759
+ // query: {
3760
+ // bool: {
3761
+ // must: [
3762
+ // {
3763
+ // term: {
3764
+ // _id: req.body.checklistId,
3765
+ // },
3766
+ // },
3767
+ // ],
3768
+ // },
3769
+ // },
3770
+ // };
3771
+
3645
3772
 
3646
3773
  // 1) Launch browser page + fetch OpenSearch data in parallel
3647
3774
  const [ browser, aiDetails ] = await Promise.all( [
3648
3775
  getBrowserInstance(),
3649
- getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3776
+ // getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3777
+ processedchecklist.findOne( { _id: req.body.checklistId } ),
3650
3778
  ] );
3779
+ console.log( aiDetails );
3651
3780
 
3652
- if ( aiDetails?.statusCode != 200 || !aiDetails?.body?.hits?.hits.length ) {
3781
+ if ( !aiDetails ) {
3653
3782
  return res.sendError( 'Checklist not found', 404 );
3654
3783
  }
3655
3784
 
3656
- const doc = { ...aiDetails.body.hits.hits[0]._source };
3785
+ const doc = { ...aiDetails?.toObject() };
3786
+
3787
+ console.log( doc );
3657
3788
 
3658
3789
  // 2) Fetch brandInfo + compliance data in parallel
3659
3790
  const complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
@@ -1765,6 +1765,7 @@ 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']=[];
1768
1769
  if ( requestSection[i].validationImage.length ) {
1769
1770
  for ( let image of requestSection[i].validationImage ) {
1770
1771
  let validationAnswer = decodeURIComponent( image.split( '?' )[0] );
@@ -1777,6 +1778,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1777
1778
  };
1778
1779
  }
1779
1780
  }
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
+ }
1780
1793
  }
1781
1794
  // qaans[k].descriptivetype = qaAnswers[j].descriptivetype || '';
1782
1795
  qaans[k].validationAnswer = requestSection[i].validationAnswer || '';
@@ -2127,12 +2140,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2127
2140
  logger.error( { message: requestData.questionAnswers, error: 'QuestionanswersPayload' } );
2128
2141
 
2129
2142
  if ( requestData.submittype == 'submit' ) {
2143
+ requestData.userComplianceCount = 0;
2130
2144
  for ( let section of sectionFormat ) {
2131
2145
  for ( let question of section.questions ) {
2132
2146
  let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
2133
2147
  if ( mustValidate && ( !question.userAnswer || !question.userAnswer.length ) ) {
2134
2148
  return res.sendError( 'Please Fill All Fields', 400 );
2135
2149
  }
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
+ }
2136
2155
  }
2137
2156
  }
2138
2157
  }
@@ -2776,6 +2795,15 @@ export async function submitChecklist( req, res ) {
2776
2795
  updateData.checklistStatus = 'submit';
2777
2796
  updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
2778
2797
  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
+ }
2779
2807
  }
2780
2808
  // updateData.questionAnswers.forEach( ( section ) => {
2781
2809
  // section.questions.forEach( ( question ) => {
@@ -2876,34 +2904,34 @@ export async function submitChecklist( req, res ) {
2876
2904
  userType: req.user.userType,
2877
2905
  };
2878
2906
  insertOpenSearchData( openSearch.traxActivityLog, inserttraxlogs );
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
- }
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
+ // }
2907
2935
  }
2908
2936
  if ( getchecklist?.[0]?.redoEdit?.toString() != requestData?.redoEdit ) {
2909
2937
  return res.sendSuccess( 'New Questions Added So,Checklist moved back to In Progress. Please complete the updated checklist.' );
@@ -4177,6 +4205,9 @@ export async function questionList( req, res ) {
4177
4205
  streamId: { $ifNull: [ '$streamId', '' ] },
4178
4206
  export: { $ifNull: [ '$export', false ] },
4179
4207
  redoEdit: 1,
4208
+ userVerification: 1,
4209
+ userSignature: 1,
4210
+ userImage: 1,
4180
4211
  },
4181
4212
  } );
4182
4213
 
@@ -4187,6 +4218,7 @@ export async function questionList( req, res ) {
4187
4218
  logger.info( `v5 => Checklist Continue => store Name: ${getchecklist[0].storeName}, User Email: ${getchecklist[0].userEmail}, Checklist Name: ${getchecklist[0].checkListName}` );
4188
4219
  // let bucket = JSON.parse( process.env.BUCKET );
4189
4220
  let cdnurl = JSON.parse( process.env.CDNURL );
4221
+ getchecklist[0].userImage = `${cdnurl}/${getchecklist[0].userImage}`;
4190
4222
  for ( let [ secIndex, section ] of getchecklist[0].questionAnswers.entries() ) {
4191
4223
  for ( let [ questionIndex, question ] of section.questions.entries() ) {
4192
4224
  let Multianswer = [];
@@ -4242,10 +4274,15 @@ export async function questionList( req, res ) {
4242
4274
  }
4243
4275
  if ( userAns.validationType == 'Capture Multiple Image with description' ) {
4244
4276
  let imageAnswers = userAns.validationImage;
4245
- userAns.validationImage = [];
4277
+ getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationImage = [];
4246
4278
  for ( let image of imageAnswers ) {
4247
4279
  getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].validationImage.push( `${cdnurl.TraxAnswerCDN}${image}` );
4248
4280
  }
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
+ }
4249
4286
  }
4250
4287
  if ( [ 'image', 'descriptiveImage', 'video' ].includes( question.answerType ) && userAns.answer != '' ) {
4251
4288
  getchecklist[0].questionAnswers[secIndex].questions[questionIndex].userAnswer[userAnsIndex].answer =`${cdnurl.TraxAnswerCDN}${userAns.answer}`;
@@ -5490,6 +5527,9 @@ export async function downloadChecklist( req, res ) {
5490
5527
  if ( [ 'Capture Multiple Image with description' ].includes( answer.validationType ) && answer?.validationImage?.length ) {
5491
5528
  answer.validationImage = answer?.validationImage?.map( ( ele ) => JSON.parse( process.env.CDNURL )?.TraxAnswerCDN+ele );
5492
5529
  }
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
+ }
5493
5533
  if ( answer?.answer?.trim() && [ 'image', 'descriptiveImage', 'multipleImage', 'image/video', 'video' ].includes( question.answerType ) ) {
5494
5534
  answer.answer = JSON.parse( process.env.CDNURL )?.TraxAnswerCDN+answer.answer;
5495
5535
  }
@@ -625,6 +625,7 @@ export const zoneList = async ( req, res ) => {
625
625
  const allowedChecklistsStreams = [
626
626
  'Camera Angle Change Compliance',
627
627
  ];
628
+ console.log( allowedChecklists.includes( inputBody.checkListName ) );
628
629
 
629
630
  if ( inputBody.checkListName && allowedChecklists.includes( inputBody.checkListName ) ) {
630
631
  console.log( '==================' );
@@ -1919,7 +1920,6 @@ export const updateConfigure = async ( req, res ) => {
1919
1920
 
1920
1921
  export const updateConfigurev1 = async ( req, res ) => {
1921
1922
  try {
1922
- console.log( '123' );
1923
1923
  let inputBody = req.body;
1924
1924
  let id;
1925
1925
  let checklistDetails;
@@ -3977,6 +3977,7 @@ async function insertPCBulkV4( getCLconfig, checklistId, currentdate, updatedche
3977
3977
  element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
3978
3978
  element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
3979
3979
  element4.complianceCount = getCLconfig?.complianceCount || 0;
3980
+ element4.userVerification = getCLconfig?.userVerification;
3980
3981
  assignUserList.push( { ...element4 } );
3981
3982
  }
3982
3983
  } ) );
@@ -4043,7 +4044,7 @@ async function insertPCBulkV4( getCLconfig, checklistId, currentdate, updatedche
4043
4044
  }, { userId: 1, store_id: 1 } );
4044
4045
 
4045
4046
  if ( inprogressData.length ) {
4046
- await processedchecklist.updateMany( { _id: { $in: inprogressData.map( ( ele ) => new ObjectId( ele._id ) ) } }, { scheduleEndTime: getCLconfig.scheduleEndTime, scheduleEndTime_iso: endTimeIso.format() } );
4047
+ await processedchecklist.updateMany( { _id: { $in: inprogressData.map( ( ele ) => new ObjectId( ele._id ) ) } }, { scheduleEndTime: getCLconfig.scheduleEndTime, scheduleEndTime_iso: endTimeIso.format(), complianceCount: getCLconfig.complianceCount, allowedOverTime: getCLconfig.allowedOverTime, allowedStoreLocation: getCLconfig.allowedStoreLocation, userVerification: getCLconfig.userVerification } );
4047
4048
  inprogressData.forEach( ( item ) => {
4048
4049
  let checkData = assignUserList.find( ( ele ) => ele.userId.toString() == item.userId.toString() && ele.store_id == item.store_id );
4049
4050
  if ( !checkData ) {