tango-app-api-trax 3.9.55 → 3.9.56
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/package.json
CHANGED
|
@@ -2141,7 +2141,6 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
|
|
|
2141
2141
|
|
|
2142
2142
|
if ( requestData.submittype == 'submit' ) {
|
|
2143
2143
|
requestData.userComplianceCount = 0;
|
|
2144
|
-
requestData.complianceCount = 0;
|
|
2145
2144
|
for ( let section of sectionFormat ) {
|
|
2146
2145
|
for ( let question of section.questions ) {
|
|
2147
2146
|
let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
|
|
@@ -2149,27 +2148,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
|
|
|
2149
2148
|
return res.sendError( 'Please Fill All Fields', 400 );
|
|
2150
2149
|
}
|
|
2151
2150
|
if ( question.compliance ) {
|
|
2152
|
-
// score = user's achieved value; max = best possible for the question
|
|
2153
|
-
// (same max formula as config-time complianceCount in trax.controller).
|
|
2154
2151
|
const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
|
|
2155
2152
|
let score = scores.length ? Math.max( ...scores ) : 0;
|
|
2156
|
-
|
|
2157
|
-
const answerScores = ( question.answers || [] ).map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) );
|
|
2158
|
-
let max = answerScores.length ? Math.max( ...answerScores ) : 0;
|
|
2159
|
-
|
|
2160
|
-
// Single-choice questions answered 'NA' / 'N/A' (case-insensitive) are
|
|
2161
|
-
// excluded from compliance entirely (score AND max = 0), matching the
|
|
2162
|
-
// visit-checklist PDF and dashboard compliance calculation.
|
|
2163
|
-
if ( question.answerType === 'multiplechoicesingle' ) {
|
|
2164
|
-
const naAnswer = ( question.userAnswer?.[0]?.answer || '' ).toString().trim().toLowerCase();
|
|
2165
|
-
if ( naAnswer === 'na' || naAnswer === 'n/a' ) {
|
|
2166
|
-
score = 0;
|
|
2167
|
-
max = 0;
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
2153
|
requestData.userComplianceCount += score;
|
|
2172
|
-
requestData.complianceCount += max;
|
|
2173
2154
|
}
|
|
2174
2155
|
}
|
|
2175
2156
|
}
|
|
@@ -2815,8 +2796,6 @@ export async function submitChecklist( req, res ) {
|
|
|
2815
2796
|
updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
|
|
2816
2797
|
updateData.submitMobileTime = requestData?.currentTime;
|
|
2817
2798
|
updateData.userComplianceCount = requestData?.userComplianceCount;
|
|
2818
|
-
// NA-aware max (denominator) recomputed at submit, alongside the score above.
|
|
2819
|
-
updateData.complianceCount = requestData?.complianceCount;
|
|
2820
2799
|
updateData.userSignature = requestData?.userSignature;
|
|
2821
2800
|
if ( requestData?.userImage ) {
|
|
2822
2801
|
let splitImgUrl = requestData?.userImage.split( '/' );
|
|
@@ -297,21 +297,15 @@ export const checklistPerformance = async ( req, res ) => {
|
|
|
297
297
|
|
|
298
298
|
findQuery.push( { $match: { $and: findAndQuery } } );
|
|
299
299
|
|
|
300
|
-
// Same search expressed as a plain find condition, so the parallel compliance
|
|
301
|
-
// query (below) stays scoped to the same checklists without depending on the
|
|
302
|
-
// aggregation's result.
|
|
303
|
-
let complianceSearch = null;
|
|
304
300
|
if ( requestData.searchValue && requestData.searchValue != '' ) {
|
|
305
301
|
let checkListSearch = requestData.searchValue.split( ',' ).map( ( item ) => item.trim().toLowerCase() );
|
|
306
302
|
let query;
|
|
307
303
|
if ( checkListSearch.length > 1 ) {
|
|
308
304
|
findQuery.push( { $addFields: { cheklistlowercase: { $toLower: '$checkListName' } } } );
|
|
309
305
|
query = { cheklistlowercase: { $in: checkListSearch } };
|
|
310
|
-
complianceSearch = { $expr: { $in: [ { $toLower: '$checkListName' }, checkListSearch ] } };
|
|
311
306
|
} else {
|
|
312
307
|
const safeSearch = escapeRegex( req.body?.searchValue );
|
|
313
308
|
query = { checkListName: { $regex: safeSearch, $options: 'i' } };
|
|
314
|
-
complianceSearch = { checkListName: { $regex: safeSearch, $options: 'i' } };
|
|
315
309
|
}
|
|
316
310
|
findQuery.push( { $match: { $or: [ query ] } } );
|
|
317
311
|
}
|
|
@@ -350,6 +344,16 @@ export const checklistPerformance = async ( req, res ) => {
|
|
|
350
344
|
timeFlag: { $sum: '$timeFlag' },
|
|
351
345
|
questionFlagCount: { $sum: '$questionFlag' },
|
|
352
346
|
runAIFlagCount: { $sum: '$runAIFlag' },
|
|
347
|
+
userComplianceCountTotal: {
|
|
348
|
+
$sum: {
|
|
349
|
+
$cond: [
|
|
350
|
+
{ $eq: [ '$checklistStatus', 'submit' ] },
|
|
351
|
+
{ $ifNull: [ '$userComplianceCount', 0 ] },
|
|
352
|
+
0,
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
complianceCount: { $first: '$complianceCount' },
|
|
353
357
|
checkListType: { $last: '$checkListType' },
|
|
354
358
|
redo: { $sum: { $cond: [ { $eq: [ '$redoStatus', true ] }, 1, 0 ] } },
|
|
355
359
|
task: {
|
|
@@ -409,104 +413,79 @@ export const checklistPerformance = async ( req, res ) => {
|
|
|
409
413
|
checkListType: 1,
|
|
410
414
|
redo: 1,
|
|
411
415
|
task: 1,
|
|
416
|
+
questionCompliance: {
|
|
417
|
+
$let: {
|
|
418
|
+
vars: {
|
|
419
|
+
divisor: {
|
|
420
|
+
$cond: [
|
|
421
|
+
{ $gt: [ '$submittedChecklist', 1 ] },
|
|
422
|
+
{ $multiply: [ { $ifNull: [ '$complianceCount', 0 ] }, '$submittedChecklist' ] },
|
|
423
|
+
{ $ifNull: [ '$complianceCount', 0 ] },
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
in: {
|
|
428
|
+
$cond: [
|
|
429
|
+
{ $gt: [ '$$divisor', 0 ] },
|
|
430
|
+
{
|
|
431
|
+
$round: [
|
|
432
|
+
{
|
|
433
|
+
$multiply: [
|
|
434
|
+
{ $divide: [ '$userComplianceCountTotal', '$$divisor' ] },
|
|
435
|
+
100,
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
0,
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
0,
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
},
|
|
412
446
|
},
|
|
413
447
|
} );
|
|
414
448
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// matching the visit-checklist PDF) and sort + paginate in JS — because the table
|
|
420
|
-
// can sort by the compliance column, which no longer exists in the pipeline.
|
|
421
|
-
const complianceFilter = {
|
|
422
|
-
$and: [
|
|
423
|
-
...findAndQuery,
|
|
424
|
-
{ checklistStatus: 'submit' },
|
|
425
|
-
...( complianceSearch ? [ complianceSearch ] : [] ),
|
|
426
|
-
],
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
const [ getChecklistPerformanceData, complianceDocs ] = await Promise.all( [
|
|
430
|
-
processedchecklistService.aggregate( findQuery ),
|
|
431
|
-
// Only the compliance-relevant subfields, so we transfer far less than the
|
|
432
|
-
// full questionAnswers blob.
|
|
433
|
-
processedchecklistService.find( complianceFilter, {
|
|
434
|
-
'sourceCheckList_id': 1,
|
|
435
|
-
'questionAnswers.questions.compliance': 1,
|
|
436
|
-
'questionAnswers.questions.answerType': 1,
|
|
437
|
-
'questionAnswers.questions.answers.complianceScore': 1,
|
|
438
|
-
'questionAnswers.questions.answers.matchedCount': 1,
|
|
439
|
-
'questionAnswers.questions.answers.notMatchedCount': 1,
|
|
440
|
-
'questionAnswers.questions.userAnswer.complianceScore': 1,
|
|
441
|
-
'questionAnswers.questions.userAnswer.answer': 1,
|
|
442
|
-
} ).lean(),
|
|
443
|
-
] );
|
|
444
|
-
|
|
445
|
-
if ( !getChecklistPerformanceData.length ) {
|
|
446
|
-
return res.sendError( 'no data found', 204 );
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Sum NA-aware { score, max } per checklist.
|
|
450
|
-
const complianceByChecklist = {};
|
|
451
|
-
for ( const doc of complianceDocs ) {
|
|
452
|
-
const id = doc.sourceCheckList_id?.toString();
|
|
453
|
-
if ( !id ) continue;
|
|
454
|
-
if ( !complianceByChecklist[id] ) complianceByChecklist[id] = { score: 0, max: 0 };
|
|
455
|
-
|
|
456
|
-
for ( const section of ( doc.questionAnswers || [] ) ) {
|
|
457
|
-
for ( const q of ( section.questions || [] ) ) {
|
|
458
|
-
if ( !q?.compliance ) continue;
|
|
459
|
-
|
|
460
|
-
// Single-choice answered NA/N/A (case-insensitive) is excluded from both.
|
|
461
|
-
if ( q.answerType === 'multiplechoicesingle' ) {
|
|
462
|
-
const ans = ( q.userAnswer?.[0]?.answer || '' ).toString().trim().toLowerCase();
|
|
463
|
-
if ( ans === 'na' || ans === 'n/a' ) continue;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const max = Math.max( 0, ...( q.answers || [] ).map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) ) );
|
|
467
|
-
const score = Math.max( 0, ...( q.userAnswer || [] ).map( ( o ) => o?.complianceScore ?? 0 ) );
|
|
468
|
-
|
|
469
|
-
complianceByChecklist[id].score += score;
|
|
470
|
-
complianceByChecklist[id].max += max;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
449
|
+
if ( requestData.sortColumnName && requestData.sortColumnName != '' && requestData.sortBy && requestData.sortBy !='' ) {
|
|
450
|
+
findQuery.push( { $sort: { [requestData.sortColumnName]: requestData.sortBy } } );
|
|
451
|
+
} else {
|
|
452
|
+
findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
|
|
473
453
|
}
|
|
474
454
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const av = a[sortColumn];
|
|
485
|
-
const bv = b[sortColumn];
|
|
486
|
-
if ( typeof av === 'string' || typeof bv === 'string' ) {
|
|
487
|
-
return ( av ?? '' ).toString().localeCompare( ( bv ?? '' ).toString() ) * sortDir;
|
|
488
|
-
}
|
|
489
|
-
return ( ( av ?? 0 ) - ( bv ?? 0 ) ) * sortDir;
|
|
455
|
+
findQuery.push( {
|
|
456
|
+
$facet: {
|
|
457
|
+
data: [
|
|
458
|
+
{ $skip: skip }, { $limit: limit },
|
|
459
|
+
],
|
|
460
|
+
count: [
|
|
461
|
+
{ $count: 'total' },
|
|
462
|
+
],
|
|
463
|
+
},
|
|
490
464
|
} );
|
|
491
|
-
|
|
492
|
-
|
|
465
|
+
let getChecklistPerformanceData = await processedchecklistService.aggregate( findQuery );
|
|
466
|
+
if ( !getChecklistPerformanceData[0].data.length ) {
|
|
467
|
+
return res.sendError( 'no data found', 204 );
|
|
468
|
+
}
|
|
493
469
|
|
|
494
470
|
if ( requestData.export ) {
|
|
495
|
-
const exportdata =
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
471
|
+
const exportdata = [];
|
|
472
|
+
getChecklistPerformanceData[0].data.forEach( ( element ) => {
|
|
473
|
+
exportdata.push( {
|
|
474
|
+
'Checklist Name': element.checkListName || '--',
|
|
475
|
+
'Coverage': element.coverage || '--',
|
|
476
|
+
'Scheduled': element.scheduleRepeatedType || '--',
|
|
477
|
+
'Assigned To': element.storeCount || '--',
|
|
478
|
+
'Submitted': element.submittedChecklist || '--',
|
|
479
|
+
'Flags': element.flaggedChecklist || '--',
|
|
480
|
+
'Tasks': element.task || '--',
|
|
481
|
+
'ReDo': element.redo || '--',
|
|
482
|
+
} );
|
|
483
|
+
} );
|
|
505
484
|
return await download( exportdata, res );
|
|
506
485
|
}
|
|
507
486
|
|
|
508
|
-
result.totalCount = getChecklistPerformanceData.
|
|
509
|
-
result.checklistPerformance =
|
|
487
|
+
result.totalCount = getChecklistPerformanceData[0].count[0].total;
|
|
488
|
+
result.checklistPerformance = getChecklistPerformanceData[0].data;
|
|
510
489
|
return res.sendSuccess( result );
|
|
511
490
|
} catch ( error ) {
|
|
512
491
|
console.log( 'error =>', error );
|