tango-app-api-infra 3.9.5-vms.78 → 3.9.5-vms.79

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.
@@ -392,6 +392,85 @@ export async function tangoReviewTicket( req, res ) {
392
392
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
393
393
 
394
394
  if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
395
+ if ( record.status = 'Closed' && inputData.ticketType !== 'internal' ) {
396
+ const query = {
397
+ storeId: inputData?.storeId,
398
+ isVideoStream: true,
399
+ };
400
+ const getStoreType = await countDocumnetsCamera( query );
401
+ const revopInfoQuery = {
402
+ size: 10000,
403
+ query: {
404
+ bool: {
405
+ must: [
406
+ {
407
+ term: {
408
+ 'storeId.keyword': inputData.storeId,
409
+ },
410
+ },
411
+ {
412
+ term: {
413
+ 'dateString': inputData.dateString,
414
+ },
415
+ },
416
+ {
417
+ term: {
418
+ 'isParent': false,
419
+ },
420
+ },
421
+ {
422
+ term: {
423
+ isChecked: true,
424
+ },
425
+ },
426
+ ],
427
+ },
428
+ },
429
+ _source: [ 'tempId' ],
430
+
431
+ };
432
+ const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
433
+ // Get all tempIds from revopInfo response
434
+ const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
435
+ // Prepare management eyeZone query based on storeId and dateString
436
+ const managerEyeZoneQuery = {
437
+ size: 1,
438
+ query: {
439
+ bool: {
440
+ must: [
441
+ {
442
+ term: {
443
+ 'storeId.keyword': inputData.storeId,
444
+ },
445
+ },
446
+ {
447
+ term: {
448
+ 'storeDate': inputData.dateString,
449
+ },
450
+ },
451
+ ],
452
+ },
453
+ },
454
+ _source: [ 'originalToTrackerCustomerMapping' ],
455
+ };
456
+
457
+ // Query the managerEyeZone index for the matching document
458
+ const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
459
+ const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
460
+ // Extract originalToTrackerCustomerMapping if it exists
461
+ const mapping =
462
+ managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
463
+ managerEyeZoneHit.originalToTrackerCustomerMapping :
464
+ {};
465
+
466
+ // Find tempIds that exist in both revopInfo results and manager mapping
467
+ const temp = [];
468
+ tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
469
+ const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
470
+ if ( isSendMessge == true ) {
471
+ logger.info( '....1' );
472
+ }
473
+ }
395
474
  return res.sendSuccess( 'Ticket closed successfully' );
396
475
  } else {
397
476
  return res.sendError( 'Internal Server Error', 500 );
@@ -514,22 +593,6 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
514
593
  } );
515
594
  }
516
595
 
