tango-app-api-infra 3.9.5-vms.61 → 3.9.5-vms.63

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.61",
3
+ "version": "3.9.5-vms.63",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -6,9 +6,9 @@ import { findOneRevopDownload, upsertRevopDownload } from '../services/revopDown
6
6
  import dayjs from 'dayjs';
7
7
  import utc from 'dayjs/plugin/utc.js';
8
8
  import timezone from 'dayjs/plugin/timezone.js';
9
- import { findUser } from '../services/user.service.js';
10
9
  import { findOneClient } from '../services/client.service.js';
11
-
10
+ import { findUser, findOneUser } from '../services/user.service.js';
11
+ import { sendPushNotification } from 'tango-app-api-middleware';
12
12
  dayjs.extend( utc );
13
13
  dayjs.extend( timezone );
14
14
 
@@ -157,6 +157,7 @@ export async function tangoReviewTicket( req, res ) {
157
157
  };
158
158
  let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
159
159
  let Ticket = findTicket.body?.hits?.hits;
160
+ console.log( '🚀 ~ tangoReviewTicket ~ Ticket:', Ticket );
160
161
  if ( Ticket.length === 0 ) {
161
162
  return res.sendError( 'Ticket not found', 400 );
162
163
  }
@@ -391,6 +392,28 @@ export async function tangoReviewTicket( req, res ) {
391
392
  );
392
393
  }
393
394
 
395
+ if ( Ticket[0]?._source?.type==='store' ) {
396
+ let userData = await findOneUser( { email: Ticket[0]?._source?.createdByEmail } );
397
+ let title = `Received response for the Footfall ticket raised.`;
398
+ let createdOn = dayjs( Ticket[0]?._source?.dateString ).format( 'DD MMM YYYY' );
399
+ let description = `Raised on ${createdOn}`;
400
+ console.log( '🚀 ~ ticketCreation ~ userData.role:', userData.email );
401
+ let Data = {
402
+ 'title': title,
403
+ 'body': description,
404
+ 'type': 'closed',
405
+ 'date': Ticket[0]?._source?.dateString,
406
+ 'storeId': Ticket[0]?._source?.storeId,
407
+ 'clientId': Ticket[0]?._source?.clientId,
408
+ 'ticketId': Ticket[0]?._source?.ticketId,
409
+ };
410
+ if ( userData && userData.fcmToken ) {
411
+ const fcmToken = userData.fcmToken;
412
+ await sendPushNotification( title, description, fcmToken, Data );
413
+ }
414
+ }
415
+ // return;
416
+
394
417
  let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
