tango-app-api-infra 3.9.25-vmsbug.1 → 3.9.25
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 +2 -2
- package/src/controllers/footfallDirectory.controllers.js +1045 -104
- package/src/controllers/internalInfra.controller.js +334 -89
- package/src/dtos/footfallDirectory.dtos.js +32 -0
- package/src/routes/footfallDirectory.routes.js +4 -2
- package/src/validations/footfallDirectory.validation.js +472 -338
|
@@ -567,7 +567,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
567
567
|
let isAutoCloseEnable = getConfig?.footfallDirectoryConfigs?.isAutoCloseEnable; ;
|
|
568
568
|
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy; ;
|
|
569
569
|
|
|
570
|
-
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
570
|
+
const getNumber = autoCloseAccuracy ? autoCloseAccuracy.split( '%' )[0] : '90';
|
|
571
571
|
|
|
572
572
|
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
573
573
|
let revisedPercentage = Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 );
|
|
@@ -577,7 +577,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
577
577
|
isAutoCloseEnable === true &&
|
|
578
578
|
revisedPercentage >= autoCloseAccuracyValue
|
|
579
579
|
) {
|
|
580
|
-
record.status = 'Closed';
|
|
580
|
+
record.status = 'Auto-Closed';
|
|
581
581
|
record.mappingInfo = [
|
|
582
582
|
{
|
|
583
583
|
type: 'tagging',
|
|
@@ -633,7 +633,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
633
633
|
status: 'Open',
|
|
634
634
|
dueDate: ( () => {
|
|
635
635
|
const due = new Date( Date.now() + reviwerDueDate * 24 * 60 * 60 * 1000 );
|
|
636
|
-
due.setHours(
|
|
636
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
637
637
|
return due;
|
|
638
638
|
} )(),
|
|
639
639
|
createdAt: new Date(),
|
|
@@ -648,7 +648,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
648
648
|
status: 'Open',
|
|
649
649
|
dueDate: ( () => {
|
|
650
650
|
const due = new Date( Date.now() + approverDueDate * 24 * 60 * 60 * 1000 );
|
|
651
|
-
due.setHours(
|
|
651
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
652
652
|
return due;
|
|
653
653
|
} )(),
|
|
654
654
|
createdAt: new Date(),
|
|
@@ -662,7 +662,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
662
662
|
status: 'Open',
|
|
663
663
|
dueDate: ( () => {
|
|
664
664
|
const due = new Date( Date.now() + tangoDueDate * 24 * 60 * 60 * 1000 );
|
|
665
|
-
due.setHours(
|
|
665
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
666
666
|
return due;
|
|
667
667
|
} )(),
|
|
668
668
|
createdAt: new Date(),
|
|
@@ -806,7 +806,7 @@ export async function ticketCreation( req, res, next ) {
|
|
|
806
806
|
// Get all tempIds from revopInfo response
|
|
807
807
|
const tempIds =
|
|
808
808
|
revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
809
|
-
|
|
809
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
810
810
|
const managerEyeZoneQuery = {
|
|
811
811
|
size: 1,
|
|
812
812
|
query: {
|
|
@@ -857,177 +857,13 @@ export async function ticketCreation( req, res, next ) {
|
|
|
857
857
|
managerEyeZoneQuery: managerEyeZoneQuery|| '',
|
|
858
858
|
};
|
|
859
859
|
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) : '' );
|
|
860
|
-
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
860
|
+
const isSendMessge = ( isAutoCloseEnable === true && revisedPercentage >= autoCloseAccuracyValue )? false :await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
861
861
|
if ( isSendMessge == true ) {
|
|
862
862
|
logger.info( '....1' );
|
|
863
863
|
// return true; // res.sendSuccess( 'Ticket has been updated successfully' );
|
|
864
864
|
} // Example: log or use these tempIds for further logic
|
|
865
865
|
}
|
|
866
|
-
// Check if ticketCount exceeds breach limit within config months and revised footfall percentage > config accuracy
|
|
867
|
-
|
|
868
|
-
if ( req.accuracyBreach && req.accuracyBreach.ticketCount && req.accuracyBreach.days && req.accuracyBreach.accuracy ) {
|
|
869
|
-
const breachDays = Number( req.accuracyBreach.days );
|
|
870
|
-
const breachCount = Number( req.accuracyBreach.ticketCount );
|
|
871
|
-
|
|
872
|
-
// req.accuracyBreach.accuracy is a string like "95%", so remove "%" and convert to Number
|
|
873
|
-
const breachAccuracy = Number( req.accuracyBreach.accuracy.replace( '%', '' ) );
|
|
874
|
-
const storeId = inputData.storeId;
|
|
875
|
-
const ticketName = inputData.ticketName || 'footfall-directory'; // adjust if ticket name variable differs
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
const formatDate = ( d ) =>
|
|
879
|
-
`${d.getFullYear()}-${String( d.getMonth() + 1 ).padStart( 2, '0' )}-${String( d.getDate() ).padStart( 2, '0' )}`;
|
|
880
|
-
|
|
881
|
-
// Compute current date object
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
// Calculate start date based on config days
|
|
885
|
-
// If 30 days: start from first day of current month
|
|
886
|
-
// If 60 days: start from first day of last month
|
|
887
|
-
const currentDateObj = new Date(); // Declare currentDateObj as today's date
|
|
888
|
-
const startDateObj = new Date( currentDateObj );
|
|
889
|
-
|
|
890
|
-
if ( breachDays === 30 ) {
|
|
891
|
-
// Consider within this month
|
|
892
|
-
startDateObj.setDate( 1 ); // First day of current month
|
|
893
|
-
} else if ( breachDays === 60 ) {
|
|
894
|
-
// Consider this month and last month
|
|
895
|
-
startDateObj.setMonth( startDateObj.getMonth() - 1 );
|
|
896
|
-
startDateObj.setDate( 1 ); // First day of last month
|
|
897
|
-
} else {
|
|
898
|
-
// For other values, calculate months from days
|
|
899
|
-
const breachMonths = Math.ceil( breachDays / 30 );
|
|
900
|
-
startDateObj.setMonth( startDateObj.getMonth() - breachMonths + 1 );
|
|
901
|
-
startDateObj.setDate( 1 );
|
|
902
|
-
}
|
|
903
|
-
|
|
904
866
|
|
|
905
|
-
const startDate = startDateObj;
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
const endDate = new Date;
|
|
909
|
-
// Query for tickets within this months window for this store and ticket name
|
|
910
|
-
const query = {
|
|
911
|
-
query: {
|
|
912
|
-
bool: {
|
|
913
|
-
must: [
|
|
914
|
-
{ term: { 'storeId.keyword': storeId } },
|
|
915
|
-
{ term: { 'ticketName.keyword': ticketName } },
|
|
916
|
-
{ range: { createdAt: { gte: startDate, lte: endDate } } },
|
|
917
|
-
],
|
|
918
|
-
},
|
|
919
|
-
},
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
// Search in OpenSearch for recent similar tickets
|
|
923
|
-
const breachTicketsResult = await getOpenSearchData( openSearch.footfallDirectory, query );
|
|
924
|
-
const tickets = breachTicketsResult?.body?.hits?.hits || [];
|
|
925
|
-
|
|
926
|
-
// Filter tickets where revised footfall percentage > config accuracy
|
|
927
|
-
let breachTicketsCount = 0;
|
|
928
|
-
for ( const ticket of tickets ) {
|
|
929
|
-
const source = ticket._source || {};
|
|
930
|
-
const footfallCount = source.footfallCount || 0;
|
|
931
|
-
const revicedFootfall = source.mappingInfo?.[0]?.revicedFootfall || 0;
|
|
932
|
-
|
|
933
|
-
// Calculate revised footfall percentage
|
|
934
|
-
let revisedPercentage = 0;
|
|
935
|
-
if ( footfallCount > 0 ) {
|
|
936
|
-
revisedPercentage = ( revicedFootfall / footfallCount ) * 100;
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// Check if revised footfall percentage > config accuracy
|
|
940
|
-
if ( revisedPercentage > breachAccuracy ) {
|
|
941
|
-
breachTicketsCount++;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
if ( breachTicketsCount >= breachCount ) {
|
|
946
|
-
// Calculate remaining future days in the config period
|
|
947
|
-
const futureDates = [];
|
|
948
|
-
|
|
949
|
-
// Calculate end date of config period
|
|
950
|
-
const configEndDateObj = new Date( currentDateObj );
|
|
951
|
-
if ( breachDays === 30 ) {
|
|
952
|
-
// End of current month
|
|
953
|
-
configEndDateObj.setMonth( configEndDateObj.getMonth() + 1 );
|
|
954
|
-
configEndDateObj.setDate( 0 ); // Last day of current month
|
|
955
|
-
} else if ( breachDays === 60 ) {
|
|
956
|
-
// End of next month
|
|
957
|
-
configEndDateObj.setMonth( configEndDateObj.getMonth() + 2 );
|
|
958
|
-
configEndDateObj.setDate( 0 ); // Last day of next month
|
|
959
|
-
} else {
|
|
960
|
-
// For other values, add the remaining days
|
|
961
|
-
const remainingDays = breachDays - ( Math.floor( ( currentDateObj - startDateObj ) / ( 1000 * 60 * 60 * 24 ) ) );
|
|
962
|
-
configEndDateObj.setDate( configEndDateObj.getDate() + remainingDays );
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// Generate all dates from tomorrow until end of config period
|
|
966
|
-
const tomorrow = new Date( currentDateObj );
|
|
967
|
-
tomorrow.setDate( tomorrow.getDate() + 0 );
|
|
968
|
-
|
|
969
|
-
const dateIterator = new Date( tomorrow );
|
|
970
|
-
while ( dateIterator <= configEndDateObj ) {
|
|
971
|
-
futureDates.push( formatDate( dateIterator ) );
|
|
972
|
-
dateIterator.setDate( dateIterator.getDate() + 1 );
|
|
973
|
-
}
|
|
974
|
-
// Also check the past 3 days; for each day, if footfallDirectory index does not have a record for this storeId/dateString/type:"store", block that day too
|
|
975
|
-
// const missingPastDays = [];
|
|
976
|
-
for ( let delta = -3; delta < 0; delta++ ) {
|
|
977
|
-
const dateObj = new Date( currentDateObj );
|
|
978
|
-
dateObj.setDate( dateObj.getDate() + delta );
|
|
979
|
-
const dateStr = formatDate( dateObj );
|
|
980
|
-
// Query OpenSearch for this day
|
|
981
|
-
const pastQuery = {
|
|
982
|
-
query: {
|
|
983
|
-
bool: {
|
|
984
|
-
must: [
|
|
985
|
-
{ term: { 'storeId.keyword': inputData?.storeId } },
|
|
986
|
-
{ term: { dateString: dateStr } },
|
|
987
|
-
{ term: { 'type.keyword': 'store' } },
|
|
988
|
-
],
|
|
989
|
-
},
|
|
990
|
-
},
|
|
991
|
-
};
|
|
992
|
-
// Use getOpenSearchData to check if exists
|
|
993
|
-
let found = false;
|
|
994
|
-
|
|
995
|
-
const searchRes = await getOpenSearchData( openSearch.footfallDirectory, pastQuery );
|
|
996
|
-
const foundHits = searchRes?.body?.hits?.hits || [];
|
|
997
|
-
|
|
998
|
-
if ( foundHits.length > 0 ) {
|
|
999
|
-
found = true;
|
|
1000
|
-
} else {
|
|
1001
|
-
found = false;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
if ( !found ) {
|
|
1005
|
-
const record = {
|
|
1006
|
-
clientId: getstoreName?.clientId,
|
|
1007
|
-
storeId: inputData?.storeId,
|
|
1008
|
-
storeName: getstoreName?.storeName,
|
|
1009
|
-
dateString: dateStr,
|
|
1010
|
-
status: 'block',
|
|
1011
|
-
};
|
|
1012
|
-
await updateOneUpsertVmsStoreRequest( { storeId: inputData?.storeId, dateString: dateStr }, record );
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
// Insert a record for each future date
|
|
1018
|
-
for ( const futureDateString of futureDates ) {
|
|
1019
|
-
const record = {
|
|
1020
|
-
clientId: getstoreName?.clientId,
|
|
1021
|
-
storeId: inputData?.storeId,
|
|
1022
|
-
storeName: getstoreName?.storeName,
|
|
1023
|
-
dateString: futureDateString,
|
|
1024
|
-
status: 'block',
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
|
-
await updateOneUpsertVmsStoreRequest( { storeId: inputData?.storeId, dateString: futureDateString }, record );
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
867
|
const sqsName = sqs.vmsPickleExtention;
|
|
1032
868
|
const sqsProduceQueue = {
|
|
1033
869
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
@@ -1365,9 +1201,6 @@ export async function ticketReview( req, res, next ) {
|
|
|
1365
1201
|
revicedPerc: Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) + '%',
|
|
1366
1202
|
reviced: Number( Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) ),
|
|
1367
1203
|
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
1368
|
-
// createdByEmail: req?.user?.email,
|
|
1369
|
-
// createdByUserName: req?.user?.userName,
|
|
1370
|
-
// createdByRole: req?.user?.role,
|
|
1371
1204
|
|
|
1372
1205
|
};
|
|
1373
1206
|
const getTagging = ticketData?.[0]?._source?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedFootfall;
|
|
@@ -1419,12 +1252,14 @@ export async function ticketReview( req, res, next ) {
|
|
|
1419
1252
|
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
|
|
1420
1253
|
|
|
1421
1254
|
|
|
1422
|
-
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
1255
|
+
const getNumber = autoCloseAccuracy ? autoCloseAccuracy.split( '%' )[0] : '90';
|
|
1423
1256
|
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
1424
1257
|
let revisedPercentage = Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 );
|
|
1425
1258
|
const revised = Number( Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) );
|
|
1426
|
-
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] );
|
|
1259
|
+
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] ) || 0;
|
|
1427
1260
|
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
1261
|
+
|
|
1262
|
+
|
|
1428
1263
|
if (
|
|
1429
1264
|
isAutoCloseEnable === true &&
|
|
1430
1265
|
revisedPercentage >= autoCloseAccuracyValue
|
|
@@ -1491,6 +1326,7 @@ export async function ticketReview( req, res, next ) {
|
|
|
1491
1326
|
createdByEmail: req?.user?.email,
|
|
1492
1327
|
createdByUserName: req?.user?.userName,
|
|
1493
1328
|
createdByRole: req?.user?.role,
|
|
1329
|
+
contactEmail: req?.user?.email,
|
|
1494
1330
|
createdAt: new Date(),
|
|
1495
1331
|
updatedAt: new Date(),
|
|
1496
1332
|
isUp: taggingRevised < revisedFootfall? true : false,
|
|
@@ -1521,7 +1357,7 @@ export async function ticketReview( req, res, next ) {
|
|
|
1521
1357
|
status: 'Open',
|
|
1522
1358
|
dueDate: ( () => {
|
|
1523
1359
|
const due = new Date( Date.now() + approverDueDate * 24 * 60 * 60 * 1000 );
|
|
1524
|
-
due.setHours(
|
|
1360
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
1525
1361
|
return due;
|
|
1526
1362
|
} )(),
|
|
1527
1363
|
|
|
@@ -1586,7 +1422,7 @@ export async function ticketReview( req, res, next ) {
|
|
|
1586
1422
|
status: 'Open',
|
|
1587
1423
|
dueDate: ( () => {
|
|
1588
1424
|
const due = new Date( Date.now() + tangoDueDate * 24 * 60 * 60 * 1000 );
|
|
1589
|
-
due.setHours(
|
|
1425
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
1590
1426
|
return due;
|
|
1591
1427
|
} )(),
|
|
1592
1428
|
createdAt: new Date(),
|
|
@@ -1670,7 +1506,7 @@ export async function ticketReview( req, res, next ) {
|
|
|
1670
1506
|
status: 'Open',
|
|
1671
1507
|
dueDate: ( () => {
|
|
1672
1508
|
const due = new Date( Date.now() + tangoDueDate * 24 * 60 * 60 * 1000 );
|
|
1673
|
-
due.setHours(
|
|
1509
|
+
due.setHours( 18, 29, 59, 999 );
|
|
1674
1510
|
return due;
|
|
1675
1511
|
} )(),
|
|
1676
1512
|
createdAt: new Date(),
|
|
@@ -1740,6 +1576,7 @@ export async function ticketReview( req, res, next ) {
|
|
|
1740
1576
|
createdByEmail: req?.user?.email,
|
|
1741
1577
|
createdByUserName: req?.user?.userName,
|
|
1742
1578
|
createdByRole: req?.user?.role,
|
|
1579
|
+
contactEmail: req?.user?.email,
|
|
1743
1580
|
createdAt: new Date(),
|
|
1744
1581
|
updatedAt: new Date(),
|
|
1745
1582
|
isUp: taggingRevised < revisedFootfall? true : false,
|
|
@@ -1813,100 +1650,106 @@ export async function ticketReview( req, res, next ) {
|
|
|
1813
1650
|
|
|
1814
1651
|
if ( insertResult && insertResult.statusCode === 201 || insertResult.statusCode === 200 ) {
|
|
1815
1652
|
if ( record.status === 'Closed' ) {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
query
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1653
|
+
if ( isAutoCloseEnable === true && revisedPercentage >= autoCloseAccuracyValue ) {
|
|
1654
|
+
logger.info( 'Ticket auto-closed due to meeting auto-close accuracy threshold' );
|
|
1655
|
+
} else {
|
|
1656
|
+
const query = {
|
|
1657
|
+
storeId: inputData?.storeId,
|
|
1658
|
+
isVideoStream: true,
|
|
1659
|
+
};
|
|
1660
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
1661
|
+
const revopInfoQuery = {
|
|
1662
|
+
size: 50000,
|
|
1663
|
+
query: {
|
|
1664
|
+
bool: {
|
|
1665
|
+
must: [
|
|
1666
|
+
{
|
|
1667
|
+
term: {
|
|
1668
|
+
'storeId.keyword': inputData.storeId,
|
|
1669
|
+
},
|
|
1829
1670
|
},
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1671
|
+
{
|
|
1672
|
+
term: {
|
|
1673
|
+
'dateString': inputData.dateString,
|
|
1674
|
+
},
|
|
1834
1675
|
},
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1676
|
+
{
|
|
1677
|
+
term: {
|
|
1678
|
+
'isParent': false,
|
|
1679
|
+
},
|
|
1839
1680
|
},
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1681
|
+
{
|
|
1682
|
+
term: {
|
|
1683
|
+
isChecked: true,
|
|
1684
|
+
},
|
|
1844
1685
|
},
|
|
1845
|
-
|
|
1846
|
-
|
|
1686
|
+
],
|
|
1687
|
+
},
|
|
1847
1688
|
},
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1689
|
+
_source: [ 'tempId' ],
|
|
1690
|
+
|
|
1691
|
+
};
|
|
1692
|
+
const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
|
|
1693
|
+
// Get all tempIds from revopInfo response
|
|
1694
|
+
const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
1695
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
1696
|
+
const managerEyeZoneQuery = {
|
|
1697
|
+
size: 1,
|
|
1698
|
+
query: {
|
|
1699
|
+
bool: {
|
|
1700
|
+
must: [
|
|
1701
|
+
{
|
|
1702
|
+
term: {
|
|
1703
|
+
'storeId.keyword': inputData.storeId,
|
|
1704
|
+
},
|
|
1864
1705
|
},
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1706
|
+
{
|
|
1707
|
+
term: {
|
|
1708
|
+
'zoneId.keyword': 'Overall Store',
|
|
1709
|
+
},
|
|
1869
1710
|
},
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1711
|
+
{
|
|
1712
|
+
term: {
|
|
1713
|
+
'storeDate': inputData.dateString,
|
|
1714
|
+
},
|
|
1874
1715
|
},
|
|
1875
|
-
|
|
1876
|
-
|
|
1716
|
+
],
|
|
1717
|
+
},
|
|
1877
1718
|
},
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
const mapping =
|
|
1719
|
+
_source: [ 'originalToTrackerCustomerMapping' ],
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
// Query the managerEyeZone index for the matching document
|
|
1723
|
+
const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
|
|
1724
|
+
const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
|
|
1725
|
+
// Extract originalToTrackerCustomerMapping if it exists
|
|
1726
|
+
const mapping =
|
|
1887
1727
|
managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
|
|
1888
1728
|
managerEyeZoneHit.originalToTrackerCustomerMapping :
|
|
1889
1729
|
{ tempId: '' };
|
|
1890
1730
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1731
|
+
// Find tempIds that exist in both revopInfo results and manager mapping
|
|
1732
|
+
const temp = [];
|
|
1733
|
+
const temp1 = [];
|
|
1734
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp1.push( { temp: mapping[tid], managerEye: tid } ) : '' );
|
|
1735
|
+
|
|
1736
|
+
inputData.log = {
|
|
1737
|
+
managerEyeZoneHit: managerEyeZoneHit || '',
|
|
1738
|
+
mapping: mapping || '',
|
|
1739
|
+
tempIds: tempIds || '',
|
|
1740
|
+
temp1: temp1 || '',
|
|
1741
|
+
managerEyeZoneQuery: managerEyeZoneQuery|| '',
|
|
1742
|
+
};
|
|
1743
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) : '' );
|
|
1744
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
1745
|
+
if ( isSendMessge == true ) {
|
|
1746
|
+
logger.info( '....1' );
|
|
1747
|
+
}
|
|
1907
1748
|
}
|
|
1749
|
+
|
|
1908
1750
|
const id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
1909
1751
|
let Ticket = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
1752
|
+
|
|
1910
1753
|
if ( Ticket?.body?._source?.type === 'store' ) {
|
|
1911
1754
|
let findTagging = Ticket?.body?._source?.mappingInfo.filter( ( data ) => data.type === 'tagging' );
|
|
1912
1755
|
if ( findTagging?.length > 0 && findTagging[0].createdByEmail != '' ) {
|
|
@@ -2250,9 +2093,6 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2250
2093
|
revicedPerc: Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) + '%',
|
|
2251
2094
|
reviced: Number( Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) ),
|
|
2252
2095
|
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
2253
|
-
// createdByEmail: req?.user?.email,
|
|
2254
|
-
// createdByUserName: req?.user?.userName,
|
|
2255
|
-
// createdByRole: req?.user?.role,
|
|
2256
2096
|
|
|
2257
2097
|
};
|
|
2258
2098
|
|
|
@@ -2261,19 +2101,18 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2261
2101
|
|
|
2262
2102
|
|
|
2263
2103
|
// Retrieve client footfallDirectoryConfigs revision
|
|
2264
|
-
let isAutoCloseEnable = getConfig
|
|
2104
|
+
let isAutoCloseEnable = getConfig?.footfallDirectoryConfigs?.isAutoCloseEnable;
|
|
2265
2105
|
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
|
|
2266
2106
|
|
|
2267
|
-
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
2107
|
+
const getNumber = autoCloseAccuracy ? autoCloseAccuracy.split( '%' )[0] : '90';
|
|
2268
2108
|
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
2269
2109
|
let revisedPercentage = Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 );
|
|
2270
2110
|
const revised = Number( Math.round( ( revisedFootfall / footfallCount ) * 100 || 0 ) );
|
|
2271
|
-
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] );
|
|
2111
|
+
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] ) || 0;
|
|
2272
2112
|
const tangoApproved = getConfig?.footfallDirectoryConfigs?.tangoApproved || false;
|
|
2273
2113
|
const tangoDueDate = getConfig?.footfallDirectoryConfigs?.allowTangoReview || 0;
|
|
2274
2114
|
|
|
2275
|
-
|
|
2276
|
-
|
|
2115
|
+
logger.info( { tangoReview, revised, autoCloseAccuracyValue, status: record.status } );
|
|
2277
2116
|
if (
|
|
2278
2117
|
isAutoCloseEnable === true &&
|
|
2279
2118
|
revisedPercentage >= autoCloseAccuracyValue
|
|
@@ -2345,11 +2184,13 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2345
2184
|
createdByEmail: req?.user?.email,
|
|
2346
2185
|
createdByUserName: req?.user?.userName,
|
|
2347
2186
|
createdByRole: req?.user?.role,
|
|
2187
|
+
contactEmail: req?.user?.email,
|
|
2348
2188
|
isUp: reviewRevised < revisedFootfall ? true : false,
|
|
2349
2189
|
createdAt: new Date(),
|
|
2350
2190
|
},
|
|
2351
2191
|
);
|
|
2352
2192
|
} else if ( revised < tangoReview ) {
|
|
2193
|
+
logger.info( 'Ticket under Tango Review due to not meeting Tango Review threshold' );
|
|
2353
2194
|
let approverMapping = null;
|
|
2354
2195
|
let tangoReviewMapping = null;
|
|
2355
2196
|
if ( tangoApproved ) {
|
|
@@ -2409,7 +2250,7 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2409
2250
|
status: 'Open',
|
|
2410
2251
|
dueDate: ( () => {
|
|
2411
2252
|
const due = new Date( Date.now() + tangoDueDate * 24 * 60 * 60 * 1000 );
|
|
2412
|
-
due.setHours(
|
|
2253
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
2413
2254
|
return due;
|
|
2414
2255
|
} )(),
|
|
2415
2256
|
createdAt: new Date(),
|
|
@@ -2481,6 +2322,7 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2481
2322
|
createdByEmail: req?.user?.email,
|
|
2482
2323
|
createdByUserName: req?.user?.userName,
|
|
2483
2324
|
createdByRole: req?.user?.role,
|
|
2325
|
+
contactEmail: req?.user?.email,
|
|
2484
2326
|
isUp: reviewRevised < revisedFootfall ? true : false,
|
|
2485
2327
|
createdAt: new Date(),
|
|
2486
2328
|
updatedAt: new Date(),
|
|
@@ -2493,7 +2335,7 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2493
2335
|
status: 'Open',
|
|
2494
2336
|
dueDate: ( () => {
|
|
2495
2337
|
const due = new Date( Date.now() + tangoDueDate * 24 * 60 * 60 * 1000 );
|
|
2496
|
-
due.setHours(
|
|
2338
|
+
due.setHours( 18, 29, 59, 999 ); // TEMPORARY due time set to 18:29:59.999 for testing, can be adjusted as needed
|
|
2497
2339
|
return due;
|
|
2498
2340
|
} )(),
|
|
2499
2341
|
createdAt: new Date(),
|
|
@@ -2572,6 +2414,7 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2572
2414
|
createdByEmail: req?.user?.email,
|
|
2573
2415
|
createdByUserName: req?.user?.userName,
|
|
2574
2416
|
createdByRole: req?.user?.role,
|
|
2417
|
+
contactEmail: req?.user?.email,
|
|
2575
2418
|
isUp: reviewRevised < revisedFootfall ? true : false,
|
|
2576
2419
|
createdAt: new Date(),
|
|
2577
2420
|
updatedAt: new Date(),
|
|
@@ -2627,104 +2470,395 @@ export async function ticketApprove( req, res, next ) {
|
|
|
2627
2470
|
|
|
2628
2471
|
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
2629
2472
|
|
|
2630
|
-
|
|
2631
2473
|
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
2632
|
-
|
|
2474
|
+
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
2475
|
+
|
|
2476
|
+
if ( req.accuracyBreach && req.accuracyBreach.ticketCount && req.accuracyBreach.days && req.accuracyBreach.accuracy ) {
|
|
2477
|
+
// Check if ticketCount exceeds breach limit within config months and revised footfall percentage > config accuracy
|
|
2478
|
+
|
|
2479
|
+
|
|
2480
|
+
const breachDays = Number( req.accuracyBreach.days );
|
|
2481
|
+
const breachCount = Number( req.accuracyBreach.ticketCount );
|
|
2482
|
+
|
|
2483
|
+
// req.accuracyBreach.accuracy is a string like "95%", so remove "%" and convert to Number
|
|
2484
|
+
// const breachAccuracy = Number( req.accuracyBreach.accuracy.replace( '%', '' ) );
|
|
2485
|
+
const storeId = inputData.storeId;
|
|
2486
|
+
const ticketName = inputData.ticketName || 'footfall-directory'; // adjust if ticket name variable differs
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
const formatDate = ( d ) =>
|
|
2490
|
+
`${d.getFullYear()}-${String( d.getMonth() + 1 ).padStart( 2, '0' )}-${String( d.getDate() ).padStart( 2, '0' )}`;
|
|
2491
|
+
|
|
2492
|
+
// Compute current date object
|
|
2493
|
+
|
|
2494
|
+
|
|
2495
|
+
// Calculate start date based on config days
|
|
2496
|
+
// If 30 days: start from first day of current month
|
|
2497
|
+
// If 60 days: start from first day of last month
|
|
2498
|
+
const currentDateObj = new Date( inputData.dateString ); // Declare currentDateObj as today's date
|
|
2499
|
+
const startDateObj = new Date( currentDateObj );
|
|
2500
|
+
|
|
2501
|
+
if ( breachDays === 7 ) {
|
|
2502
|
+
// Consider within this week (start from Monday)
|
|
2503
|
+
const dayOfWeek = startDateObj.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat
|
|
2504
|
+
const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
2505
|
+
startDateObj.setDate( startDateObj.getDate() - daysFromMonday );
|
|
2506
|
+
} else if ( breachDays === 30 ) {
|
|
2507
|
+
// Consider within this month
|
|
2508
|
+
startDateObj.setDate( 1 ); // First day of current month
|
|
2509
|
+
} else if ( breachDays === 60 ) {
|
|
2510
|
+
// Consider this month and last month
|
|
2511
|
+
startDateObj.setMonth( startDateObj.getMonth() - 1 );
|
|
2512
|
+
startDateObj.setDate( 1 ); // First day of last month
|
|
2513
|
+
} else {
|
|
2514
|
+
// For other values, calculate months from days
|
|
2515
|
+
const breachMonths = Math.ceil( breachDays / 30 );
|
|
2516
|
+
startDateObj.setMonth( startDateObj.getMonth() - breachMonths + 1 );
|
|
2517
|
+
startDateObj.setDate( 1 );
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
|
|
2521
|
+
startDateObj.setUTCHours( 0, 0, 0, 0 );
|
|
2522
|
+
const startDate = formatDate( startDateObj );
|
|
2523
|
+
|
|
2524
|
+
// Calculate end date of the period (end of week/month) for dateString range
|
|
2525
|
+
const endDateObj = new Date( currentDateObj );
|
|
2526
|
+
if ( breachDays === 7 ) {
|
|
2527
|
+
const dayOfWeek = endDateObj.getDay();
|
|
2528
|
+
const daysUntilSunday = dayOfWeek === 0 ? 0 : 7 - dayOfWeek;
|
|
2529
|
+
endDateObj.setDate( endDateObj.getDate() + daysUntilSunday );
|
|
2530
|
+
} else if ( breachDays === 30 ) {
|
|
2531
|
+
endDateObj.setMonth( endDateObj.getMonth() + 1, 0 );
|
|
2532
|
+
} else if ( breachDays === 60 ) {
|
|
2533
|
+
endDateObj.setMonth( endDateObj.getMonth() + 2, 0 );
|
|
2534
|
+
} else {
|
|
2535
|
+
const remainingDays = breachDays - ( Math.floor( ( currentDateObj - startDateObj ) / ( 1000 * 60 * 60 * 24 ) ) );
|
|
2536
|
+
endDateObj.setDate( endDateObj.getDate() + remainingDays );
|
|
2537
|
+
}
|
|
2538
|
+
const endDate = formatDate( endDateObj );
|
|
2539
|
+
|
|
2540
|
+
// Query for tickets within this week/month window for this store and ticket name
|
|
2541
|
+
const tangoApproved = getConfig?.footfallDirectoryConfigs?.tangoApproved || false;
|
|
2542
|
+
const mappingInfoFilter = [
|
|
2543
|
+
{
|
|
2544
|
+
nested: {
|
|
2545
|
+
path: 'mappingInfo',
|
|
2546
|
+
query: {
|
|
2547
|
+
bool: {
|
|
2548
|
+
must: [
|
|
2549
|
+
{ term: { 'mappingInfo.type': 'approve' } },
|
|
2550
|
+
{ term: { 'mappingInfo.status': 'Closed' } },
|
|
2551
|
+
],
|
|
2552
|
+
},
|
|
2553
|
+
},
|
|
2554
|
+
},
|
|
2555
|
+
},
|
|
2556
|
+
];
|
|
2557
|
+
if ( tangoApproved ) {
|
|
2558
|
+
mappingInfoFilter.push( {
|
|
2559
|
+
nested: {
|
|
2560
|
+
path: 'mappingInfo',
|
|
2561
|
+
query: {
|
|
2562
|
+
term: { 'mappingInfo.type': 'tangoreview' },
|
|
2563
|
+
},
|
|
2564
|
+
},
|
|
2565
|
+
} );
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2633
2568
|
const query = {
|
|
2634
|
-
storeId: inputData?.storeId,
|
|
2635
|
-
isVideoStream: true,
|
|
2636
|
-
};
|
|
2637
|
-
const getStoreType = await countDocumnetsCamera( query );
|
|
2638
|
-
const revopInfoQuery = {
|
|
2639
|
-
size: 50000,
|
|
2640
2569
|
query: {
|
|
2641
2570
|
bool: {
|
|
2642
2571
|
must: [
|
|
2572
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
2573
|
+
{ term: { 'ticketName.keyword': ticketName } },
|
|
2574
|
+
{ range: { dateString: { gte: startDate, lte: endDate } } },
|
|
2575
|
+
],
|
|
2576
|
+
filter: [
|
|
2643
2577
|
{
|
|
2644
|
-
|
|
2645
|
-
|
|
2578
|
+
bool: {
|
|
2579
|
+
should: mappingInfoFilter,
|
|
2580
|
+
minimum_should_match: 1,
|
|
2646
2581
|
},
|
|
2647
2582
|
},
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2583
|
+
],
|
|
2584
|
+
},
|
|
2585
|
+
},
|
|
2586
|
+
};
|
|
2587
|
+
// Search in OpenSearch for recent similar tickets
|
|
2588
|
+
const breachTicketsResult = await getOpenSearchData( openSearch.footfallDirectory, query );
|
|
2589
|
+
logger.info( { breachTicketsResult, message: 'breachTicketsResult;;;;;;;1' } );
|
|
2590
|
+
const tickets = breachTicketsResult?.body?.hits?.hits || [];
|
|
2591
|
+
const tangoTaggingDuDate = getConfig?.footfallDirectoryConfigs?.tangoReview || '0%';
|
|
2592
|
+
const gettingTangoreview = Number( String( tangoTaggingDuDate ).split( '%' )[0] || 0 );
|
|
2593
|
+
|
|
2594
|
+
const deltaBreach = getConfig?.footfallDirectoryConfigs?.deltaBreach || '>';
|
|
2595
|
+
const deltaCountBreach = Number( getConfig?.footfallDirectoryConfigs?.deltaCountBreach ?? 10 );
|
|
2596
|
+
const taggingBreach = getConfig?.footfallDirectoryConfigs?.taggingBreach || '>=';
|
|
2597
|
+
const taggingCountBreach = Number( getConfig?.footfallDirectoryConfigs?.taggingCountBreach ?? 10 );
|
|
2598
|
+
|
|
2599
|
+
const compareByOperator = ( value, operator, threshold ) => {
|
|
2600
|
+
switch ( operator ) {
|
|
2601
|
+
case '>': return value > threshold;
|
|
2602
|
+
case '>=': return value >= threshold;
|
|
2603
|
+
case '<': return value < threshold;
|
|
2604
|
+
case '<=': return value <= threshold;
|
|
2605
|
+
case '==': return value == threshold;
|
|
2606
|
+
case '===': return value === threshold;
|
|
2607
|
+
default: return value > threshold;
|
|
2608
|
+
}
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
// Filter tickets where revised footfall percentage > config accuracy
|
|
2612
|
+
let breachTicketsCount = 0;
|
|
2613
|
+
logger.info( { tickets, message: 'tickets;;;;;;;1' } );
|
|
2614
|
+
for ( const ticket of tickets ) {
|
|
2615
|
+
const source = ticket._source || {};
|
|
2616
|
+
|
|
2617
|
+
const ticketMappingInfo = source.mappingInfo || [];
|
|
2618
|
+
|
|
2619
|
+
|
|
2620
|
+
// Get Store/AOM accuracy (tagging stage)
|
|
2621
|
+
const taggingEntry = ticketMappingInfo.find( ( m ) => m.type === 'tagging' );
|
|
2622
|
+
const storeAccuracy = Number( taggingEntry?.reviced ) || 0;
|
|
2623
|
+
const storeRevisedFF = Number( taggingEntry?.revicedFootfall )|| 0;
|
|
2624
|
+
|
|
2625
|
+
// Get Central Ops(tangoreview stage)
|
|
2626
|
+
const centralOpsEntry = ticketMappingInfo.find( ( m ) => m.type === 'approve' );
|
|
2627
|
+
const centralOpsAccuracy = Number( centralOpsEntry?.reviced ) || 0;
|
|
2628
|
+
const centralOpsRevisedFF = Number( centralOpsEntry?.revicedFootfall )|| 0;
|
|
2629
|
+
|
|
2630
|
+
// Get Central Ops/Tango accuracy (tangoreview stage)
|
|
2631
|
+
const tangoEntry = ticketMappingInfo.find( ( m ) => m.type === 'tangoreview' );
|
|
2632
|
+
const tangoAccuracy = Number( tangoEntry?.reviced ) || 0;
|
|
2633
|
+
const tangoRevisedFF = Number( tangoEntry?.revicedFootfall )|| 0;
|
|
2634
|
+
|
|
2635
|
+
// Calculate delta between Store/AOM and Central Ops/Tango accuracy
|
|
2636
|
+
|
|
2637
|
+
const accuracyDelta1 = tangoAccuracy -storeAccuracy;
|
|
2638
|
+
const accuracyDelta2 = centralOpsAccuracy - storeAccuracy;
|
|
2639
|
+
|
|
2640
|
+
|
|
2641
|
+
// Check existing breach condition OR new accuracy delta breach conditions:
|
|
2642
|
+
// Existing: revised footfall percentage > config accuracy and not Auto-Closed
|
|
2643
|
+
// New conditions (all must be true):
|
|
2644
|
+
// a) Delta between Store/AOM and Central Ops/Tango accuracy > deltaBreach config
|
|
2645
|
+
// b) Post-review (Tango) accuracy > 85%
|
|
2646
|
+
// c) Total images marked for validation > taggingBreach config
|
|
2647
|
+
// const existingBreach = ticketRevisedPercentage > breachAccuracy && status !== 'Auto-Closed';
|
|
2648
|
+
const isHighDelta = compareByOperator( accuracyDelta1, deltaBreach, deltaCountBreach ) || compareByOperator( accuracyDelta2, deltaBreach, deltaCountBreach );
|
|
2649
|
+
const isCrossingThreshold =source?.reviced > gettingTangoreview;
|
|
2650
|
+
logger.info( { source: source?.reviced, source: source, gettingTangoreview } );
|
|
2651
|
+
const isLowData = compareByOperator( tangoRevisedFF - storeRevisedFF, taggingBreach, taggingCountBreach ) || compareByOperator( centralOpsRevisedFF - storeRevisedFF, taggingBreach, taggingCountBreach );
|
|
2652
|
+
|
|
2653
|
+
const deltaBreachCondition =
|
|
2654
|
+
isHighDelta && isCrossingThreshold && isLowData;
|
|
2655
|
+
// const deltaBreachCondition = accuracyDelta > 10 || ( storeAccuracy < gettingTangoreview && tangoAccuracy > gettingTangoreview) || totalImages <= 10;
|
|
2656
|
+
logger.info( { breachTicketsCount, deltaBreachCondition, isCrossingThreshold, isHighDelta, isLowData } );
|
|
2657
|
+
|
|
2658
|
+
if ( deltaBreachCondition ) {
|
|
2659
|
+
breachTicketsCount++;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
logger.info( { breachTicketsCount, breachCount }, 'total Breach Tickets Count1' );
|
|
2663
|
+
|
|
2664
|
+
// process.exit( 0 );
|
|
2665
|
+
if ( breachTicketsCount > breachCount ) {
|
|
2666
|
+
// Calculate remaining future days in the config period
|
|
2667
|
+
const futureDates = [];
|
|
2668
|
+
|
|
2669
|
+
// Calculate end date of config period
|
|
2670
|
+
const configEndDateObj = new Date( currentDateObj );
|
|
2671
|
+
if ( breachDays === 7 ) {
|
|
2672
|
+
// End of current week (Sunday)
|
|
2673
|
+
const dayOfWeek = configEndDateObj.getDay(); // 0=Sun, 6=Sat
|
|
2674
|
+
const daysUntilEndOfWeek = dayOfWeek === 0 ? 0 : 7 - dayOfWeek;
|
|
2675
|
+
configEndDateObj.setDate( configEndDateObj.getDate() + daysUntilEndOfWeek );
|
|
2676
|
+
} else if ( breachDays === 30 ) {
|
|
2677
|
+
// End of current month (use 2-arg setMonth to avoid month rollover on dates like 31st)
|
|
2678
|
+
configEndDateObj.setMonth( configEndDateObj.getMonth() + 1, 0 ); // Last day of current month
|
|
2679
|
+
} else if ( breachDays === 60 ) {
|
|
2680
|
+
// End of next month (use 2-arg setMonth to avoid month rollover on dates like 31st)
|
|
2681
|
+
configEndDateObj.setMonth( configEndDateObj.getMonth() + 2, 0 ); // Last day of next month
|
|
2682
|
+
} else {
|
|
2683
|
+
// For other values, add the remaining days
|
|
2684
|
+
const remainingDays = breachDays - ( Math.floor( ( currentDateObj - startDateObj ) / ( 1000 * 60 * 60 * 24 ) ) );
|
|
2685
|
+
configEndDateObj.setDate( configEndDateObj.getDate() + remainingDays );
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
// Generate future dates from the day after current date until end of config period
|
|
2689
|
+
const tomorrow = new Date( currentDateObj );
|
|
2690
|
+
tomorrow.setDate( tomorrow.getDate() + 1 );
|
|
2691
|
+
|
|
2692
|
+
const dateIterator = new Date( tomorrow );
|
|
2693
|
+
while ( dateIterator <= configEndDateObj ) {
|
|
2694
|
+
futureDates.push( formatDate( dateIterator ) );
|
|
2695
|
+
dateIterator.setDate( dateIterator.getDate() + 1 );
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
// Check past days within the period (from startDate to currentDate - 1);
|
|
2699
|
+
// if footfallDirectory index does not have a record for this storeId/dateString/type:"store", block that day
|
|
2700
|
+
const pastIterator = new Date( startDateObj );
|
|
2701
|
+
while ( pastIterator < currentDateObj ) {
|
|
2702
|
+
const dateStr = formatDate( pastIterator );
|
|
2703
|
+
const pastQuery = {
|
|
2704
|
+
query: {
|
|
2705
|
+
bool: {
|
|
2706
|
+
must: [
|
|
2707
|
+
{ term: { 'storeId.keyword': inputData?.storeId } },
|
|
2708
|
+
{ term: { dateString: dateStr } },
|
|
2709
|
+
{ term: { 'type.keyword': 'store' } },
|
|
2710
|
+
],
|
|
2652
2711
|
},
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2712
|
+
},
|
|
2713
|
+
};
|
|
2714
|
+
|
|
2715
|
+
const searchRes = await getOpenSearchData( openSearch.footfallDirectory, pastQuery );
|
|
2716
|
+
const foundHits = searchRes?.body?.hits?.hits || [];
|
|
2717
|
+
|
|
2718
|
+
if ( foundHits.length === 0 ) {
|
|
2719
|
+
const record = {
|
|
2720
|
+
clientId: getstoreName?.clientId,
|
|
2721
|
+
storeId: inputData?.storeId,
|
|
2722
|
+
storeName: getstoreName?.storeName,
|
|
2723
|
+
dateString: dateStr,
|
|
2724
|
+
status: 'block',
|
|
2725
|
+
};
|
|
2726
|
+
await updateOneUpsertVmsStoreRequest( { storeId: inputData?.storeId, dateString: dateStr }, record );
|
|
2727
|
+
}
|
|
2728
|
+
pastIterator.setDate( pastIterator.getDate() + 1 );
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// Insert a block record for each future date where no store record exists
|
|
2732
|
+
for ( const futureDateString of futureDates ) {
|
|
2733
|
+
const pastQuery = {
|
|
2734
|
+
query: {
|
|
2735
|
+
bool: {
|
|
2736
|
+
must: [
|
|
2737
|
+
{ term: { 'storeId.keyword': inputData?.storeId } },
|
|
2738
|
+
{ term: { dateString: futureDateString } },
|
|
2739
|
+
{ term: { 'type.keyword': 'store' } },
|
|
2740
|
+
],
|
|
2657
2741
|
},
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2742
|
+
},
|
|
2743
|
+
};
|
|
2744
|
+
|
|
2745
|
+
const searchRes = await getOpenSearchData( openSearch.footfallDirectory, pastQuery );
|
|
2746
|
+
const foundHits = searchRes?.body?.hits?.hits || [];
|
|
2747
|
+
|
|
2748
|
+
if ( foundHits.length === 0 ) {
|
|
2749
|
+
const record = {
|
|
2750
|
+
clientId: getstoreName?.clientId,
|
|
2751
|
+
storeId: inputData?.storeId,
|
|
2752
|
+
storeName: getstoreName?.storeName,
|
|
2753
|
+
dateString: futureDateString,
|
|
2754
|
+
status: 'block',
|
|
2755
|
+
};
|
|
2756
|
+
await updateOneUpsertVmsStoreRequest( { storeId: inputData?.storeId, dateString: futureDateString }, record );
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
if ( record.status === 'Closed' ) {
|
|
2762
|
+
if ( isAutoCloseEnable === true && revisedPercentage >= autoCloseAccuracyValue ) {
|
|
2763
|
+
logger.info( 'Ticket auto-closed due to meeting auto-close accuracy threshold' );
|
|
2764
|
+
} else {
|
|
2765
|
+
const query = {
|
|
2766
|
+
storeId: inputData?.storeId,
|
|
2767
|
+
isVideoStream: true,
|
|
2768
|
+
};
|
|
2769
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
2770
|
+
const revopInfoQuery = {
|
|
2771
|
+
size: 50000,
|
|
2772
|
+
query: {
|
|
2773
|
+
bool: {
|
|
2774
|
+
must: [
|
|
2775
|
+
{
|
|
2776
|
+
term: {
|
|
2777
|
+
'storeId.keyword': inputData.storeId,
|
|
2778
|
+
},
|
|
2661
2779
|
},
|
|
2662
|
-
|
|
2663
|
-
|
|
2780
|
+
{
|
|
2781
|
+
term: {
|
|
2782
|
+
'dateString': inputData.dateString,
|
|
2783
|
+
},
|
|
2784
|
+
},
|
|
2785
|
+
{
|
|
2786
|
+
term: {
|
|
2787
|
+
'isParent': false,
|
|
2788
|
+
},
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
term: {
|
|
2792
|
+
isChecked: true,
|
|
2793
|
+
},
|
|
2794
|
+
},
|
|
2795
|
+
],
|
|
2796
|
+
},
|
|
2664
2797
|
},
|
|
2665
|
-
|
|
2666
|
-
_source: [ 'tempId' ],
|
|
2798
|
+
_source: [ 'tempId' ],
|
|
2667
2799
|
|
|
2668
|
-
|
|
2669
|
-
|
|
2800
|
+
};
|
|
2801
|
+
const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
|
|
2670
2802
|
|
|
2671
|
-
|
|
2672
|
-
|
|
2803
|
+
const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
2804
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
2673
2805
|
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2806
|
+
const managerEyeZoneQuery = {
|
|
2807
|
+
size: 1,
|
|
2808
|
+
query: {
|
|
2809
|
+
bool: {
|
|
2810
|
+
must: [
|
|
2811
|
+
{
|
|
2812
|
+
term: {
|
|
2813
|
+
'storeId.keyword': inputData.storeId,
|
|
2814
|
+
},
|
|
2682
2815
|
},
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2816
|
+
{
|
|
2817
|
+
term: {
|
|
2818
|
+
'zoneId.keyword': 'Overall Store',
|
|
2819
|
+
},
|
|
2687
2820
|
},
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2821
|
+
{
|
|
2822
|
+
term: {
|
|
2823
|
+
'storeDate': inputData.dateString,
|
|
2824
|
+
},
|
|
2692
2825
|
},
|
|
2693
|
-
|
|
2694
|
-
|
|
2826
|
+
],
|
|
2827
|
+
},
|
|
2695
2828
|
},
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
};
|
|
2829
|
+
_source: [ 'originalToTrackerCustomerMapping' ],
|
|
2830
|
+
};
|
|
2699
2831
|
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2832
|
+
// Query the managerEyeZone index for the matching document
|
|
2833
|
+
const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
|
|
2834
|
+
const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
|
|
2703
2835
|
|
|
2704
|
-
|
|
2705
|
-
|
|
2836
|
+
// Extract originalToTrackerCustomerMapping if it exists
|
|
2837
|
+
const mapping =
|
|
2706
2838
|
managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
|
|
2707
2839
|
managerEyeZoneHit.originalToTrackerCustomerMapping :
|
|
2708
2840
|
{ tempId: '' };
|
|
2709
2841
|
|
|
2710
2842
|
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2843
|
+
// Find tempIds that exist in both revopInfo results and manager mapping
|
|
2844
|
+
const temp = [];
|
|
2845
|
+
const temp1 = [];
|
|
2846
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp1.push( { temp: mapping[tid], managerEye: tid } ) : '' );
|
|
2847
|
+
|
|
2848
|
+
inputData.log = {
|
|
2849
|
+
managerEyeZoneHit: managerEyeZoneHit || '',
|
|
2850
|
+
mapping: mapping || '',
|
|
2851
|
+
tempIds: tempIds || '',
|
|
2852
|
+
temp1: temp1 || '',
|
|
2853
|
+
managerEyeZoneQuery: managerEyeZoneQuery|| '',
|
|
2854
|
+
};
|
|
2855
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) : '' );
|
|
2856
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
2857
|
+
if ( isSendMessge == true ) {
|
|
2858
|
+
logger.info( '....1' );
|
|
2859
|
+
}
|
|
2727
2860
|
}
|
|
2861
|
+
|
|
2728
2862
|
const id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
2729
2863
|
logger.info( { status: id } );
|
|
2730
2864
|
|