517
-
518
- // If no review mapping existed, push a new one
519
- // if ( record.mappingInfo.length === 0 ) {
520
- // record.mappingInfo.push( {
521
- // type: 'tangoreview',
522
- // mode: inputData.mappingInfo?.mode,
523
- // revicedFootfall: temp?.mappingInfo?.revicedFootfall,
524
- // revicedPerc: temp?.mappingInfo?.revicedPerc,
525
- // count: temp?.mappingInfo?.count,
526
- // revisedDetail: temp?.mappingInfo?.revisedDetail,
527
- // status: 'Closed',
528
- // createdByEmail: req?.user?.email,
529
- // createdByUserName: req?.user?.userName,
530
- // createdByRole: req?.user?.role,
531
- // } );
532
- // }
533
596
  record.mappingInfo.push(
534
597
  {
535
598
  type: 'finalRevision',
@@ -555,6 +618,84 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
555
618
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
556
619
 
557
620
  if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
621
+ const query = {
622
+ storeId: inputData?.storeId,
623
+ isVideoStream: true,
624
+ };
625
+ const getStoreType = await countDocumnetsCamera( query );
626
+ const revopInfoQuery = {
627
+ size: 10000,
628
+ query: {
629
+ bool: {
630
+ must: [
631
+ {
632
+ term: {
633
+ 'storeId.keyword': inputData.storeId,
634
+ },
635
+ },
636
+ {
637
+ term: {
638
+ 'dateString': inputData.dateString,
639
+ },
640
+ },
641
+ {
642
+ term: {
643
+ 'isParent': false,
644
+ },
645
+ },
646
+ {
647
+ term: {
648
+ isChecked: true,
649
+ },
650
+ },
651
+ ],
652
+ },
653
+ },
654
+ _source: [ 'tempId' ],
655
+
656
+ };
657
+ const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
658
+ // Get all tempIds from revopInfo response
659
+ const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
660
+ // Prepare management eyeZone query based on storeId and dateString
661
+ const managerEyeZoneQuery = {
662
+ size: 1,
663
+ query: {
664
+ bool: {
665
+ must: [
666
+ {
667
+ term: {
668
+ 'storeId.keyword': inputData.storeId,
669
+ },
670
+ },
671
+ {
672
+ term: {
673
+ 'storeDate': inputData.dateString,
674
+ },
675
+ },
676
+ ],
677
+ },
678
+ },
679
+ _source: [ 'originalToTrackerCustomerMapping' ],
680
+ };
681
+
682
+ // Query the managerEyeZone index for the matching document
683
+ const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
684
+ const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
685
+ // Extract originalToTrackerCustomerMapping if it exists
686
+ const mapping =
687
+ managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
688
+ managerEyeZoneHit.originalToTrackerCustomerMapping :
689
+ {};
690
+
691
+ // Find tempIds that exist in both revopInfo results and manager mapping
692
+ const temp = [];
693
+ tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
694
+ const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
695
+ if ( isSendMessge == true ) {
696
+ logger.info( '....1' );
697
+ }
698
+
558
699
  return res.sendSuccess( 'Ticket closed successfully' );
559
700
  } else {
560
701
  return res.sendError( 'Internal Server Error', 500 );
@@ -1101,69 +1242,503 @@ export async function ticketSummary( req, res ) {
1101
1242
  let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
1102
1243
  {
1103
1244
  script: {
1104
- script: {
1105
- lang: 'painless',
1106
- source: `
1107
- doc['revicedPerc.keyword'].size()!=0 &&
1108
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1109
- `,
1110
- params: { num: 85 },
1111
- },
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 },
1252
+ },
1253
+ },
1254
+ },
1255
+
1256
+
1257
+ ] );
1258
+ const aboveRespInternal = await getOpenSearchData( openSearch.footfallDirectory, aboveQinternal );
1259
+ internalTicketAccuracyAbove = aboveRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1260
+
1261
+ // ticketAccuracyBelow: avg of revicedPerc < 85%
1262
+ let internalTicketAccuracyBelow = 0;
1263
+
1264
+ let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
1265
+ {
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 },
1274
+ },
1275
+ },
1276
+ },
1277
+
1278
+ ] );
1279
+ const belowRespInternal = await getOpenSearchData( openSearch.footfallDirectory, belowQIneranl );
1280
+ internalTicketAccuracyBelow = belowRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1281
+
1282
+ // Final result object
1283
+ result = {
1284
+ totalTickets: totalInternalTickets,
1285
+ averageAccuracyOverAll: internalAverageAccuracyOverAll+'%',
1286
+ openTickets: openInternalTickets,
1287
+ openInfraIssues: openInternalInfraIssues,
1288
+ inprogress: inprogressIntrenal,
1289
+ closedTickets: closedInternalTickets,
1290
+ ticketAccuracyAbove: internalTicketAccuracyAbove+'%',
1291
+ ticketAccuracyBelow: internalTicketAccuracyBelow+'%',
1292
+ };
1293
+ break;
1294
+ default: '';
1295
+ }
1296
+ } else if ( req?.user?.userType === 'client' ) {
1297
+ if ( ticketsFeature && !ticketsApproveFeature ) {
1298
+ const storeQuery = {
1299
+ size: 0,
1300
+ query: {
1301
+ bool: {
1302
+ must: [
1303
+ {
1304
+ 'range': {
1305
+ 'dateString': {
1306
+ 'gte': inputData?.fromDate,
1307
+ 'lte': inputData?.toDate,
1308
+ 'format': 'yyyy-MM-dd',
1309
+ },
1310
+ },
1311
+ },
1312
+ {
1313
+ nested: {
1314
+ path: 'mappingInfo',
1315
+ query: {
1316
+ bool: {
1317
+ must: [
1318
+ {
1319
+ term: {
1320
+ 'mappingInfo.type': 'review',
1321
+ },
1322
+ },
1323
+ ],
1324
+ },
1325
+ },
1326
+ },
1327
+ },
1328
+
1329
+ ],
1330
+ },
1331
+ },
1332
+ };
1333
+
1334
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1335
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1336
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1337
+ // Remove any previous mappingInfo.status term
1338
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1339
+ if ( nested ) {
1340
+ // filter out all mappingInfo.status
1341
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1342
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1343
+ } );
1344
+ // add desired status
1345
+ nested.nested.query.bool.must.push( {
1346
+ term: {
1347
+ 'mappingInfo.status': statusValue,
1348
+ },
1349
+ } );
1350
+ }
1351
+ return q;
1352
+ }
1353
+
1354
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1355
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1356
+
1357
+ // locate nested section
1358
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1359
+
1360
+ if ( nested ) {
1361
+ // remove old status filters
1362
+ nested.nested.query.bool.must =
1363
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1364
+
1365
+ // add new filters
1366
+ nested.nested.query.bool.must.push( ...filters );
1367
+ }
1368
+
1369
+ return {
1370
+ ...q,
1371
+ size: 0,
1372
+ aggs: {
1373
+ 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
+ },
1388
+ },
1389
+ },
1390
+ };
1391
+ };
1392
+
1393
+
1394
+ // Get OpenSearch connection
1395
+
1396
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1397
+
1398
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1399
+ let totalTickets = 0;
1400
+
1401
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1402
+
1403
+ allQuery.size = 0;
1404
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1405
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1406
+
1407
+ // openTickets: mappingInfo.status: 'Open'
1408
+ let openTickets = 0;
1409
+
1410
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1411
+ otQ.size = 0;
1412
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1413
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1414
+ // logger.info( { msd: '..............2', openResp } );
1415
+
1416
+
1417
+ // inprogress: mappingInfo.status: 'in-Progress'
1418
+ let inprogress = 0;
1419
+
1420
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1421
+ ipQ.size = 0;
1422
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1423
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1424
+
1425
+ // closedTickets: mappingInfo.status: 'closed'
1426
+ let closedTickets = 0;
1427
+
1428
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1429
+ clQ.size = 0;
1430
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1431
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1432
+
1433
+ let dueToday = 0;
1434
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1435
+
1436
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1437
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1438
+ // Locate nested mappingInfo query
1439
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1440
+ if ( nestedDue ) {
1441
+ // Remove any previous mappingInfo.dueDate term
1442
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1443
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1444
+ );
1445
+ // Add new dueDate filter
1446
+ nestedDue.nested.query.bool.must.push( {
1447
+ term: { 'mappingInfo.dueDate': todayDateString },
1448
+ } );
1449
+ }
1450
+ dueTodayQuery.size = 0;
1451
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1452
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1453
+
1454
+ // filter expired Tickets
1455
+ let expiredTickets = 0;
1456
+
1457
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1458
+ eQ.size = 0;
1459
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1460
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1461
+
1462
+ let ticketAccuracyAbove = 0;
1463
+ // For this, add a filter on revicedPerc >= 85
1464
+ let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1465
+ {
1466
+ script: {
1467
+ script: {
1468
+ lang: 'painless',
1469
+ source: `
1470
+ doc['revicedPerc.keyword'].size()!=0 &&
1471
+ Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1472
+ `,
1473
+ params: { num: 85 },
1474
+ },
1475
+ },
1476
+ },
1477
+
1478
+
1479
+ ] );
1480
+ const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1481
+ ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1482
+
1483
+ // 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';
1503
+
1504
+ // Final result object
1505
+ result = {
1506
+ totalTickets,
1507
+ openTickets,
1508
+ inprogress,
1509
+ closedTickets,
1510
+ dueToday: dueToday,
1511
+ Expired: expiredTickets,
1512
+ avgTicket: ticketAccuracyAbove+'%',
1513
+ avgAccuracy: ticketAccuracyBelow+'%',
1514
+ };
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',
1529
+ },
1530
+ },
1531
+ },
1532
+ {
1533
+ nested: {
1534
+ path: 'mappingInfo',
1535
+ query: {
1536
+ bool: {
1537
+ must: [
1538
+ {
1539
+ term: {
1540
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1541
+ },
1542
+ },
1543
+ ],
1544
+ },
1545
+ },
1546
+ },
1547
+ },
1548
+
1549
+ ],
1550
+ },
1551
+ },
1552
+ };
1553
+
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,
1568
+ },
1569
+ } );
1570
+ }
1571
+ return q;
1572
+ }
1573
+
1574
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1575
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1576
+
1577
+ // locate nested section
1578
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1579
+
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'] ) );
1584
+
1585
+ // add new filters
1586
+ nested.nested.query.bool.must.push( ...filters );
1587
+ }
1588
+
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
+ },
1607
+ },
1608
+ },
1609
+ },
1610
+ };
1611
+ };
1612
+
1613
+
1614
+ // Get OpenSearch connection
1615
+
1616
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1617
+
1618
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1619
+ let totalTickets = 0;
1620
+
1621
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1622
+
1623
+ allQuery.size = 0;
1624
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1625
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1626
+
1627
+ // openTickets: mappingInfo.status: 'Open'
1628
+ let openTickets = 0;
1629
+
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 } );
1635
+
1636
+
1637
+ // inprogress: mappingInfo.status: 'in-Progress'
1638
+ let inprogress = 0;
1639
+
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
+
1645
+ // closedTickets: mappingInfo.status: 'closed'
1646
+ let closedTickets = 0;
1647
+
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)
1653
+
1654
+ // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1655
+ let dueToday = 0;
1656
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1657
+
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;
1675
+
1676
+
1677
+ // filter expired Tickets
1678
+ let expiredTickets = 0;
1679
+
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;
1684
+
1685
+ // ticketAccuracyAbove: avg of revicedPerc > 85%
1686
+ let ticketAccuracyAbove = 0;
1687
+
1688
+
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 },
1112
1700
  },
