tango-app-api-infra 3.9.5-vms.83 → 3.9.5-vms.85

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.
@@ -312,6 +312,33 @@ export async function tangoReviewTicket( req, res ) {
312
312
 
313
313
  record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
314
314
  ...temp ];
315
+
316
+ if ( Array.isArray( record.mappingInfo ) ) {
317
+ record.mappingInfo = record.mappingInfo.map( ( item ) => {
318
+ return {
319
+ ...item,
320
+ status: item.type === 'approve'? 'Tango Review Done':'Closed',
321
+ };
322
+ } );
323
+ }
324
+
325
+ record.mappingInfo.push(
326
+ {
327
+ type: 'finalRevision',
328
+ mode: inputData.mappingInfo?.mode,
329
+ revicedFootfall: revisedFootfall,
330
+ revicedPerc: inputData.mappingInfo?.revicedPerc,
331
+ count: inputData.mappingInfo?.count,
332
+ revisedDetail: inputData.mappingInfo?.revisedDetail,
333
+ status: 'Closed',
334
+ createdByEmail: req?.user?.email,
335
+ createdByUserName: req?.user?.userName,
336
+ createdByRole: req?.user?.role,
337
+ createdAt: new Date(),
338
+ },
339
+
340
+
341
+ );
315
342
  }
