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.
- package/index.js +2 -1
- package/package.json +1 -1
- package/src/controllers/internalTrax.controller.js +225 -94
- package/src/controllers/mobileTrax.controller.js +69 -29
- package/src/controllers/trax.controller.js +3 -2
- package/src/controllers/traxDashboard.controllers.js +69 -55
- package/src/hbs/flag.hbs +1 -1
- package/src/hbs/template.hbs +7 -0
- package/src/hbs/visit-checklist.hbs +77 -93
- package/src/logging/activityLogFlusher.js +59 -0
- package/src/logging/activityLogMiddleware.js +45 -0
- package/src/logging/activityLogStore.js +91 -0
- package/src/logging/compressBatches.js +83 -0
- package/src/logging/config.js +24 -0
- package/src/logging/createLoggableService.js +46 -0
- package/src/logging/logExternalCall.js +37 -0
- package/src/routes/internalTraxApi.router.js +1 -0
- package/src/services/app.service.js +15 -9
- package/src/services/approver.service.js +23 -15
- package/src/services/authentication.service.js +9 -3
- package/src/services/camera.service.js +19 -13
- package/src/services/checklist.service.js +35 -27
- package/src/services/checklistAssign.service.js +43 -38
- package/src/services/checklistQuestion.service.js +39 -34
- package/src/services/checklistlog.service.js +39 -34
- package/src/services/clientRequest.service.js +9 -2
- package/src/services/clients.services.js +23 -18
- package/src/services/cluster.service.js +31 -23
- package/src/services/domain.service.js +23 -18
- package/src/services/download.services.js +35 -25
- package/src/services/group.service.js +23 -17
- package/src/services/lenskartEmployeeMapping.service.js +15 -10
- package/src/services/locus.service.js +35 -28
- package/src/services/notification.service.js +35 -26
- package/src/services/otp.service.js +20 -13
- package/src/services/planogram.service.js +9 -2
- package/src/services/processedTaskConfig.service.js +35 -27
- package/src/services/processedTaskList.service.js +32 -26
- package/src/services/processedchecklist.services.js +55 -47
- package/src/services/processedchecklistconfig.services.js +39 -34
- package/src/services/recurringFlagTracker.service.js +39 -32
- package/src/services/runAIFeatures.services.js +32 -27
- package/src/services/runAIRequest.services.js +43 -38
- package/src/services/store.service.js +32 -27
- package/src/services/tagging.service.js +9 -2
- package/src/services/taskConfig.service.js +35 -27
- package/src/services/teams.service.js +35 -24
- package/src/services/ticket.service.js +15 -10
- package/src/services/user.service.js +27 -20
- package/src/services/userAssignedstores.service.js +12 -5
- 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
|
@@ -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
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
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: '
|
|
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().
|
|
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,
|
|
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
|
-
|
|
3550
|
-
|
|
3551
|
-
store.
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
} )
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
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
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
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 (
|
|
3781
|
+
if ( !aiDetails ) {
|
|
3653
3782
|
return res.sendError( 'Checklist not found', 404 );
|
|
3654
3783
|
}
|
|
3655
3784
|
|
|
3656
|
-
const doc = { ...aiDetails
|
|
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
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
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
|
-
|
|
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 ) {
|