1113
1701
  },
1702
+ },
1114
1703
 
1115
1704
 
1116
- ] );
1117
- const aboveRespInternal = await getOpenSearchData( openSearch.footfallDirectory, aboveQinternal );
1118
- internalTicketAccuracyAbove = aboveRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1705
+ ] );
1706
+ const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1707
+ ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1119
1708
 
1120
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1121
- let internalTicketAccuracyBelow = 0;
1709
+ // ticketAccuracyBelow: avg of revicedPerc < 85%
1710
+ let ticketAccuracyBelow = 0;
1122
1711
 
1123
- let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
1124
- {
1712
+ let belowQ = buildAggStoreQuery( baseStoreQuery, [
1713
+ {
1714
+ script: {
1125
1715
  script: {
1126
- script: {
1127
- lang: 'painless',
1128
- source: `
1129
- doc['revicedPerc.keyword'].size()!=0 &&
1130
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1131
- `,
1132
- params: { num: 85 },
1133
- },
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 },
1134
1722
  },
1135
1723
  },
1724
+ },
1136
1725
 
1137
- ] );
1138
- const belowRespInternal = await getOpenSearchData( openSearch.footfallDirectory, belowQIneranl );
1139
- internalTicketAccuracyBelow = belowRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1726
+ ] );
1727
+ const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1728
+ ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1140
1729
 
