tango-app-api-infra 3.9.5-vms.90 → 3.9.5-vms.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-infra",
3
- "version": "3.9.5-vms.90",
3
+ "version": "3.9.5-vms.93",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -151,6 +151,7 @@ export async function tangoReviewTicket( req, res ) {
151
151
  query: {
152
152
  bool: {
153
153
  must: [
154
+
154
155
  {
155
156
  term: {
156
157
  'storeId.keyword': inputData.storeId,
@@ -177,6 +178,11 @@ export async function tangoReviewTicket( req, res ) {
177
178
  query: {
178
179
  bool: {
179
180
  must: [
181
+ {
182
+ term: {
183
+ 'type.keyword': 'store',
184
+ },
185
+ },
180
186
  {
181
187
  term: {
182
188
  'storeId.keyword': inputData.storeId,
@@ -505,6 +511,12 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
505
511
  query: {
506
512
  bool: {
507
513
  must: [
514
+ {
515
+ term: {
516
+ 'type.keyword': 'store',
517
+ },
518
+ },
519
+
508
520
  {
509
521
  term: {
510
522
  'storeId.keyword': inputData.storeId,
@@ -572,36 +584,17 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
572
584
  subComments: inputData?.subComments ||'',
573
585
  } ) );
574
586
 
575
- record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
576
- ...temp ];
577
- // if ( Array.isArray( record.mappingInfo ) ) {
578
- // record.mappingInfo = record.mappingInfo.map( ( item ) => {
579
- // return {
580
- // ...item,
581
- // status: 'Closed',
582
- // };
583
- // } );
584
- // }
585
-
586
- record.mappingInfo.push(
587
- {
588
- type: 'finalRevision',
589
- mode: 'web',
590
- revicedFootfall: temp?.[0]?.revicedFootfall,
591
- revicedPerc: temp?.[0].revicedPerc,
592
- count: temp?.[0].count,
593
- revisedDetail: temp?.[0]?.revisedDetail,
594
- status: 'Closed',
595
- createdByEmail: req?.user?.email,
596
- createdByUserName: req?.user?.userName,
597
- createdByRole: req?.user?.role,
598
- createdAt: new Date(),
587
+ const temp2 = record.mappingInfo
588
+ .filter( ( item ) => item.type === 'finalRevision' )
589
+ .map( ( item ) => ( {
590
+ ...item,
599
591
  comments: inputData?.comments || '',
600
- subComments: inputData?.subComments || '',
601
- },
602
- );
603
- }
592
+ subComments: inputData?.subComments ||'',
593
+ } ) );
604
594
 
595
+ record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -2 ),
596
+ ...temp, ...temp2 ];
597
+ }
605
598
 
606
599
  // return;
607
600
 
@@ -610,84 +603,6 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
610
603
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
611
604
 
612
605
  if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
613
- const query = {
614
- storeId: inputData?.storeId,
615
- isVideoStream: true,
616
- };
617
- const getStoreType = await countDocumnetsCamera( query );
618
- const revopInfoQuery = {
619
- size: 10000,
620
- query: {
621
- bool: {
622
- must: [
623
- {
624
- term: {
625
- 'storeId.keyword': inputData.storeId,
626
- },
627
- },
628
- {
629
- term: {
630
- 'dateString': inputData.dateString,
631
- },
632
- },
633
- {
634
- term: {
635
- 'isParent': false,
636
- },
637
- },
638
- {
639
- term: {
640
- isChecked: true,
641
- },
642
- },
643
- ],
644
- },
645
- },
646
- _source: [ 'tempId' ],
647
-
648
- };
649
- const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
650
- // Get all tempIds from revopInfo response
651
- const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
652
- // Prepare management eyeZone query based on storeId and dateString
653
- const managerEyeZoneQuery = {
654
- size: 1,
655
- query: {
656
- bool: {
657
- must: [
658
- {
659
- term: {
660
- 'storeId.keyword': inputData.storeId,
661
- },
662
- },
663
- {
664
- term: {
665
- 'storeDate': inputData.dateString,
666
- },
667
- },
668
- ],
669
- },
670
- },
671
- _source: [ 'originalToTrackerCustomerMapping' ],
672
- };
673
-
674
- // Query the managerEyeZone index for the matching document
675
- const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
676
- const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
677
- // Extract originalToTrackerCustomerMapping if it exists
678
- const mapping =
679
- managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
680
- managerEyeZoneHit.originalToTrackerCustomerMapping :
681
- {};
682
-
683
- // Find tempIds that exist in both revopInfo results and manager mapping
684
- const temp = [];
685
- tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
686
- const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
687
- if ( isSendMessge == true ) {
688
- logger.info( '....1' );
689
- }
690
-
691
606
  return res.sendSuccess( 'Ticket closed successfully' );
692
607
  } else {
693
608
  return res.sendError( 'Internal Server Error', 500 );
@@ -1511,6 +1426,220 @@ export async function ticketSummary( req, res ) {
1511
1426
  avgTicket: ticketPercentageAvg+'%',
1512
1427
  avgAccuracy: ticketAccuracy+'%',
1513
1428
  };
1429
+ } else if ( !ticketsFeature && ticketsApproveFeature ) {
1430
+ const storeQuery = {
1431
+ size: 0,
1432
+ query: {
1433
+ bool: {
1434
+ must: [
1435
+ {
1436
+ 'range': {
1437
+ 'dateString': {
1438
+ 'gte': inputData?.fromDate,
1439
+ 'lte': inputData?.toDate,
1440
+ 'format': 'yyyy-MM-dd',
1441
+ },
1442
+ },
1443
+ },
1444
+ {
1445
+ terms: {
1446
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1447
+ inputData.clientId :
1448
+ [ inputData.clientId ],
1449
+ },
1450
+
1451
+ },
1452
+ {
1453
+ nested: {
1454
+ path: 'mappingInfo',
1455
+ query: {
1456
+ bool: {
1457
+ must: [
1458
+ {
1459
+ term: {
1460
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1461
+ },
1462
+ },
1463
+ ],
1464
+ },
1465
+ },
1466
+ },
1467
+ },
1468
+
1469
+ ],
1470
+ },
1471
+ },
1472
+ };
1473
+
1474
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1475
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1476
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1477
+ // Remove any previous mappingInfo.status term
1478
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1479
+ if ( nested ) {
1480
+ // filter out all mappingInfo.status
1481
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1482
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1483
+ } );
1484
+ // add desired status
1485
+ nested.nested.query.bool.must.push( {
1486
+ term: {
1487
+ 'mappingInfo.status': statusValue,
1488
+ },
1489
+ } );
1490
+ }
1491
+ return q;
1492
+ }
1493
+
1494
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1495
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1496
+
1497
+ // locate nested section
1498
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1499
+
1500
+ if ( nested ) {
1501
+ // remove old status filters
1502
+ nested.nested.query.bool.must =
1503
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1504
+
1505
+ // add new filters
1506
+ nested.nested.query.bool.must.push( ...filters );
1507
+ }
1508
+
1509
+ return {
1510
+ ...q,
1511
+ size: 0,
1512
+ aggs: {
1513
+ avg_value: {
1514
+ avg: 'mappingInfp.reviced',
1515
+ },
1516
+ },
1517
+ };
1518
+ };
1519
+
1520
+
1521
+ // Get OpenSearch connection
1522
+
1523
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1524
+
1525
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1526
+ let totalTickets = 0;
1527
+
1528
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1529
+
1530
+ allQuery.size = 0;
1531
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1532
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1533
+
1534
+ // openTickets: mappingInfo.status: 'Open'
1535
+ let openTickets = 0;
1536
+
1537
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1538
+ otQ.size = 0;
1539
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1540
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1541
+ // logger.info( { msd: '..............2', openResp } );
1542
+
1543
+
1544
+ // inprogress: mappingInfo.status: 'in-Progress'
1545
+ let inprogress = 0;
1546
+
1547
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1548
+ ipQ.size = 0;
1549
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1550
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1551
+
1552
+ // closedTickets: mappingInfo.status: 'closed'
1553
+ let closedTickets = 0;
1554
+
1555
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1556
+ clQ.size = 0;
1557
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1558
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1559
+ // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1560
+ let dueToday = 0;
1561
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1562
+
1563
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1564
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1565
+ // Locate nested mappingInfo query
1566
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1567
+ if ( nestedDue ) {
1568
+ // Remove any previous mappingInfo.dueDate term
1569
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1570
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1571
+ );
1572
+ // Add new dueDate filter
1573
+ nestedDue.nested.query.bool.must.push( {
1574
+ term: { 'mappingInfo.dueDate': todayDateString },
1575
+ } );
1576
+ }
1577
+ dueTodayQuery.size = 0;
1578
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1579
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1580
+
1581
+
1582
+ // filter expired Tickets
1583
+ let expiredTickets = 0;
1584
+
1585
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1586
+ eQ.size = 0;
1587
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1588
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1589
+
1590
+ // filter under tango review
1591
+
1592
+ let undertangoTickets = 0;
1593
+
1594
+ let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1595
+ utrQ.size = 0;
1596
+ const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1597
+ undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1598
+
1599
+ let ticketPercentageAvg =0;
1600
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1601
+ avgTicketPercentageQuery.size = 0;
1602
+ avgTicketPercentageQuery.aggs = {
1603
+ avg_ticket_percentage: {
1604
+ avg: {
1605
+ script: {
1606
+ source: `
1607
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1608
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1609
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1610
+ } else {
1611
+ return null;
1612
+ }
1613
+ `,
1614
+ lang: 'painless',
1615
+ },
1616
+ },
1617
+ },
1618
+ };
1619
+
1620
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1621
+
1622
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1623
+ logger.info( { avgTicketPercentageResp } );
1624
+ let ticketAccuracy = 0;
1625
+
1626
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1627
+ logger.info( { belowQ } );
1628
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1629
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1630
+ logger.info( { accuracyResp } );
1631
+ // Final result object
1632
+ result = {
1633
+ totalTickets,
1634
+ openTickets,
1635
+ inprogress,
1636
+ closedTickets,
1637
+ dueToday: dueToday,
1638
+ Expired: expiredTickets,
1639
+ underTangoReview: undertangoTickets,
1640
+ avgTicket: ticketPercentageAvg+'%',
1641
+ avgAccuracy: ticketAccuracy+'%',
1642
+ };
1514
1643
  } else if ( ticketsFeature && ticketsApproveFeature ) {
1515
1644
  if ( inputData?.permissionType === 'review' ) {
1516
1645
  const storeQuery = {
@@ -2212,6 +2341,20 @@ export async function ticketList( req, res ) {
2212
2341
  },
2213
2342
  };
2214
2343
 
2344
+ if ( req?.user?.userType !== 'tango', req?.user?.role !== 'superadmin' ) {
2345
+ if ( req?.body?.assignedStores?.length > 0 ) {
2346
+ searchQuery.query.bool.must.push( {
2347
+ terms: {
2348
+ 'storeId.keyword': Array.isArray( req?.body?.assignedStores ) ?
2349
+ req?.body?.assignedStores :
2350
+ [ req?.body?.assignedStores ],
2351
+ },
2352
+ } );
2353
+ } else {
2354
+ return res.sendError( 'no data', 204 );
2355
+ }
2356
+ }
2357
+
2215
2358
  if ( inputData.sortBy ) {
2216
2359
  let sortOrder = inputData.sortOrder === 1 ? 'asc' : 'desc';
2217
2360
 
@@ -2423,6 +2566,28 @@ export async function ticketList( req, res ) {
2423
2566
  },
2424
2567
  },
2425
2568
  } );
2569
+ } else if ( !ticketsFeature && ticketsApproveFeature ) {
2570
+ searchQuery.query.bool.must.push( {
2571
+ nested: {
2572
+ path: 'mappingInfo',
2573
+ query: {
2574
+ bool: {
2575
+ must: [
2576
+ {
2577
+ term: {
2578
+ 'mappingInfo.type': 'approve',
2579
+ },
2580
+ },
2581
+ {
2582
+ terms: {
2583
+ 'mappingInfo.status': inputData?.filterByStatus,
2584
+ },
2585
+ },
2586
+ ],
2587
+ },
2588
+ },
2589
+ },
2590
+ } );
2426
2591
  } else if ( ticketsFeature && ticketsApproveFeature ) {
2427
2592
  switch ( inputData.permisisionType ) {
2428
2593
  case 'review':
@@ -2992,9 +3157,18 @@ export async function ticketList( req, res ) {
2992
3157
  'Ticket ID': item?.ticketId,
2993
3158
  'store Name': item?.storeName,
2994
3159
  'store ID': item?.storeId,
2995
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
2996
- 'Issue Date': item?.dateString,
2997
- 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
3160
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3161
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3162
+ 'Due Date': ( () => {
3163
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate;
3164
+ if ( dueDate ) {
3165
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3166
+ return 'Due Today';
3167
+ }
3168
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3169
+ }
3170
+ return '';
3171
+ } )(),
2998
3172
  'Actual FF': item?.footfallCount,
2999
3173
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3000
3174
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
@@ -3011,20 +3185,29 @@ export async function ticketList( req, res ) {
3011
3185
  for ( let item of ticketListData ) {
3012
3186
  temp.push( {
3013
3187
 
3014
- ticketId: item?.ticketId,
3015
- storeId: item?.storeId,
3016
- storeName: item?.storeName,
3017
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
3018
- issueDate: item?.dateString,
3019
- dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
3020
- footfall: item?.footfallCount,
3021
- storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3022
- reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3023
- approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3024
- tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3025
- status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3026
- comments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
3027
- subComments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
3188
+ 'ticketId': item?.ticketId,
3189
+ 'storeId': item?.storeId,
3190
+ 'storeName': item?.storeName,
3191
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3192
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3193
+ 'Due Date': ( () => {
3194
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate;
3195
+ if ( dueDate ) {
3196
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3197
+ return 'Due Today';
3198
+ }
3199
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3200
+ }
3201
+ return '';
3202
+ } )(),
3203
+ 'footfall': item?.footfallCount,
3204
+ 'storeRevisedAccuracy': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3205
+ 'reviewerRevisedAccuracy': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3206
+ 'approverRevisedAccuracy': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3207
+ 'tangoRevisedAccuracy': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3208
+ 'status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3209
+ 'comments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
3210
+ 'subComments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
3028
3211
 
3029
3212
  } );
3030
3213
  }
@@ -3033,29 +3216,50 @@ export async function ticketList( req, res ) {
3033
3216
  if ( inputData?.isExport ) {
3034
3217
  const exportData = [];
3035
3218
  for ( let item of ticketListData ) {
3219
+ console.log( item );
3036
3220
  exportData.push( {
3037
3221
 
3038
3222
  'Ticket ID': item?.ticketId,
3039
3223
  'Store Name': item?.storeName,
3040
3224
  'Store ID': item?.storeId,
3041
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
3042
- 'Issue Date': item?.dateString,
3225
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3226
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3043
3227
  'Ticket Type': item?.type,
3044
3228
  'Actual FF': item?.footfallCount,
3045
- // Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevisoon'
3229
+ // Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevison'
3230
+
3046
3231
  'Due Date': ( () => {
3047
3232
  if ( Array.isArray( item?.mappingInfo ) ) {
3048
- const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
3233
+ const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevison' );
3049
3234
  return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
3050
3235
  }
3051
3236
  return '';
3052
3237
  } )(),
3238
+ 'Due Date (Formatted)': ( () => {
3239
+ if ( Array.isArray( item?.mappingInfo ) ) {
3240
+ // Find last mappingInfo whose type is NOT 'finalRevison'
3241
+ const filtered = item.mappingInfo.filter(
3242
+ ( f ) => f.dueDate && f.type !== 'finalRevison',
3243
+ );
3244
+ if ( filtered.length > 0 ) {
3245
+ const lastDueDate = filtered[filtered.length - 1].dueDate;
3246
+ if ( lastDueDate ) {
3247
+ if ( dayjs( lastDueDate ).isSame( dayjs(), 'day' ) ) {
3248
+ return 'Due Today';
3249
+ }
3250
+ return dayjs( lastDueDate ).format( 'DD MMM, YYYY' );
3251
+ }
3252
+ }
3253
+ }
3254
+ return '';
3255
+ } )(),
3256
+
3053
3257
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3054
3258
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3055
3259
  'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
3056
3260
  'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
3057
- 'Ticket Status': item?.status,
3058
- 'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
3261
+ 'Ticket Status': item?.type === 'store'? item?.status : '',
3262
+ 'Tango Status': item?.type === 'store'? item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--' : item.status,
3059
3263
 
3060
3264
  } );
3061
3265
  }
@@ -3067,12 +3271,12 @@ export async function ticketList( req, res ) {
3067
3271
  ticketId: item?.ticketId,
3068
3272
  storeId: item?.storeId,
3069
3273
  storeName: item?.storeName,
3070
- ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
3274
+ ticketRaised: item?.mappingInfo?.find( ( f ) => f?.type === 'tagging' )?.createdAt,
3071
3275
  issueDate: item?.dateString,
3072
3276
  footfall: item?.footfallCount,
3073
3277
  dueDate: ( () => {
3074
3278
  if ( Array.isArray( item?.mappingInfo ) ) {
3075
- const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
3279
+ const filtered = item.mappingInfo.filter( ( f ) => f?.dueDate && f?.type !== 'finalRevisoon' );
3076
3280
  return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
3077
3281
  }
3078
3282
  return '';
@@ -3099,9 +3303,18 @@ export async function ticketList( req, res ) {
3099
3303
  'Ticket ID': item?.ticketId,
3100
3304
  'Store Name': item?.storeName,
3101
3305
  'Store ID': item?.storeId,
3102
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3103
- 'Issue Date': item?.dateString,
3104
- 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3306
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3307
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3308
+ 'Due Date': ( () => {
3309
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate;
3310
+ if ( dueDate ) {
3311
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3312
+ return 'Due Today';
3313
+ }
3314
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3315
+ }
3316
+ return '';
3317
+ } )(),
3105
3318
  'Actual FF': item?.footfallCount,
3106
3319
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3107
3320
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
@@ -3146,10 +3359,19 @@ export async function ticketList( req, res ) {
3146
3359
  'Ticket ID': item?.ticketId,
3147
3360
  'Store Name': item?.storeName,
3148
3361
  'Store ID': item?.storeId,
3149
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3150
- 'Issue Date': item?.dateString,
3362
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3363
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3364
+ 'Due Date': ( () => {
3365
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate;
3366
+ if ( dueDate ) {
3367
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3368
+ return 'Due Today';
3369
+ }
3370
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3371
+ }
3372
+ return '';
3373
+ } )(),
3151
3374
  'Actual FF': item?.footfallCount,
3152
- 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3153
3375
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3154
3376
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3155
3377
  'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
@@ -3189,10 +3411,20 @@ export async function ticketList( req, res ) {
3189
3411
  'Ticket ID': item?.ticketId,
3190
3412
  'Store ID': item?.storeId,
3191
3413
  'Store Name': item?.storeName,
3192
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
3193
- 'Issue Date': item?.dateString,
3414
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3415
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3416
+ 'Due Date': ( () => {
3417
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate;
3418
+ if ( dueDate ) {
3419
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3420
+ return 'Due Today';
3421
+ }
3422
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3423
+ }
3424
+ return '';
3425
+ } )(),
3194
3426
  'Actual FF': item?.footfallCount,
3195
- 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
3427
+
3196
3428
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3197
3429
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
3198
3430
  'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
@@ -3230,9 +3462,18 @@ export async function ticketList( req, res ) {
3230
3462
  'Ticket ID': item?.ticketId,
3231
3463
  'Store Name': item?.storeName,
3232
3464
  'Store ID': item?.storeId,
3233
- 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
3234
- 'Issue Date': item?.dateString,
3235
- 'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
3465
+ 'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt? dayjs( item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt ).format( 'DD MMM, YYYY' ) : '',
3466
+ 'Issue Date': item?.dateString ? dayjs( item.dateString ).format( 'DD MMM, YYYY' ) : '',
3467
+ 'Due Date': ( () => {
3468
+ const dueDate = item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate;
3469
+ if ( dueDate ) {
3470
+ if ( dayjs( dueDate ).isSame( dayjs(), 'day' ) ) {
3471
+ return 'Due Today';
3472
+ }
3473
+ return dayjs( dueDate ).format( 'DD MMM, YYYY' );
3474
+ }
3475
+ return '';
3476
+ } )(),
3236
3477
  'Actual FF': item?.footfallCount,
3237
3478
  'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
3238
3479
  'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
@@ -3287,7 +3528,7 @@ export async function getTickets( req, res ) {
3287
3528
  const inputData = req.query;
3288
3529
 
3289
3530
 
3290
- let source = [ 'storeId', 'type', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail', 'type' ];
3531
+ let source = [ 'storeId', 'createdByEmail', 'type', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail', 'type' ];
3291
3532
  let filter = [
3292
3533
 
3293
3534
  {
@@ -3334,6 +3575,7 @@ export async function getTickets( req, res ) {
3334
3575
  response.map( ( hit ) => {
3335
3576
  const defaultData = {
3336
3577
  storeId: hit._source.storeId,
3578
+ createdByEmail: hit?._source?.createdByEmail,
3337
3579
  dateString: hit?._source?.dateString,
3338
3580
  ticketName: hit?._source?.ticketName,
3339
3581
  status: hit?._source?.status?.revicedFootfall,
@@ -4457,6 +4699,14 @@ async function extractTempIds( document ) {
4457
4699
  export async function reviewerList( req, res ) {
4458
4700
  try {
4459
4701
  const inputData = req.query;
4702
+
4703
+ console.log( '🚀 ~ reviewerList ~ inputData.tangotype:', inputData.tangotype );
4704
+ if ( inputData.tangotype!=''&&inputData.tangotype==='internal' ) {
4705
+ const getUserlist = await findUser( { userType: 'tango' }, { userName: 1, email: 1, role: 1 } );
4706
+ return res.sendSuccess( getUserlist || [] );
4707
+ }
4708
+
4709
+
4460
4710
  // Build the query for users who have rolespermission with featureName "FootfallDirectory",
4461
4711
  // and a module "Reviewer" where isAdd or isEdit is true.
4462
4712
  const reviewerRoleQuery = {
@@ -515,6 +515,7 @@ export const downloadTicketsValid = {
515
515
 
516
516
  export const reviewerListSchema = Joi.object().keys( {
517
517
  clientId: Joi.string().required(),
518
+ tangotype: Joi.string().optional(),
518
519
  type: Joi.string().required().allow( 'approve', 'review' ),
519
520
  } );
520
521
 
@@ -15,7 +15,7 @@ footfallDirectoryRouter.post( '/tango-review-accuracy-ticket', isAllowedSessionH
15
15
 
16
16
  footfallDirectoryRouter.get( '/ticket-summary', isAllowedSessionHandler, bulkValidate( ticketSummaryValid ), ticketSummary );
17
17
 
18
- footfallDirectoryRouter.get( '/ticket-list', isAllowedSessionHandler, bulkValidate( ticketListValid ), ticketList );
18
+ footfallDirectoryRouter.get( '/ticket-list', isAllowedSessionHandler, bulkValidate( ticketListValid ), getAssinedStore, ticketList );
19
19
  footfallDirectoryRouter.get( '/get-tickets', isAllowedSessionHandler, bulkValidate( getTicketsValid ), getTickets );
20
20
  footfallDirectoryRouter.get( '/get-tagged-stores', isAllowedSessionHandler, bulkValidate( getTaggedStoresValid ), getAssinedStore, getClusters, getTaggedStores );
21
21
  footfallDirectoryRouter.put( '/update-status', isAllowedSessionHandler, bulkValidate( updateStatusValid ), updateStatus );
@@ -1,4 +1,4 @@
1
- import { bulkUpdate, getOpenSearchCount, getOpenSearchData, insertWithId, logger, sendMessageToFIFOQueue, updateOpenSearchData } from 'tango-app-api-middleware';
1
+ import { bulkUpdate, getOpenSearchCount, getOpenSearchData, insertWithId, logger, sendMessageToFIFOQueue, updateOpenSearchData, getOpenSearchById } from 'tango-app-api-middleware';
2
2
  import { aggregateCluster } from '../services/cluster.service.js';
3
3
  import { findOneRevopDownload } from '../services/revopDownload.service.js';
4
4
  import { findOneStore } from '../services/store.service.js';
@@ -957,13 +957,14 @@ export async function ticketReview( req, res, next ) {
957
957
  // check the createtion permission from the user permission
958
958
  const userInfo = req?.user;
959
959
  const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
960
+ logger.info( { ticketsFeature, userInfo } );
960
961
  if ( !ticketsFeature ) {
961
962
  return res.sendError( 'Forbidden to Reiew this Ticket', 403 );
962
963
  }
963
964
 
964
965
  // get store info by the storeId into mongo db
965
966
  const getstoreName = await findOneStore( { storeId: inputData.storeId, status: 'active' }, { storeId: 1, storeName: 1, clientId: 1 } );
966
-
967
+ logger.info( { getstoreName } );
967
968
  if ( !getstoreName || getstoreName == null ) {
968
969
  return res.sendError( 'The store ID is either inActive or not found', 400 );
969
970
  }
@@ -1074,6 +1075,8 @@ export async function ticketReview( req, res, next ) {
1074
1075
  return res.sendError( 'The Client ID is either not configured or not found', 400 );
1075
1076
  }
1076
1077
 
1078
+ logger.info( { config } );
1079
+
1077
1080
  // Get taggingLimitation from config (check both possible paths)
1078
1081
  const taggingLimitation = getConfig?.effectiveLimitation?.values;
1079
1082
  // Initialize count object from taggingLimitation
@@ -1148,7 +1151,7 @@ export async function ticketReview( req, res, next ) {
1148
1151
 
1149
1152
  const revopData = await getOpenSearchData( openSearch.revop, revopQuery );
1150
1153
  const buckets = revopData?.body?.aggregations?.type_counts?.buckets || [];
1151
-
1154
+ logger.info( { revopData } );
1152
1155
  // Map OpenSearch revopsType values to count object keys
1153
1156
  buckets.forEach( ( bucket ) => {
1154
1157
  const revopsType = bucket.key;
@@ -1181,6 +1184,7 @@ export async function ticketReview( req, res, next ) {
1181
1184
  if ( footfallCount - revisedFootfall == 0 ) {
1182
1185
  return res.sendError( 'Cannot review a ticket because footfall hasn’t changed', 400 );
1183
1186
  }
1187
+ logger.info( { footfallCount, revisedFootfall } );
1184
1188
  const taggingData = {
1185
1189
  size: 10000,
1186
1190
  query: {
@@ -1213,6 +1217,16 @@ export async function ticketReview( req, res, next ) {
1213
1217
  query: {
1214
1218
  bool: {
1215
1219
  must: [
1220
+ {
1221
+ term: {
1222
+ 'type.keyword': 'store',
1223
+ },
1224
+ },
1225
+ {
1226
+ term: {
1227
+ 'type.keyword': 'store',
1228
+ },
1229
+ },
1216
1230
  {
1217
1231
  term: {
1218
1232
  'storeId.keyword': inputData.storeId,
@@ -1233,6 +1247,7 @@ export async function ticketReview( req, res, next ) {
1233
1247
  if ( !ticketData || ticketData?.length == 0 ) {
1234
1248
  return res.sendError( 'You don’t have any tagged images right now', 400 );
1235
1249
  }
1250
+ logger.info( { ticketData, mappingInfo: ticketData?.[0]?._source?.mappingInfo } );
1236
1251
  const record = {
1237
1252
 
1238
1253
  status: 'Reviewer-Closed',
@@ -1245,7 +1260,7 @@ export async function ticketReview( req, res, next ) {
1245
1260
  // createdByRole: req?.user?.role,
1246
1261
 
1247
1262
  };
1248
-
1263
+ logger.info( { record } );
1249
1264
  if ( Array.isArray( record.mappingInfo ) ) {
1250
1265
  const temp = record.mappingInfo
1251
1266
  .filter( ( item ) => item.type === 'review' )
@@ -1287,7 +1302,7 @@ export async function ticketReview( req, res, next ) {
1287
1302
  // Retrieve client footfallDirectoryConfigs revision
1288
1303
  let isAutoCloseEnable = getConfig?.footfallDirectoryConfigs?.isAutoCloseEnable; ;
1289
1304
  let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
1290
-
1305
+ logger.info( { isAutoCloseEnable, autoCloseAccuracy } );
1291
1306
 
1292
1307
  const getNumber = autoCloseAccuracy.split( '%' )[0];
1293
1308
  let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
@@ -1366,13 +1381,14 @@ export async function ticketReview( req, res, next ) {
1366
1381
  createdAt: new Date(),
1367
1382
  },
1368
1383
  );
1384
+ logger.info( { revisedPercentage } );
1369
1385
  } else {
1370
1386
  // If ticket is closed, do not proceed with revision mapping
1371
1387
  let revisionArray = [];
1372
1388
 
1373
1389
 
1374
1390
  revisionArray = getConfig?.footfallDirectoryConfigs?.revision || [];
1375
-
1391
+ logger.info( { revisionArray } );
1376
1392
 
1377
1393
  // Default fallbacks
1378
1394
  let revisionMapping = null;
@@ -1405,7 +1421,7 @@ export async function ticketReview( req, res, next ) {
1405
1421
  }
1406
1422
  }
1407
1423
  }
1408
-
1424
+ logger.info( { record } );
1409
1425
  // Insert appropriate mappingInfo blocks
1410
1426
  if ( revisionMapping ) {
1411
1427
  // If reviewer and checked
@@ -1422,7 +1438,7 @@ export async function ticketReview( req, res, next ) {
1422
1438
  let checkreview = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'reviewer' && data.isChecked === true );
1423
1439
 
1424
1440
 
1425
- if ( checkreview.length > 0 ) {
1441
+ if ( checkreview.length > 0&&record.status!='Reviewer-Closed' ) {
1426
1442
  let userQuery = [
1427
1443
  {
1428
1444
  $match: {
@@ -1441,6 +1457,7 @@ export async function ticketReview( req, res, next ) {
1441
1457
  let createdOn = dayjs().format( 'DD MMM YYYY' );
1442
1458
  let description = `Created on ${createdOn}`;
1443
1459
 
1460
+
1444
1461
  let Data = {
1445
1462
  'title': title,
1446
1463
  'body': description,
@@ -1463,6 +1480,36 @@ export async function ticketReview( req, res, next ) {
1463
1480
  }
1464
1481
 
1465
1482
  const id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
1483
+ console.log( '-----', record.status );
1484
+ if ( record.status==='Reviewer-Closed' ) {
1485
+ console.log( '🚀 ~ ticketReview ~ id:', id );
1486
+ let Ticket = await getOpenSearchById( openSearch.footfallDirectory, id );
1487
+ console.log( '🚀 ~ ticketReview ~ Ticket:', Ticket.body );
1488
+ if ( Ticket?.body?._source?.type==='store' ) {
1489
+ let findTagging = Ticket?.body?._source?.mappingInfo.filter( ( data ) => data.type==='tagging' );
1490
+ if ( findTagging?.length>0&&findTagging[0].createdByEmail!='' ) {
1491
+ let userData = await findOneUser( { email: findTagging[0]?.createdByEmail } );
1492
+ console.log( '🚀 ~ ticketReview ~ findTagging[0]?.createdByEmail:', findTagging[0]?.createdByEmail );
1493
+ let title = `Received response for the Footfall ticket raised.`;
1494
+ let createdOn = dayjs( Ticket?.body?._source?.dateString ).format( 'DD MMM YYYY' );
1495
+ let description = `Raised on ${createdOn}`;
1496
+
1497
+ let Data = {
1498
+ 'title': title,
1499
+ 'body': description,
1500
+ 'type': 'closed',
1501
+ 'date': Ticket?.body?._source?.dateString,
1502
+ 'storeId': Ticket?.body?._source?.storeId,
1503
+ 'clientId': Ticket?.body?._source?.clientId,
1504
+ 'ticketId': Ticket?.body?._source?.ticketId,
1505
+ };
1506
+ if ( userData && userData.fcmToken ) {
1507
+ const fcmToken = userData.fcmToken;
1508
+ await sendPushNotification( title, description, fcmToken, Data );
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1466
1513
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
1467
1514
 
1468
1515
  if ( insertResult && insertResult.statusCode === 201 || insertResult.statusCode === 200 ) {
@@ -1552,7 +1599,7 @@ export async function ticketReview( req, res, next ) {
1552
1599
  } catch ( error ) {
1553
1600
  const err = error.message || 'Internal Server Error';
1554
1601
  logger.error( { error: err, funtion: 'ticketreview' } );
1555
- return res.sendError( err, 500 );
1602
+ return res.sendError( error, 500 );
1556
1603
  }
1557
1604
  }
1558
1605
 
@@ -1834,6 +1881,11 @@ export async function ticketApprove( req, res, next ) {
1834
1881
  query: {
1835
1882
  bool: {
1836
1883
  must: [
1884
+ {
1885
+ term: {
1886
+ 'type.keyword': 'store',
1887
+ },
1888
+ },
1837
1889
  {
1838
1890
  term: {
1839
1891
  'storeId.keyword': inputData.storeId,
@@ -2136,6 +2188,37 @@ export async function ticketApprove( req, res, next ) {
2136
2188
  }
2137
2189
 
2138
2190
  const id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
2191
+ console.log( '🚀 ~ ticketReview ~ id:', id );
2192
+ console.log( '🚀 ~ ticketReview ~ id:', id );
2193
+ console.log( '🚀 ~ ticketReview ~ id:', id );
2194
+ if ( record.status==='Approver-Closed' ) {
2195
+ let Ticket = await getOpenSearchById( openSearch.footfallDirectory, id );
2196
+ console.log( '🚀 ~ ticketApprove ~ Ticket:', Ticket?.body );
2197
+ if ( Ticket?.body?._source?.type==='store' ) {
2198
+ let findTagging = Ticket?.body?._source?.mappingInfo.filter( ( data ) => data.type==='tagging' );
2199
+ console.log( '🚀 ~ ticketApprove ~ findTagging[0].createdByEmail:', findTagging[0].createdByEmail );
2200
+ if ( findTagging?.length>0&&findTagging[0].createdByEmail!='' ) {
2201
+ let userData = await findOneUser( { email: findTagging[0]?.createdByEmail } );
2202
+ let title = `Received response for the Footfall ticket raised.`;
2203
+ let createdOn = dayjs( Ticket?.body?._source?.dateString ).format( 'DD MMM YYYY' );
2204
+ let description = `Raised on ${createdOn}`;
2205
+
2206
+ let Data = {
2207
+ 'title': title,
2208
+ 'body': description,
2209
+ 'type': 'closed',
2210
+ 'date': Ticket?.body?._source?.dateString,
2211
+ 'storeId': Ticket?.body?._source?.storeId,
2212
+ 'clientId': Ticket?.body?._source?.clientId,
2213
+ 'ticketId': Ticket?.body?._source?.ticketId,
2214
+ };
2215
+ if ( userData && userData.fcmToken ) {
2216
+ const fcmToken = userData.fcmToken;
2217
+ await sendPushNotification( title, description, fcmToken, Data );
2218
+ }
2219
+ }
2220
+ }
2221
+ }
2139
2222
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
2140
2223
 
2141
2224