316
343
  } else {
317
344
  if ( Array.isArray( record.mappingInfo ) ) {
@@ -392,7 +419,7 @@ export async function tangoReviewTicket( req, res ) {
392
419
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
393
420
 
394
421
  if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
395
- if ( record.status = 'Closed' && inputData.ticketType !== 'internal' ) {
422
+ if ( ( record.status === 'Closed' || record.status === 'Open - Accuracy Issue' ) && inputData.ticketType !== 'internal' ) {
396
423
  const query = {
397
424
  storeId: inputData?.storeId,
398
425
  isVideoStream: true,
@@ -532,7 +559,7 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
532
559
  },
533
560
  },
534
561
  };
535
- if ( Ticket[0]?._source?.type != 'internal' ) {
562
+ if ( Ticket[0]?._source?.type !== 'internal' ) {
536
563
  getTicket.query.bool.must.push(
537
564
  {
538
565
  nested: {
@@ -576,22 +603,24 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
576
603
  .filter( ( item ) => item.type === 'tangoreview' )
577
604
  .map( ( item ) => ( {
578
605
  ...item,
579
- status: 'Closed',
606
+ status: 'Closed - Accuracy Issue',
580
607
  createdByEmail: req?.user?.email,
581
608
  createdByUserName: req?.user?.userName,
582
609
  createdByRole: req?.user?.role,
610
+ comments: inputData?.comments || '',
611
+ subComments: inputData?.subComments ||'',
583
612
  } ) );
584
613
 
585
614
  record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
586
615
  ...temp ];
587
- if ( Array.isArray( record.mappingInfo ) ) {
588
- record.mappingInfo = record.mappingInfo.map( ( item ) => {
589
- return {
590
- ...item,
591
- status: 'Closed',
592
- };
593
- } );
594
- }
616
+ // if ( Array.isArray( record.mappingInfo ) ) {
617
+ // record.mappingInfo = record.mappingInfo.map( ( item ) => {
618
+ // return {
619
+ // ...item,
620
+ // status: 'Closed',
621
+ // };
622
+ // } );
623
+ // }
595
624
 
596
625
  record.mappingInfo.push(
597
626
  {
@@ -606,6 +635,8 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
606
635
  createdByUserName: req?.user?.userName,
607
636
  createdByRole: req?.user?.role,
608
637
  createdAt: new Date(),
638
+ comments: inputData?.comments || '',
639
+ subComments: inputData?.subComments || '',
609
640
  },
610
641
  );
611
642
  }
@@ -871,9 +902,13 @@ export async function ticketSummary( req, res ) {
871
902
  const userInfo = req.user;
872
903
  const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
873
904
  const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
874
-
905
+ const getConfig = await findOneClient( { clientId: inputData?.clientId }, { footfallDirectoryConfigs: 1 } );
906
+ if ( !getConfig || getConfig ==null ) {
907
+ return res.sendError( 'this client not configured against footfall directory', 400 );
908
+ }
909
+ inputData.clientId = inputData?.clientId?.split( ',' );
875
910
  // const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
876
- if ( req?.user?.userType === 'tango' ) {
911
+ if ( req?.user?.userType !== 'tango' ) {
877
912
  switch ( inputData?.tangoType ) {
878
913
  case 'store':
879
914
  const storeQuery = {
@@ -890,6 +925,24 @@ export async function ticketSummary( req, res ) {
890
925
  },
891
926
  },
892
927
  },
928
+
929
+ {
930
+ terms: {
931
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
932
+ inputData.clientId :
933
+ [ inputData.clientId ],
934
+ },
935
+
936
+ },
937
+
938
+ {
939
+ range: {
940
+ reviced: {
941
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
942
+ }
943
+ ,
944
+ },
945
+ },
893
946
  {
894
947
  nested: {
895
948
  path: 'mappingInfo',
@@ -952,20 +1005,7 @@ export async function ticketSummary( req, res ) {
952
1005
  size: 0,
953
1006
  aggs: {
954
1007
  avg_value: {
955
- avg: {
956
- script: {
957
- lang: 'painless',
958
- source: `
959
- if (doc['revicedPerc.keyword'].size() == 0) return null;
960
- String v = doc['revicedPerc.keyword'].value.replace('%','');
961
- try {
962
- return Double.parseDouble(v);
963
- } catch (Exception e) {
964
- return null;
965
- }
966
- `,
967
- },
968
- },
1008
+ avg: 'reviced',
969
1009
  },
970
1010
  },
971
1011
  };
@@ -998,11 +1038,10 @@ export async function ticketSummary( req, res ) {
998
1038
  // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
999
1039
  let openInfraIssues = 0;
1000
1040
 
1001
- let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
1041
+ let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open - Accuracy Issue' );
1002
1042
  oiQ.size = 0;
1003
1043
  const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
1004
1044
  openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
1005
-
1006
1045
  // inprogress: mappingInfo.status: 'in-Progress'
1007
1046
  let inprogress = 0;
1008
1047
 
@@ -1032,14 +1071,9 @@ export async function ticketSummary( req, res ) {
1032
1071
  // For this, add a filter on revicedPerc >= 85
1033
1072
  let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1034
1073
  {
1035
- script: {
1036
- script: {
1037
- lang: 'painless',
1038
- source: `
1039
- doc['revicedPerc.keyword'].size()!=0 &&
1040
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1041
- `,
1042
- params: { num: 85 },
1074
+ range: {
1075
+ reviced: {
1076
+ gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1043
1077
  },
1044
1078
  },
1045
1079
  },
@@ -1054,14 +1088,9 @@ export async function ticketSummary( req, res ) {
1054
1088
 
1055
1089
  let belowQ = buildAggStoreQuery( baseStoreQuery, [
1056
1090
  {
1057
- script: {
1058
- script: {
1059
- lang: 'painless',
1060
- source: `
1061
- doc['revicedPerc.keyword'].size()!=0 &&
1062
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1063
- `,
1064
- params: { num: 85 },
1091
+ range: {
1092
+ reviced: {
1093
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1065
1094
  },
1066
1095
  },
1067
1096
  },
@@ -1097,23 +1126,48 @@ export async function ticketSummary( req, res ) {
1097
1126
  },
1098
1127
  },
1099
1128
  },
1129
+ {
1130
+ terms: {
1131
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1132
+ inputData.clientId :
1133
+ [ inputData.clientId ],
1134
+ },
1135
+
1136
+ },
1137
+
1138
+ ],
1139
+ },
1140
+ },
1141
+ };
1100
1142
 
1143
+ const internalOpen = {
1144
+ size: 0,
1145
+ query: {
1146
+ bool: {
1147
+ must: [
1101
1148
  {
1102
- nested: {
1103
- path: 'mappingInfo',
1104
- query: {
1105
- bool: {
1106
- must: [
1107
- {
1108
- terms: {
1109
- 'mappingInfo.type': [ 'tagging', 'review', 'approve', 'tangoreview' ],
1110
- },
1111
- },
1112
- ],
1113
- },
1149
+ 'range': {
1150
+ 'dateString': {
1151
+ 'gte': inputData?.fromDate,
1152
+ 'lte': inputData?.toDate,
1153
+ 'format': 'yyyy-MM-dd',
1114
1154
  },
1115
1155
  },
1116
1156
  },
1157
+ {
1158
+ terms: {
1159
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1160
+ inputData.clientId :
1161
+ [ inputData.clientId ],
1162
+ },
1163
+
1164
+ },
1165
+ {
1166
+ terms: {
1167
+ 'status.keyword': [ 'Open', 'Raised' ],
1168
+ },
1169
+
1170
+ },
1117
1171
 
1118
1172
  ],
1119
1173
  },
@@ -1123,20 +1177,32 @@ export async function ticketSummary( req, res ) {
1123
1177
  // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1124
1178
  function buildInternalQueryWithStatus( baseQuery, statusValue ) {
1125
1179
  let q = JSON.parse( JSON.stringify( baseQuery ) );
1126
- // Remove any previous mappingInfo.status term
1180
+ // Check if nested query exists, if not create it
1127
1181
  let nested = q.query.bool.must.find( ( m ) => m.nested );
1128
- if ( nested ) {
1129
- // filter out all mappingInfo.status
1130
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1131
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1132
- } );
1133
- // add desired status
1134
- nested.nested.query.bool.must.push( {
1135
- term: {
1136
- 'mappingInfo.status': statusValue,
1182
+ if ( !nested ) {
1183
+ // Create nested query structure
1184
+ nested = {
1185
+ nested: {
1186
+ path: 'mappingInfo',
1187
+ query: {
1188
+ bool: {
1189
+ must: [],
1190
+ },
1191
+ },
1137
1192
  },
1138
- } );
1193
+ };
1194
+ q.query.bool.must.push( nested );
1139
1195
  }
1196
+ // filter out all mappingInfo.status
1197
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1198
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1199
+ } );
1200
+ // add desired status
1201
+ nested.nested.query.bool.must.push( {
1202
+ term: {
1203
+ 'mappingInfo.status': statusValue,
1204
+ },
1205
+ } );
1140
1206
  return q;
1141
1207
  }
1142
1208
 
@@ -1152,7 +1218,7 @@ export async function ticketSummary( req, res ) {
1152
1218
  nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1153
1219
 
1154
1220
  // add new filters
1155
- nested.nested.query.bool.must.push( ...filters );
1221
+ q.query.bool.must.push( ...filters );
1156
1222
  }
1157
1223
 
1158
1224
  return {
@@ -1161,18 +1227,7 @@ export async function ticketSummary( req, res ) {
1161
1227
  aggs: {
1162
1228
  avg_value: {
1163
1229
  avg: {
1164
- script: {
1165
- lang: 'painless',
1166
- source: `
1167
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1168
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1169
- try {
1170
- return Double.parseDouble(v);
1171
- } catch (Exception e) {
1172
- return null;
1173
- }
1174
- `,
1175
- },
1230
+ field: 'reviced',
1176
1231
  },
1177
1232
  },
1178
1233
  },
@@ -1193,13 +1248,14 @@ export async function ticketSummary( req, res ) {
1193
1248
  allInternalQuery.size = 0;
1194
1249
  const totalInternalResp = await getOpenSearchData( openSearch.footfallDirectory, allInternalQuery );
1195
1250
  totalInternalTickets = totalInternalResp?.body?.hits?.total?.value || 0;
1251
+ logger.info( { totalInternalResp } );
1196
1252
 
1197
1253
  // openTickets: mappingInfo.status: 'Open'
1198
1254
  let openInternalTickets = 0;
1199
1255
 
1200
- let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
1201
- otQInternal.size = 0;
1202
- const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, otQInternal );
1256
+ // let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
1257
+ // otQInternal.size = 0;
1258
+ const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, internalOpen );
1203
1259
  openInternalTickets = openInternalResp?.body?.hits?.total?.value || 0;
1204
1260
  // logger.info( { msd: '..............2', openResp } );
1205
1261
 
@@ -1207,11 +1263,12 @@ export async function ticketSummary( req, res ) {
1207
1263
  // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
1208
1264
  let openInternalInfraIssues = 0;
1209
1265
 
1210
- let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open Accuracy Issue' );
1266
+ let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open - Accuracy Issue' );
1211
1267
  oiQinternal.size = 0;
1212
1268
  const infraInternalResp = await getOpenSearchData( openSearch.footfallDirectory, oiQinternal );
1213
1269
  openInternalInfraIssues = infraInternalResp?.body?.hits?.total?.value || 0;
1214
1270
 
1271
+
1215
1272
  // inprogress: mappingInfo.status: 'in-Progress'
1216
1273
  let inprogressIntrenal = 0;
1217
1274
 
@@ -1241,14 +1298,9 @@ export async function ticketSummary( req, res ) {
1241
1298
  // For this, add a filter on revicedPerc >= 85
1242
1299
  let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
1243
1300
  {
1244
- script: {
1245
- script: {
1246
- lang: 'painless',
1247
- source: `
1248
- doc['revicedPerc.keyword'].size()!=0 &&
1249
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1250
- `,
1251
- params: { num: 85 },
1301
+ range: {
1302
+ reviced: {
1303
+ gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1252
1304
  },
1253
1305
  },
1254
1306
  },
@@ -1263,14 +1315,9 @@ export async function ticketSummary( req, res ) {
1263
1315
 
1264
1316
  let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
1265
1317
  {
1266
- script: {
1267
- script: {
1268
- lang: 'painless',
1269
- source: `
1270
- doc['revicedPerc.keyword'].size()!=0 &&
1271
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1272
- `,
1273
- params: { num: 85 },
1318
+ range: {
1319
+ reviced: {
1320
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1274
1321
  },
1275
1322
  },
1276
1323
  },
@@ -1293,8 +1340,10 @@ export async function ticketSummary( req, res ) {
1293
1340
  break;
1294
1341
  default: '';
1295
1342
  }
1296
- } else if ( req?.user?.userType === 'client' ) {
1343
+ } else if ( req?.user?.userType === 'tango' ) {
1344
+ logger.info( { msg: '.........1', ticketsFeature, ticketsApproveFeature } );
1297
1345
  if ( ticketsFeature && !ticketsApproveFeature ) {
1346
+ logger.info( '.........3' );
1298
1347
  const storeQuery = {
1299
1348
  size: 0,
1300
1349
  query: {
@@ -1309,6 +1358,14 @@ export async function ticketSummary( req, res ) {
1309
1358
  },
1310
1359
  },
1311
1360
  },
1361
+ {
1362
+ terms: {
1363
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1364
+ inputData.clientId :
1365
+ [ inputData.clientId ],
1366
+ },
1367
+
1368
+ },
1312
1369
  {
1313
1370
  nested: {
1314
1371
  path: 'mappingInfo',
@@ -1371,20 +1428,7 @@ export async function ticketSummary( req, res ) {
1371
1428
  size: 0,
1372
1429
  aggs: {
1373
1430
  avg_value: {
1374
- avg: {
1375
- script: {
1376
- lang: 'painless',
1377
- source: `
1378
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1379
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1380
- try {
1381
- return Double.parseDouble(v);
1382
- } catch (Exception e) {
1383
- return null;
1384
- }
1385
- `,
1386
- },
1387
- },
1431
+ avg: 'mappingInfo.reviced',
1388
1432
  },
1389
1433
  },
1390
1434
  };
@@ -1459,48 +1503,44 @@ try {
1459
1503
  const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1460
1504
  expiredTickets = eResp?.body?.hits?.total?.value || 0;
1461
1505
 
1462
- let ticketAccuracyAbove = 0;
1463
- // For this, add a filter on revicedPerc >= 85
1464
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1465
- {
1466
- script: {
1506
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1507
+
1508
+ // Build aggregation query for ticket percentage
1509
+ let ticketPercentageAvg = 0;
1510
+
1511
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1512
+ avgTicketPercentageQuery.size = 0;
1513
+ avgTicketPercentageQuery.aggs = {
1514
+ avg_ticket_percentage: {
1515
+ avg: {
1467
1516
  script: {
1468
- lang: 'painless',
1469
1517
  source: `
1470
- doc['revicedPerc.keyword'].size()!=0 &&
1471
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1518
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1519
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1520
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1521
+ } else {
1522
+ return null;
1523
+ }
1472
1524
  `,
1473
- params: { num: 85 },
1525
+ lang: 'painless',
1474
1526
  },
1475
1527
  },
1476
1528
  },
1529
+ };
1477
1530
 
1531
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1478
1532
 
1479
- ] );
1480
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1481
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1533
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1482
1534
 
1535
+ logger.info( { avgTicketPercentageResp } );
1483
1536
  // ticketAccuracyBelow: avg of revicedPerc < 85%
1484
- let ticketAccuracyBelow = 0;
1485
-
1486
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1487
- {
1488
- script: {
1489
- script: {
1490
- lang: 'painless',
1491
- source: `
1492
- doc['revicedPerc.keyword'].size()!=0 &&
1493
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1494
- `,
1495
- params: { num: 85 },
1496
- },
1497
- },
1498
- },
1499
-
1500
- ] );
1501
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1502
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1537
+ let ticketAccuracy = 0;
1503
1538
 
1539
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1540
+ logger.info( { belowQ } );
1541
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1542
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1543
+ logger.info( { accuracyResp } );
1504
1544
  // Final result object
1505
1545
  result = {
1506
1546
  totalTickets,
@@ -1509,465 +1549,435 @@ try {
1509
1549
  closedTickets,
1510
1550
  dueToday: dueToday,
1511
1551
  Expired: expiredTickets,
1512
- avgTicket: ticketAccuracyAbove+'%',
1513
- avgAccuracy: ticketAccuracyBelow+'%',
1552
+ avgTicket: ticketPercentageAvg+'%',
1553
+ avgAccuracy: ticketAccuracy+'%',
1514
1554
  };
1515
- }
1516
- } else if ( ticketsFeature && ticketsApproveFeature ) {
1517
- if ( inputData?.permissionType === 'review' ) {
1518
- const storeQuery = {
1519
- size: 0,
1520
- query: {
1521
- bool: {
1522
- must: [
1523
- {
1524
- 'range': {
1525
- 'dateString': {
1526
- 'gte': inputData?.fromDate,
1527
- 'lte': inputData?.toDate,
1528
- 'format': 'yyyy-MM-dd',
1555
+ } else if ( ticketsFeature && ticketsApproveFeature ) {
1556
+ logger.info( '.........2' );
1557
+ if ( inputData?.permissionType === 'review' ) {
1558
+ const storeQuery = {
1559
+ size: 0,
1560
+ query: {
1561
+ bool: {
1562
+ must: [
1563
+ {
1564
+ 'range': {
1565
+ 'dateString': {
1566
+ 'gte': inputData?.fromDate,
1567
+ 'lte': inputData?.toDate,
1568
+ 'format': 'yyyy-MM-dd',
1569
+ },
1529
1570
  },
1530
1571
  },
1531
- },
1532
- {
1533
- nested: {
1534
- path: 'mappingInfo',
1535
- query: {
1536
- bool: {
1537
- must: [
1538
- {
1539
- term: {
1540
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1572
+ {
1573
+ terms: {
1574
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1575
+ inputData.clientId :
1576
+ [ inputData.clientId ],
1577
+ },
1578
+
1579
+ },
1580
+ {
1581
+ nested: {
1582
+ path: 'mappingInfo',
1583
+ query: {
1584
+ bool: {
1585
+ must: [
1586
+ {
1587
+ term: {
1588
+ 'mappingInfo.type': 'review',
1589
+ },
1541
1590
  },
1542
- },
1543
- ],
1591
+ ],
1592
+ },
1544
1593
  },
1545
1594
  },
1546
1595
  },
1547
- },
1548
-
1549
- ],
1550
- },
1551
- },
1552
- };
1553
1596
 
1554
- // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1555
- function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1556
- let q = JSON.parse( JSON.stringify( baseQuery ) );
1557
- // Remove any previous mappingInfo.status term
1558
- let nested = q.query.bool.must.find( ( m ) => m.nested );
1559
- if ( nested ) {
1560
- // filter out all mappingInfo.status
1561
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1562
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1563
- } );
1564
- // add desired status
1565
- nested.nested.query.bool.must.push( {
1566
- term: {
1567
- 'mappingInfo.status': statusValue,
1597
+ ],
1568
1598
  },
1569
- } );
1570
- }
1571
- return q;
1572
- }
1573
-
1574
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1575
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1599
+ },
1600
+ };
1576
1601
 
1577
- // locate nested section
1578
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1602
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1603
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1604
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1605
+ // Remove any previous mappingInfo.status term
1606
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1607
+ if ( nested ) {
1608
+ // filter out all mappingInfo.status
1609
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1610
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1611
+ } );
1612
+ // add desired status
1613
+ nested.nested.query.bool.must.push( {
1614
+ term: {
1615
+ 'mappingInfo.status': statusValue,
1616
+ },
1617
+ } );
1618
+ }
1619
+ return q;
1620
+ }
1579
1621
 
1580
- if ( nested ) {
1581
- // remove old status filters
1582
- nested.nested.query.bool.must =
1583
- nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1622
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1623
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1584
1624
 
1585
- // add new filters
1586
- nested.nested.query.bool.must.push( ...filters );
1587
- }
1625
+ // locate nested section
1626
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1588
1627
 
1589
- return {
1590
- ...q,
1591
- size: 0,
1592
- aggs: {
1593
- avg_value: {
1594
- avg: {
1595
- script: {
1596
- lang: 'painless',
1597
- source: `
1598
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1599
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1600
- try {
1601
- return Double.parseDouble(v);
1602
- } catch (Exception e) {
1603
- return null;
1604
- }
1605
- `,
1606
- },
1628
+ if ( nested ) {
1629
+ // remove old status filters
1630
+ nested.nested.query.bool.must =
1631
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1632
+
1633
+ // add new filters
1634
+ nested.nested.query.bool.must.push( ...filters );
1635
+ }
1636
+
1637
+ return {
1638
+ ...q,
1639
+ size: 0,
1640
+ aggs: {
1641
+ avg_value: {
1642
+ avg: 'mappingInfo.reviced',
1607
1643
  },
1608
1644
  },
1609
- },
1645
+ };
1610
1646
  };
1611
- };
1612
-
1613
1647
 
1614
- // Get OpenSearch connection
1615
1648
 
1616
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1617
-
1618
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1619
- let totalTickets = 0;
1649
+ // Get OpenSearch connection
1620
1650
 
1621
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1651
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1622
1652
 
1623
- allQuery.size = 0;
1624
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1625
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1653
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1654
+ let totalTickets = 0;
1626
1655
 
1627
- // openTickets: mappingInfo.status: 'Open'
1628
- let openTickets = 0;
1656
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1629
1657
 
1630
- let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1631
- otQ.size = 0;
1632
- const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1633
- openTickets = openResp?.body?.hits?.total?.value || 0;
1634
- // logger.info( { msd: '..............2', openResp } );
1658
+ allQuery.size = 0;
1659
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1660
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1635
1661
 
1662
+ // openTickets: mappingInfo.status: 'Open'
1663
+ let openTickets = 0;
1636
1664
 
1637
- // inprogress: mappingInfo.status: 'in-Progress'
1638
- let inprogress = 0;
1665
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1666
+ otQ.size = 0;
1667
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1668
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1669
+ // logger.info( { msd: '..............2', openResp } );
1639
1670
 
1640
- let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1641
- ipQ.size = 0;
1642
- const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1643
- inprogress = ipResp?.body?.hits?.total?.value || 0;
1644
1671
 
1645
- // closedTickets: mappingInfo.status: 'closed'
1646
- let closedTickets = 0;
1672
+ // inprogress: mappingInfo.status: 'in-Progress'
1673
+ let inprogress = 0;
1647
1674
 
1648
- let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1649
- clQ.size = 0;
1650
- const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1651
- closedTickets = clResp?.body?.hits?.total?.value || 0;
1652
- // Average revisedPerc (for all tangoreview)
1675
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1676
+ ipQ.size = 0;
1677
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1678
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1653
1679
 
1654
- // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1655
- let dueToday = 0;
1656
- let todayDateString = new Date().toISOString().slice( 0, 10 );
1680
+ // closedTickets: mappingInfo.status: 'closed'
1681
+ let closedTickets = 0;
1657
1682
 
1658
- // Build a query for tickets with mappingInfo.dueDate == todayDateString
1659
- let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1660
- // Locate nested mappingInfo query
1661
- let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1662
- if ( nestedDue ) {
1663
- // Remove any previous mappingInfo.dueDate term
1664
- nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1665
- ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1666
- );
1667
- // Add new dueDate filter
1668
- nestedDue.nested.query.bool.must.push( {
1669
- term: { 'mappingInfo.dueDate': todayDateString },
1670
- } );
1671
- }
1672
- dueTodayQuery.size = 0;
1673
- const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1674
- dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1683
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1684
+ clQ.size = 0;
1685
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1686
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1675
1687
 
1688
+ let dueToday = 0;
1689
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1690
+
1691
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1692
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1693
+ // Locate nested mappingInfo query
1694
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1695
+ if ( nestedDue ) {
1696
+ // Remove any previous mappingInfo.dueDate term
1697
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1698
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1699
+ );
1700
+ // Add new dueDate filter
1701
+ nestedDue.nested.query.bool.must.push( {
1702
+ term: { 'mappingInfo.dueDate': todayDateString },
1703
+ } );
1704
+ }
1705
+ dueTodayQuery.size = 0;
1706
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1707
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1676
1708
 
1677
- // filter expired Tickets
1678
- let expiredTickets = 0;
1709
+ // filter expired Tickets
1710
+ let expiredTickets = 0;
1679
1711
 
1680
- let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1681
- eQ.size = 0;
1682
- const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1683
- expiredTickets = eResp?.body?.hits?.total?.value || 0;
1712
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1713
+ eQ.size = 0;
1714
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1715
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1684
1716
 
1685
- // ticketAccuracyAbove: avg of revicedPerc > 85%
1686
- let ticketAccuracyAbove = 0;
1717
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1687
1718
 
1719
+ // Build aggregation query for ticket percentage
1720
+ let ticketPercentageAvg = 0;
1688
1721
 
1689
- // For this, add a filter on revicedPerc >= 85
1690
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1691
- {
1692
- script: {
1693
- script: {
1694
- lang: 'painless',
1695
- source: `
1696
- doc['revicedPerc.keyword'].size()!=0 &&
1697
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1698
- `,
1699
- params: { num: 85 },
1722
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1723
+ avgTicketPercentageQuery.size = 0;
1724
+ avgTicketPercentageQuery.aggs = {
1725
+ avg_ticket_percentage: {
1726
+ avg: {
1727
+ script: {
1728
+ source: `
1729
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1730
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1731
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1732
+ } else {
1733
+ return null;
1734
+ }
1735
+ `,
1736
+ lang: 'painless',
1737
+ },
1700
1738
  },
1701
1739
  },
1702
- },
1703
-
1704
-
1705
- ] );
1706
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1707
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1740
+ };
1708
1741
 
1709
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1710
- let ticketAccuracyBelow = 0;
1742
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1711
1743
 
1712
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1713
- {
1714
- script: {
1715
- script: {
1716
- lang: 'painless',
1717
- source: `
1718
- doc['revicedPerc.keyword'].size()!=0 &&
1719
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1720
- `,
1721
- params: { num: 85 },
1722
- },
1723
- },
1724
- },
1744
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1725
1745
 
1726
- ] );
1727
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1728
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1746
+ logger.info( { avgTicketPercentageResp } );
1747
+ // ticketAccuracyBelow: avg of revicedPerc < 85%
1748
+ let ticketAccuracy = 0;
1729
1749
 
1730
- // Final result object
1731
- result = {
1732
- totalTickets,
1733
- openTickets,
1734
- inprogress,
1735
- closedTickets,
1736
- dueToday: dueToday,
1737
- Expired: expiredTickets,
1738
- avgTicket: ticketAccuracyAbove+'%',
1739
- avgAccuracy: ticketAccuracyBelow+'%',
1740
- };
1741
- } else {
1742
- const storeQuery = {
1743
- size: 0,
1744
- query: {
1745
- bool: {
1746
- must: [
1747
- {
1748
- 'range': {
1749
- 'dateString': {
1750
- 'gte': inputData?.fromDate,
1751
- 'lte': inputData?.toDate,
1752
- 'format': 'yyyy-MM-dd',
1750
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1751
+ logger.info( { belowQ } );
1752
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1753
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1754
+ logger.info( { accuracyResp } );
1755
+ // Final result object
1756
+ result = {
1757
+ totalTickets,
1758
+ openTickets,
1759
+ inprogress,
1760
+ closedTickets,
1761
+ dueToday: dueToday,
1762
+ Expired: expiredTickets,
1763
+ avgTicket: ticketPercentageAvg+'%',
1764
+ avgAccuracy: ticketAccuracy+'%',
1765
+ };
1766
+ } else {
1767
+ const storeQuery = {
1768
+ size: 0,
1769
+ query: {
1770
+ bool: {
1771
+ must: [
1772
+ {
1773
+ 'range': {
1774
+ 'dateString': {
1775
+ 'gte': inputData?.fromDate,
1776
+ 'lte': inputData?.toDate,
1777
+ 'format': 'yyyy-MM-dd',
1778
+ },
1753
1779
  },
1754
1780
  },
1755
- },
1756
- {
1757
- nested: {
1758
- path: 'mappingInfo',
1759
- query: {
1760
- bool: {
1761
- must: [
1762
- {
1763
- term: {
1764
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1781
+ {
1782
+ terms: {
1783
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1784
+ inputData.clientId :
1785
+ [ inputData.clientId ],
1786
+ },
1787
+
1788
+ },
1789
+ {
1790
+ nested: {
1791
+ path: 'mappingInfo',
1792
+ query: {
1793
+ bool: {
1794
+ must: [
1795
+ {
1796
+ term: {
1797
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1798
+ },
1765
1799
  },
1766
- },
1767
- ],
1800
+ ],
1801
+ },
1768
1802
  },
1769
1803
  },
1770
1804
  },
1771
- },
1772
1805
 
1773
- ],
1806
+ ],
1807
+ },
1774
1808
  },
1775
- },
1776
- };
1809
+ };
1777
1810
 
1778
- // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1779
- function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1780
- let q = JSON.parse( JSON.stringify( baseQuery ) );
1781
- // Remove any previous mappingInfo.status term
1782
- let nested = q.query.bool.must.find( ( m ) => m.nested );
1783
- if ( nested ) {
1784
- // filter out all mappingInfo.status
1785
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1786
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1787
- } );
1788
- // add desired status
1789
- nested.nested.query.bool.must.push( {
1790
- term: {
1791
- 'mappingInfo.status': statusValue,
1792
- },
1793
- } );
1811
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1812
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1813
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1814
+ // Remove any previous mappingInfo.status term
1815
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1816
+ if ( nested ) {
1817
+ // filter out all mappingInfo.status
1818
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1819
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1820
+ } );
1821
+ // add desired status
1822
+ nested.nested.query.bool.must.push( {
1823
+ term: {
1824
+ 'mappingInfo.status': statusValue,
1825
+ },
1826
+ } );
1827
+ }
1828
+ return q;
1794
1829
  }
1795
- return q;
1796
- }
1797
1830
 
1798
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1799
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1831
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1832
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1800
1833
 
1801
- // locate nested section
1802
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1834
+ // locate nested section
1835
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1803
1836
 
1804
- if ( nested ) {
1805
- // remove old status filters
1806
- nested.nested.query.bool.must =
1807
- nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1837
+ if ( nested ) {
1838
+ // remove old status filters
1839
+ nested.nested.query.bool.must =
1840
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1808
1841
 
1809
- // add new filters
1810
- nested.nested.query.bool.must.push( ...filters );
1811
- }
1842
+ // add new filters
1843
+ nested.nested.query.bool.must.push( ...filters );
1844
+ }
1812
1845
 
1813
- return {
1814
- ...q,
1815
- size: 0,
1816
- aggs: {
1817
- avg_value: {
1818
- avg: {
1819
- script: {
1820
- lang: 'painless',
1821
- source: `
1822
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1823
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1824
- try {
1825
- return Double.parseDouble(v);
1826
- } catch (Exception e) {
1827
- return null;
1828
- }
1829
- `,
1830
- },
1846
+ return {
1847
+ ...q,
1848
+ size: 0,
1849
+ aggs: {
1850
+ avg_value: {
1851
+ avg: 'mappingInfp.reviced',
1831
1852
  },
1832
1853
  },
1833
- },
1854
+ };
1834
1855
  };
1835
- };
1836
1856
 
1837
1857
 
1838
- // Get OpenSearch connection
1839
-
1840
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1858
+ // Get OpenSearch connection
1841
1859
 
1842
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1843
- let totalTickets = 0;
1860
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1844
1861
 
1845
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1862
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1863
+ let totalTickets = 0;
1846
1864
 
1847
- allQuery.size = 0;
1848
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1849
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1865
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1850
1866
 
1851
- // openTickets: mappingInfo.status: 'Open'
1852
- let openTickets = 0;
1867
+ allQuery.size = 0;
1868
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1869
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1853
1870
 
1854
- let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1855
- otQ.size = 0;
1856
- const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1857
- openTickets = openResp?.body?.hits?.total?.value || 0;
1858
- // logger.info( { msd: '..............2', openResp } );
1871
+ // openTickets: mappingInfo.status: 'Open'
1872
+ let openTickets = 0;
1859
1873
 
1874
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1875
+ otQ.size = 0;
1876
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1877
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1878
+ // logger.info( { msd: '..............2', openResp } );
1860
1879
 
1861
- // inprogress: mappingInfo.status: 'in-Progress'
1862
- let inprogress = 0;
1863
1880
 
1864
- let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1865
- ipQ.size = 0;
1866
- const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1867
- inprogress = ipResp?.body?.hits?.total?.value || 0;
1881
+ // inprogress: mappingInfo.status: 'in-Progress'
1882
+ let inprogress = 0;
1868
1883
 
1869
- // closedTickets: mappingInfo.status: 'closed'
1870
- let closedTickets = 0;
1884
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1885
+ ipQ.size = 0;
1886
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1887
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1871
1888
 
1872
- let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1873
- clQ.size = 0;
1874
- const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1875
- closedTickets = clResp?.body?.hits?.total?.value || 0;
1876
- // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1877
- let dueToday = 0;
1878
- let todayDateString = new Date().toISOString().slice( 0, 10 );
1889
+ // closedTickets: mappingInfo.status: 'closed'
1890
+ let closedTickets = 0;
1879
1891
 
1880
- // Build a query for tickets with mappingInfo.dueDate == todayDateString
1881
- let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1882
- // Locate nested mappingInfo query
1883
- let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1884
- if ( nestedDue ) {
1885
- // Remove any previous mappingInfo.dueDate term
1886
- nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1887
- ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1888
- );
1889
- // Add new dueDate filter
1890
- nestedDue.nested.query.bool.must.push( {
1891
- term: { 'mappingInfo.dueDate': todayDateString },
1892
- } );
1893
- }
1894
- dueTodayQuery.size = 0;
1895
- const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1896
- dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1892
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1893
+ clQ.size = 0;
1894
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1895
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1896
+ // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1897
+ let dueToday = 0;
1898
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1899
+
1900
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1901
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1902
+ // Locate nested mappingInfo query
1903
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1904
+ if ( nestedDue ) {
1905
+ // Remove any previous mappingInfo.dueDate term
1906
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1907
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1908
+ );
1909
+ // Add new dueDate filter
1910
+ nestedDue.nested.query.bool.must.push( {
1911
+ term: { 'mappingInfo.dueDate': todayDateString },
1912
+ } );
1913
+ }
1914
+ dueTodayQuery.size = 0;
1915
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1916
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1897
1917
 
1898
1918
 
1899
- // filter expired Tickets
1900
- let expiredTickets = 0;
1919
+ // filter expired Tickets
1920
+ let expiredTickets = 0;
1901
1921
 
1902
- let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1903
- eQ.size = 0;
1904
- const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1905
- expiredTickets = eResp?.body?.hits?.total?.value || 0;
1922
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1923
+ eQ.size = 0;
1924
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1925
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1906
1926
 
1907
- // filter under tango review
1927
+ // filter under tango review
1908
1928
 
1909
- let undertangoTickets = 0;
1929
+ let undertangoTickets = 0;
1910
1930
 
1911
- let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1912
- utrQ.size = 0;
1913
- const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1914
- undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1931
+ let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1932
+ utrQ.size = 0;
1933
+ const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1934
+ undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1915
1935
 
1916
- // For this, add a filter on revicedPerc >= 85
1917
- let ticketAccuracyAbove = 0;
1918
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1919
- {
1920
- script: {
1921
- script: {
1922
- lang: 'painless',
1923
- source: `
1924
- doc['revicedPerc.keyword'].size()!=0 &&
1925
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1926
- `,
1927
- params: { num: 85 },
1936
+ let ticketPercentageAvg =0;
1937
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1938
+ avgTicketPercentageQuery.size = 0;
1939
+ avgTicketPercentageQuery.aggs = {
1940
+ avg_ticket_percentage: {
1941
+ avg: {
1942
+ script: {
1943
+ source: `
1944
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1945
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1946
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1947
+ } else {
1948
+ return null;
1949
+ }
1950
+ `,
1951
+ lang: 'painless',
1952
+ },
1928
1953
  },
1929
1954
  },
1930
- },
1931
-
1955
+ };
1932
1956
 
1933
- ] );
1957
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1934
1958
 
1935
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1936
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1959
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1960
+ logger.info( { avgTicketPercentageResp } );
1961
+ let ticketAccuracy = 0;
1937
1962
 
1938
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1939
- let ticketAccuracyBelow = 0;
1940
-
1941
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1942
- {
1943
- script: {
1944
- script: {
1945
- lang: 'painless',
1946
- source: `
1947
- doc['revicedPerc.keyword'].size()!=0 &&
1948
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1949
- `,
1950
- params: { num: 85 },
1951
- },
1952
- },
1953
- },
1954
-
1955
- ] );
1956
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1957
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1958
-
1959
- // Final result object
1960
- result = {
1961
- totalTickets,
1962
- openTickets,
1963
- inprogress,
1964
- closedTickets,
1965
- dueToday: dueToday,
1966
- Expired: expiredTickets,
1967
- underTangoReview: undertangoTickets,
1968
- avgTicket: ticketAccuracyAbove+'%',
1969
- avgAccuracy: ticketAccuracyBelow+'%',
1970
- };
1963
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1964
+ logger.info( { belowQ } );
1965
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1966
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1967
+ logger.info( { accuracyResp } );
1968
+ // Final result object
1969
+ result = {
1970
+ totalTickets,
1971
+ openTickets,
1972
+ inprogress,
1973
+ closedTickets,
1974
+ dueToday: dueToday,
1975
+ Expired: expiredTickets,
1976
+ underTangoReview: undertangoTickets,
1977
+ avgTicket: ticketPercentageAvg+'%',
1978
+ avgAccuracy: ticketAccuracy+'%',
1979
+ };
1980
+ }
1971
1981
  }
1972
1982
  }
1973
1983
 
@@ -2209,7 +2219,6 @@ export async function ticketList( req, res ) {
2209
2219
  const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
2210
2220
  inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
2211
2221
 
2212
-
2213
2222
  const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
2214
2223
 
2215
2224
  const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
@@ -3034,6 +3043,8 @@ export async function ticketList( req, res ) {
3034
3043
  'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3035
3044
  'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3036
3045
  'Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3046
+ 'Comments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
3047
+ 'Sub Comments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
3037
3048
 
3038
3049
  } );
3039
3050
  }
@@ -3054,6 +3065,8 @@ export async function ticketList( req, res ) {
3054
3065
  approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3055
3066
  tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3056
3067
  status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3068
+ comments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
3069
+ subComments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
3057
3070
 
3058
3071
  } );
3059
3072
  }
@@ -4891,7 +4904,7 @@ export async function updateUserTicketStatus( req, res ) {
4891
4904
 
4892
4905
  const updatePayload = {
4893
4906
  doc: {
4894
- status: 'In-Progress',
4907
+ status: lastEntry?.type == 'review'? 'Reviewer In progress':lastEntry?.type == 'approve'? 'Approver In progress':ticketSource?.status,
4895
4908
  mappingInfo: updatedMappingInfo,
4896
4909
  updatedAt: currentTime,
4897
4910
  },