395
418
  if ( inputData.ticketType === 'internal' ) {
396
419
  id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
@@ -883,14 +906,14 @@ export async function ticketList( req, res ) {
883
906
  };
884
907
 
885
908
  if ( inputData.sortBy ) {
886
- let sortOrder = inputData.sortOrder === 1? 'asc': 'desc';
909
+ let sortOrder = inputData.sortOrder === 1 ? 'asc' : 'desc';
887
910
 
888
911
  // Remove default sort so we don't duplicate/conflict
889
912
  // INSERT_YOUR_CODE
890
913
  // If sortBy is present, check if the field needs ".keyword" (for string fields like storeName, storeId, ticketId)
891
914
  // This avoids OpenSearch errors about sorting on text fields.
892
915
  const stringKeywordFields = [ 'storeName', 'storeId', 'ticketId', 'status', 'type', 'clientId' ];
893
- let sortField = inputData.sortBy == 'footfall'? 'footfallCount' :inputData.sortBy == 'issueDate'?'dateString':inputData.sortBy;
916
+ let sortField = inputData.sortBy == 'footfall' ? 'footfallCount' : inputData.sortBy == 'issueDate' ? 'dateString' : inputData.sortBy;
894
917
  if ( stringKeywordFields.includes( sortField ) ) {
895
918
  sortField = `${sortField}.keyword`;
896
919
  }
@@ -930,7 +953,7 @@ export async function ticketList( req, res ) {
930
953
  searchQuery.query.bool.must.push( {
931
954
  term: {
932
955
  'mappingInfo.type': ticketsFeature ? 'review' : ticketsApproveFeature ?
933
- 'approve' :'tagging',
956
+ 'approve' : 'tagging',
934
957
  },
935
958
  },
936
959
  {
@@ -944,14 +967,14 @@ export async function ticketList( req, res ) {
944
967
  searchQuery.query.bool.must.push( {
945
968
  term: {
946
969
  'mappingInfo.type': inputData?.permissionType == 'review' ? 'review' :
947
- 'approve',
970
+ 'approve',
948
971
  },
949
972
  } );
950
973
  }
951
974
 
952
975
  if ( inputData.searchValue && inputData.searchValue !== '' ) {
953
- searchQuery.query.bool['should'] =[];
954
- searchQuery.query.bool.should=[
976
+ searchQuery.query.bool['should'] = [];
977
+ searchQuery.query.bool.should = [
955
978
 
956
979
  {
957
980
  'wildcard': {
@@ -1034,7 +1057,7 @@ export async function ticketList( req, res ) {
1034
1057
  }
1035
1058
  }
1036
1059
  } else {
1037
- if ( inputData?.permissionType ==='approve' ) {
1060
+ if ( inputData?.permissionType === 'approve' ) {
1038
1061
  for ( let item of ticketListData ) {
1039
1062
  temp.push( {
1040
1063
 
@@ -1058,7 +1081,7 @@ export async function ticketList( req, res ) {
1058
1081
 
1059
1082
  } );
1060
1083
  }
1061
- } else if ( inputData?.permissionType ==='review' ) {
1084
+ } else if ( inputData?.permissionType === 'review' ) {
1062
1085
  for ( let item of ticketListData ) {
1063
1086
  temp.push( {
1064
1087
 
@@ -2365,7 +2388,7 @@ export async function openTicketList( req, res ) {
2365
2388
  // INSERT_YOUR_CODE
2366
2389
  // Add sorting by revicedPerc descending (highest revised accuracy first)
2367
2390
  openSearchQuery.sort = [
2368
- { 'revicedPerc.keyword': { order: inputData?.sortOrder === 1? 'asc':'desc' } },
2391
+ { 'revicedPerc.keyword': { order: inputData?.sortOrder === 1 ? 'asc' : 'desc' } },
2369
2392
  ];
2370
2393
 
2371
2394
 
@@ -2587,7 +2610,7 @@ export async function updateTempStatus( req, res ) {
2587
2610
  );
2588
2611
 
2589
2612
 
2590
- const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0? getSearchResp?.body?.hits?.hits : [];
2613
+ const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0 ? getSearchResp?.body?.hits?.hits : [];
2591
2614
  const logs = {
2592
2615
  type: inputData.type,
2593
2616
  storeId: taggedImages?.[0]?._source?.storeId,
@@ -549,11 +549,17 @@ export async function ticketCreation( req, res, next ) {
549
549
  }
550
550
  }
551
551
 
552
- let checkreview = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'reviewer' && data.isChecked === true );
553
- let checkapprove = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'approver' && data.isChecked === true );
552
+ const revision = getConfig.footfallDirectoryConfigs?.revision ?? [];
554
553
 
555
- if ( checkreview.length > 0 || checkapprove.length > 0 ) {
556
- let userQuery = [
554
+ const hasReviewer = revision.some(
555
+ ( data ) => data.actionType === 'reviewer' && data.isChecked === true,
556
+ );
557
+ const hasApprover = revision.some(
558
+ ( data ) => data.actionType === 'approver' && data.isChecked === true,
559
+ );
560
+
561
+ if ( hasReviewer || hasApprover ) {
562
+ const userQuery = [
557
563
  {
558
564
  $match: {
559
565
  clientId: getstoreName.clientId,
@@ -561,40 +567,49 @@ export async function ticketCreation( req, res, next ) {
561
567
  },
562
568
  },
563
569
  ];
564
- let finduserList = await aggregateUser( userQuery );
565
570
 
571
+ const finduserList = await aggregateUser( userQuery );
566
572
 
567
- for ( let userData of finduserList ) {
568
- let title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
569
- let createdOn = dayjs().format( 'DD MMM YYYY' );
570
- let description = `Created on ${createdOn}`;
571
- let Data = {
572
- 'title': title,
573
- 'body': description,
574
- 'type': 'create',
575
- 'date': record.dateString,
576
- 'storeId': record.storeId,
577
- 'clientId': record.clientId,
578
- 'ticketId': record.ticketId,
579
- };
573
+ const createdOn = dayjs().format( 'DD MMM YYYY' );
574
+ const title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
575
+ const description = `Created on ${createdOn}`;
580
576
 
581
- const ticketsFeature = userData?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
577
+ const Data = {
578
+ title,
579
+ body: description,
580
+ type: 'create',
581
+ date: record.dateString,
582
+ storeId: record.storeId,
583
+ clientId: record.clientId,
584
+ ticketId: record.ticketId,
585
+ };
582
586
 
583
- if ( ticketsFeature ) {
584
- let notifyuser = await getAssinedStore( userData, req.body.storeId );
585
- if ( userData && userData.fcmToken && notifyuser ) {
586
- const fcmToken = userData.fcmToken;
587
- await sendPushNotification( title, description, fcmToken, Data );
588
- }
589
- }
590
- }
587
+ await Promise.all(
588
+ ( finduserList || [] ).map( async ( userData ) => {
589
+ const ticketsFeature = userData?.rolespermission?.some(
590
+ ( f ) =>
591
+ f.featureName === 'FootfallDirectory' &&
592
+ f.modules?.some(
593
+ ( m ) =>
594
+ m.name === 'reviewer' && ( m.isAdd === true || m.isEdit === true ),
595
+ ),
596
+ );
597
+
598
+ if ( !ticketsFeature ) return;
599
+
600
+ const notifyUser = await getAssinedStore( userData, req.body.storeId );
601
+ if ( !notifyUser || !userData?.fcmToken ) return;
602
+
603
+ await sendPushNotification( title, description, userData.fcmToken, Data );
604
+ } ),
605
+ );
591
606
  }
592
607
 
593
608
 
594
609
  const id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
595
610
  const insertResult = await insertWithId( openSearch.footfallDirectory, id, record );
596
611
  if ( insertResult && insertResult.statusCode === 201 ) {
597
- // After successful ticket creation, update status to "submitted" in revop index for the relevant records
612
+ // After successful ticket creation, update status to "submitted" in revop index for the relevant records
598
613
 
599
614
 
600
615
  const bulkUpdateBody = taggingImages.map( ( img ) => [
@@ -718,14 +733,14 @@ export async function ticketCreation( req, res, next ) {
718
733
  const startDateObj = new Date( currentDateObj );
719
734
 
720
735
  if ( breachDays === 30 ) {
721
- // Consider within this month
736
+ // Consider within this month
722
737
  startDateObj.setDate( 1 ); // First day of current month
723
738
  } else if ( breachDays === 60 ) {
724
- // Consider this month and last month
739
+ // Consider this month and last month
725
740
  startDateObj.setMonth( startDateObj.getMonth() - 1 );
726
741
  startDateObj.setDate( 1 ); // First day of last month
727
742
  } else {
728
- // For other values, calculate months from days
743
+ // For other values, calculate months from days
729
744
  const breachMonths = Math.ceil( breachDays / 30 );
730
745
  startDateObj.setMonth( startDateObj.getMonth() - breachMonths + 1 );
731
746
  startDateObj.setDate( 1 );
@@ -773,21 +788,21 @@ export async function ticketCreation( req, res, next ) {
773
788
  }
774
789
 
775
790
  if ( breachTicketsCount >= breachCount ) {
776
- // Calculate remaining future days in the config period
791
+ // Calculate remaining future days in the config period
777
792
  const futureDates = [];
778
793
 
779
794
  // Calculate end date of config period
780
795
  const configEndDateObj = new Date( currentDateObj );
781
796
  if ( breachDays === 30 ) {
782
- // End of current month
797
+ // End of current month
783
798
  configEndDateObj.setMonth( configEndDateObj.getMonth() + 1 );
784
799
  configEndDateObj.setDate( 0 ); // Last day of current month
785
800
  } else if ( breachDays === 60 ) {
786
- // End of next month
801
+ // End of next month
787
802
  configEndDateObj.setMonth( configEndDateObj.getMonth() + 2 );
788
803
  configEndDateObj.setDate( 0 ); // Last day of next month
789
804
  } else {
790
- // For other values, add the remaining days
805
+ // For other values, add the remaining days
791
806
  const remainingDays = breachDays - ( Math.floor( ( currentDateObj - startDateObj ) / ( 1000 * 60 * 60 * 24 ) ) );
792
807
  configEndDateObj.setDate( configEndDateObj.getDate() + remainingDays );
793
808
  }
@@ -1793,71 +1808,85 @@ export async function ticketApprove( req, res, next ) {
1793
1808
  }
1794
1809
  }
1795
1810
 
1796
-
1797
1811
  export async function getAssinedStore( user, storeId ) {
1798
- if ( user && user.userType === 'client' && user.role !== 'superadmin' ) {
1799
- let storeIds = new Set( user.assignedStores?.map( ( store ) => store.storeId ) );
1800
-
1801
- // Fetch clusters and teams in parallel
1802
- const [ clustersList, teamsList ] = await Promise.all( [
1803
- findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
1804
- findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
1805
- ] );
1806
-
1807
- // Process clusters
1808
- if ( clustersList.length > 0 ) {
1809
- for ( let cluster of clustersList ) {
1810
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
1811
- }
1812
- }
1812
+ if ( !user || user.userType !== 'client' || user.role === 'superadmin' ) {
1813
+ return;
1814
+ }
1813
1815
 
1814
- // Process teams
1815
- if ( teamsList.length > 0 ) {
1816
- for ( let team of teamsList ) {
1817
- for ( let user of team.users ) {
1818
- let findUser = await findOneUser( { _id: user.userId } );
1819
- if ( findUser && findUser.assignedStores?.length > 0 ) {
1820
- findUser.assignedStores.forEach( ( store ) => storeIds.add( store.storeId ) );
1821
- }
1816
+ const clientId = user.clientId;
1817
+ const storeIds = new Set(
1818
+ user.assignedStores?.map( ( store ) => store.storeId ) ?? [],
1819
+ );
1822
1820
 
1823
- // Fetch clusters for the user
1824
- let userClustersList = await findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: findUser.email } } } );
1825
- if ( userClustersList.length > 0 ) {
1826
- for ( let cluster of userClustersList ) {
1827
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
1828
- }
1829
- }
1830
- }
1831
- }
1832
- }
1833
- let TeamMember = await findteams( { clientId: user.clientId, users: { $elemMatch: { email: user.email } } } );
1834
- if ( TeamMember && TeamMember.length > 0 ) {
1835
- for ( let team of TeamMember ) {
1836
- let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
1837
- if ( clusterList.length > 0 ) {
1838
- for ( let cluster of clusterList ) {
1839
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
1840
- }
1841
- }
1842
- }
1821
+ const addClusterStores = ( clusters ) => {
1822
+ if ( !clusters?.length ) return;
1823
+ for ( const cluster of clusters ) {
1824
+ cluster.stores?.forEach( ( store ) => storeIds.add( store.storeId ) );
1843
1825
  }
1844
- let TeamLeader = await findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } );
1845
- if ( TeamLeader && TeamLeader.length > 0 ) {
1846
- for ( let team of TeamLeader ) {
1847
- let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
1848
- if ( clusterList.length > 0 ) {
1849
- for ( let cluster of clusterList ) {
1850
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
1851
- }
1852
- }
1853
- }
1826
+ };
1827
+
1828
+ // Fetch all top-level data in parallel
1829
+ const [ clustersList, teamsList, teamMemberList ] = await Promise.all( [
1830
+ findcluster( {
1831
+ clientId,
1832
+ Teamlead: { $elemMatch: { email: user.email } },
1833
+ } ),
1834
+ findteams( {
1835
+ clientId,
1836
+ Teamlead: { $elemMatch: { email: user.email } },
1837
+ } ),
1838
+ findteams( {
1839
+ clientId,
1840
+ users: { $elemMatch: { email: user.email } },
1841
+ } ),
1842
+ ] );
1843
+
1844
+ // 1) Clusters where this user is Teamlead
1845
+ addClusterStores( clustersList );
1846
+
1847
+ // 2) Teams where this user is Teamlead → their users + their clusters
1848
+ if ( teamsList?.length ) {
1849
+ for ( const team of teamsList ) {
1850
+ if ( !team.users?.length ) continue;
1851
+
1852
+ await Promise.all(
1853
+ team.users.map( async ( teamUser ) => {
1854
+ const foundUser = await findOneUser( { _id: teamUser.userId } );
1855
+ if ( !foundUser ) return;
1856
+
1857
+ // Direct assigned stores of that user
1858
+ if ( foundUser.assignedStores?.length ) {
1859
+ foundUser.assignedStores.forEach( ( store ) =>
1860
+ storeIds.add( store.storeId ),
1861
+ );
1862
+ }
1863
+
1864
+ // Clusters where this user is Teamlead
1865
+ const userClustersList = await findcluster( {
1866
+ clientId,
1867
+ Teamlead: { $elemMatch: { email: foundUser.email } },
1868
+ } );
1869
+ addClusterStores( userClustersList );
1870
+ } ),
1871
+ );
1854
1872
  }
1855
- // Convert Set back to Array if needed
1856
- let assignedStores = Array.from( storeIds );
1857
- if ( assignedStores.includes( storeId ) ) {
1858
- return true;
1859
- } else {
1860
- return true;
1873
+ }
1874
+
1875
+ // 3) Teams where this user is a member → clusters by teamName
1876
+ if ( teamMemberList?.length ) {
1877
+ for ( const team of teamMemberList ) {
1878
+ const clusterList = await findcluster( {
1879
+ clientId,
1880
+ teams: { $elemMatch: { name: team.teamName } },
1881
+ } );
1882
+ addClusterStores( clusterList );
1861
1883
  }
1862
1884
  }
1885
+
1886
+ const assignedStores = Array.from( storeIds );
1887
+
1888
+ // Previously you returned `true` in both branches.
1889
+ // Assuming you actually want to check membership:
1890
+ return assignedStores.includes( storeId );
1863
1891
  }
1892
+