1141
- // Final result object
1142
- result = {
1143
- totalTickets: totalInternalTickets,
1144
- averageAccuracyOverAll: internalAverageAccuracyOverAll+'%',
1145
- openTickets: openInternalTickets,
1146
- openInfraIssues: openInternalInfraIssues,
1147
- inprogress: inprogressIntrenal,
1148
- closedTickets: closedInternalTickets,
1149
- ticketAccuracyAbove: internalTicketAccuracyAbove+'%',
1150
- ticketAccuracyBelow: internalTicketAccuracyBelow+'%',
1151
- };
1152
- break;
1153
- default: '';
1154
- }
1155
- // result = {
1156
- // totalTickets: 0,
1157
- // averageAccuracyOverAll: 0,
1158
- // openTickets: 0,
1159
- // openInfraIssues: 0,
1160
- // inprogress: 0,
1161
- // closedTickets: 0,
1162
- // ticketAccuracyAbove: '0%',
1163
- // ticketAccuracyBelow: '0%',
1164
- // };
1165
- } else if ( req?.user?.userType === 'client' ) {
1166
- if ( ticketsFeature && !ticketsApproveFeature ) {
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 {
1167
1742
  const storeQuery = {
1168
1743
  size: 0,
1169
1744
  query: {
@@ -1186,7 +1761,7 @@ export async function ticketSummary( req, res ) {
1186
1761
  must: [
1187
1762
  {
1188
1763
  term: {
1189
- 'mappingInfo.type': 'review',
1764
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1190
1765
  },
1191
1766
  },
1192
1767
  ],
@@ -1245,12 +1820,12 @@ export async function ticketSummary( req, res ) {
1245
1820
  lang: 'painless',
1246
1821
  source: `
1247
1822
  if (doc['revicedPerc.keyword'].size() == 0) return null;
1248
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1249
- try {
1823
+ String v = doc['revicedPerc.keyword'].value.replace('%','');
1824
+ try {
1250
1825
  return Double.parseDouble(v);
1251
- } catch (Exception e) {
1826
+ } catch (Exception e) {
1252
1827
  return null;
1253
- }
1828
+ }
1254
1829
  `,
1255
1830
  },
1256
1831
  },
@@ -1283,14 +1858,6 @@ try {
1283
1858
  // logger.info( { msd: '..............2', openResp } );
1284
1859
 
1285
1860
 
1286
- // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
1287
- let openInfraIssues = 0;
1288
-
1289
- let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
1290
- oiQ.size = 0;
1291
- const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
1292
- openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
1293
-
1294
1861
  // inprogress: mappingInfo.status: 'in-Progress'
1295
1862
  let inprogress = 0;
1296
1863
 
@@ -1306,18 +1873,48 @@ try {
1306
1873
  clQ.size = 0;
1307
1874
  const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1308
1875
  closedTickets = clResp?.body?.hits?.total?.value || 0;
1309
- // Average revisedPerc (for all tangoreview)
1310
- let averageAccuracyOverAll = 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 );
1879
+
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;
1311
1897
 
1312
- let avgQ = buildAggStoreQuery( baseStoreQuery );
1313
- const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
1314
- averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1315
1898
 
1316
- // ticketAccuracyAbove: avg of revicedPerc > 85%
1317
- let ticketAccuracyAbove = 0;
1899
+ // filter expired Tickets
1900
+ let expiredTickets = 0;
1318
1901
 
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;
1906
+
1907
+ // filter under tango review
1908
+
1909
+ let undertangoTickets = 0;
1910
+
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;
1319
1915
 
1320
1916
  // For this, add a filter on revicedPerc >= 85
1917
+ let ticketAccuracyAbove = 0;
1321
1918
  let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1322
1919
  {
1323
1920
  script: {
@@ -1334,6 +1931,7 @@ try {
1334
1931
 
1335
1932
 
1336
1933
  ] );
1934
+
1337
1935
  const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1338
1936
  ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1339
1937
 
@@ -1361,246 +1959,16 @@ try {
1361
1959
  // Final result object
1362
1960
  result = {
1363
1961
  totalTickets,
1364
- averageAccuracyOverAll: averageAccuracyOverAll+'%',
1365
1962
  openTickets,
1366
- openInfraIssues,
1367
1963
  inprogress,
1368
1964
  closedTickets,
1369
- ticketAccuracyAbove: ticketAccuracyAbove+'%',
1370
- ticketAccuracyBelow: ticketAccuracyBelow+'%',
1965
+ dueToday: dueToday,
1966
+ Expired: expiredTickets,
1967
+ underTangoReview: undertangoTickets,
1968
+ avgTicket: ticketAccuracyAbove+'%',
1969
+ avgAccuracy: ticketAccuracyBelow+'%',
1371
1970
  };
1372
1971
  }
1373
- } else if ( ticketsFeature && !ticketsApproveFeature ) {
1374
- const storeQuery = {
1375
- size: 0,
1376
- query: {
1377
- bool: {
1378
- must: [
1379
- {
1380
- 'range': {
1381
- 'dateString': {
1382
- 'gte': inputData?.fromDate,
1383
- 'lte': inputData?.toDate,
1384
- 'format': 'yyyy-MM-dd',
1385
- },
1386
- },
1387
- },
1388
- {
1389
- nested: {
1390
- path: 'mappingInfo',
1391
- query: {
1392
- bool: {
1393
- must: [
1394
- {
1395
- term: {
1396
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1397
- },
1398
- },
1399
- ],
1400
- },
1401
- },
1402
- },
1403
- },
1404
-
1405
- ],
1406
- },
1407
- },
1408
- };
1409
-
1410
- // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1411
- function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1412
- let q = JSON.parse( JSON.stringify( baseQuery ) );
1413
- // Remove any previous mappingInfo.status term
1414
- let nested = q.query.bool.must.find( ( m ) => m.nested );
1415
- if ( nested ) {
1416
- // filter out all mappingInfo.status
1417
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1418
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1419
- } );
1420
- // add desired status
1421
- nested.nested.query.bool.must.push( {
1422
- term: {
1423
- 'mappingInfo.status': statusValue,
1424
- },
1425
- } );
1426
- }
1427
- return q;
1428
- }
1429
-
1430
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1431
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1432
-
1433
- // locate nested section
1434
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1435
-
1436
- if ( nested ) {
1437
- // remove old status filters
1438
- nested.nested.query.bool.must =
1439
- nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1440
-
1441
- // add new filters
1442
- nested.nested.query.bool.must.push( ...filters );
1443
- }
1444
-
1445
- return {
1446
- ...q,
1447
- size: 0,
1448
- aggs: {
1449
- avg_value: {
1450
- avg: {
1451
- script: {
1452
- lang: 'painless',
1453
- source: `
1454
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1455
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1456
- try {
1457
- return Double.parseDouble(v);
1458
- } catch (Exception e) {
1459
- return null;
1460
- }
1461
- `,
1462
- },
1463
- },
1464
- },
1465
- },
1466
- };
1467
- };
1468
-
1469
-
1470
- // Get OpenSearch connection
1471
-
1472
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1473
-
1474
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1475
- let totalTickets = 0;
1476
-
1477
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1478
-
1479
- allQuery.size = 0;
1480
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1481
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1482
-
1483
- // openTickets: mappingInfo.status: 'Open'
1484
- let openTickets = 0;
1485
-
1486
- let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1487
- otQ.size = 0;
1488
- const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1489
- openTickets = openResp?.body?.hits?.total?.value || 0;
1490
- // logger.info( { msd: '..............2', openResp } );
1491
-
1492
-
1493
- // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
1494
- let openInfraIssues = 0;
1495
-
1496
- let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
1497
- oiQ.size = 0;
1498
- const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
1499
- openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
1500
-
1501
- // inprogress: mappingInfo.status: 'in-Progress'
1502
- let inprogress = 0;
1503
-
1504
- let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1505
- ipQ.size = 0;
1506
- const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1507
- inprogress = ipResp?.body?.hits?.total?.value || 0;
1508
-
1509
- // closedTickets: mappingInfo.status: 'closed'
1510
- let closedTickets = 0;
1511
-
1512
- let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1513
- clQ.size = 0;
1514
- const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1515
- closedTickets = clResp?.body?.hits?.total?.value || 0;
1516
- // Average revisedPerc (for all tangoreview)
1517
- let averageAccuracyOverAll = 0;
1518
-
1519
- let avgQ = buildAggStoreQuery( baseStoreQuery );
1520
- const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
1521
- averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1522
-
1523
- // ticketAccuracyAbove: avg of revicedPerc > 85%
1524
- let ticketAccuracyAbove = 0;
1525
-
1526
-
1527
- // For this, add a filter on revicedPerc >= 85
1528
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1529
- {
1530
- script: {
1531
- script: {
1532
- lang: 'painless',
1533
- source: `
1534
- doc['revicedPerc.keyword'].size()!=0 &&
1535
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1536
- `,
1537
- params: { num: 85 },
1538
- },
1539
- },
1540
- },
1541
-
1542
-
1543
- ] );
1544
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1545
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1546
-
1547
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1548
- let ticketAccuracyBelow = 0;
1549
-
1550
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1551
- {
1552
- script: {
1553
- script: {
1554
- lang: 'painless',
1555
- source: `
1556
- doc['revicedPerc.keyword'].size()!=0 &&
1557
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1558
- `,
1559
- params: { num: 85 },
1560
- },
1561
- },
1562
- },
1563
-
1564
- ] );
1565
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1566
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1567
-
1568
- // Final result object
1569
- result = {
1570
- totalTickets,
1571
- averageAccuracyOverAll: averageAccuracyOverAll+'%',
1572
- openTickets,
1573
- openInfraIssues,
1574
- inprogress,
1575
- closedTickets,
1576
- ticketAccuracyAbove: ticketAccuracyAbove+'%',
1577
- ticketAccuracyBelow: ticketAccuracyBelow+'%',
1578
- };
1579
- } else {
1580
- result = req.user.role === 'superadmin' ?
1581
- {
1582
- totalTickets: 0,
1583
- openTickets: 0,
1584
- inprogress: 0,
1585
- closedTickets: 0,
1586
- dueToday: 0,
1587
- Expired: 0,
1588
- underTangoReview: 0,
1589
- avgTicket: '0%',
1590
- avgAccuracy: '0%',
1591
- } :
1592
- req.user.role === 'user' ? 'NA' :
1593
- ticketsFeature ?
1594
- {
1595
- totalTickets: 0,
1596
- openTickets: 0,
1597
- inprogress: 0,
1598
- closedTickets: 0,
1599
- dueToday: 0,
1600
- Expired: 0,
1601
- avgTicket: '0%',
1602
- avgAccuracy: '0%',
1603
- } : 'NA';
1604
1972
  }
