tango-app-api-analysis-traffic 3.8.7-vms.8 → 3.8.8

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-analysis-traffic",
3
- "version": "3.8.7-vms.8",
3
+ "version": "3.8.8",
4
4
  "description": "Traffic Analysis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "mongodb": "^6.8.0",
24
24
  "nodemon": "^3.1.4",
25
25
  "swagger-ui-express": "^5.0.1",
26
- "tango-api-schema": "^2.4.29",
26
+ "tango-api-schema": "^2.4.9",
27
27
  "tango-app-api-middleware": "^3.6.5",
28
28
  "winston": "^3.13.1",
29
29
  "winston-daily-rotate-file": "^5.0.0"
@@ -2,8 +2,7 @@ import { logger, insertOpenSearchData, getOpenSearchData, updateOpenSearchData }
2
2
  import { findOnerevopConfig } from '../services/revopConfig.service.js';
3
3
  import * as clientService from '../services/clients.services.js';
4
4
  import { bulkUpdate, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
5
- import { findOneVmsStoreRequest } from '../services/vmsStoreRequest.service.js';
6
- // import dayjs from 'dayjs';
5
+ import dayjs from 'dayjs';
7
6
  // Lamda Service Call //
8
7
  async function LamdaServiceCall( url, data ) {
9
8
  try {
@@ -212,162 +211,44 @@ export async function storeProcessedData( req, res ) {
212
211
  try {
213
212
  const openSearch = JSON.parse( process.env.OPENSEARCH );
214
213
  const inputData = req.query;
215
- const { fromDate, toDate, storeId } = inputData;
216
-
217
- // Multi-date range handling for a single store
218
- if ( fromDate && toDate && storeId ) {
219
- const dayjs = ( await import( 'dayjs' ) ).default;
220
- const isSameOrBefore = ( await import( 'dayjs/plugin/isSameOrBefore.js' ) ).default;
221
- dayjs.extend( isSameOrBefore );
222
-
223
- let start = dayjs( fromDate );
224
- let end = dayjs( toDate );
225
-
226
- if ( !start.isValid() || !end.isValid() ) {
227
- return res.sendError( 'Invalid date range supplied', 400 );
228
- }
229
-
230
- if ( end.isBefore( start ) ) {
231
- [ start, end ] = [ end, start ];
232
- }
233
-
234
- const allDateStrings = [];
235
- const orderedDates = [];
236
-
237
- while ( start.isSameOrBefore( end ) ) {
238
- const formatted = start.format( 'YYYY-MM-DD' );
239
- orderedDates.push( formatted );
240
- allDateStrings.push( `${storeId}_${formatted}` );
241
- start = start.add( 1, 'day' );
242
- }
243
-
244
- if ( allDateStrings.length === 0 ) {
245
- return res.sendSuccess( [] );
246
- }
247
-
248
- const footfallQuery = {
249
- query: {
250
- terms: {
251
- _id: allDateStrings,
252
- },
214
+ const previousDate = dayjs( inputData.dateString ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
215
+ const dateString = `${inputData.storeId}_${inputData.dateString}`;
216
+ const dateStringPrevious = `${inputData.storeId}_${previousDate}`;
217
+ const getQuery = {
218
+ query: {
219
+ terms: {
220
+ _id: [ dateString, dateStringPrevious ],
253
221
  },
254
- _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
255
- sort: [
256
- { date_iso: { order: 'asc' } },
257
- ],
258
- size: allDateStrings.length,
259
- };
260
-
261
- const multiGet = await getOpenSearchData( openSearch.footfall, footfallQuery );
262
- const multiHits = multiGet?.body?.hits?.hits || [];
263
- const hitsMap = new Map();
264
- multiHits.forEach( ( hit ) => {
265
- hitsMap.set( hit?._id, hit?._source || null );
266
- } );
267
-
268
- const responseArray = [];
269
-
270
- for ( let i = 0; i < orderedDates.length; i++ ) {
271
- const currentDate = orderedDates[i];
272
- const currentId = `${storeId}_${currentDate}`;
273
- const processedData = hitsMap.get( currentId );
274
- if ( !processedData ) {
275
- responseArray.push( {
276
- date: currentDate,
277
- footfallCount: 0,
278
- footfallCountTrend: 0,
279
- downtime: 0,
280
- } );
281
- continue;
282
- }
283
-
284
- const prevDate = dayjs( currentDate ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
285
- const prevId = `${storeId}_${prevDate}`;
286
- const previousData = hitsMap.get( prevId );
287
-
288
- let footfallCountTrend = 0;
289
- if ( previousData && previousData.footfall_count ) {
290
- footfallCountTrend = Math.round(
291
- ( ( processedData.footfall_count - previousData.footfall_count ) / previousData.footfall_count ) * 100,
292
- );
293
- }
294
- // Add ticket status from openSearch.footfallDirectory (_source.status)
295
- let ticketStatus = null;
296
- // Try to find a matching footfallDirectory record for this date+storeId
297
- // const ticketKey = `${storeId}_${currentDate}`;
298
- const footfallDirQuery = {
299
- query: {
300
- bool: {
301
- must: [
302
- { term: { 'storeId.keyword': storeId } },
303
- { term: { 'dateString': currentDate } },
304
- { term: { 'ticketName.keyword': 'footfall-directory' } },
305
- ],
306
- },
222
+ },
223
+ _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
224
+ sort: [
225
+ {
226
+ date_iso: {
227
+ order: 'desc',
307
228
  },
308
- size: 1,
309
- _source: [ 'status', 'mappingInfo' ],
310
- };
311
- try {
312
- const footfallDirRes = await getOpenSearchData( openSearch.footfallDirectory, footfallDirQuery );
313
- const hit = footfallDirRes?.body?.hits?.hits?.[0];
314
- if ( hit?._source?.mappingInfo && Array.isArray( hit._source.mappingInfo ) ) {
315
- for ( let i = 0; i < hit._source.mappingInfo.length; i++ ) {
316
- if ( hit._source.mappingInfo[i].type === 'tagging' ) {
317
- ticketStatus = hit._source.mappingInfo[i].status;
318
- break;
319
- }
320
- }
321
- }
322
- } catch ( err ) {
323
- logger.warn( { message: 'Could not get ticket status from footfallDirectory', error: err } );
324
- }
325
- // Check if request status ("raised") should be enabled or disabled by querying MongoDB config
326
- // We'll assume findOnerevopConfig can fetch the record with status for this storeId and dateString
327
- let raisedStatusEnabled = 'reset'; // default: enabled
328
- try {
329
- const mongoConfig = await findOneVmsStoreRequest( { storeId: storeId, dateString: currentDate } );
330
- if ( mongoConfig && mongoConfig.status ) {
331
- raisedStatusEnabled = mongoConfig.status;
332
- }
333
- } catch ( err ) {
334
- logger.warn( { message: 'Could not get request status from MongoDB', error: err } );
335
- // Leave raisedStatusEnabled as default
336
- }
337
-
338
- responseArray.push( {
339
- date: processedData?.date_string || currentDate,
340
- footfallCount: processedData?.footfall_count || 0,
341
- footfallCountTrend,
342
- downtime: processedData?.down_time || 0,
343
- ticketStatus,
344
- raisedStatusEnabled,
345
-
346
- } );
347
-
348
- if ( raisedStatusEnabled === 'block' ) {
349
- // Calculate the number of days from currentDate + 1 to the end of the month
350
- // Assume currentDate is in format 'YYYY-MM-DD'
351
- const currentDateObj = new Date( currentDate );
352
- // Move to next day
353
- const nextDay = new Date( currentDateObj );
354
- nextDay.setDate( currentDateObj.getDate() + 1 );
229
+ },
230
+ ],
231
+ };
355
232
 
356
- // Get the last date of the current month
357
- const endOfMonth = new Date( currentDateObj.getFullYear(), currentDateObj.getMonth() + 1, 0 );
233
+ const getData = await getOpenSearchData( openSearch.footfall, getQuery );
234
+ const hits = getData?.body?.hits?.hits || [];
358
235
 
359
- // Calculate number of days (inclusive of end date, exclusive of nextDay)
360
- let noOfBlockedDays = Math.floor( ( endOfMonth - nextDay ) / ( 1000 * 60 * 60 * 24 ) ) + 1;
361
- if ( noOfBlockedDays < 0 ) noOfBlockedDays = 0;
236
+ const processedData = hits.find( ( d ) => d._id === dateString )?._source || null;
237
+ const previousData = hits.find( ( d ) => d._id === dateStringPrevious )?._source || null;
362
238
 
363
- // Add to response
364
- responseArray[responseArray.length - 1].noOfBlockedDays = noOfBlockedDays;
365
- }
366
- }
239
+ let footfallCountTrend = 0;
367
240
 
368
- return res.sendSuccess( responseArray );
241
+ if ( processedData && previousData && previousData.footfall_count ) {
242
+ footfallCountTrend = Math.round(
243
+ ( ( processedData.footfall_count - previousData.footfall_count ) / previousData.footfall_count ) * 100,
244
+ );
369
245
  }
370
- return res.sendError( 'Required parameters missing', 400 );
246
+
247
+ return res.sendSuccess( {
248
+ footfallCount: processedData?.footfall_count || 0,
249
+ footfallCountTrend,
250
+ downtime: processedData?.down_time || 0,
251
+ } );
371
252
  } catch ( error ) {
372
253
  logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
373
254
  const err = error.message || 'Internal Server Error';
@@ -406,177 +287,40 @@ export async function footFallImages( req, res ) {
406
287
  ],
407
288
  },
408
289
  },
409
- '_source': [ 'dateString', 'storeId', 'mappingInfo', 'revicedFootfall', 'revicedPerc', 'createdAt', 'updatedAt', 'footfallCount' ],
290
+ '_source': [ 'dateString', 'storeId', 'duplicateCount', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'junkCount', 'status', 'ticketId', 'comments', 'userName', 'role', 'createdAt', 'email', 'houseKeepingACCount', 'duplicateACCount', 'employeeACCount', 'junkACCount', 'approverEmail', 'approverRole', 'approverUserName' ],
410
291
 
411
292
  };
412
293
 
413
294
  const getData = await getOpenSearchData( opensearch.footfallDirectory, query );
414
295
  const ticketDetails = getData?.body?.hits?.hits[0];
415
296
  let temp = [];
416
- const footfallValue = ticketDetails?._source?.footfallCount ?? 0;
417
- const mappingInfoArray = ticketDetails?._source?.mappingInfo ?? [];
418
- // Helper to get mappingInfo for an actionType
419
- function getMappingForType( type ) {
420
- return mappingInfoArray.find( ( m ) => m.type === type ) || {};
421
- }
422
-
423
- // List of actionTypes to process in sequence
424
-
425
- if ( req.user.userType !== 'tango' && req.user.role !== 'superadmin' ) {
426
- switch ( req.user.role ) {
427
- case 'user':
428
- const actionTypesUser = [ 'tagging', 'finalreview' ];
429
-
430
- temp = [];
431
- actionTypesUser.forEach( ( type ) => {
432
- const mapping = getMappingForType( type );
433
- if ( type === 'tagging' ) {
434
- const revisedFootfall = mapping.revicedFootfall ?? 0;
435
- const revisedPerc =
436
- footfallValue > 0 ?
437
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
438
- '0';
439
- const countObj = mapping.count ? [ ...mapping.count ] : [];
440
- temp.push( {
441
- actionType: type,
442
- footfall: footfallValue,
443
- revicedFootfall: revisedFootfall,
444
- revicedPerc: revisedPerc,
445
- count: countObj,
446
- createdAt: mapping.createdAt ?? '',
447
- createdByEmail: mapping.createdByEmail ?? '',
448
- createdByUserName: mapping.createdByUserName ?? '',
449
- createdByRole: mapping.createdByRole ?? '',
450
- isUp: false,
451
- } );
452
- } else if ( type !== 'tagging' && mapping.status === 'closed' ) {
453
- const revisedFootfall = mapping.revicedFootfall ?? 0;
454
- const revisedPerc =
455
- footfallValue > 0 ?
456
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
457
- '0';
458
- const countObj = mapping.count ? [ ...mapping.count ] : [];
459
- temp.push( {
460
- actionType: type,
461
- footfall: footfallValue,
462
- revicedFootfall: revisedFootfall,
463
- revicedPerc: revisedPerc,
464
- count: countObj,
465
- createdAt: mapping.createdAt ?? '',
466
- createdByEmail: mapping.createdByEmail ?? '',
467
- createdByUserName: mapping.createdByUserName ?? '',
468
- createdByRole: mapping.createdByRole ?? '',
469
- } );
470
- }
471
- } );
472
- break;
473
- case 'admin':
474
- const actionTypesAdmin = [ 'tagging', 'review', 'finalreview' ];
475
- temp = [];
476
- actionTypesAdmin.forEach( ( type ) => {
477
- const mapping = getMappingForType( type );
478
- if ( type === 'tagging' ) {
479
- const revisedFootfall = mapping.revicedFootfall ?? 0;
480
- const revisedPerc =
481
- footfallValue > 0 ?
482
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
483
- '0';
484
- const countObj = mapping.count ? [ ...mapping.count ] : [];
485
- temp.push( {
486
- actionType: type,
487
- footfall: footfallValue,
488
- revicedFootfall: revisedFootfall,
489
- revicedPerc: revisedPerc,
490
- count: countObj,
491
- createdAt: mapping.createdAt ?? '',
492
- createdByEmail: mapping.createdByEmail ?? '',
493
- createdByUserName: mapping.createdByUserName ?? '',
494
- createdByRole: mapping.createdByRole ?? '',
495
- isUp: false,
496
- } );
497
- } else if ( type !== 'tagging' && mapping.status === 'closed' ) {
498
- const revisedFootfall = mapping.revicedFootfall ?? 0;
499
- const revisedPerc =
500
- footfallValue > 0 ?
501
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
502
- '0';
503
- const countObj = mapping.count ? [ ...mapping.count ] : [];
504
- temp.push( {
505
- actionType: type,
506
- footfall: footfallValue,
507
- revicedFootfall: revisedFootfall,
508
- revicedPerc: revisedPerc,
509
- count: countObj,
510
- createdAt: mapping.createdAt ?? '',
511
- createdByEmail: mapping.createdByEmail ?? '',
512
- createdByUserName: mapping.createdByUserName ?? '',
513
- createdByRole: mapping.createdByRole ?? '',
514
- } );
515
- }
516
- } );
517
- }
518
- } else {
519
- const actionTypes = [ 'tagging', 'review', 'approve', 'finalreview' ];
520
-
521
- // Dynamically add to temp only if actionType matches and status is 'closed',
522
- // except for 'tagging' where status must be 'raised'
523
- temp = [];
524
- actionTypes.forEach( ( type ) => {
525
- const mapping = getMappingForType( type );
526
- if ( type === 'tagging' ) {
527
- const revisedFootfall = mapping.revicedFootfall ?? 0;
528
- const revisedPerc =
529
- footfallValue > 0 ?
530
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
531
- '0';
532
- const countObj = mapping.count ? [ ...mapping.count ] : [];
533
- temp.push( {
534
- actionType: type,
535
- footfall: footfallValue,
536
- revicedFootfall: revisedFootfall,
537
- revicedPerc: revisedPerc,
538
- count: countObj,
539
- createdAt: mapping.createdAt ?? '',
540
- createdByEmail: mapping.createdByEmail ?? '',
541
- createdByUserName: mapping.createdByUserName ?? '',
542
- createdByRole: mapping.createdByRole ?? '',
543
- isUp: false,
544
- } );
545
- } else if ( type !== 'tagging' && mapping.status === 'closed' ) {
546
- const revisedFootfall = mapping.revicedFootfall ?? 0;
547
- const revisedPerc =
548
- footfallValue > 0 ?
549
- `${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
550
- '0';
551
- const countObj = mapping.count ? [ ...mapping.count ] : [];
552
- temp.push( {
553
- actionType: type,
554
- footfall: footfallValue,
555
- revicedFootfall: revisedFootfall,
556
- revicedPerc: revisedPerc,
557
- count: countObj,
558
- createdAt: mapping.createdAt ?? '',
559
- createdByEmail: mapping.createdByEmail ?? '',
560
- createdByUserName: mapping.createdByUserName ?? '',
561
- createdByRole: mapping.createdByRole ?? '',
562
- } );
563
- }
564
- } );
565
- }
566
-
567
-
568
- const LamdaURL = revop.getImages;
569
- let resultData = await LamdaServiceCall( LamdaURL, inputData );
570
- if ( resultData ) {
571
- // temp.length? temp[0].status = 'open': null;
572
- if ( resultData.status_code == '200' ) {
573
- return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
574
- } else {
575
- return res.sendError( 'No Content', 204 );
576
- }
577
- } else {
578
- return res.sendError( 'No Content', 204 );
579
- }
297
+ ticketDetails?._source? temp.push( ticketDetails?._source ) :null;
298
+ // temp[0].status = 'open';
299
+ if ( ticketDetails?._source?.status == 'closed' ) {
300
+ delete temp[0].status;
301
+ temp.push( { ...ticketDetails?._source, status: 'closed' } );
302
+
303
+ temp[1].userName = getData?.body?.hits?.hits?.[0]?._source?.approverUserName;
304
+ temp[1].email = getData?.body?.hits?.hits?.[0]?._source?.approverEmail;
305
+ temp[1].role = getData?.body?.hits?.hits?.[0]?._source?.approverRole;
306
+ temp[1].employeeCount = getData?.body?.hits?.hits?.[0]?._source?.employeeACCount;
307
+ temp[1].houseKeepingCount = getData?.body?.hits?.hits?.[0]?._source?.houseKeepingACCount;
308
+ temp[1].duplicateCount = getData?.body?.hits?.hits?.[0]?._source?.duplicateACCount;
309
+ temp[1].junkCount = getData?.body?.hits?.hits?.[0]?._source?.junkACCount;
310
+ }
311
+ const LamdaURL = revop.getImages;
312
+ let resultData = await LamdaServiceCall( LamdaURL, inputData );
313
+ logger.info( { resultData: resultData } );
314
+ if ( resultData ) {
315
+ temp.length? temp[0].status = 'open': null;
316
+ if ( resultData.status_code == '200' ) {
317
+ return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0? temp : null, config: req?.store?.revopTagging } );
318
+ } else {
319
+ return res.sendError( 'No Content', 204 );
320
+ }
321
+ } else {
322
+ return res.sendError( 'No Content', 204 );
323
+ }
580
324
  } catch ( error ) {
581
325
  logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
582
326
  const err = error.message || 'Internal Server Error';
@@ -592,7 +336,6 @@ export async function tagTempId( req, res ) {
592
336
  clientId: inputData.storeId.split( '-' )[0],
593
337
  storeId: inputData.storeId,
594
338
  tempId: inputData.tempId,
595
- id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
596
339
  dateString: inputData.dateString,
597
340
  timeRange: inputData.timeRange,
598
341
  processType: inputData.processType,
@@ -605,15 +348,6 @@ export async function tagTempId( req, res ) {
605
348
  isChecked: inputData.isChecked,
606
349
  duplicateImage: inputData?.duplicateImage?.length>0? inputData?.duplicateImage :[],
607
350
  type: 'tagging-reflect',
608
- ticketStatus: 'submitted',
609
- isParent: inputData?.revopsType === 'duplicate'? true : false,
610
- actions: [
611
- {
612
- actionType: 'tagging',
613
- action: 'submitted',
614
- },
615
- ],
616
- comments: inputData.comments || '',
617
351
  createdAt: new Date(),
618
352
  updatedAt: new Date(),
619
353
 
@@ -652,7 +386,6 @@ export async function tagTempId( req, res ) {
652
386
  clientId: inputData.storeId.split( '-' )[0],
653
387
  storeId: inputData.storeId,
654
388
  tempId: item.tempId,
655
- id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
656
389
  dateString: inputData.dateString,
657
390
  timeRange: item.timeRange,
658
391
  isChecked: item.isChecked,
@@ -664,16 +397,7 @@ export async function tagTempId( req, res ) {
664
397
  status: item?.revopsType == 'non-tagging' ?'':'submitted',
665
398
  description: '',
666
399
  duplicateImage: [],
667
- isParent: false,
668
400
  type: 'tagging-reflect',
669
- ticketStatus: 'submitted',
670
- comments: inputData.comments || '',
671
- actions: [
672
- {
673
- actionType: 'tagging',
674
- action: 'submitted',
675
- },
676
- ],
677
401
  createdAt: new Date(),
678
402
  updatedAt: new Date(),
679
403
 
@@ -4,8 +4,7 @@ import dayjs from 'dayjs';
4
4
  export const storeProcessedDataSchema = joi.object( {
5
5
 
6
6
  storeId: joi.string().required(),
7
- fromDate: joi.string().required(),
8
- toDate: joi.string().required(),
7
+ dateString: joi.string().required(),
9
8
 
10
9
  } );
11
10
 
@@ -56,8 +55,8 @@ export const tagTempIdSchema = joi.object( {
56
55
 
57
56
  const diff = today.diff( inputDate, 'day' );
58
57
 
59
- if ( diff > 7 ) {
60
- return helpers.message( 'Tagging is not allowed for a period exceeding 7 days' );
58
+ if ( diff > 3 ) {
59
+ return helpers.message( 'Tagging is not allowed for a period exceeding 3 days' );
61
60
  }
62
61
 
63
62
  return value;
@@ -66,9 +65,6 @@ export const tagTempIdSchema = joi.object( {
66
65
  revopsType: joi.string().required(),
67
66
  timeRange: joi.string().required(),
68
67
  isChecked: joi.boolean().required().allow( null ),
69
- mode: joi.string().valid( 'web', 'mobile' ).required().messages( {
70
- 'any.only': 'type must be one of [mobile,web]',
71
- } ),
72
68
  duplicateImage: joi.array().items( joi.object(
73
69
  {
74
70
  tempId: joi.number().required(),
@@ -84,7 +80,6 @@ export const tagTempIdSchema = joi.object( {
84
80
  entryTime: joi.string().required(),
85
81
  exitTime: joi.string().required(),
86
82
  filePath: joi.string().required(),
87
- comments: joi.string().optional().allow( '' ),
88
83
 
89
84
  } );
90
85
 
@@ -1,24 +1,11 @@
1
1
  import { getOpenSearchCount, logger } from 'tango-app-api-middleware';
2
- import { deleteByQuery, getOpenSearchData } from 'tango-app-api-middleware/src/utils/openSearch.js';
3
- import { findOne } from '../services/clients.services.js';
2
+ import { findOneStore } from '../services/stores.service.js';
3
+ import { deleteByQuery } from 'tango-app-api-middleware/src/utils/openSearch.js';
4
4
 
5
5
  export async function getTaggingConfig( req, res, next ) {
6
6
  try {
7
7
  const inputData= req.query;
8
- const clientId = inputData.storeId.split( '-' )[0];
9
- const getData = await findOne( { clientId: clientId }, { footfallDirectoryConfigs: 1 } );
10
-
11
- // Convert "taggingLimitation" array (if present) to "config" object with expected key-value pairs
12
- let config = {};
13
- if ( getData && Array.isArray( getData.taggingLimitation ) ) {
14
- for ( const item of getData.taggingLimitation ) {
15
- if ( item && item.type && typeof item.value !== 'undefined' && item.unit ) {
16
- config[item.type] = `${item.value}${item.unit}`;
17
- }
18
- }
19
- }
20
- getData.config = config;
21
-
8
+ const getData = await findOneStore( { storeId: inputData.storeId }, { revopTagging: 1 } );
22
9
  req.store = getData;
23
10
  next();
24
11
  } catch ( error ) {
@@ -64,54 +51,10 @@ export async function getFootfallCount( req, res, next ) {
64
51
  export async function mappingConfig( req, res, next ) {
65
52
  try {
66
53
  const inputData = req.body;
67
-
68
54
  const openSearch = JSON.parse( process.env.OPENSEARCH );
69
- const footfallQuery ={
70
- query: {
71
- bool: {
72
- must: [
73
- {
74
- term: {
75
- 'store_id.keyword': inputData.storeId,
76
- },
77
- },
78
- {
79
- term: {
80
- 'date_string': inputData.dateString,
81
- },
82
- },
83
-
84
- ],
85
- },
86
- },
87
- };
88
-
89
-
90
- const footfallOutput = await getOpenSearchData( openSearch.footfall, footfallQuery );
91
- if ( footfallOutput?.body?.hits?.hits?.length === 0 ) {
92
- return res.sendError( 'No updated footfall for this date', 400 );
93
- }
94
- const getFootfallCount = footfallOutput?.body?.hits?.hits;
95
- const footfall = getFootfallCount?.[0]?._source?.footfall_count;
96
-
97
- const getConfig = await findOne( { clientId: inputData?.storeId?.split( '-' )[0] }, { footfallDirectoryConfigs: 1 } );
98
- const taggingLimitation = getConfig?.footfallDirectoryConfigs?.taggingLimitation;
99
- // Find the tagging limitation for the given revopsType
100
- let matchedLimitation = null;
101
- if ( Array.isArray( taggingLimitation ) ) {
102
- matchedLimitation = taggingLimitation.find(
103
- ( l ) => l.type === inputData.revopsType,
104
- );
105
- }
106
-
107
- if ( matchedLimitation ) {
108
- // Determine the limit value
109
- let limitValue = Number( matchedLimitation.value ) || 0;
110
- let unit = matchedLimitation.unit;
111
-
112
- // Assuming getData and/or getOpenSearchCount provides the actual tagged count for revopsType
113
- // Query OpenSearch for current tagged count for this revopsType
114
- const taggedCountQuery = {
55
+ const config =await findOneStore( { storeId: inputData.storeId }, { revopTagging: 1 } );
56
+ if ( inputData.revopsType == 'employee' ) {
57
+ const getQuery = {
115
58
  query: {
116
59
  bool: {
117
60
  must: [
@@ -130,51 +73,123 @@ export async function mappingConfig( req, res, next ) {
130
73
  'revopsType.keyword': inputData.revopsType,
131
74
  },
132
75
  },
76
+ ],
77
+ },
78
+ },
79
+ };
80
+
81
+ const getData = await getOpenSearchCount( openSearch.revop, getQuery );
82
+ if ( getData && getData?.body?.count >= config?.revopTagging?.employee ) {
83
+ return res.sendError( `Select up to ${config?.revopTagging?.employee} items only`, 400 );
84
+ } else {
85
+ next();
86
+ }
87
+ } else if ( inputData.revopsType == 'house-keeping' ) {
88
+ const getQuery = {
89
+ query: {
90
+ bool: {
91
+ must: [
133
92
  {
134
93
  term: {
135
- 'isParent': false,
94
+ 'storeId.keyword': inputData.storeId,
95
+ },
96
+ },
97
+ {
98
+ term: {
99
+ 'dateString': inputData.dateString,
100
+ },
101
+ },
102
+ {
103
+ term: {
104
+ 'revopsType.keyword': inputData.revopsType,
136
105
  },
137
106
  },
138
107
  ],
139
108
  },
140
109
  },
141
110
  };
142
- const taggedData = await getOpenSearchCount( openSearch.revop, taggedCountQuery );
143
- const taggedValue = ( taggedData?.body?.count || 0 )+ ( inputData.revopsType == 'duplicate'? inputData?.duplicateImage?.length : 1 );
144
- // If the unit is %, compare percentage of taggedValue/footfall, otherwise compare taggedValue to limitValue directly
145
- let isLimitExceeded = false;
146
- if ( unit === '%' ) {
147
- // footfall may be undefined, treat as 0 (avoid division by zero)
148
- const totalFootfall = Number( footfall ) || 0;
149
- const taggedPercent = totalFootfall > 0 ? ( taggedValue / totalFootfall ) * 100 : 0;
150
- isLimitExceeded = taggedPercent > limitValue;
151
- logger.info( {
152
- limitType: 'PERCENT',
153
- taggedValue,
154
- totalFootfall,
155
- taggedPercent,
156
- limitValue,
157
- isLimitExceeded,
158
- forRevopsType: inputData.revopsType,
159
- } );
111
+ const getData = await getOpenSearchCount( openSearch.revop, getQuery );
112
+ if ( getData && getData?.body?.count >= config?.revopTagging?.houseKeeping ) {
113
+ return res.sendError( `Select up to ${config?.revopTagging?.houseKeeping} items only`, 400 );
160
114
  } else {
161
- // Non-percent, treat limitValue as an absolute number
162
- isLimitExceeded = taggedValue > limitValue;
163
- logger.info( {
164
- limitType: 'ABSOLUTE',
165
- taggedValue,
166
- limitValue,
167
- isLimitExceeded,
168
- forRevopsType: inputData.revopsType,
169
- } );
115
+ next();
170
116
  }
171
- if ( isLimitExceeded ) {
172
- return res.sendError( `Limit exceed: Only ${limitValue}${unit || ''} items allowed for ${inputData.revopsType}`, 400 );
117
+ } else if ( inputData.revopsType === 'junk' ) {
118
+ if ( ( req?.user?.userType == 'client' && req?.user?.userType !== 'user ' ) || req?.user?.userType === 'tango' ) {
119
+ next();
120
+ return;
121
+ } else {
122
+ return res.sendError( 'Forbidden to junk mapping', 500 );
173
123
  }
174
- return next();
175
124
  } else {
176
- return next();
125
+ next();
177
126
  }
127
+ // else if ( inputData.revopsType == 'duplicate' ) {
128
+ // const getFootfallQuery = {
129
+ // query: {
130
+ // terms: {
131
+ // _id: [ inputData?.dateString ],
132
+ // },
133
+ // },
134
+ // _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
135
+ // sort: [
136
+ // {
137
+ // date_iso: {
138
+ // order: 'desc',
139
+ // },
140
+ // },
141
+ // ],
142
+ // };
143
+
144
+ // const getFootfall = await getOpenSearchData( openSearch.footfall, getFootfallQuery );
145
+ // const footfall = getFootfall?.body?.hites?.hits?.[0]?._source?.footfall_count;
146
+ // const getQuery = {
147
+ // query: {
148
+ // bool: {
149
+ // must: [
150
+ // {
151
+ // term: {
152
+ // 'storeId.keyword': inputData.storeId,
153
+ // },
154
+ // },
155
+ // {
156
+ // term: {
157
+ // 'dateString': inputData.dateString,
158
+ // },
159
+ // },
160
+ // {
161
+ // term: {
162
+ // 'revopsType.keyword': inputData.revopsType,
163
+ // },
164
+ // },
165
+ // {
166
+ // term: {
167
+ // 'parent.keyword': null,
168
+ // },
169
+ // },
170
+ // ],
171
+ // },
172
+ // },
173
+ // };
174
+ // const getData = await getOpenSearchCount( openSearch.revop, getQuery );
175
+ // logger.info( { getData: getData, footfall: footfall, duplicate: config?.revopTagging?.duplicate } );
176
+ // if ( getData && footfall && config?.revopTagging?.duplicate ) {
177
+ // const data = config?.revopTagging?.duplicate;
178
+ // // Convert "20%" → 0.2 (handle both "20%" and 20)
179
+ // const percentStr = typeof data === 'string' ? data.replace( '%', '' ) : data;
180
+ // logger.info( { percentStr: percentStr } );
181
+ // const percentValue =percentStr / 100;
182
+
183
+ // const result = percentValue * footfall;
184
+
185
+ // logger.info( { result: result, footfall: footfall } );
186
+ // }
187
+ // if ( getData && getData?.body?.count >= Math.round( result ) ) {
188
+ // return res.sendError( `Select up to ${config?.revopTagging?.duplicate} items only`, 400 );
189
+ // } else {
190
+ // next();
191
+ // }
192
+ // }
178
193
  } catch ( error ) {
179
194
  logger.error( { error: error, message: req.body, function: 'traffic-revop-getTaggingConfig' } );
180
195
  next();
@@ -1,5 +0,0 @@
1
- import vmsStoreRequestModel from 'tango-api-schema/schema/vmsStoreRequest.model.js';
2
-
3
- export async function findOneVmsStoreRequest( query = {} ) {
4
- return await vmsStoreRequestModel.findOne( query );
5
- };