tango-app-api-infra 3.9.5-vms.6 → 3.9.5-vms.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-infra",
|
|
3
|
-
"version": "3.9.5-vms.
|
|
3
|
+
"version": "3.9.5-vms.8",
|
|
4
4
|
"description": "infra",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"mongodb": "^6.4.0",
|
|
28
28
|
"nodemon": "^3.1.0",
|
|
29
29
|
"swagger-ui-express": "^5.0.0",
|
|
30
|
-
"tango-api-schema": "^2.4.
|
|
30
|
+
"tango-api-schema": "^2.4.28",
|
|
31
31
|
"tango-app-api-middleware": "^3.1.93",
|
|
32
32
|
"winston": "^3.12.0",
|
|
33
33
|
"winston-daily-rotate-file": "^5.0.0"
|
|
@@ -497,7 +497,7 @@ export async function ticketList( req, res ) {
|
|
|
497
497
|
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name =='reviewer' && ( m.isAdd==true || m.isEdit==true ) ) ) );
|
|
498
498
|
|
|
499
499
|
if ( req.user.userType =='tango' ) {
|
|
500
|
-
result =inputData.
|
|
500
|
+
result =inputData.tangotype == 'store'?
|
|
501
501
|
|
|
502
502
|
|
|
503
503
|
[
|
|
@@ -716,18 +716,18 @@ export async function ticketList( req, res ) {
|
|
|
716
716
|
[
|
|
717
717
|
|
|
718
718
|
{
|
|
719
|
-
ticketId: '
|
|
719
|
+
ticketId: 'TE_FDT_1763860421803',
|
|
720
720
|
storeId: '11-1716',
|
|
721
721
|
storeName: 'LKST1916',
|
|
722
|
-
ticketRaised: '2025-11-
|
|
723
|
-
issueDate: '2025-11-
|
|
724
|
-
footfall:
|
|
725
|
-
type: '
|
|
726
|
-
storeRevisedAccuracy: '
|
|
727
|
-
reviewerRevisedAccuracy: '
|
|
728
|
-
approverRevisedAccuracy: '
|
|
729
|
-
tangoRevisedAccuracy: '
|
|
730
|
-
status: '
|
|
722
|
+
ticketRaised: '2025-11-21',
|
|
723
|
+
issueDate: '2025-11-20',
|
|
724
|
+
footfall: 94,
|
|
725
|
+
type: 'store',
|
|
726
|
+
storeRevisedAccuracy: '95%',
|
|
727
|
+
reviewerRevisedAccuracy: '--',
|
|
728
|
+
approverRevisedAccuracy: '--',
|
|
729
|
+
tangoRevisedAccuracy: '--',
|
|
730
|
+
status: 'Open',
|
|
731
731
|
tangoStatus: 'Open',
|
|
732
732
|
},
|
|
733
733
|
{
|
|
@@ -1023,13 +1023,13 @@ req.user.role === 'user'? 'NA':
|
|
|
1023
1023
|
ticketsFeature?
|
|
1024
1024
|
[
|
|
1025
1025
|
{
|
|
1026
|
-
ticketId: '
|
|
1026
|
+
ticketId: 'TE_FDT_1763860421803',
|
|
1027
1027
|
storeId: '11-1716',
|
|
1028
1028
|
storeName: 'LKST1916',
|
|
1029
|
-
ticketRaised: '2025-11-
|
|
1030
|
-
issueDate: '2025-11-
|
|
1029
|
+
ticketRaised: '2025-11-21',
|
|
1030
|
+
issueDate: '2025-11-20',
|
|
1031
1031
|
dueDate: 'Due Today',
|
|
1032
|
-
footfall:
|
|
1032
|
+
footfall: 90,
|
|
1033
1033
|
storeRevisedAccuracy: '90%',
|
|
1034
1034
|
reviewerRevisedAccuracy: '0%',
|
|
1035
1035
|
status: 'Open',
|
|
@@ -1039,13 +1039,13 @@ ticketsFeature?
|
|
|
1039
1039
|
ticketId: 'TE_FDT_1763539990346',
|
|
1040
1040
|
storeId: '11-2000',
|
|
1041
1041
|
storeName: 'LKST2368',
|
|
1042
|
-
ticketRaised: '2025-11-
|
|
1043
|
-
issueDate: '2025-11-
|
|
1044
|
-
dueDate: '2025-11-
|
|
1042
|
+
ticketRaised: '2025-11-21',
|
|
1043
|
+
issueDate: '2025-11-20',
|
|
1044
|
+
dueDate: '2025-11-26',
|
|
1045
1045
|
footfall: 90,
|
|
1046
1046
|
storeRevisedAccuracy: '90%',
|
|
1047
1047
|
reviewerRevisedAccuracy: '--',
|
|
1048
|
-
status: '
|
|
1048
|
+
status: 'In-Progress',
|
|
1049
1049
|
ReviewedBy: 'mu_mu@yopmail.com',
|
|
1050
1050
|
},
|
|
1051
1051
|
{
|
|
@@ -2310,7 +2310,7 @@ export async function openTicketList( req, res ) {
|
|
|
2310
2310
|
}
|
|
2311
2311
|
}
|
|
2312
2312
|
|
|
2313
|
-
export async function
|
|
2313
|
+
export async function assignTicket( req, res ) {
|
|
2314
2314
|
try {
|
|
2315
2315
|
const inputData = req.body;
|
|
2316
2316
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
@@ -2384,7 +2384,154 @@ export async function updateiTcket( req, res ) {
|
|
|
2384
2384
|
return res.sendSuccess( { updated: response?.body?.updated ?? 0 } );
|
|
2385
2385
|
} catch ( error ) {
|
|
2386
2386
|
const err = error.message || 'Internal Server Error';
|
|
2387
|
-
logger.error( { error: error, function: '
|
|
2387
|
+
logger.error( { error: error, function: 'assignTicket' } );
|
|
2388
|
+
return res.sendError( err, 500 );
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
export async function updateTempStatus( req, res ) {
|
|
2393
|
+
try {
|
|
2394
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2395
|
+
const { id, status } = req.body;
|
|
2396
|
+
|
|
2397
|
+
// Use bulk update API via bucketing (batch update) -- fetch docs, then bulk-update
|
|
2398
|
+
// 1. Search for all documents matching the ticket IDs
|
|
2399
|
+
const searchBody = {
|
|
2400
|
+
query: {
|
|
2401
|
+
bool: {
|
|
2402
|
+
must: [
|
|
2403
|
+
{
|
|
2404
|
+
terms: {
|
|
2405
|
+
'id.keyword': id,
|
|
2406
|
+
},
|
|
2407
|
+
},
|
|
2408
|
+
],
|
|
2409
|
+
},
|
|
2410
|
+
},
|
|
2411
|
+
_source: [ '_id' ], // Only bring _id for efficiency
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
const searchResp = await getOpenSearchData(
|
|
2415
|
+
openSearch.revop,
|
|
2416
|
+
searchBody,
|
|
2417
|
+
);
|
|
2418
|
+
logger.info( { searchResp: searchResp } );
|
|
2419
|
+
// Extract bulk IDs to update
|
|
2420
|
+
const hits = searchResp?.body?.hits?.hits ?? [];
|
|
2421
|
+
logger.info( { hits: hits } );
|
|
2422
|
+
if ( !hits.length ) {
|
|
2423
|
+
return res.sendError( 'no data', 204 );
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
// 2. Build bulk update commands
|
|
2427
|
+
// Each doc: { update: { _id: ..., _index: ... } }, { doc: { status: status } }
|
|
2428
|
+
|
|
2429
|
+
// 1. Get all IDs from hits
|
|
2430
|
+
const docIdToIndex = {};
|
|
2431
|
+
hits.forEach( ( doc ) => {
|
|
2432
|
+
docIdToIndex[doc._id] = doc._index;
|
|
2433
|
+
} );
|
|
2434
|
+
const docIds = hits.map( ( doc ) => doc._id );
|
|
2435
|
+
logger.info( { docIds } );
|
|
2436
|
+
// 2. Fetch all docs by ID to get 'actions' (in chunks if large)
|
|
2437
|
+
const getBody = [];
|
|
2438
|
+
for ( const doc of hits ) {
|
|
2439
|
+
getBody.push( { _index: doc._index, _id: doc._id } );
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
let mgetResp;
|
|
2443
|
+
try {
|
|
2444
|
+
mgetResp = await getOpenSearchData(
|
|
2445
|
+
openSearch.revop,
|
|
2446
|
+
{
|
|
2447
|
+
query: {
|
|
2448
|
+
ids: {
|
|
2449
|
+
values: docIds,
|
|
2450
|
+
},
|
|
2451
|
+
},
|
|
2452
|
+
_source: true,
|
|
2453
|
+
},
|
|
2454
|
+
);
|
|
2455
|
+
} catch ( err ) {
|
|
2456
|
+
logger.error( { error: err } );
|
|
2457
|
+
mgetResp = undefined;
|
|
2458
|
+
}
|
|
2459
|
+
logger.info( { mgetResp } );
|
|
2460
|
+
// (If you have a utility for multi-get, you may want to use that. Else, you might need to fetch each by ID.)
|
|
2461
|
+
// For fallback, fetch all source fields via another search
|
|
2462
|
+
let fullDocs = [];
|
|
2463
|
+
if ( mgetResp && mgetResp.body && mgetResp.body.docs && Array.isArray( mgetResp.body.docs ) ) {
|
|
2464
|
+
fullDocs = mgetResp.body.docs;
|
|
2465
|
+
} else if ( searchResp.body && searchResp.body.hits && searchResp.body.hits.hits ) {
|
|
2466
|
+
// fallback: use searchResp docs (request _source above)
|
|
2467
|
+
fullDocs = searchResp.body.hits.hits;
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
// 3. Prepare the new actions array for each doc, and set up bulk update payloads
|
|
2471
|
+
const reviewActions = [ 'approved', 'rejected' ];
|
|
2472
|
+
const docsToUpdate = [];
|
|
2473
|
+
logger.info( { fullDocs: fullDocs } );
|
|
2474
|
+
for ( const doc of fullDocs ) {
|
|
2475
|
+
const source = doc._source || doc.fields || {}; // support mget and search hits
|
|
2476
|
+
let actions = Array.isArray( source.actions ) ? [ ...source.actions ] : [];
|
|
2477
|
+
if ( reviewActions.includes( status ) ) {
|
|
2478
|
+
// for review: update or push 'review'
|
|
2479
|
+
let found = false;
|
|
2480
|
+
actions = actions.map( ( item ) => {
|
|
2481
|
+
if ( item.actionType === 'review' ) {
|
|
2482
|
+
found = true;
|
|
2483
|
+
return { ...item, action: status };
|
|
2484
|
+
}
|
|
2485
|
+
return item;
|
|
2486
|
+
} );
|
|
2487
|
+
if ( !found ) {
|
|
2488
|
+
actions.push( { actionType: 'review', action: status } );
|
|
2489
|
+
}
|
|
2490
|
+
} else {
|
|
2491
|
+
// tagging: update or push 'tagging'
|
|
2492
|
+
let found = false;
|
|
2493
|
+
actions = actions.map( ( item ) => {
|
|
2494
|
+
if ( item.actionType === 'tagging' ) {
|
|
2495
|
+
found = true;
|
|
2496
|
+
return { ...item, action: 'submitted' };
|
|
2497
|
+
}
|
|
2498
|
+
return item;
|
|
2499
|
+
} );
|
|
2500
|
+
if ( !found ) {
|
|
2501
|
+
actions.push( { actionType: 'tagging', action: 'submitted' } );
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
docsToUpdate.push( {
|
|
2505
|
+
_index: doc._index || docIdToIndex[doc._id],
|
|
2506
|
+
_id: doc._id,
|
|
2507
|
+
actions,
|
|
2508
|
+
} );
|
|
2509
|
+
}
|
|
2510
|
+
const bulkPayload = [];
|
|
2511
|
+
// 4. Build bulk update payload
|
|
2512
|
+
for ( const doc of docsToUpdate ) {
|
|
2513
|
+
bulkPayload.push( {
|
|
2514
|
+
update: { _index: doc._index, _id: doc._id },
|
|
2515
|
+
} );
|
|
2516
|
+
bulkPayload.push( {
|
|
2517
|
+
doc: { actions: doc.actions },
|
|
2518
|
+
} );
|
|
2519
|
+
}
|
|
2520
|
+
logger.info( { bulkPayload: bulkPayload } );
|
|
2521
|
+
|
|
2522
|
+
|
|
2523
|
+
// 3. Execute bulk update
|
|
2524
|
+
const bulkResp = await bulkUpdate( bulkPayload );
|
|
2525
|
+
|
|
2526
|
+
// Count successes
|
|
2527
|
+
const updatedCount = bulkResp?.body?.items?.filter( ( item ) => item?.update?.result === 'updated' || item?.update?.result === 'noop' ).length ?? 0;
|
|
2528
|
+
|
|
2529
|
+
logger.info( { updated: updatedCount, by: 'updateTempStatus', ids: id } );
|
|
2530
|
+
|
|
2531
|
+
return res.sendSuccess( { updated: updatedCount } );
|
|
2532
|
+
} catch ( error ) {
|
|
2533
|
+
const err = error.message;
|
|
2534
|
+
logger.info( { error: err, function: 'updateTempStatus' } );
|
|
2388
2535
|
return res.sendError( err, 500 );
|
|
2389
2536
|
}
|
|
2390
2537
|
}
|
|
@@ -463,7 +463,7 @@ export const openTicketListValid = {
|
|
|
463
463
|
};
|
|
464
464
|
|
|
465
465
|
|
|
466
|
-
export const
|
|
466
|
+
export const assignTicketSchema = Joi.object().keys( {
|
|
467
467
|
email: Joi.string().required(),
|
|
468
468
|
userName: Joi.string().optional(),
|
|
469
469
|
role: Joi.string().optional(),
|
|
@@ -473,6 +473,18 @@ export const updateTicketListSchema = Joi.object().keys( {
|
|
|
473
473
|
|
|
474
474
|
} );
|
|
475
475
|
|
|
476
|
-
export const
|
|
477
|
-
body:
|
|
476
|
+
export const assignTicketValid = {
|
|
477
|
+
body: assignTicketSchema,
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
export const updateTempStatusSchema = Joi.object().keys( {
|
|
481
|
+
id: Joi.array().items( Joi.string().required() ).required(),
|
|
482
|
+
status: Joi.string().required(),
|
|
483
|
+
type: Joi.string().required(),
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
} );
|
|
487
|
+
|
|
488
|
+
export const updateTempStatusValid = {
|
|
489
|
+
body: updateTempStatusSchema,
|
|
478
490
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { getClusters, getConfig, isGrantedUsers, isTicketExists, ticketCreation } from '../validations/footfallDirectory.validation.js';
|
|
3
|
-
import { createTicket, downloadTickets, getTaggedStores, getTickets, openTicketList, reviewerList, ticketList, ticketSummary,
|
|
4
|
-
import { createTicketValid, downloadTicketsValid, getTaggedStoresValid, getTicketsValid, openTicketListValid, reviewerListValid, ticketListValid, ticketSummaryValid, updateStatusValid,
|
|
3
|
+
import { assignTicket, createTicket, downloadTickets, getTaggedStores, getTickets, openTicketList, reviewerList, ticketList, ticketSummary, updateStatus, updateTempStatus } from '../controllers/footfallDirectory.controllers.js';
|
|
4
|
+
import { createTicketValid, downloadTicketsValid, getTaggedStoresValid, getTicketsValid, openTicketListValid, reviewerListValid, ticketListValid, ticketSummaryValid, updateStatusValid, assignTicketValid, updateTempStatusValid } from '../dtos/footfallDirectory.dtos.js';
|
|
5
5
|
import { bulkValidate, getAssinedStore, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
6
6
|
|
|
7
7
|
export const footfallDirectoryRouter = express.Router();
|
|
@@ -17,6 +17,7 @@ footfallDirectoryRouter.put( '/update-status', isAllowedSessionHandler, bulkVali
|
|
|
17
17
|
footfallDirectoryRouter.get( '/download-tickets', isAllowedSessionHandler, bulkValidate( downloadTicketsValid ), isTicketExists, downloadTickets );
|
|
18
18
|
footfallDirectoryRouter.get( '/reviewer-list', isAllowedSessionHandler, bulkValidate( reviewerListValid ), reviewerList );
|
|
19
19
|
footfallDirectoryRouter.post( '/open-ticket-list', isAllowedSessionHandler, bulkValidate( openTicketListValid ), openTicketList );
|
|
20
|
-
footfallDirectoryRouter.post( '/
|
|
20
|
+
footfallDirectoryRouter.post( '/assign-ticket', isAllowedSessionHandler, bulkValidate( assignTicketValid ), assignTicket );
|
|
21
|
+
footfallDirectoryRouter.post( '/update-temp-status', isAllowedSessionHandler, bulkValidate( updateTempStatusValid ), updateTempStatus );
|
|
21
22
|
|
|
22
23
|
|
|
@@ -228,14 +228,13 @@ export async function ticketCreation( req, res, next ) {
|
|
|
228
228
|
// check the createtion permission from the user permission
|
|
229
229
|
const userInfo = req?.user;
|
|
230
230
|
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name =='creator' && ( m.isAdd==true || m.isEdit==true ) ) ) );
|
|
231
|
-
logger.info( { ticketsFeature } );
|
|
232
231
|
if ( !ticketsFeature ) {
|
|
233
232
|
return res.sendError( 'Forbidden to Create Ticket', 403 );
|
|
234
233
|
}
|
|
235
234
|
|
|
236
235
|
// get store info by the storeId into mongo db
|
|
237
236
|
const getstoreName = await findOneStore( { storeId: inputData.storeId, status: 'active' }, { storeId: 1, storeName: 1, clientId: 1 } );
|
|
238
|
-
|
|
237
|
+
|
|
239
238
|
if ( !getstoreName || getstoreName == null ) {
|
|
240
239
|
return res.sendError( 'The store ID is either inActive or not found', 400 );
|
|
241
240
|
}
|
|
@@ -261,22 +260,20 @@ export async function ticketCreation( req, res, next ) {
|
|
|
261
260
|
|
|
262
261
|
const getFootfallCount = await getOpenSearchData( openSearch.footfall, getQuery );
|
|
263
262
|
const hits = getFootfallCount?.body?.hits?.hits || [];
|
|
264
|
-
logger.info( { hits } );
|
|
265
263
|
if ( hits?.[0]?._source?.footfall_count <= 0 ) {
|
|
266
264
|
return res.sendError( 'You can’t create a ticket because this store has 0 footfall data' );
|
|
267
265
|
}
|
|
268
266
|
|
|
269
267
|
// get category details from the client level configuration
|
|
270
268
|
const getConfig = await findOneClient( { clientId: getstoreName.clientId }, { footfallDirectoryConfigs: 1 } );
|
|
271
|
-
logger.info( { getConfig, ta123: getConfig?.footfallDirectoryConfigs } );
|
|
272
269
|
if ( !getConfig || getConfig == null ) {
|
|
273
270
|
return res.sendError( 'The Client ID is either not configured or not found', 400 );
|
|
274
271
|
}
|
|
275
272
|
|
|
276
273
|
// Get taggingLimitation from config (check both possible paths)
|
|
277
274
|
const taggingLimitation = getConfig?.footfallDirectoryConfigs?.taggingLimitation;
|
|
278
|
-
logger.info( { taggingLimitation, tagginngs: getConfig?.footfallDirectoryConfigs } );
|
|
279
275
|
// Initialize count object from taggingLimitation
|
|
276
|
+
const tempAcc = [];
|
|
280
277
|
const getCategory = taggingLimitation?.reduce( ( acc, item ) => {
|
|
281
278
|
if ( item?.type ) {
|
|
282
279
|
// Convert type to camelCase with "Count" suffix
|
|
@@ -289,11 +286,21 @@ export async function ticketCreation( req, res, next ) {
|
|
|
289
286
|
// Convert first letter to lowercase and append "Count"
|
|
290
287
|
key = typeLower.charAt( 0 ) + typeLower.slice( 1 ) + 'Count';
|
|
291
288
|
}
|
|
292
|
-
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
// To change from an object to the desired array structure, assemble an array of objects:
|
|
292
|
+
tempAcc.push( {
|
|
293
|
+
name: item.name,
|
|
294
|
+
value: 0,
|
|
295
|
+
key: key,
|
|
296
|
+
type: item.type,
|
|
297
|
+
} );
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
return acc;
|
|
293
301
|
}
|
|
294
|
-
return acc;
|
|
295
302
|
}, {} ) || {};
|
|
296
|
-
|
|
303
|
+
|
|
297
304
|
// Query OpenSearch revop index to get actual counts for each type
|
|
298
305
|
if ( taggingLimitation && taggingLimitation.length > 0 ) {
|
|
299
306
|
const revopQuery = {
|
|
@@ -324,51 +331,38 @@ export async function ticketCreation( req, res, next ) {
|
|
|
324
331
|
},
|
|
325
332
|
};
|
|
326
333
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
} else if ( revopsType === 'housekeeping' ) {
|
|
348
|
-
if ( getCategory.hasOwnProperty( 'houseKeepingCount' ) ) {
|
|
349
|
-
getCategory.houseKeepingCount = count;
|
|
350
|
-
}
|
|
351
|
-
} else if ( revopsType === 'junk' ) {
|
|
352
|
-
if ( getCategory.hasOwnProperty( 'junkCount' ) ) {
|
|
353
|
-
getCategory.junkCount = count;
|
|
354
|
-
}
|
|
334
|
+
|
|
335
|
+
const revopData = await getOpenSearchData( openSearch.revop, revopQuery );
|
|
336
|
+
const buckets = revopData?.body?.aggregations?.type_counts?.buckets || [];
|
|
337
|
+
|
|
338
|
+
// Map OpenSearch revopsType values to count object keys
|
|
339
|
+
buckets.forEach( ( bucket ) => {
|
|
340
|
+
const revopsType = bucket.key;
|
|
341
|
+
const count = bucket.doc_count || 0;
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if ( Array.isArray( tempAcc ) ) {
|
|
345
|
+
// Find the tempAcc entry whose type (case-insensitive) matches revopsType
|
|
346
|
+
const accMatch = tempAcc.find(
|
|
347
|
+
( acc ) =>
|
|
348
|
+
acc.type &&
|
|
349
|
+
acc.type === revopsType,
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if ( accMatch && accMatch.key ) {
|
|
353
|
+
tempAcc.find( ( a ) => a.key === accMatch.key ).value = count;
|
|
355
354
|
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
logger.error( { error: error, message: 'Error fetching revop counts', function: 'footfallDirectoryTicket-ticketCreation' } );
|
|
359
|
-
// Continue with default 0 values if query fails
|
|
360
|
-
}
|
|
355
|
+
}
|
|
356
|
+
} );
|
|
361
357
|
}
|
|
362
358
|
|
|
363
|
-
logger.info( { getCategory: getCategory } );
|
|
364
359
|
|
|
365
360
|
// Calculate revisedFootfall: footfallCount - (sum of all counts)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
361
|
+
|
|
362
|
+
const totalCount = Array.isArray( tempAcc ) ?
|
|
363
|
+
tempAcc.reduce( ( sum, acc ) => sum + ( acc.value || 0 ), 0 ) :
|
|
364
|
+
0;
|
|
370
365
|
const footfallCount = hits?.[0]?._source?.footfall_count || 0;
|
|
371
|
-
logger.info( { footfallCount, totalCount } );
|
|
372
366
|
const revisedFootfall = Math.max( 0, footfallCount - totalCount );
|
|
373
367
|
if ( footfallCount - revisedFootfall == 0 ) {
|
|
374
368
|
return res.sendError( 'Cannot create a ticket because footfall hasn’t changed', 400 );
|
|
@@ -400,7 +394,6 @@ export async function ticketCreation( req, res, next ) {
|
|
|
400
394
|
return res.sendError( 'You don’t have any tagged images right now', 400 );
|
|
401
395
|
}
|
|
402
396
|
const formattedTaggingData = formatRevopTaggingHits( taggingImages );
|
|
403
|
-
logger.info( { revopTaggingData: formattedTaggingData } );
|
|
404
397
|
|
|
405
398
|
const record = {
|
|
406
399
|
storeId: inputData.storeId,
|
|
@@ -420,12 +413,13 @@ export async function ticketCreation( req, res, next ) {
|
|
|
420
413
|
type: 'tagging',
|
|
421
414
|
mode: inputData.mode,
|
|
422
415
|
revicedFootfall: revisedFootfall,
|
|
423
|
-
count:
|
|
416
|
+
count: tempAcc,
|
|
424
417
|
revisedDetail: formattedTaggingData,
|
|
425
418
|
status: 'raised',
|
|
426
419
|
createdByEmail: req?.user?.email,
|
|
427
420
|
createdByUserName: req?.user?.userName,
|
|
428
421
|
createdByRole: req?.user?.role,
|
|
422
|
+
createdAt: new Date(),
|
|
429
423
|
},
|
|
430
424
|
],
|
|
431
425
|
};
|