1605
1973
 
1606
1974
  return res.sendSuccess( { result: result } );
@@ -1615,7 +1983,7 @@ export async function ticketList1( req, res ) {
1615
1983
  try {
1616
1984
  const openSearch = JSON.parse( process.env.OPENSEARCH );
1617
1985
  const inputData = req.query;
1618
- const limit = inputData.limit || 10;
1986
+ const limit =inputData?.isExport ? 10000: inputData.limit || 10;
1619
1987
  const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
1620
1988
  const order = inputData?.sortOrder || -1;
1621
1989
 
@@ -1837,7 +2205,7 @@ export async function ticketList( req, res ) {
1837
2205
  const openSearch = JSON.parse( process.env.OPENSEARCH );
1838
2206
  const inputData = req.query;
1839
2207
  const userInfo = req.user;
1840
- const limit = inputData?.limit || 10;
2208
+ const limit =inputData?.isExport? 10000: inputData?.limit || 10;
1841
2209
  const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
1842
2210
  inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
1843
2211
 
@@ -2642,137 +3010,285 @@ export async function ticketList( req, res ) {
2642
3010
  let temp = [];
2643
3011
  if ( req.user.userType === 'tango' ) {
2644
3012
  if ( inputData.tangoType === 'store' ) {
2645
- for ( let item of ticketListData ) {
2646
- temp.push( {
2647
-
2648
- ticketId: item?.ticketId,
2649
- storeId: item?.storeId,
2650
- storeName: item?.storeName,
2651
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2652
- issueDate: item?.dateString,
2653
- dueDate: '',
2654
- footfall: item?.footfallCount,
2655
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2656
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2657
- approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
2658
- tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2659
- status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3013
+ if ( inputData?.isExport ) {
3014
+ const exportData = [];
3015
+ for ( let item of ticketListData ) {
3016
+ exportData.push( {
3017
+
3018
+ 'Ticket ID': item?.ticketId,
3019
+ 'store Name': item?.storeName,
3020
+ 'store ID': item?.storeId,
3021
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
3022
+ 'Issue Date': item?.dateString,
3023
+ 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
3024
+ 'Actual FF': item?.footfallCount,
3025
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3026
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3027
+ 'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3028
+ 'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3029
+ 'Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
2660
3030
 
2661
- } );
3031
+ } );
3032
+ }
3033
+ return await download( exportData, res );
3034
+ } else {
3035
+ for ( let item of ticketListData ) {
3036
+ temp.push( {
3037
+
3038
+ ticketId: item?.ticketId,
3039
+ storeId: item?.storeId,
3040
+ storeName: item?.storeName,
3041
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
3042
+ issueDate: item?.dateString,
3043
+ dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
3044
+ footfall: item?.footfallCount,
3045
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3046
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3047
+ approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3048
+ tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3049
+ status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3050
+
3051
+ } );
3052
+ }
2662
3053
  }
2663
3054
  } else {
2664
- for ( let item of ticketListData ) {
2665
- temp.push( {
2666
-
2667
- ticketId: item?.ticketId,
2668
- storeId: item?.storeId,
2669
- storeName: item?.storeName,
2670
-
2671
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2672
- issueDate: item?.dateString,
2673
- footfall: item?.footfallCount,
2674
- dueDate: '',
2675
- type: item?.type || 'store',
2676
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2677
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2678
- approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
2679
- tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2680
- status: item?.status,
2681
- tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3055
+ if ( inputData?.isExport ) {
3056
+ const exportData = [];
3057
+ for ( let item of ticketListData ) {
3058
+ exportData.push( {
3059
+
3060
+ 'Ticket ID': item?.ticketId,
3061
+ 'Store Name': item?.storeName,
3062
+ 'Store ID': item?.storeId,
3063
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
3064
+ 'Issue Date': item?.dateString,
3065
+ 'Ticket Type': item?.type,
3066
+ 'Actual FF': item?.footfallCount,
3067
+ // Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevisoon'
3068
+ 'Due Date': ( () => {
3069
+ if ( Array.isArray( item?.mappingInfo ) ) {
3070
+ const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
3071
+ return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
3072
+ }
3073
+ return '';
3074
+ } )(),
3075
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3076
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3077
+ 'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3078
+ 'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3079
+ 'Ticket Status': item?.status,
3080
+ 'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
2682
3081
 
2683
- } );
3082
+ } );
3083
+ }
3084
+ return await download( exportData, res );
3085
+ } else {
3086
+ for ( let item of ticketListData ) {
3087
+ temp.push( {
3088
+
3089
+ ticketId: item?.ticketId,
3090
+ storeId: item?.storeId,
3091
+ storeName: item?.storeName,
3092
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
3093
+ issueDate: item?.dateString,
3094
+ footfall: item?.footfallCount,
3095
+ dueDate: ( () => {
3096
+ if ( Array.isArray( item?.mappingInfo ) ) {
3097
+ const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
3098
+ return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
3099
+ }
3100
+ return '';
3101
+ } )(),
3102
+ type: item?.type || 'store',
3103
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3104
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3105
+ approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3106
+ tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3107
+ status: item?.status,
3108
+ tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3109
+
3110
+ } );
3111
+ }
2684
3112
  }
2685
3113
  }
2686
3114
  } else {
2687
3115
  if ( inputData?.permissionType === 'approve' ) {
2688
- for ( let item of ticketListData ) {
2689
- temp.push( {
2690
-
2691
- ticketId: item?.ticketId,
2692
- storeId: item?.storeId,
2693
- storeName: item?.storeName,
2694
-
2695
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2696
- issueDate: item?.dateString,
2697
- dueDate: '',
2698
- footfall: item?.footfallCount,
2699
-
2700
- type: item.type || 'store',
2701
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2702
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2703
- approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
2704
- tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2705
- status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
2706
- tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2707
- approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
3116
+ if ( inputData.exportData ) {
3117
+ const exportData = [];
3118
+ for ( let item of ticketListData ) {
3119
+ exportData.push( {
3120
+
3121
+ 'Ticket ID': item?.ticketId,
3122
+ 'Store Name': item?.storeName,
3123
+ 'Store ID': item?.storeId,
3124
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3125
+ 'Issue Date': item?.dateString,
3126
+ 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3127
+ 'Actual FF': item?.footfallCount,
3128
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3129
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3130
+ 'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3131
+ 'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3132
+ 'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
3133
+ 'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3134
+ 'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
2708
3135
 
2709
- } );
3136
+ } );
3137
+ }
3138
+ return await download( exportData, res );
3139
+ } else {
3140
+ for ( let item of ticketListData ) {
3141
+ temp.push( {
3142
+
3143
+ ticketId: item?.ticketId,
3144
+ storeId: item?.storeId,
3145
+ storeName: item?.storeName,
3146
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3147
+ issueDate: item?.dateString,
3148
+ dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3149
+ footfall: item?.footfallCount,
3150
+ type: item.type || 'store',
3151
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3152
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3153
+ approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3154
+ tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3155
+ status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
3156
+ tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3157
+ approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
3158
+
3159
+ } );
3160
+ }
2710
3161
  }
2711
3162
  } else if ( inputData?.permissionType === 'review' ) {
2712
- for ( let item of ticketListData ) {
2713
- temp.push( {
2714
-
2715
- ticketId: item?.ticketId,
2716
- storeId: item?.storeId,
2717
- storeName: item?.storeName,
2718
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2719
- issueDate: item?.dateString,
2720
- footfall: item?.footfallCount,
2721
- dueDate: '',
2722
- type: item.type || 'store',
2723
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2724
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2725
-
2726
- status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
2727
- ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
3163
+ if ( inputData?.isExport ) {
3164
+ const exportData = [];
3165
+ for ( let item of ticketListData ) {
3166
+ exportData.push( {
3167
+
3168
+ 'Ticket ID': item?.ticketId,
3169
+ 'Store Name': item?.storeName,
3170
+ 'Store ID': item?.storeId,
3171
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3172
+ 'Issue Date': item?.dateString,
3173
+ 'Actual FF': item?.footfallCount,
3174
+ 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3175
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3176
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3177
+ 'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
3178
+ 'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
2728
3179
 
2729
- } );
3180
+ } );
3181
+ }
3182
+ return await download( exportData, res );
3183
+ } else {
3184
+ for ( let item of ticketListData ) {
3185
+ temp.push( {
3186
+
3187
+ ticketId: item?.ticketId,
3188
+ storeId: item?.storeId,
3189
+ storeName: item?.storeName,
3190
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3191
+ issueDate: item?.dateString,
3192
+ footfall: item?.footfallCount,
3193
+ dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3194
+ type: item.type || 'store',
3195
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3196
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3197
+ status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
3198
+ ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
3199
+
3200
+ } );
3201
+ }
2730
3202
  }
2731
3203
  } else if ( req.user.role === 'user' ) {
2732
3204
  temp = [];
2733
3205
  } else if ( ticketsFeature ) {
2734
- for ( let item of ticketListData ) {
2735
- temp.push( {
2736
-
2737
- ticketId: item?.ticketId,
2738
- storeId: item?.storeId,
2739
- storeName: item?.storeName,
2740
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2741
- issueDate: item?.dateString,
2742
- footfall: item?.footfallCount,
2743
- dueDate: '',
2744
- type: item.type || 'store',
2745
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2746
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2747
-
2748
- status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
2749
- ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
3206
+ if ( inputData?.isExport ) {
3207
+ const exportData = [];
3208
+ for ( let item of ticketListData ) {
3209
+ exportData.push( {
3210
+
3211
+ 'Ticket ID': item?.ticketId,
3212
+ 'Store ID': item?.storeId,
3213
+ 'Store Name': item?.storeName,
3214
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3215
+ 'Issue Date': item?.dateString,
3216
+ 'Actual FF': item?.footfallCount,
3217
+ 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3218
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3219
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3220
+ 'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
3221
+ 'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
2750
3222
 
2751
- } );
3223
+ } );
3224
+ }
3225
+ return await download( exportData, res );
3226
+ } else {
3227
+ for ( let item of ticketListData ) {
3228
+ temp.push( {
3229
+
3230
+ ticketId: item?.ticketId,
3231
+ storeId: item?.storeId,
3232
+ storeName: item?.storeName,
3233
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3234
+ issueDate: item?.dateString,
3235
+ footfall: item?.footfallCount,
3236
+ dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3237
+ type: item.type || 'store',
3238
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3239
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3240
+ status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
3241
+ ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
3242
+
3243
+ } );
3244
+ }
2752
3245
  }
2753
3246
  } else if ( ticketsApproveFeature ) {
2754
- for ( let item of ticketListData ) {
2755
- temp.push( {
2756
-
2757
- ticketId: item?.ticketId,
2758
- storeId: item?.storeId,
2759
- storeName: item?.storeName,
2760
-
2761
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
2762
- issueDate: item?.dateString,
2763
- dueDate: '',
2764
- footfall: item?.footfallCount,
2765
-
2766
- type: item.type || 'store',
2767
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
2768
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
2769
- approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
2770
- tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2771
- status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
2772
- tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
2773
- approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
3247
+ if ( inputData.isExport ) {
3248
+ const exportData = [];
3249
+ for ( let item of ticketListData ) {
3250
+ exportData.push( {
3251
+
3252
+ 'Ticket ID': item?.ticketId,
3253
+ 'Store Name': item?.storeName,
3254
+ 'Store ID': item?.storeId,
3255
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3256
+ 'Issue Date': item?.dateString,
3257
+ 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3258
+ 'Actual FF': item?.footfallCount,
3259
+ 'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3260
+ 'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3261
+ 'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3262
+ 'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3263
+ 'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
3264
+ 'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3265
+ 'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
2774
3266
 
2775
- } );
3267
+ } );
3268
+ }
3269
+ return await download( exportData, res );
3270
+ } else {
3271
+ for ( let item of ticketListData ) {
3272
+ temp.push( {
3273
+
3274
+ ticketId: item?.ticketId,
3275
+ storeId: item?.storeId,
3276
+ storeName: item?.storeName,
3277
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3278
+ issueDate: item?.dateString,
3279
+ dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3280
+ footfall: item?.footfallCount,
3281
+ type: item.type || 'store',
3282
+ storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3283
+ reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3284
+ approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3285
+ tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3286
+ status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
3287
+ tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3288
+ approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
3289
+
3290
+ } );
3291
+ }
2776
3292
  }
2777
3293
  } else {
2778
3294
  temp = [];
@@ -4038,6 +4554,7 @@ export async function openTicketList( req, res ) {
4038
4554
  },
4039
4555
  ];
4040
4556
 
4557
+
4041
4558
  const openSearchQuery = {
4042
4559
  size: 10000,
4043
4560
  query: {
@@ -4047,6 +4564,31 @@ export async function openTicketList( req, res ) {
4047
4564
  },
4048
4565
  _source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
4049
4566
  };
4567
+
4568
+ if ( inputData.searchValue && inputData.searchValue !== '' ) {
4569
+ openSearchQuery.query.bool['should'] = [];
4570
+ openSearchQuery.query.bool.should = [
4571
+
4572
+ {
4573
+ 'wildcard': {
4574
+ 'storeName.keyword': {
4575
+ 'value': `*${inputData.searchValue}*`,
4576
+ },
4577
+ },
4578
+ },
4579
+ {
4580
+ 'wildcard': {
4581
+ 'ticketId.keyword': {
4582
+ 'value': `*${inputData.searchValue}*`,
4583
+ },
4584
+ },
4585
+ },
4586
+
4587
+
4588
+ ];
4589
+ openSearchQuery.query.bool['minimum_should_match'] = 1;
4590
+ }
4591
+
4050
4592
  // INSERT_YOUR_CODE
4051
4593
  // Add sorting by revicedPerc descending (highest revised accuracy first)
4052
4594
  openSearchQuery.sort = [