tango-app-api-analysis-traffic 3.8.13 → 3.8.15-alpha.0
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/.eslintrc.cjs
CHANGED
package/package.json
CHANGED
|
@@ -63,7 +63,6 @@ export async function addBills( req, res ) {
|
|
|
63
63
|
let resData = [];
|
|
64
64
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
65
65
|
const inputData = req.tempInserData;
|
|
66
|
-
console.log( '🚀 ~ addBills ~ inputData:', inputData.length );
|
|
67
66
|
// return;
|
|
68
67
|
for ( let i = 0; i < inputData?.length; i++ ) {
|
|
69
68
|
await updateOneNobBilling( inputData[i]?.query, inputData[i]?.data );
|
|
@@ -166,7 +165,6 @@ export async function getNobData( req, res ) {
|
|
|
166
165
|
};
|
|
167
166
|
|
|
168
167
|
const getNobData = await getOpenSearchData( openSearch.nob, nobQuery );
|
|
169
|
-
console.log( '🚀 ~ getNobData ~ openSearch.nob:', openSearch.nob );
|
|
170
168
|
const nobData = getNobData?.body?.hits?.hits;
|
|
171
169
|
if ( !nobData || nobData?.length == 0 ) {
|
|
172
170
|
if ( inputData.searchValue && inputData.searchValue !== '' || inputData.offset > 1 ) {
|
|
@@ -593,7 +593,6 @@ export async function expireReviewStatus( req, res ) {
|
|
|
593
593
|
|
|
594
594
|
cutoffDate.setUTCHours( 23, 59, 59, 0 );
|
|
595
595
|
|
|
596
|
-
console.log( cutoffDate );
|
|
597
596
|
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
598
597
|
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
599
598
|
}
|
|
@@ -703,12 +702,16 @@ export async function expireReviewStatus( req, res ) {
|
|
|
703
702
|
export async function expireApproveStatus( req, res ) {
|
|
704
703
|
try {
|
|
705
704
|
const {
|
|
706
|
-
thresholdDate
|
|
705
|
+
thresholdDate,
|
|
707
706
|
batchSize = 500,
|
|
708
707
|
storeId,
|
|
709
708
|
dateString,
|
|
710
709
|
} = req.body;
|
|
711
710
|
const cutoffDate = new Date( thresholdDate );
|
|
711
|
+
// Convert cutoffDate to "2026-01-12T23:59:59.000Z"
|
|
712
|
+
|
|
713
|
+
cutoffDate.setUTCHours( 23, 59, 59, 0 );
|
|
714
|
+
|
|
712
715
|
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
713
716
|
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
714
717
|
}
|
|
@@ -762,7 +765,6 @@ export async function expireApproveStatus( req, res ) {
|
|
|
762
765
|
let updatedMapping = mappingInfo.map( ( item ) => {
|
|
763
766
|
if ( item?.type === 'approve' && item?.status !== 'Closed' && item?.dueDate ) {
|
|
764
767
|
const due = new Date( item.dueDate );
|
|
765
|
-
logger.info( { item, due, msg: '..........1', cutoffDate } );
|
|
766
768
|
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
767
769
|
changed = true;
|
|
768
770
|
|
|
@@ -772,24 +774,19 @@ export async function expireApproveStatus( req, res ) {
|
|
|
772
774
|
|
|
773
775
|
return item;
|
|
774
776
|
} );
|
|
775
|
-
logger.info( { updatedMapping, msh: '.......12' } );
|
|
776
777
|
if ( changed ) {
|
|
777
|
-
logger.info( { changed, msg: '.......2' } );
|
|
778
778
|
updatedMapping = updatedMapping.map( ( item ) => {
|
|
779
|
-
logger.info( { item, msg: '.......3' } );
|
|
780
779
|
if ( item?.type === 'tagging' ) {
|
|
781
|
-
logger.info( { item: item?.type, msg: '.......4' } );
|
|
782
780
|
return { ...item, status: 'Expired' };
|
|
783
781
|
}
|
|
784
782
|
return item;
|
|
785
783
|
} );
|
|
786
784
|
}
|
|
787
785
|
|
|
788
|
-
logger.info( { updatedMapping, msh: '.......13' } );
|
|
789
786
|
if ( changed ) {
|
|
790
787
|
const doc = {
|
|
791
788
|
mappingInfo: updatedMapping,
|
|
792
|
-
status: 'Expired',
|
|
789
|
+
status: 'Approver-Expired',
|
|
793
790
|
};
|
|
794
791
|
logger.info( { updatedMapping } );
|
|
795
792
|
bulkBody.push(
|
|
@@ -815,7 +812,130 @@ export async function expireApproveStatus( req, res ) {
|
|
|
815
812
|
|
|
816
813
|
return res.sendSuccess( { message: 'Expired approve status updated', updated: totalUpdated } );
|
|
817
814
|
} catch ( error ) {
|
|
818
|
-
logger.error( { error: error, message: req.body, function: '
|
|
815
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
816
|
+
return res.sendError( { error: error }, 500 );
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
export async function expireTangoReviewStatus( req, res ) {
|
|
822
|
+
try {
|
|
823
|
+
const {
|
|
824
|
+
thresholdDate,
|
|
825
|
+
batchSize = 500,
|
|
826
|
+
storeId,
|
|
827
|
+
dateString,
|
|
828
|
+
} = req.body;
|
|
829
|
+
const cutoffDate = new Date( thresholdDate );
|
|
830
|
+
// Convert cutoffDate to "2026-01-12T23:59:59.000Z"
|
|
831
|
+
|
|
832
|
+
cutoffDate.setUTCHours( 23, 59, 59, 0 );
|
|
833
|
+
|
|
834
|
+
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
835
|
+
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
840
|
+
const query = {
|
|
841
|
+
size: batchSize,
|
|
842
|
+
query: {
|
|
843
|
+
bool: {
|
|
844
|
+
must: [
|
|
845
|
+
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
846
|
+
{ term: { 'type.keyword': 'store' } },
|
|
847
|
+
],
|
|
848
|
+
must_not: [
|
|
849
|
+
{ terms: { 'status.keyword': [ 'Closed' ] } },
|
|
850
|
+
],
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
if ( storeId ) {
|
|
857
|
+
query.query.bool.must.push( { term: { 'storeId.keyword': storeId } } );
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
if ( dateString ) {
|
|
862
|
+
query.query.bool.must.push( {
|
|
863
|
+
terms: {
|
|
864
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
865
|
+
},
|
|
866
|
+
} );
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
let totalUpdated = 0;
|
|
871
|
+
let scrollId = null;
|
|
872
|
+
|
|
873
|
+
let firstResponse = await searchOpenSearchData( openSearch.footfallDirectory, query );
|
|
874
|
+
let hitsBatch = firstResponse?.body?.hits?.hits || [];
|
|
875
|
+
scrollId = firstResponse?.body?._scroll_id;
|
|
876
|
+
|
|
877
|
+
while ( hitsBatch.length > 0 ) {
|
|
878
|
+
const bulkBody = [];
|
|
879
|
+
|
|
880
|
+
for ( const hit of hitsBatch ) {
|
|
881
|
+
const src = hit._source || {};
|
|
882
|
+
const mappingInfo = Array.isArray( src.mappingInfo ) ? src.mappingInfo : [];
|
|
883
|
+
let changed = false;
|
|
884
|
+
let updatedMapping = mappingInfo.map( ( item ) => {
|
|
885
|
+
if ( item?.type === 'tangoreview' && item?.status !== 'Closed' && item?.dueDate ) {
|
|
886
|
+
const due = new Date( item.dueDate );
|
|
887
|
+
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
888
|
+
changed = true;
|
|
889
|
+
|
|
890
|
+
return { ...item, status: 'Expired' };
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return item;
|
|
895
|
+
} );
|
|
896
|
+
if ( changed ) {
|
|
897
|
+
const getConfig = await clientService.findOne( src.clientId, { 'footfallDirectoryConfigs.tangoApproved': 1 } );
|
|
898
|
+
if ( getConfig?.footfallDirectoryConfigs?.tangoApproved && getConfig?.footfallDirectoryConfigs?.tangoApproved === true ) {
|
|
899
|
+
;
|
|
900
|
+
updatedMapping = updatedMapping.map( ( item ) => {
|
|
901
|
+
if ( item?.type === 'tagging' ) {
|
|
902
|
+
return { ...item, status: 'Expired' };
|
|
903
|
+
}
|
|
904
|
+
return item;
|
|
905
|
+
} );
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if ( changed ) {
|
|
910
|
+
const doc = {
|
|
911
|
+
mappingInfo: updatedMapping,
|
|
912
|
+
status: 'Tango-Expired',
|
|
913
|
+
};
|
|
914
|
+
logger.info( { updatedMapping } );
|
|
915
|
+
bulkBody.push(
|
|
916
|
+
{ update: { _index: openSearch.footfallDirectory, _id: hit._id } },
|
|
917
|
+
{ doc: doc, doc_as_upsert: true },
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if ( bulkBody.length > 0 ) {
|
|
923
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
924
|
+
if ( bulkRes?.errors ) {
|
|
925
|
+
logger.error( { message: 'Bulk expire errors', items: bulkRes.items } );
|
|
926
|
+
}
|
|
927
|
+
totalUpdated += bulkBody.length / 2;
|
|
928
|
+
}
|
|
929
|
+
logger.info( { totalUpdated, msg: '........9' } );
|
|
930
|
+
if ( !scrollId ) break;
|
|
931
|
+
const nextRes = await scrollResponse( scrollId );
|
|
932
|
+
hitsBatch = nextRes?.body?.hits?.hits || [];
|
|
933
|
+
scrollId = nextRes?.body?._scroll_id;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return res.sendSuccess( { message: 'Expired approve status updated', updated: totalUpdated } );
|
|
937
|
+
} catch ( error ) {
|
|
938
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
819
939
|
return res.sendError( { error: error }, 500 );
|
|
820
940
|
}
|
|
821
941
|
}
|
|
@@ -1010,14 +1130,7 @@ export async function storeProcessedData( req, res ) {
|
|
|
1010
1130
|
const footfallDirRes = await getOpenSearchData( openSearch.footfallDirectory, footfallDirQuery );
|
|
1011
1131
|
const hit = footfallDirRes?.body?.hits?.hits?.[0];
|
|
1012
1132
|
receivedfootfall = hit._source.footfallCount;
|
|
1013
|
-
|
|
1014
|
-
for ( let i = 0; i < hit._source.mappingInfo.length; i++ ) {
|
|
1015
|
-
if ( hit._source.mappingInfo[i].type === 'tagging' ) {
|
|
1016
|
-
ticketStatus = hit._source.mappingInfo[i].status;
|
|
1017
|
-
break;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1133
|
+
ticketStatus = hit?._source?.status;
|
|
1021
1134
|
} catch ( err ) {
|
|
1022
1135
|
logger.warn( { message: 'Could not get ticket status from footfallDirectory', error: err } );
|
|
1023
1136
|
}
|
|
@@ -1044,7 +1157,6 @@ export async function storeProcessedData( req, res ) {
|
|
|
1044
1157
|
raisedStatusEnabled,
|
|
1045
1158
|
footfallticketCount: statusArray.includes( ticketStatus )?receivedfootfall:processedData?.footfall_count,
|
|
1046
1159
|
} );
|
|
1047
|
-
console.log( '🚀 ~ storeProcessedData ~ ticketStatus:', ticketStatus );
|
|
1048
1160
|
|
|
1049
1161
|
if ( raisedStatusEnabled === 'block' ) {
|
|
1050
1162
|
// Calculate the number of days from currentDate + 1 to the end of the month
|
|
@@ -1274,6 +1386,9 @@ export async function footFallImages( req, res ) {
|
|
|
1274
1386
|
// temp.length? temp[0].status = 'open': null;
|
|
1275
1387
|
|
|
1276
1388
|
if ( resultData.status_code == '200' ) {
|
|
1389
|
+
if ( !req?.store?.footfallDirectoryConfigs?.contactEmail ||( req?.store?.footfallDirectoryConfigs && req?.store?.footfallDirectoryConfigs?.contactEmail && req?.store?.footfallDirectoryConfigs?.contactEmail === '' ) ) {
|
|
1390
|
+
req.store.footfallDirectoryConfigs.contactEmail = getFinal?.contactEmail || '';
|
|
1391
|
+
}
|
|
1277
1392
|
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
1278
1393
|
} else {
|
|
1279
1394
|
return res.sendError( 'No Content', 204 );
|
|
@@ -1300,6 +1415,7 @@ export async function tagTempId( req, res ) {
|
|
|
1300
1415
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1301
1416
|
|
|
1302
1417
|
const upsertRecord = {
|
|
1418
|
+
mode: inputData.mode || '',
|
|
1303
1419
|
clientId: inputData.storeId.split( '-' )[0],
|
|
1304
1420
|
storeId: inputData.storeId,
|
|
1305
1421
|
tempId: inputData.tempId,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus } from '../controllers/revop.controller.js';
|
|
3
|
+
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus, expireApproveStatus } from '../controllers/revop.controller.js';
|
|
4
4
|
import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
5
5
|
import { footfallImagesValid, getCategorizedImagesValid, storeProcessedDataValid, tagTempIdValid, vmsDataMigrationValid } from '../dtos/revop.dtos.js';
|
|
6
|
-
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig } from '../validations/revop.validation.js';
|
|
6
|
+
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig, validateDateAndFilePath } from '../validations/revop.validation.js';
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
export const revopRouter = express.Router();
|
|
9
10
|
|
|
@@ -13,12 +14,13 @@ revopRouter
|
|
|
13
14
|
.post( '/getrevoptagging', isAllowedSessionHandler, getrevoptagging )
|
|
14
15
|
.post( '/migrate-revop', migrateRevopIndex )
|
|
15
16
|
.post( '/expire-review-status', expireReviewStatus )
|
|
17
|
+
.post( '/expire-approve-status', expireApproveStatus )
|
|
16
18
|
.post( '/revoptaggingcount', isAllowedSessionHandler, revoptaggingcount )
|
|
17
19
|
|
|
18
|
-
// new
|
|
20
|
+
// new enhancement (for footfall directory)
|
|
19
21
|
.get( '/store-processed-data', isAllowedSessionHandler, validate( storeProcessedDataValid ), storeProcessedData )
|
|
20
22
|
.get( '/footfall-images', isAllowedSessionHandler, validate( footfallImagesValid ), getTaggingConfig, footFallImages )
|
|
21
|
-
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, tagTempId )
|
|
23
|
+
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, validateDateAndFilePath, tagTempId )
|
|
22
24
|
.post( '/get-categorized-images', isAllowedSessionHandler, validate( getCategorizedImagesValid ), getCategorizedImages )
|
|
23
25
|
.post( '/vms-data-migration', validate( vmsDataMigrationValid ), vmsDataMigration );
|
|
24
26
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import nobBillingModel from 'tango-api-schema/schema/nobBilling.model.js';
|
|
2
2
|
|
|
3
3
|
export async function updateOneNobBilling( query, record ) {
|
|
4
|
-
console.log( '🚀 ~ updateOneNobBilling ~ record:', record );
|
|
5
4
|
return await nobBillingModel.updateOne( query, { $set: record }, { upsert: true } );
|
|
6
5
|
}
|
|
7
6
|
|
|
@@ -284,6 +284,14 @@ export async function mappingConfig( req, res, next ) {
|
|
|
284
284
|
},
|
|
285
285
|
},
|
|
286
286
|
],
|
|
287
|
+
must_not: [
|
|
288
|
+
{
|
|
289
|
+
term: {
|
|
290
|
+
'parent': inputData?.tempId || '', // Exclude current tempId if it's a duplicate tagging (to avoid counting the same image twice)
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
|
|
287
295
|
},
|
|
288
296
|
},
|
|
289
297
|
};
|
|
@@ -358,3 +366,38 @@ export async function deleteTaggedDuplicate( req, res, next ) {
|
|
|
358
366
|
}
|
|
359
367
|
}
|
|
360
368
|
|
|
369
|
+
|
|
370
|
+
export async function validateDateAndFilePath( req, res, next ) {
|
|
371
|
+
try {
|
|
372
|
+
const inputData = req.body;
|
|
373
|
+
const getDateString = inputData?.dateString;
|
|
374
|
+
const dateString = getDateString ?
|
|
375
|
+
getDateString.split( '-' ).reverse().join( '-' ) :
|
|
376
|
+
'';
|
|
377
|
+
const filePath = inputData?.filePath;
|
|
378
|
+
|
|
379
|
+
if ( !dateString ) {
|
|
380
|
+
return res.sendError( 'Missing payload field: dateString', 400 );
|
|
381
|
+
}
|
|
382
|
+
if ( !filePath ) {
|
|
383
|
+
return res.sendError( 'Missing payload field: filePath', 400 );
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Based on requirement: match payload dateString with filePath.split[1]
|
|
387
|
+
// Example: 11-1001/06-05-2026/5_customer_entry_frame.jpeg
|
|
388
|
+
// split('/') => [ '11-1001', '06-05-2026', '5_customer_entry_frame.jpeg' ] => index 1 is '06-05-2026'
|
|
389
|
+
const parts = String( filePath ).split( '/' );
|
|
390
|
+
const pathDateString = parts?.[1];
|
|
391
|
+
|
|
392
|
+
if ( dateString !== pathDateString ) {
|
|
393
|
+
return res.sendError( `Invalid payload: Image Date Mismatch `, 400 );
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
next();
|
|
397
|
+
} catch ( error ) {
|
|
398
|
+
logger.error( { error: error, message: req.body, function: 'traffic-revop-validateDateAndFilePath' } );
|
|
399
|
+
return res.sendError( error.message || 'Error validating date and filePath', 400 );
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|