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.
- package/index.js +1 -2
- package/package.json +2 -2
- package/src/controllers/internalTrax.controller.js +99 -221
- package/src/controllers/mobileTrax.controller.js +29 -69
- package/src/controllers/teaxFlag.controller.js +953 -15
- package/src/controllers/trax.controller.js +2 -3
- package/src/controllers/traxDashboard.controllers.js +55 -69
- package/src/hbs/flag.hbs +1 -1
- package/src/hbs/login-otp.hbs +943 -943
- package/src/hbs/template.hbs +0 -7
- package/src/hbs/visit-checklist.hbs +91 -71
- package/src/routes/internalTraxApi.router.js +0 -1
- package/src/routes/trax.routes.js +3 -0
- package/src/routes/traxFlag.router.js +24 -0
- package/src/services/app.service.js +9 -15
- package/src/services/approver.service.js +15 -23
- package/src/services/authentication.service.js +3 -9
- package/src/services/camera.service.js +13 -19
- package/src/services/checklist.service.js +27 -35
- package/src/services/checklistAssign.service.js +38 -43
- package/src/services/checklistQuestion.service.js +34 -39
- package/src/services/checklistlog.service.js +34 -39
- package/src/services/clientRequest.service.js +2 -9
- package/src/services/clients.services.js +18 -23
- package/src/services/cluster.service.js +23 -31
- package/src/services/domain.service.js +18 -23
- package/src/services/download.services.js +25 -35
- package/src/services/group.service.js +17 -23
- package/src/services/lenskartEmployeeMapping.service.js +10 -15
- package/src/services/locus.service.js +28 -35
- package/src/services/notification.service.js +26 -35
- package/src/services/otp.service.js +13 -20
- package/src/services/planogram.service.js +2 -9
- package/src/services/processedTaskConfig.service.js +27 -35
- package/src/services/processedTaskList.service.js +26 -32
- package/src/services/processedchecklist.services.js +47 -55
- package/src/services/processedchecklistconfig.services.js +34 -39
- package/src/services/recurringFlagTracker.service.js +32 -39
- package/src/services/runAIFeatures.services.js +27 -32
- package/src/services/runAIRequest.services.js +38 -43
- package/src/services/store.service.js +27 -32
- package/src/services/tagging.service.js +2 -9
- package/src/services/taskConfig.service.js +27 -35
- package/src/services/teams.service.js +24 -35
- package/src/services/ticket.service.js +10 -15
- package/src/services/user.service.js +20 -27
- package/src/services/userAssignedstores.service.js +5 -12
- package/src/utils/visitChecklistPdf.utils.js +21 -449
- package/src/logging/activityLogFlusher.js +0 -59
- package/src/logging/activityLogMiddleware.js +0 -45
- package/src/logging/activityLogStore.js +0 -91
- package/src/logging/compressBatches.js +0 -83
- package/src/logging/config.js +0 -24
- package/src/logging/createLoggableService.js +0 -46
- 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
|
|
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.
|
|
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.
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
2772
|
-
|
|
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
|
|
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: '
|
|
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,
|
|
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
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
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
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
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
|
-
|
|
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
|
}
|