tango-app-api-infra 3.9.5-vms.9 → 3.9.5-vms.90
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 +3 -2
- package/src/controllers/footfallDirectory.controllers.js +3758 -682
- package/src/dtos/footfallDirectory.dtos.js +188 -60
- package/src/routes/footfallDirectory.routes.js +15 -5
- package/src/services/storeAccuracyIssues.service.js +9 -0
- package/src/validations/footfallDirectory.validation.js +1720 -90
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { chunkArray, download, logger, sendMessageToFIFOQueue, sendMessageToQueue } from 'tango-app-api-middleware';
|
|
2
|
-
import { bulkUpdate, getOpenSearchById, getOpenSearchCount, getOpenSearchData, insertWithId, updateOpenSearchData
|
|
2
|
+
import { bulkUpdate, getOpenSearchById, getOpenSearchCount, getOpenSearchData, insertWithId, updateOpenSearchData } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
3
3
|
import { findOneStore } from '../services/store.service.js';
|
|
4
4
|
import { countDocumnetsCamera } from '../services/camera.service.js';
|
|
5
5
|
import { findOneRevopDownload, upsertRevopDownload } from '../services/revopDownload.service.js';
|
|
6
6
|
import dayjs from 'dayjs';
|
|
7
7
|
import utc from 'dayjs/plugin/utc.js';
|
|
8
8
|
import timezone from 'dayjs/plugin/timezone.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import { findOneClient } from '../services/client.service.js';
|
|
10
|
+
import { findUser, findOneUser } from '../services/user.service.js';
|
|
11
|
+
import { sendPushNotification } from 'tango-app-api-middleware';
|
|
12
|
+
import { findStoreAccuracIssues, upsertStoreAccuracIssues } from '../services/storeAccuracyIssues.service.js';
|
|
11
13
|
dayjs.extend( utc );
|
|
12
14
|
dayjs.extend( timezone );
|
|
13
15
|
|
|
@@ -24,7 +26,7 @@ export async function createTicket( req, res ) {
|
|
|
24
26
|
inputData.userName = req?.user?.userName;
|
|
25
27
|
inputData.email = req?.user?.email;
|
|
26
28
|
inputData.role = req?.user?.role;
|
|
27
|
-
inputData.status = '
|
|
29
|
+
inputData.status = 'Open';
|
|
28
30
|
inputData.duplicateACCount = 0;
|
|
29
31
|
inputData.employeeACCount = 0;
|
|
30
32
|
inputData.houseKeepingACCount = 0;
|
|
@@ -60,6 +62,643 @@ export async function createTicket( req, res ) {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
export async function createinternalTicket( req, res ) {
|
|
66
|
+
try {
|
|
67
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
68
|
+
let inputData = req.body;
|
|
69
|
+
let record = {
|
|
70
|
+
|
|
71
|
+
storeId: inputData.storeId,
|
|
72
|
+
type: 'internal',
|
|
73
|
+
dateString: inputData.dateString,
|
|
74
|
+
storeName: inputData?.storeName,
|
|
75
|
+
ticketName: inputData.ticketName || 'footfall-directory',
|
|
76
|
+
footfallCount: inputData.footfallCount,
|
|
77
|
+
clientId: inputData?.clientId,
|
|
78
|
+
ticketId: 'TE_FDT_' + new Date().valueOf(),
|
|
79
|
+
createdAt: new Date(),
|
|
80
|
+
updatedAt: new Date(),
|
|
81
|
+
status: 'Open',
|
|
82
|
+
comments: inputData?.comments || '',
|
|
83
|
+
createdByEmail: req?.user?.email,
|
|
84
|
+
createdByUserName: req?.user?.userName,
|
|
85
|
+
createdByRole: req?.user?.role,
|
|
86
|
+
mappingInfo: [],
|
|
87
|
+
};
|
|
88
|
+
const id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
89
|
+
let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
90
|
+
if ( getExistingOne?.body?._source ) {
|
|
91
|
+
return res.sendError( 'Ticket Already Exists', 500 );
|
|
92
|
+
}
|
|
93
|
+
const insertResult = await insertWithId( openSearch.footfallDirectory, id, record );
|
|
94
|
+
if ( insertResult && insertResult.statusCode === 201 ) {
|
|
95
|
+
return res.sendSuccess( 'Ticket raised successfully' );
|
|
96
|
+
}
|
|
97
|
+
} catch ( error ) {
|
|
98
|
+
const err = error.message || 'Internal Server Error';
|
|
99
|
+
logger.error( { error: error, funtion: 'createinternalTicket' } );
|
|
100
|
+
return res.sendError( err, 500 );
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export async function tangoReviewTicket( req, res ) {
|
|
104
|
+
try {
|
|
105
|
+
const inputData = req.body;
|
|
106
|
+
|
|
107
|
+
logger.info( { inputData, msg: '...........1' } );
|
|
108
|
+
// get store info by the storeId into mongo db
|
|
109
|
+
const getstoreName = await findOneStore( { storeId: inputData.storeId, status: 'active' }, { storeId: 1, storeName: 1, clientId: 1 } );
|
|
110
|
+
logger.info( { getstoreName, msg: '...........2' } );
|
|
111
|
+
if ( !getstoreName || getstoreName == null ) {
|
|
112
|
+
return res.sendError( 'The store ID is either inActive or not found', 400 );
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// get the footfall count from opensearch
|
|
116
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
117
|
+
const dateString = `${inputData.storeId}_${inputData.dateString}`;
|
|
118
|
+
logger.info( { dateString, msg: '...........3' } );
|
|
119
|
+
const getQuery = {
|
|
120
|
+
query: {
|
|
121
|
+
terms: {
|
|
122
|
+
_id: [ dateString ],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
_source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
126
|
+
sort: [
|
|
127
|
+
{
|
|
128
|
+
date_iso: {
|
|
129
|
+
order: 'desc',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const getFootfallCount = await getOpenSearchData( openSearch.footfall, getQuery );
|
|
136
|
+
logger.info( { getFootfallCount, msg: '...........4' } );
|
|
137
|
+
const hits = getFootfallCount?.body?.hits?.hits || [];
|
|
138
|
+
if ( hits?.[0]?._source?.footfall_count <= 0 ) {
|
|
139
|
+
return res.sendError( 'You can’t create a ticket because this store has 0 footfall data' );
|
|
140
|
+
}
|
|
141
|
+
logger.info( { hits, msg: '...........5' } );
|
|
142
|
+
// get category details from the client level configuration
|
|
143
|
+
const getConfig = await findOneClient( { clientId: getstoreName.clientId }, { footfallDirectoryConfigs: 1 } );
|
|
144
|
+
logger.info( { getConfig, msg: '...........6' } );
|
|
145
|
+
if ( !getConfig || getConfig == null ) {
|
|
146
|
+
return res.sendError( 'The Client ID is either not configured or not found', 400 );
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let findQuery = {
|
|
150
|
+
size: 10000,
|
|
151
|
+
query: {
|
|
152
|
+
bool: {
|
|
153
|
+
must: [
|
|
154
|
+
{
|
|
155
|
+
term: {
|
|
156
|
+
'storeId.keyword': inputData.storeId,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
term: {
|
|
161
|
+
'dateString': inputData.dateString,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
169
|
+
logger.info( { findTicket, msg: '...........7' } );
|
|
170
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
171
|
+
logger.info( { Ticket, msg: '...........8' } );
|
|
172
|
+
if ( Ticket.length === 0 ) {
|
|
173
|
+
return res.sendError( 'Ticket not found', 400 );
|
|
174
|
+
}
|
|
175
|
+
const getTicket = {
|
|
176
|
+
size: 10000,
|
|
177
|
+
query: {
|
|
178
|
+
bool: {
|
|
179
|
+
must: [
|
|
180
|
+
{
|
|
181
|
+
term: {
|
|
182
|
+
'storeId.keyword': inputData.storeId,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
term: {
|
|
187
|
+
'dateString': inputData.dateString,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
if ( Ticket[0]?._source?.type !== 'internal' ) {
|
|
196
|
+
getTicket.query.bool.must.push(
|
|
197
|
+
{
|
|
198
|
+
nested: {
|
|
199
|
+
path: 'mappingInfo',
|
|
200
|
+
query: {
|
|
201
|
+
bool: {
|
|
202
|
+
must: [
|
|
203
|
+
{
|
|
204
|
+
term: {
|
|
205
|
+
'mappingInfo.type': 'tangoreview',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const getFootfallticketData = await getOpenSearchData( openSearch.footfallDirectory, getTicket );
|
|
217
|
+
const ticketData = getFootfallticketData?.body?.hits?.hits;
|
|
218
|
+
logger.info( { ticketData, msg: '...........9' } );
|
|
219
|
+
if ( !ticketData || ticketData?.length == 0 ) {
|
|
220
|
+
return res.sendError( 'You don’t have any tagged images right now', 400 );
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const record = {
|
|
224
|
+
|
|
225
|
+
status: parseInt( inputData?.mappingInfo?.[0]?.revicedPerc || 0 ) < parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview|| 0 ) ? 'Open - Accuracy Issue' : 'Closed',
|
|
226
|
+
revicedFootfall: inputData.mappingInfo?.[0]?.revicedFootfall,
|
|
227
|
+
revicedPerc: inputData.mappingInfo?.[0]?.revicedPerc,
|
|
228
|
+
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
229
|
+
createdByEmail: req?.user?.email,
|
|
230
|
+
createdByUserName: req?.user?.userName,
|
|
231
|
+
createdByRole: req?.user?.role,
|
|
232
|
+
|
|
233
|
+
};
|
|
234
|
+
logger.info( { record, msg: '...........10' } );
|
|
235
|
+
|
|
236
|
+
// Retrieve client footfallDirectoryConfigs revision
|
|
237
|
+
let isAutoCloseEnable = getConfig.footfallDirectoryConfigs.isAutoCloseEnable;
|
|
238
|
+
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
|
|
239
|
+
logger.info( { isAutoCloseEnable, autoCloseAccuracy, msg: '...........11' } );
|
|
240
|
+
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
241
|
+
logger.info( { getNumber, msg: '...........12' } );
|
|
242
|
+
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
243
|
+
logger.info( { autoCloseAccuracyValue, msg: '...........13' } );
|
|
244
|
+
let revisedPercentage = inputData.mappingInfo?.revicedPerc;
|
|
245
|
+
logger.info( { revisedPercentage, msg: '...........14' } );
|
|
246
|
+
const revised = Number( revisedPercentage?.split( '%' )[0] );
|
|
247
|
+
logger.info( { revised, msg: '...........15' } );
|
|
248
|
+
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] );
|
|
249
|
+
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
250
|
+
logger.info( { tangoReview, msg: '...........16' } );
|
|
251
|
+
if (
|
|
252
|
+
isAutoCloseEnable === true &&
|
|
253
|
+
revisedPercentage >= autoCloseAccuracyValue
|
|
254
|
+
) {
|
|
255
|
+
logger.info( { isAutoCloseEnable, revisedPercentage, autoCloseAccuracyValue, msg: '...........17' } );
|
|
256
|
+
record.status = 'Closed';
|
|
257
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
258
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
259
|
+
logger.info( { msg: '...........18' } );
|
|
260
|
+
const temp = record.mappingInfo
|
|
261
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
262
|
+
.map( ( item ) => ( {
|
|
263
|
+
...item,
|
|
264
|
+
|
|
265
|
+
mode: inputData.mappingInfo?.mode,
|
|
266
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
267
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
268
|
+
count: inputData.mappingInfo?.count,
|
|
269
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
270
|
+
status: 'Closed',
|
|
271
|
+
createdByEmail: req?.user?.email,
|
|
272
|
+
createdByUserName: req?.user?.userName,
|
|
273
|
+
createdByRole: req?.user?.role,
|
|
274
|
+
createdAt: new Date(),
|
|
275
|
+
} ) );
|
|
276
|
+
|
|
277
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
278
|
+
...temp ];
|
|
279
|
+
// If updating the mapping config to mark [i].status as 'Closed'
|
|
280
|
+
// Make sure all relevant mappingInfo items of type 'approve' are set to status 'Closed'
|
|
281
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
282
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
283
|
+
return {
|
|
284
|
+
...item,
|
|
285
|
+
status: item.type === 'approve'? 'Tango Review Done':'Closed',
|
|
286
|
+
};
|
|
287
|
+
} );
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
record.mappingInfo.push(
|
|
291
|
+
{
|
|
292
|
+
type: 'finalRevision',
|
|
293
|
+
mode: inputData.mappingInfo?.mode,
|
|
294
|
+
revicedFootfall: revisedFootfall,
|
|
295
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
296
|
+
count: inputData.mappingInfo?.count,
|
|
297
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
298
|
+
status: 'Closed',
|
|
299
|
+
createdByEmail: req?.user?.email,
|
|
300
|
+
createdByUserName: req?.user?.userName,
|
|
301
|
+
createdByRole: req?.user?.role,
|
|
302
|
+
createdAt: new Date(),
|
|
303
|
+
},
|
|
304
|
+
);
|
|
305
|
+
} else if ( revised < tangoReview ) {
|
|
306
|
+
logger.info( { msg: '...........19', revised, tangoReview, status: record.status } );
|
|
307
|
+
// If ticket is closed, do not proceed with revision mapping
|
|
308
|
+
|
|
309
|
+
record.status = 'Open - Accuracy Issue';
|
|
310
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
311
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
312
|
+
logger.info( { msg: '...........20', status: record.status } );
|
|
313
|
+
const temp = record.mappingInfo
|
|
314
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
315
|
+
.map( ( item ) => ( {
|
|
316
|
+
...item,
|
|
317
|
+
mode: inputData.mappingInfo?.mode,
|
|
318
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
319
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
320
|
+
reviced: parseInt( inputData.mappingInfo?.revicedPerc ),
|
|
321
|
+
count: inputData.mappingInfo?.count,
|
|
322
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
323
|
+
status: 'Open - Accuracy Issue',
|
|
324
|
+
createdByEmail: req?.user?.email,
|
|
325
|
+
createdByUserName: req?.user?.userName,
|
|
326
|
+
createdByRole: req?.user?.role,
|
|
327
|
+
createdAt: new Date(),
|
|
328
|
+
} ) );
|
|
329
|
+
|
|
330
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
331
|
+
...temp ];
|
|
332
|
+
|
|
333
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
334
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
335
|
+
return {
|
|
336
|
+
...item,
|
|
337
|
+
status: item.type === 'approve'? 'Tango Review Done': item.type === 'tangoreview'? 'Open - Accuracy Issue': 'Closed',
|
|
338
|
+
};
|
|
339
|
+
} );
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
record.mappingInfo.push(
|
|
343
|
+
{
|
|
344
|
+
type: 'finalRevision',
|
|
345
|
+
mode: inputData.mappingInfo?.mode,
|
|
346
|
+
revicedFootfall: inputData.mappingInfo?.revisedFootfall,
|
|
347
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
348
|
+
reviced: parseInt( inputData.mappingInfo?.revicedPerc ),
|
|
349
|
+
count: inputData.mappingInfo?.count,
|
|
350
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
351
|
+
status: 'Closed',
|
|
352
|
+
createdByEmail: req?.user?.email,
|
|
353
|
+
createdByUserName: req?.user?.userName,
|
|
354
|
+
createdByRole: req?.user?.role,
|
|
355
|
+
createdAt: new Date(),
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
363
|
+
logger.info( { msg: '...........21', status: record.status } );
|
|
364
|
+
const temp = record.mappingInfo
|
|
365
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
366
|
+
.map( ( item ) => ( {
|
|
367
|
+
...item,
|
|
368
|
+
mode: inputData.mappingInfo?.mode,
|
|
369
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
370
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
371
|
+
reviced: parseInt( inputData.mappingInfo?.revicedPerc ),
|
|
372
|
+
count: inputData.mappingInfo?.count,
|
|
373
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
374
|
+
status: 'Closed',
|
|
375
|
+
createdByEmail: req?.user?.email,
|
|
376
|
+
createdByUserName: req?.user?.userName,
|
|
377
|
+
createdByRole: req?.user?.role,
|
|
378
|
+
createdAt: new Date(),
|
|
379
|
+
} ) );
|
|
380
|
+
|
|
381
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
382
|
+
...temp ];
|
|
383
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
384
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
385
|
+
return {
|
|
386
|
+
...item,
|
|
387
|
+
status: item.type === 'approve'?'Tango Review Done': 'Closed',
|
|
388
|
+
};
|
|
389
|
+
} );
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
record.mappingInfo.push(
|
|
393
|
+
{
|
|
394
|
+
type: 'finalRevision',
|
|
395
|
+
mode: inputData.mappingInfo?.mode,
|
|
396
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
397
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
398
|
+
reviced: parseInt( inputData.mappingInfo?.revicedPerc ),
|
|
399
|
+
count: inputData.mappingInfo?.count,
|
|
400
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
401
|
+
status: 'Closed',
|
|
402
|
+
createdByEmail: req?.user?.email,
|
|
403
|
+
createdByUserName: req?.user?.userName,
|
|
404
|
+
createdByRole: req?.user?.role,
|
|
405
|
+
createdAt: new Date(),
|
|
406
|
+
},
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if ( Ticket[0]?._source?.type==='store' ) {
|
|
411
|
+
let findTagging = Ticket[0]?._source?.mappingInfo.filter( ( data ) => data.type==='tagging' );
|
|
412
|
+
if ( findTagging?.length>0&&findTagging[0].createdByEmail!='' ) {
|
|
413
|
+
let userData = await findOneUser( { email: findTagging[0]?.createdByEmail } );
|
|
414
|
+
let title = `Received response for the Footfall ticket raised.`;
|
|
415
|
+
let createdOn = dayjs( Ticket[0]?._source?.dateString ).format( 'DD MMM YYYY' );
|
|
416
|
+
let description = `Raised on ${createdOn}`;
|
|
417
|
+
|
|
418
|
+
let Data = {
|
|
419
|
+
'title': title,
|
|
420
|
+
'body': description,
|
|
421
|
+
'type': 'closed',
|
|
422
|
+
'date': Ticket[0]?._source?.dateString,
|
|
423
|
+
'storeId': Ticket[0]?._source?.storeId,
|
|
424
|
+
'clientId': Ticket[0]?._source?.clientId,
|
|
425
|
+
'ticketId': Ticket[0]?._source?.ticketId,
|
|
426
|
+
};
|
|
427
|
+
if ( userData && userData.fcmToken ) {
|
|
428
|
+
const fcmToken = userData.fcmToken;
|
|
429
|
+
await sendPushNotification( title, description, fcmToken, Data );
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// return;
|
|
434
|
+
|
|
435
|
+
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
436
|
+
let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
437
|
+
if ( inputData.ticketType === 'internal' &&!getExistingOne?.body?._source ) {
|
|
438
|
+
id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
442
|
+
|
|
443
|
+
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
444
|
+
if ( ( record.status === 'Closed' || record.status === 'Open - Accuracy Issue' ) && inputData.ticketType !== 'internal' ) {
|
|
445
|
+
const query = {
|
|
446
|
+
storeId: inputData?.storeId,
|
|
447
|
+
isVideoStream: true,
|
|
448
|
+
};
|
|
449
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
450
|
+
|
|
451
|
+
// Get all tempIds from revopInfo response
|
|
452
|
+
const temp = [];
|
|
453
|
+
inputData?.mappingInfo?.revisedDetail?.map( ( hit ) => temp.push( { tempId: hit?.tempId } ) ) || [];
|
|
454
|
+
logger.info( { msg: '...........22', temp, revisedDetails: inputData?.revisedDetail } );
|
|
455
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
456
|
+
|
|
457
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
458
|
+
if ( isSendMessge === true ) {
|
|
459
|
+
logger.info( '....1' );
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return res.sendSuccess( 'Ticket closed successfully' );
|
|
463
|
+
} else {
|
|
464
|
+
return res.sendError( 'Internal Server Error', 500 );
|
|
465
|
+
}
|
|
466
|
+
} catch ( error ) {
|
|
467
|
+
const err = error.message || 'Internal Server Error';
|
|
468
|
+
logger.error( { error: error, funtion: 'tangoReviewTicket' } );
|
|
469
|
+
return res.sendError( err, 500 );
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export async function tangoReviewAccuracyClosedTicket( req, res ) {
|
|
474
|
+
try {
|
|
475
|
+
const inputData = req.body;
|
|
476
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
477
|
+
|
|
478
|
+
let findQuery = {
|
|
479
|
+
size: 10000,
|
|
480
|
+
query: {
|
|
481
|
+
bool: {
|
|
482
|
+
must: [
|
|
483
|
+
{
|
|
484
|
+
term: {
|
|
485
|
+
'storeId.keyword': inputData.storeId,
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
term: {
|
|
490
|
+
'dateString': inputData.dateString,
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
498
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
499
|
+
|
|
500
|
+
if ( Ticket.length === 0 ) {
|
|
501
|
+
return res.sendError( 'Ticket not found', 400 );
|
|
502
|
+
}
|
|
503
|
+
const getTicket = {
|
|
504
|
+
size: 10000,
|
|
505
|
+
query: {
|
|
506
|
+
bool: {
|
|
507
|
+
must: [
|
|
508
|
+
{
|
|
509
|
+
term: {
|
|
510
|
+
'storeId.keyword': inputData.storeId,
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
term: {
|
|
515
|
+
'dateString': inputData.dateString,
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
],
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
if ( Ticket[0]?._source?.type !== 'internal' ) {
|
|
524
|
+
getTicket.query.bool.must.push(
|
|
525
|
+
{
|
|
526
|
+
nested: {
|
|
527
|
+
path: 'mappingInfo',
|
|
528
|
+
query: {
|
|
529
|
+
bool: {
|
|
530
|
+
must: [
|
|
531
|
+
{
|
|
532
|
+
term: {
|
|
533
|
+
'mappingInfo.type': 'tangoreview',
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
],
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const getFootfallticketData = await getOpenSearchData( openSearch.footfallDirectory, getTicket );
|
|
545
|
+
const ticketData = getFootfallticketData?.body?.hits?.hits;
|
|
546
|
+
|
|
547
|
+
if ( !ticketData || ticketData?.length == 0 ) {
|
|
548
|
+
return res.sendError( 'You don’t have any tagged images right now', 400 );
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const record = {
|
|
552
|
+
|
|
553
|
+
status: 'Closed - Accuracy Issue',
|
|
554
|
+
createdByEmail: req?.user?.email,
|
|
555
|
+
createdByUserName: req?.user?.userName,
|
|
556
|
+
createdByRole: req?.user?.role,
|
|
557
|
+
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
561
|
+
|
|
562
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
563
|
+
const temp = record.mappingInfo
|
|
564
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
565
|
+
.map( ( item ) => ( {
|
|
566
|
+
...item,
|
|
567
|
+
status: 'Closed - Accuracy Issue',
|
|
568
|
+
createdByEmail: req?.user?.email,
|
|
569
|
+
createdByUserName: req?.user?.userName,
|
|
570
|
+
createdByRole: req?.user?.role,
|
|
571
|
+
comments: inputData?.comments || '',
|
|
572
|
+
subComments: inputData?.subComments ||'',
|
|
573
|
+
} ) );
|
|
574
|
+
|
|
575
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
576
|
+
...temp ];
|
|
577
|
+
// if ( Array.isArray( record.mappingInfo ) ) {
|
|
578
|
+
// record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
579
|
+
// return {
|
|
580
|
+
// ...item,
|
|
581
|
+
// status: 'Closed',
|
|
582
|
+
// };
|
|
583
|
+
// } );
|
|
584
|
+
// }
|
|
585
|
+
|
|
586
|
+
record.mappingInfo.push(
|
|
587
|
+
{
|
|
588
|
+
type: 'finalRevision',
|
|
589
|
+
mode: 'web',
|
|
590
|
+
revicedFootfall: temp?.[0]?.revicedFootfall,
|
|
591
|
+
revicedPerc: temp?.[0].revicedPerc,
|
|
592
|
+
count: temp?.[0].count,
|
|
593
|
+
revisedDetail: temp?.[0]?.revisedDetail,
|
|
594
|
+
status: 'Closed',
|
|
595
|
+
createdByEmail: req?.user?.email,
|
|
596
|
+
createdByUserName: req?.user?.userName,
|
|
597
|
+
createdByRole: req?.user?.role,
|
|
598
|
+
createdAt: new Date(),
|
|
599
|
+
comments: inputData?.comments || '',
|
|
600
|
+
subComments: inputData?.subComments || '',
|
|
601
|
+
},
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
// return;
|
|
607
|
+
|
|
608
|
+
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
609
|
+
|
|
610
|
+
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
611
|
+
|
|
612
|
+
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
613
|
+
const query = {
|
|
614
|
+
storeId: inputData?.storeId,
|
|
615
|
+
isVideoStream: true,
|
|
616
|
+
};
|
|
617
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
618
|
+
const revopInfoQuery = {
|
|
619
|
+
size: 10000,
|
|
620
|
+
query: {
|
|
621
|
+
bool: {
|
|
622
|
+
must: [
|
|
623
|
+
{
|
|
624
|
+
term: {
|
|
625
|
+
'storeId.keyword': inputData.storeId,
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
term: {
|
|
630
|
+
'dateString': inputData.dateString,
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
term: {
|
|
635
|
+
'isParent': false,
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
term: {
|
|
640
|
+
isChecked: true,
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
_source: [ 'tempId' ],
|
|
647
|
+
|
|
648
|
+
};
|
|
649
|
+
const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
|
|
650
|
+
// Get all tempIds from revopInfo response
|
|
651
|
+
const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
652
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
653
|
+
const managerEyeZoneQuery = {
|
|
654
|
+
size: 1,
|
|
655
|
+
query: {
|
|
656
|
+
bool: {
|
|
657
|
+
must: [
|
|
658
|
+
{
|
|
659
|
+
term: {
|
|
660
|
+
'storeId.keyword': inputData.storeId,
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
term: {
|
|
665
|
+
'storeDate': inputData.dateString,
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
],
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
_source: [ 'originalToTrackerCustomerMapping' ],
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
// Query the managerEyeZone index for the matching document
|
|
675
|
+
const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
|
|
676
|
+
const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
|
|
677
|
+
// Extract originalToTrackerCustomerMapping if it exists
|
|
678
|
+
const mapping =
|
|
679
|
+
managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
|
|
680
|
+
managerEyeZoneHit.originalToTrackerCustomerMapping :
|
|
681
|
+
{};
|
|
682
|
+
|
|
683
|
+
// Find tempIds that exist in both revopInfo results and manager mapping
|
|
684
|
+
const temp = [];
|
|
685
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
|
|
686
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
687
|
+
if ( isSendMessge == true ) {
|
|
688
|
+
logger.info( '....1' );
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return res.sendSuccess( 'Ticket closed successfully' );
|
|
692
|
+
} else {
|
|
693
|
+
return res.sendError( 'Internal Server Error', 500 );
|
|
694
|
+
}
|
|
695
|
+
} catch ( error ) {
|
|
696
|
+
const err = error.message || 'Internal Server Error';
|
|
697
|
+
logger.error( { error: error, funtion: 'tangoReviewAccuracyClosedTicket' } );
|
|
698
|
+
return res.sendError( err, 500 );
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
63
702
|
async function bulkUpdateStatusToPending( indexName, inputData ) {
|
|
64
703
|
const bulkBody = [];
|
|
65
704
|
|
|
@@ -195,7 +834,6 @@ export async function ticketSummary1( req, res ) {
|
|
|
195
834
|
|
|
196
835
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
197
836
|
const aggs = getData?.body?.aggregations;
|
|
198
|
-
logger.info( { aggs: aggs, body: getData?.body } );
|
|
199
837
|
|
|
200
838
|
const result = {
|
|
201
839
|
totalTickets: aggs.totalTicketCount.value,
|
|
@@ -218,426 +856,2424 @@ export async function ticketSummary1( req, res ) {
|
|
|
218
856
|
|
|
219
857
|
export async function ticketSummary( req, res ) {
|
|
220
858
|
try {
|
|
859
|
+
const inputData = req.query;
|
|
860
|
+
|
|
221
861
|
let result = '';
|
|
862
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
222
863
|
const userInfo = req.user;
|
|
223
|
-
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name =='reviewer' && ( m.isAdd==true || m.isEdit==true ) ) ) );
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
averageAccuracyOverAll: 0,
|
|
229
|
-
openTickets: 0,
|
|
230
|
-
openInfraIssues: 0,
|
|
231
|
-
inprogress: 0,
|
|
232
|
-
closedTickets: 0,
|
|
233
|
-
ticketAccuracyAbove: '0%',
|
|
234
|
-
ticketAccuracyBelow: '0%',
|
|
235
|
-
};
|
|
236
|
-
} else {
|
|
237
|
-
result = req.user.role === 'superadmin'?
|
|
238
|
-
{
|
|
239
|
-
totalTickets: 0,
|
|
240
|
-
openTickets: 0,
|
|
241
|
-
inprogress: 0,
|
|
242
|
-
closedTickets: 0,
|
|
243
|
-
dueToday: 0,
|
|
244
|
-
Expired: 0,
|
|
245
|
-
underTangoReview: 0,
|
|
246
|
-
avgTicket: '0%',
|
|
247
|
-
avgAccuracy: '0%',
|
|
248
|
-
} :
|
|
249
|
-
req.user.role === 'user'? 'NA':
|
|
250
|
-
ticketsFeature?
|
|
251
|
-
{
|
|
252
|
-
totalTickets: 0,
|
|
253
|
-
openTickets: 0,
|
|
254
|
-
inprogress: 0,
|
|
255
|
-
closedTickets: 0,
|
|
256
|
-
dueToday: 0,
|
|
257
|
-
Expired: 0,
|
|
258
|
-
avgTicket: '0%',
|
|
259
|
-
avgAccuracy: '0%',
|
|
260
|
-
}: 'NA';
|
|
864
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
865
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
866
|
+
const getConfig = await findOneClient( { clientId: inputData?.clientId }, { footfallDirectoryConfigs: 1 } );
|
|
867
|
+
if ( !getConfig || getConfig ==null ) {
|
|
868
|
+
return res.sendError( 'this client not configured against footfall directory', 400 );
|
|
261
869
|
}
|
|
870
|
+
inputData.clientId = inputData?.clientId?.split( ',' );
|
|
871
|
+
// const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
872
|
+
if ( req?.user?.userType === 'tango' ) {
|
|
873
|
+
switch ( inputData?.tangoType ) {
|
|
874
|
+
case 'store':
|
|
875
|
+
const storeQuery = {
|
|
876
|
+
size: 0,
|
|
877
|
+
query: {
|
|
878
|
+
bool: {
|
|
879
|
+
must: [
|
|
880
|
+
{
|
|
881
|
+
'range': {
|
|
882
|
+
'dateString': {
|
|
883
|
+
'gte': inputData?.fromDate,
|
|
884
|
+
'lte': inputData?.toDate,
|
|
885
|
+
'format': 'yyyy-MM-dd',
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
},
|
|
262
889
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
890
|
+
{
|
|
891
|
+
terms: {
|
|
892
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
893
|
+
inputData.clientId :
|
|
894
|
+
[ inputData.clientId ],
|
|
895
|
+
},
|
|
270
896
|
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
274
|
-
const inputData = req.query;
|
|
275
|
-
const limit = inputData.limit || 10;
|
|
276
|
-
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
277
|
-
const order = inputData?.sortOrder || -1;
|
|
897
|
+
},
|
|
278
898
|
|
|
279
|
-
|
|
899
|
+
{
|
|
900
|
+
range: {
|
|
901
|
+
reviced: {
|
|
902
|
+
lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
|
|
903
|
+
}
|
|
904
|
+
,
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
nested: {
|
|
909
|
+
path: 'mappingInfo',
|
|
910
|
+
query: {
|
|
911
|
+
bool: {
|
|
912
|
+
must: [
|
|
913
|
+
{
|
|
914
|
+
term: {
|
|
915
|
+
'mappingInfo.type': 'tangoreview',
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
},
|
|
920
|
+
},
|
|
921
|
+
},
|
|
922
|
+
},
|
|
280
923
|
|
|
924
|
+
],
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
930
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
931
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
932
|
+
// Remove any previous mappingInfo.status term
|
|
933
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
934
|
+
if ( nested ) {
|
|
935
|
+
// filter out all mappingInfo.status
|
|
936
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
937
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
938
|
+
} );
|
|
939
|
+
// add desired status
|
|
940
|
+
nested.nested.query.bool.must.push( {
|
|
941
|
+
term: {
|
|
942
|
+
'mappingInfo.status': statusValue,
|
|
943
|
+
},
|
|
944
|
+
} );
|
|
945
|
+
}
|
|
946
|
+
return q;
|
|
947
|
+
}
|
|
281
948
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
949
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
950
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
951
|
+
|
|
952
|
+
// locate nested section
|
|
953
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
954
|
+
|
|
955
|
+
if ( nested ) {
|
|
956
|
+
// remove old status filters
|
|
957
|
+
nested.nested.query.bool.must =
|
|
958
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
959
|
+
|
|
960
|
+
// add new filters
|
|
961
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return {
|
|
965
|
+
...q,
|
|
966
|
+
size: 0,
|
|
967
|
+
aggs: {
|
|
968
|
+
avg_value: {
|
|
969
|
+
avg: 'reviced',
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
};
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
// Get OpenSearch connection
|
|
977
|
+
|
|
978
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
979
|
+
|
|
980
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
981
|
+
let totalTickets = 0;
|
|
982
|
+
|
|
983
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
984
|
+
|
|
985
|
+
allQuery.size = 0;
|
|
986
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
987
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
988
|
+
|
|
989
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
990
|
+
let openTickets = 0;
|
|
991
|
+
|
|
992
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
993
|
+
otQ.size = 0;
|
|
994
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
995
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
996
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1000
|
+
let openInfraIssues = 0;
|
|
1001
|
+
|
|
1002
|
+
let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open - Accuracy Issue' );
|
|
1003
|
+
oiQ.size = 0;
|
|
1004
|
+
const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
|
|
1005
|
+
openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
|
|
1006
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1007
|
+
let inprogress = 0;
|
|
1008
|
+
|
|
1009
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1010
|
+
ipQ.size = 0;
|
|
1011
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1012
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1013
|
+
|
|
1014
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1015
|
+
let closedTickets = 0;
|
|
1016
|
+
|
|
1017
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1018
|
+
clQ.size = 0;
|
|
1019
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1020
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1021
|
+
// Average revisedPerc (for all tangoreview)
|
|
1022
|
+
let averageAccuracyOverAll = 0;
|
|
1023
|
+
|
|
1024
|
+
let avgQ = buildAggStoreQuery( baseStoreQuery );
|
|
1025
|
+
const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
|
|
1026
|
+
averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1027
|
+
|
|
1028
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1029
|
+
let ticketAccuracyAbove = 0;
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1033
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1034
|
+
{
|
|
1035
|
+
range: {
|
|
1036
|
+
reviced: {
|
|
1037
|
+
gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
|
|
1038
|
+
},
|
|
1039
|
+
},
|
|
1040
|
+
},
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
] );
|
|
1044
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1045
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1046
|
+
|
|
1047
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1048
|
+
let ticketAccuracyBelow = 0;
|
|
1049
|
+
|
|
1050
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1051
|
+
{
|
|
1052
|
+
range: {
|
|
1053
|
+
reviced: {
|
|
1054
|
+
lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
|
|
1059
|
+
] );
|
|
1060
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1061
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1062
|
+
|
|
1063
|
+
// Final result object
|
|
1064
|
+
result = {
|
|
1065
|
+
totalTickets,
|
|
1066
|
+
averageAccuracyOverAll: averageAccuracyOverAll+'%',
|
|
1067
|
+
openTickets,
|
|
1068
|
+
openInfraIssues,
|
|
1069
|
+
inprogress,
|
|
1070
|
+
closedTickets,
|
|
1071
|
+
ticketAccuracyAbove: ticketAccuracyAbove+'%',
|
|
1072
|
+
ticketAccuracyBelow: ticketAccuracyBelow+'%',
|
|
1073
|
+
};
|
|
1074
|
+
break;
|
|
1075
|
+
case 'internal':
|
|
1076
|
+
const internalQuery = {
|
|
1077
|
+
size: 0,
|
|
1078
|
+
query: {
|
|
1079
|
+
bool: {
|
|
1080
|
+
must: [
|
|
1081
|
+
{
|
|
1082
|
+
'range': {
|
|
1083
|
+
'dateString': {
|
|
1084
|
+
'gte': inputData?.fromDate,
|
|
1085
|
+
'lte': inputData?.toDate,
|
|
1086
|
+
'format': 'yyyy-MM-dd',
|
|
1087
|
+
},
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
terms: {
|
|
1092
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1093
|
+
inputData.clientId :
|
|
1094
|
+
[ inputData.clientId ],
|
|
1095
|
+
},
|
|
1096
|
+
|
|
1097
|
+
},
|
|
1098
|
+
|
|
1099
|
+
],
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
const internalOpen = {
|
|
1105
|
+
size: 0,
|
|
1106
|
+
query: {
|
|
1107
|
+
bool: {
|
|
1108
|
+
must: [
|
|
1109
|
+
{
|
|
1110
|
+
'range': {
|
|
1111
|
+
'dateString': {
|
|
1112
|
+
'gte': inputData?.fromDate,
|
|
1113
|
+
'lte': inputData?.toDate,
|
|
1114
|
+
'format': 'yyyy-MM-dd',
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
terms: {
|
|
1120
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1121
|
+
inputData.clientId :
|
|
1122
|
+
[ inputData.clientId ],
|
|
1123
|
+
},
|
|
1124
|
+
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
terms: {
|
|
1128
|
+
'status.keyword': [ 'Open', 'Raised' ],
|
|
1129
|
+
},
|
|
1130
|
+
|
|
1131
|
+
},
|
|
1132
|
+
|
|
1133
|
+
],
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1139
|
+
function buildInternalQueryWithStatus( baseQuery, statusValue ) {
|
|
1140
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1141
|
+
// Check if nested query exists, if not create it
|
|
1142
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1143
|
+
if ( !nested ) {
|
|
1144
|
+
// Create nested query structure
|
|
1145
|
+
nested = {
|
|
1146
|
+
nested: {
|
|
1147
|
+
path: 'mappingInfo',
|
|
1148
|
+
query: {
|
|
1149
|
+
bool: {
|
|
1150
|
+
must: [],
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
};
|
|
1155
|
+
q.query.bool.must.push( nested );
|
|
1156
|
+
}
|
|
1157
|
+
// filter out all mappingInfo.status
|
|
1158
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1159
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1160
|
+
} );
|
|
1161
|
+
// add desired status
|
|
1162
|
+
nested.nested.query.bool.must.push( {
|
|
1163
|
+
term: {
|
|
1164
|
+
'mappingInfo.status': statusValue,
|
|
1165
|
+
},
|
|
1166
|
+
} );
|
|
1167
|
+
return q;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const buildAggInternalQuery = ( baseQuery, filters = [] ) => {
|
|
1171
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1172
|
+
|
|
1173
|
+
// locate nested section
|
|
1174
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1175
|
+
|
|
1176
|
+
if ( nested ) {
|
|
1177
|
+
// remove old status filters
|
|
1178
|
+
nested.nested.query.bool.must =
|
|
1179
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1180
|
+
|
|
1181
|
+
// add new filters
|
|
1182
|
+
q.query.bool.must.push( ...filters );
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
return {
|
|
1186
|
+
...q,
|
|
1187
|
+
size: 0,
|
|
1188
|
+
aggs: {
|
|
1189
|
+
avg_value: {
|
|
1190
|
+
avg: {
|
|
1191
|
+
field: 'reviced',
|
|
1192
|
+
},
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1195
|
+
};
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
// Get OpenSearch connection
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
const baseInternalQuery = JSON.parse( JSON.stringify( internalQuery ) );
|
|
1203
|
+
|
|
1204
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1205
|
+
let totalInternalTickets = 0;
|
|
1206
|
+
|
|
1207
|
+
let allInternalQuery = JSON.parse( JSON.stringify( baseInternalQuery ) );
|
|
1208
|
+
|
|
1209
|
+
allInternalQuery.size = 0;
|
|
1210
|
+
const totalInternalResp = await getOpenSearchData( openSearch.footfallDirectory, allInternalQuery );
|
|
1211
|
+
totalInternalTickets = totalInternalResp?.body?.hits?.total?.value || 0;
|
|
1212
|
+
logger.info( { totalInternalResp } );
|
|
1213
|
+
|
|
1214
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1215
|
+
let openInternalTickets = 0;
|
|
1216
|
+
|
|
1217
|
+
// let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
|
|
1218
|
+
// otQInternal.size = 0;
|
|
1219
|
+
const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, internalOpen );
|
|
1220
|
+
openInternalTickets = openInternalResp?.body?.hits?.total?.value || 0;
|
|
1221
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1225
|
+
let openInternalInfraIssues = 0;
|
|
1226
|
+
|
|
1227
|
+
let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open - Accuracy Issue' );
|
|
1228
|
+
oiQinternal.size = 0;
|
|
1229
|
+
const infraInternalResp = await getOpenSearchData( openSearch.footfallDirectory, oiQinternal );
|
|
1230
|
+
openInternalInfraIssues = infraInternalResp?.body?.hits?.total?.value || 0;
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1234
|
+
let inprogressIntrenal = 0;
|
|
1235
|
+
|
|
1236
|
+
let ipQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'In-Progress' );
|
|
1237
|
+
ipQInternal.size = 0;
|
|
1238
|
+
const ipInternalResp = await getOpenSearchData( openSearch.footfallDirectory, ipQInternal );
|
|
1239
|
+
inprogressIntrenal = ipInternalResp?.body?.hits?.total?.value || 0;
|
|
1240
|
+
|
|
1241
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1242
|
+
let closedInternalTickets = 0;
|
|
1243
|
+
|
|
1244
|
+
let clQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Closed' );
|
|
1245
|
+
clQInternal.size = 0;
|
|
1246
|
+
const clInternalResp = await getOpenSearchData( openSearch.footfallDirectory, clQInternal );
|
|
1247
|
+
closedInternalTickets = clInternalResp?.body?.hits?.total?.value || 0;
|
|
1248
|
+
// Average revisedPerc (for all tangoreview)
|
|
1249
|
+
let internalAverageAccuracyOverAll = 0;
|
|
1250
|
+
|
|
1251
|
+
let avgQInternal = buildAggInternalQuery( baseInternalQuery );
|
|
1252
|
+
const avgInternalResp = await getOpenSearchData( openSearch.footfallDirectory, avgQInternal );
|
|
1253
|
+
internalAverageAccuracyOverAll = avgInternalResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1254
|
+
|
|
1255
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1256
|
+
let internalTicketAccuracyAbove = 0;
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1260
|
+
let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
|
|
1261
|
+
{
|
|
1262
|
+
range: {
|
|
1263
|
+
reviced: {
|
|
1264
|
+
gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
|
|
1265
|
+
},
|
|
1266
|
+
},
|
|
1267
|
+
},
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
] );
|
|
1271
|
+
const aboveRespInternal = await getOpenSearchData( openSearch.footfallDirectory, aboveQinternal );
|
|
1272
|
+
internalTicketAccuracyAbove = aboveRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1273
|
+
|
|
1274
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1275
|
+
let internalTicketAccuracyBelow = 0;
|
|
1276
|
+
|
|
1277
|
+
let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
|
|
1278
|
+
{
|
|
1279
|
+
range: {
|
|
1280
|
+
reviced: {
|
|
1281
|
+
lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
|
|
1282
|
+
},
|
|
1283
|
+
},
|
|
1284
|
+
},
|
|
1285
|
+
|
|
1286
|
+
] );
|
|
1287
|
+
const belowRespInternal = await getOpenSearchData( openSearch.footfallDirectory, belowQIneranl );
|
|
1288
|
+
internalTicketAccuracyBelow = belowRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1289
|
+
|
|
1290
|
+
// Final result object
|
|
1291
|
+
result = {
|
|
1292
|
+
totalTickets: totalInternalTickets,
|
|
1293
|
+
averageAccuracyOverAll: internalAverageAccuracyOverAll+'%',
|
|
1294
|
+
openTickets: openInternalTickets,
|
|
1295
|
+
openInfraIssues: openInternalInfraIssues,
|
|
1296
|
+
inprogress: inprogressIntrenal,
|
|
1297
|
+
closedTickets: closedInternalTickets,
|
|
1298
|
+
ticketAccuracyAbove: internalTicketAccuracyAbove+'%',
|
|
1299
|
+
ticketAccuracyBelow: internalTicketAccuracyBelow+'%',
|
|
1300
|
+
};
|
|
1301
|
+
break;
|
|
1302
|
+
default: '';
|
|
1303
|
+
}
|
|
1304
|
+
} else if ( req?.user?.userType !== 'tango' ) {
|
|
1305
|
+
if ( ticketsFeature && !ticketsApproveFeature ) {
|
|
1306
|
+
const storeQuery = {
|
|
1307
|
+
size: 0,
|
|
1308
|
+
query: {
|
|
1309
|
+
bool: {
|
|
1310
|
+
must: [
|
|
1311
|
+
{
|
|
1312
|
+
'range': {
|
|
1313
|
+
'dateString': {
|
|
1314
|
+
'gte': inputData?.fromDate,
|
|
1315
|
+
'lte': inputData?.toDate,
|
|
1316
|
+
'format': 'yyyy-MM-dd',
|
|
1317
|
+
},
|
|
1318
|
+
},
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
terms: {
|
|
1322
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1323
|
+
inputData.clientId :
|
|
1324
|
+
[ inputData.clientId ],
|
|
1325
|
+
},
|
|
1326
|
+
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
nested: {
|
|
1330
|
+
path: 'mappingInfo',
|
|
1331
|
+
query: {
|
|
1332
|
+
bool: {
|
|
1333
|
+
must: [
|
|
1334
|
+
{
|
|
1335
|
+
term: {
|
|
1336
|
+
'mappingInfo.type': 'review',
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
],
|
|
1340
|
+
},
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
|
|
1345
|
+
],
|
|
1346
|
+
},
|
|
1347
|
+
},
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1351
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1352
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1353
|
+
// Remove any previous mappingInfo.status term
|
|
1354
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1355
|
+
if ( nested ) {
|
|
1356
|
+
// filter out all mappingInfo.status
|
|
1357
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1358
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1359
|
+
} );
|
|
1360
|
+
// add desired status
|
|
1361
|
+
nested.nested.query.bool.must.push( {
|
|
1362
|
+
term: {
|
|
1363
|
+
'mappingInfo.status': statusValue,
|
|
1364
|
+
},
|
|
1365
|
+
} );
|
|
1366
|
+
}
|
|
1367
|
+
return q;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1371
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1372
|
+
|
|
1373
|
+
// locate nested section
|
|
1374
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1375
|
+
|
|
1376
|
+
if ( nested ) {
|
|
1377
|
+
// remove old status filters
|
|
1378
|
+
nested.nested.query.bool.must =
|
|
1379
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1380
|
+
|
|
1381
|
+
// add new filters
|
|
1382
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
return {
|
|
1386
|
+
...q,
|
|
1387
|
+
size: 0,
|
|
1388
|
+
aggs: {
|
|
1389
|
+
avg_value: {
|
|
1390
|
+
avg: 'mappingInfo.reviced',
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
};
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
// Get OpenSearch connection
|
|
1398
|
+
|
|
1399
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1400
|
+
|
|
1401
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1402
|
+
let totalTickets = 0;
|
|
1403
|
+
|
|
1404
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1405
|
+
|
|
1406
|
+
allQuery.size = 0;
|
|
1407
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1408
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1409
|
+
|
|
1410
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1411
|
+
let openTickets = 0;
|
|
1412
|
+
|
|
1413
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1414
|
+
otQ.size = 0;
|
|
1415
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1416
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1417
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1421
|
+
let inprogress = 0;
|
|
1422
|
+
|
|
1423
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1424
|
+
ipQ.size = 0;
|
|
1425
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1426
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1427
|
+
|
|
1428
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1429
|
+
let closedTickets = 0;
|
|
1430
|
+
|
|
1431
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1432
|
+
clQ.size = 0;
|
|
1433
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1434
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1435
|
+
|
|
1436
|
+
let dueToday = 0;
|
|
1437
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1438
|
+
|
|
1439
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1440
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1441
|
+
// Locate nested mappingInfo query
|
|
1442
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1443
|
+
if ( nestedDue ) {
|
|
1444
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1445
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1446
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1447
|
+
);
|
|
1448
|
+
// Add new dueDate filter
|
|
1449
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1450
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1451
|
+
} );
|
|
1452
|
+
}
|
|
1453
|
+
dueTodayQuery.size = 0;
|
|
1454
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1455
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1456
|
+
|
|
1457
|
+
// filter expired Tickets
|
|
1458
|
+
let expiredTickets = 0;
|
|
1459
|
+
|
|
1460
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1461
|
+
eQ.size = 0;
|
|
1462
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1463
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1464
|
+
|
|
1465
|
+
// Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
|
|
1466
|
+
|
|
1467
|
+
// Build aggregation query for ticket percentage
|
|
1468
|
+
let ticketPercentageAvg = 0;
|
|
1469
|
+
|
|
1470
|
+
let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1471
|
+
avgTicketPercentageQuery.size = 0;
|
|
1472
|
+
avgTicketPercentageQuery.aggs = {
|
|
1473
|
+
avg_ticket_percentage: {
|
|
1474
|
+
avg: {
|
|
1475
|
+
script: {
|
|
1476
|
+
source: `
|
|
1477
|
+
if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
|
|
1478
|
+
doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
|
|
1479
|
+
return (doc['reviced'].value / doc['footfallCount'].value) * 100;
|
|
1480
|
+
} else {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
`,
|
|
1484
|
+
lang: 'painless',
|
|
1485
|
+
},
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
|
|
1491
|
+
|
|
1492
|
+
ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
|
|
1493
|
+
|
|
1494
|
+
logger.info( { avgTicketPercentageResp } );
|
|
1495
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1496
|
+
let ticketAccuracy = 0;
|
|
1497
|
+
|
|
1498
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery );
|
|
1499
|
+
logger.info( { belowQ } );
|
|
1500
|
+
const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1501
|
+
ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1502
|
+
logger.info( { accuracyResp } );
|
|
1503
|
+
// Final result object
|
|
1504
|
+
result = {
|
|
1505
|
+
totalTickets,
|
|
1506
|
+
openTickets,
|
|
1507
|
+
inprogress,
|
|
1508
|
+
closedTickets,
|
|
1509
|
+
dueToday: dueToday,
|
|
1510
|
+
Expired: expiredTickets,
|
|
1511
|
+
avgTicket: ticketPercentageAvg+'%',
|
|
1512
|
+
avgAccuracy: ticketAccuracy+'%',
|
|
1513
|
+
};
|
|
1514
|
+
} else if ( ticketsFeature && ticketsApproveFeature ) {
|
|
1515
|
+
if ( inputData?.permissionType === 'review' ) {
|
|
1516
|
+
const storeQuery = {
|
|
1517
|
+
size: 0,
|
|
1518
|
+
query: {
|
|
1519
|
+
bool: {
|
|
1520
|
+
must: [
|
|
1521
|
+
{
|
|
1522
|
+
'range': {
|
|
1523
|
+
'dateString': {
|
|
1524
|
+
'gte': inputData?.fromDate,
|
|
1525
|
+
'lte': inputData?.toDate,
|
|
1526
|
+
'format': 'yyyy-MM-dd',
|
|
1527
|
+
},
|
|
1528
|
+
},
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
terms: {
|
|
1532
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1533
|
+
inputData.clientId :
|
|
1534
|
+
[ inputData.clientId ],
|
|
1535
|
+
},
|
|
1536
|
+
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
nested: {
|
|
1540
|
+
path: 'mappingInfo',
|
|
1541
|
+
query: {
|
|
1542
|
+
bool: {
|
|
1543
|
+
must: [
|
|
1544
|
+
{
|
|
1545
|
+
term: {
|
|
1546
|
+
'mappingInfo.type': 'review',
|
|
1547
|
+
},
|
|
1548
|
+
},
|
|
1549
|
+
],
|
|
1550
|
+
},
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1553
|
+
},
|
|
1554
|
+
|
|
1555
|
+
],
|
|
1556
|
+
},
|
|
1557
|
+
},
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1560
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1561
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1562
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1563
|
+
// Remove any previous mappingInfo.status term
|
|
1564
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1565
|
+
if ( nested ) {
|
|
1566
|
+
// filter out all mappingInfo.status
|
|
1567
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1568
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1569
|
+
} );
|
|
1570
|
+
// add desired status
|
|
1571
|
+
nested.nested.query.bool.must.push( {
|
|
1572
|
+
term: {
|
|
1573
|
+
'mappingInfo.status': statusValue,
|
|
1574
|
+
},
|
|
1575
|
+
} );
|
|
1576
|
+
}
|
|
1577
|
+
return q;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1581
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1582
|
+
|
|
1583
|
+
// locate nested section
|
|
1584
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1585
|
+
|
|
1586
|
+
if ( nested ) {
|
|
1587
|
+
// remove old status filters
|
|
1588
|
+
nested.nested.query.bool.must =
|
|
1589
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1590
|
+
|
|
1591
|
+
// add new filters
|
|
1592
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
return {
|
|
1596
|
+
...q,
|
|
1597
|
+
size: 0,
|
|
1598
|
+
aggs: {
|
|
1599
|
+
avg_value: {
|
|
1600
|
+
avg: 'mappingInfo.reviced',
|
|
1601
|
+
},
|
|
1602
|
+
},
|
|
1603
|
+
};
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
// Get OpenSearch connection
|
|
1608
|
+
|
|
1609
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1610
|
+
|
|
1611
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1612
|
+
let totalTickets = 0;
|
|
1613
|
+
|
|
1614
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1615
|
+
|
|
1616
|
+
allQuery.size = 0;
|
|
1617
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1618
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1619
|
+
|
|
1620
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1621
|
+
let openTickets = 0;
|
|
1622
|
+
|
|
1623
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1624
|
+
otQ.size = 0;
|
|
1625
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1626
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1627
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1628
|
+
|
|
1629
|
+
|
|
1630
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1631
|
+
let inprogress = 0;
|
|
1632
|
+
|
|
1633
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1634
|
+
ipQ.size = 0;
|
|
1635
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1636
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1637
|
+
|
|
1638
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1639
|
+
let closedTickets = 0;
|
|
1640
|
+
|
|
1641
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1642
|
+
clQ.size = 0;
|
|
1643
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1644
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1645
|
+
|
|
1646
|
+
let dueToday = 0;
|
|
1647
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1648
|
+
|
|
1649
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1650
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1651
|
+
// Locate nested mappingInfo query
|
|
1652
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1653
|
+
if ( nestedDue ) {
|
|
1654
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1655
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1656
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1657
|
+
);
|
|
1658
|
+
// Add new dueDate filter
|
|
1659
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1660
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1661
|
+
} );
|
|
1662
|
+
}
|
|
1663
|
+
dueTodayQuery.size = 0;
|
|
1664
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1665
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1666
|
+
|
|
1667
|
+
// filter expired Tickets
|
|
1668
|
+
let expiredTickets = 0;
|
|
1669
|
+
|
|
1670
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1671
|
+
eQ.size = 0;
|
|
1672
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1673
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1674
|
+
|
|
1675
|
+
// Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
|
|
1676
|
+
|
|
1677
|
+
// Build aggregation query for ticket percentage
|
|
1678
|
+
let ticketPercentageAvg = 0;
|
|
1679
|
+
|
|
1680
|
+
let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1681
|
+
avgTicketPercentageQuery.size = 0;
|
|
1682
|
+
avgTicketPercentageQuery.aggs = {
|
|
1683
|
+
avg_ticket_percentage: {
|
|
1684
|
+
avg: {
|
|
1685
|
+
script: {
|
|
1686
|
+
source: `
|
|
1687
|
+
if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
|
|
1688
|
+
doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
|
|
1689
|
+
return (doc['reviced'].value / doc['footfallCount'].value) * 100;
|
|
1690
|
+
} else {
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
`,
|
|
1694
|
+
lang: 'painless',
|
|
1695
|
+
},
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
|
|
1701
|
+
|
|
1702
|
+
ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
|
|
1703
|
+
|
|
1704
|
+
logger.info( { avgTicketPercentageResp } );
|
|
1705
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1706
|
+
let ticketAccuracy = 0;
|
|
1707
|
+
|
|
1708
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery );
|
|
1709
|
+
logger.info( { belowQ } );
|
|
1710
|
+
const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1711
|
+
ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1712
|
+
logger.info( { accuracyResp } );
|
|
1713
|
+
// Final result object
|
|
1714
|
+
result = {
|
|
1715
|
+
totalTickets,
|
|
1716
|
+
openTickets,
|
|
1717
|
+
inprogress,
|
|
1718
|
+
closedTickets,
|
|
1719
|
+
dueToday: dueToday,
|
|
1720
|
+
Expired: expiredTickets,
|
|
1721
|
+
avgTicket: ticketPercentageAvg+'%',
|
|
1722
|
+
avgAccuracy: ticketAccuracy+'%',
|
|
1723
|
+
};
|
|
1724
|
+
} else {
|
|
1725
|
+
const storeQuery = {
|
|
1726
|
+
size: 0,
|
|
1727
|
+
query: {
|
|
1728
|
+
bool: {
|
|
1729
|
+
must: [
|
|
1730
|
+
{
|
|
1731
|
+
'range': {
|
|
1732
|
+
'dateString': {
|
|
1733
|
+
'gte': inputData?.fromDate,
|
|
1734
|
+
'lte': inputData?.toDate,
|
|
1735
|
+
'format': 'yyyy-MM-dd',
|
|
1736
|
+
},
|
|
1737
|
+
},
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
terms: {
|
|
1741
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1742
|
+
inputData.clientId :
|
|
1743
|
+
[ inputData.clientId ],
|
|
1744
|
+
},
|
|
1745
|
+
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
nested: {
|
|
1749
|
+
path: 'mappingInfo',
|
|
1750
|
+
query: {
|
|
1751
|
+
bool: {
|
|
1752
|
+
must: [
|
|
1753
|
+
{
|
|
1754
|
+
term: {
|
|
1755
|
+
'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
|
|
1756
|
+
},
|
|
1757
|
+
},
|
|
1758
|
+
],
|
|
1759
|
+
},
|
|
1760
|
+
},
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1763
|
+
|
|
1764
|
+
],
|
|
1765
|
+
},
|
|
1766
|
+
},
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1770
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1771
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1772
|
+
// Remove any previous mappingInfo.status term
|
|
1773
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1774
|
+
if ( nested ) {
|
|
1775
|
+
// filter out all mappingInfo.status
|
|
1776
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1777
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1778
|
+
} );
|
|
1779
|
+
// add desired status
|
|
1780
|
+
nested.nested.query.bool.must.push( {
|
|
1781
|
+
term: {
|
|
1782
|
+
'mappingInfo.status': statusValue,
|
|
1783
|
+
},
|
|
1784
|
+
} );
|
|
1785
|
+
}
|
|
1786
|
+
return q;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1790
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1791
|
+
|
|
1792
|
+
// locate nested section
|
|
1793
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1794
|
+
|
|
1795
|
+
if ( nested ) {
|
|
1796
|
+
// remove old status filters
|
|
1797
|
+
nested.nested.query.bool.must =
|
|
1798
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1799
|
+
|
|
1800
|
+
// add new filters
|
|
1801
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
return {
|
|
1805
|
+
...q,
|
|
1806
|
+
size: 0,
|
|
1807
|
+
aggs: {
|
|
1808
|
+
avg_value: {
|
|
1809
|
+
avg: 'mappingInfp.reviced',
|
|
1810
|
+
},
|
|
1811
|
+
},
|
|
1812
|
+
};
|
|
1813
|
+
};
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
// Get OpenSearch connection
|
|
1817
|
+
|
|
1818
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1819
|
+
|
|
1820
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1821
|
+
let totalTickets = 0;
|
|
1822
|
+
|
|
1823
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1824
|
+
|
|
1825
|
+
allQuery.size = 0;
|
|
1826
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1827
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1828
|
+
|
|
1829
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1830
|
+
let openTickets = 0;
|
|
1831
|
+
|
|
1832
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1833
|
+
otQ.size = 0;
|
|
1834
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1835
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1836
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1837
|
+
|
|
1838
|
+
|
|
1839
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1840
|
+
let inprogress = 0;
|
|
1841
|
+
|
|
1842
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1843
|
+
ipQ.size = 0;
|
|
1844
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1845
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1846
|
+
|
|
1847
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1848
|
+
let closedTickets = 0;
|
|
1849
|
+
|
|
1850
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1851
|
+
clQ.size = 0;
|
|
1852
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1853
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1854
|
+
// dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
|
|
1855
|
+
let dueToday = 0;
|
|
1856
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1857
|
+
|
|
1858
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1859
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1860
|
+
// Locate nested mappingInfo query
|
|
1861
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1862
|
+
if ( nestedDue ) {
|
|
1863
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1864
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1865
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1866
|
+
);
|
|
1867
|
+
// Add new dueDate filter
|
|
1868
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1869
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1870
|
+
} );
|
|
1871
|
+
}
|
|
1872
|
+
dueTodayQuery.size = 0;
|
|
1873
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1874
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1875
|
+
|
|
1876
|
+
|
|
1877
|
+
// filter expired Tickets
|
|
1878
|
+
let expiredTickets = 0;
|
|
1879
|
+
|
|
1880
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1881
|
+
eQ.size = 0;
|
|
1882
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1883
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1884
|
+
|
|
1885
|
+
// filter under tango review
|
|
1886
|
+
|
|
1887
|
+
let undertangoTickets = 0;
|
|
1888
|
+
|
|
1889
|
+
let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1890
|
+
utrQ.size = 0;
|
|
1891
|
+
const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
|
|
1892
|
+
undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
|
|
1893
|
+
|
|
1894
|
+
let ticketPercentageAvg =0;
|
|
1895
|
+
let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1896
|
+
avgTicketPercentageQuery.size = 0;
|
|
1897
|
+
avgTicketPercentageQuery.aggs = {
|
|
1898
|
+
avg_ticket_percentage: {
|
|
1899
|
+
avg: {
|
|
1900
|
+
script: {
|
|
1901
|
+
source: `
|
|
1902
|
+
if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
|
|
1903
|
+
doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
|
|
1904
|
+
return (doc['reviced'].value / doc['footfallCount'].value) * 100;
|
|
1905
|
+
} else {
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1908
|
+
`,
|
|
1909
|
+
lang: 'painless',
|
|
1910
|
+
},
|
|
1911
|
+
},
|
|
1912
|
+
},
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
|
|
1916
|
+
|
|
1917
|
+
ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
|
|
1918
|
+
logger.info( { avgTicketPercentageResp } );
|
|
1919
|
+
let ticketAccuracy = 0;
|
|
1920
|
+
|
|
1921
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery );
|
|
1922
|
+
logger.info( { belowQ } );
|
|
1923
|
+
const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1924
|
+
ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1925
|
+
logger.info( { accuracyResp } );
|
|
1926
|
+
// Final result object
|
|
1927
|
+
result = {
|
|
1928
|
+
totalTickets,
|
|
1929
|
+
openTickets,
|
|
1930
|
+
inprogress,
|
|
1931
|
+
closedTickets,
|
|
1932
|
+
dueToday: dueToday,
|
|
1933
|
+
Expired: expiredTickets,
|
|
1934
|
+
underTangoReview: undertangoTickets,
|
|
1935
|
+
avgTicket: ticketPercentageAvg+'%',
|
|
1936
|
+
avgAccuracy: ticketAccuracy+'%',
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
return res.sendSuccess( { result: result } );
|
|
1943
|
+
} catch ( error ) {
|
|
1944
|
+
const err = error.message || 'Internal Server Error';
|
|
1945
|
+
logger.error( { error: error, messgage: req.query } );
|
|
1946
|
+
return res.sendSuccess( err, 500 );
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
export async function ticketList1( req, res ) {
|
|
1951
|
+
try {
|
|
1952
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1953
|
+
const inputData = req.query;
|
|
1954
|
+
const limit =inputData?.isExport ? 10000: inputData.limit || 10;
|
|
1955
|
+
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
1956
|
+
const order = inputData?.sortOrder || -1;
|
|
1957
|
+
|
|
1958
|
+
inputData.clientId = inputData.clientId.split( ',' ); // convert strig to array
|
|
1959
|
+
|
|
1960
|
+
|
|
1961
|
+
let filter = [
|
|
1962
|
+
{
|
|
1963
|
+
'range': {
|
|
1964
|
+
'dateString': {
|
|
1965
|
+
'gte': inputData.fromDate,
|
|
1966
|
+
'lte': inputData.toDate,
|
|
288
1967
|
'format': 'yyyy-MM-dd',
|
|
289
1968
|
},
|
|
290
1969
|
},
|
|
291
1970
|
},
|
|
292
|
-
{
|
|
1971
|
+
{
|
|
1972
|
+
terms: {
|
|
1973
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
1974
|
+
inputData.clientId :
|
|
1975
|
+
[ inputData.clientId ],
|
|
1976
|
+
},
|
|
1977
|
+
},
|
|
1978
|
+
];
|
|
1979
|
+
|
|
1980
|
+
if ( inputData?.storeId ) {
|
|
1981
|
+
filter.push(
|
|
1982
|
+
{
|
|
1983
|
+
terms: {
|
|
1984
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1985
|
+
inputData.storeId :
|
|
1986
|
+
[ inputData.storeId ],
|
|
1987
|
+
},
|
|
1988
|
+
},
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
let search = {
|
|
1993
|
+
'must': filter,
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
1997
|
+
search = {
|
|
1998
|
+
'must': filter,
|
|
1999
|
+
'should': [
|
|
2000
|
+
{
|
|
2001
|
+
'wildcard': {
|
|
2002
|
+
'storeName.keyword': {
|
|
2003
|
+
'value': `*${inputData.searchValue}*`,
|
|
2004
|
+
},
|
|
2005
|
+
},
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
'wildcard': {
|
|
2009
|
+
'storeId.keyword': {
|
|
2010
|
+
'value': `*${inputData.searchValue}*`,
|
|
2011
|
+
},
|
|
2012
|
+
},
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
'wildcard': {
|
|
2016
|
+
'ticketId.keyword': {
|
|
2017
|
+
'value': `*${inputData.searchValue}*`,
|
|
2018
|
+
},
|
|
2019
|
+
},
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
'wildcard': {
|
|
2023
|
+
'status.keyword': {
|
|
2024
|
+
'value': `*${inputData.searchValue}*`,
|
|
2025
|
+
},
|
|
2026
|
+
},
|
|
2027
|
+
},
|
|
2028
|
+
|
|
2029
|
+
],
|
|
2030
|
+
'minimum_should_match': 1,
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
let searchQuery = {
|
|
2035
|
+
'_source': [
|
|
2036
|
+
'storeName',
|
|
2037
|
+
'storeId',
|
|
2038
|
+
'ticketId',
|
|
2039
|
+
'createdAt',
|
|
2040
|
+
'updatedAt',
|
|
2041
|
+
'footfallCount',
|
|
2042
|
+
'duplicateCount',
|
|
2043
|
+
'employeeCount',
|
|
2044
|
+
'houseKeepingCount',
|
|
2045
|
+
'junkCount',
|
|
2046
|
+
'employeeACCount',
|
|
2047
|
+
'duplicateACCount',
|
|
2048
|
+
'houseKeepingACCount',
|
|
2049
|
+
'junkACCount',
|
|
2050
|
+
'status',
|
|
2051
|
+
'dateString',
|
|
2052
|
+
],
|
|
2053
|
+
'from': offset,
|
|
2054
|
+
'size': limit,
|
|
2055
|
+
'query': {
|
|
2056
|
+
'bool': search,
|
|
2057
|
+
},
|
|
2058
|
+
'sort': [
|
|
2059
|
+
{ dateString: { order: 'desc' } },
|
|
2060
|
+
],
|
|
2061
|
+
};
|
|
2062
|
+
|
|
2063
|
+
if ( inputData.sortBy && inputData.sortBy !== '' ) {
|
|
2064
|
+
let sortByValue = '';
|
|
2065
|
+
|
|
2066
|
+
if ( [ 'storeName', 'storeId', 'ticketId', 'status' ].includes( inputData?.sortBy ) ) {
|
|
2067
|
+
sortByValue = `${inputData.sortBy}.keyword`;
|
|
2068
|
+
} else {
|
|
2069
|
+
sortByValue = inputData.sortBy;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
|
|
2073
|
+
searchQuery = {
|
|
2074
|
+
'_source': [
|
|
2075
|
+
'storeName',
|
|
2076
|
+
'storeId',
|
|
2077
|
+
'ticketId',
|
|
2078
|
+
'createdAt',
|
|
2079
|
+
'updatedAt',
|
|
2080
|
+
'footfallCount',
|
|
2081
|
+
'duplicateCount',
|
|
2082
|
+
'employeeCount',
|
|
2083
|
+
'houseKeepingCount',
|
|
2084
|
+
'junkCount',
|
|
2085
|
+
'status',
|
|
2086
|
+
'employeeACCount',
|
|
2087
|
+
'duplicateACCount',
|
|
2088
|
+
'houseKeepingACCount',
|
|
2089
|
+
'junkACCount',
|
|
2090
|
+
'dateString',
|
|
2091
|
+
],
|
|
2092
|
+
'from': offset,
|
|
2093
|
+
'size': limit,
|
|
2094
|
+
'query': {
|
|
2095
|
+
'bool': search,
|
|
2096
|
+
},
|
|
2097
|
+
'sort': [
|
|
2098
|
+
{ [sortByValue]: { order: order === -1 ? 'desc' : 'asc' } },
|
|
2099
|
+
],
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
if ( inputData.isExport == true ) {
|
|
2104
|
+
searchQuery = {
|
|
2105
|
+
'_source': [
|
|
2106
|
+
'storeName',
|
|
2107
|
+
'storeId',
|
|
2108
|
+
'ticketId',
|
|
2109
|
+
'createdAt',
|
|
2110
|
+
'updatedAt',
|
|
2111
|
+
'footfallCount',
|
|
2112
|
+
'duplicateCount',
|
|
2113
|
+
'employeeACCount',
|
|
2114
|
+
'duplicateACCount',
|
|
2115
|
+
'employeeCount',
|
|
2116
|
+
'houseKeepingACCount',
|
|
2117
|
+
'houseKeepingCount',
|
|
2118
|
+
'junkCount',
|
|
2119
|
+
'junkACCount',
|
|
2120
|
+
'status',
|
|
2121
|
+
'dateString',
|
|
2122
|
+
],
|
|
2123
|
+
'from': 0,
|
|
2124
|
+
'size': 10000,
|
|
2125
|
+
'query': {
|
|
2126
|
+
'bool': search,
|
|
2127
|
+
},
|
|
2128
|
+
'sort': [
|
|
2129
|
+
{ 'storeName.keyword': { order: 'desc' } },
|
|
2130
|
+
],
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
const getData = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
2134
|
+
const count = getData?.body?.hits?.total?.value;
|
|
2135
|
+
if ( !count || count == 0 ) {
|
|
2136
|
+
return res.sendError( 'No data found', 204 );
|
|
2137
|
+
}
|
|
2138
|
+
const searchValue = getData?.body?.hits?.hits;
|
|
2139
|
+
if ( !searchValue || searchValue?.length == 0 ) {
|
|
2140
|
+
return res.sendError( 'No data found', 204 );
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
if ( inputData.isExport == true ) {
|
|
2144
|
+
const exportData = [];
|
|
2145
|
+
for ( const item of searchValue ) {
|
|
2146
|
+
exportData.push( {
|
|
2147
|
+
'Store Name': item._source.storeName || '--',
|
|
2148
|
+
'Store ID': item._source.storeId,
|
|
2149
|
+
'Ticket raised on': dayjs( item._source.createdAt ).format( 'DD MMM, YYYY' ),
|
|
2150
|
+
'Issue Date': dayjs( item._source.dateString ).format( 'DD MMM, YYYY' ),
|
|
2151
|
+
'Total Footfalls': item._source.footfallCount,
|
|
2152
|
+
'Duplicates': item?._source?.status === 'closed' ? item._source.duplicateACCount : item._source.duplicateCount,
|
|
2153
|
+
'Employee/Staff': item?._source?.status === 'closed' ? item._source.employeeACCount : item._source.employeeCount,
|
|
2154
|
+
'HouseKeeping': item?._source?.status === 'closed' ? item._source.houseKeepingACCount : item._source.houseKeepingCount,
|
|
2155
|
+
'Junk': item?._source?.status === 'closed' ? ( item._source.junkACCount || 0 ) : ( item._source.junkCount || 0 ),
|
|
2156
|
+
'Revised Footfall': item?._source?.status === 'closed' ? item._source.footfallCount - ( item._source.duplicateACCount + item._source.employeeACCount + item._source.houseKeepingACCount + ( item._source.junkACCount || 0 ) ) : item._source.footfallCount - ( item._source.duplicateCount + item._source.employeeCount + item._source.houseKeepingCount + ( item._source.junkCount || 0 ) ),
|
|
2157
|
+
'Ticket%': item?._source?.status === 'closed' ? `${Math.round( ( ( item._source.duplicateACCount + item._source.employeeACCount + item._source.houseKeepingACCount + ( item?._source?.junkACCount || 0 ) ) / item._source.footfallCount ) * 100 ).toFixed( 0 )} %` : `${Math.round( ( ( item?._source?.duplicateCount + item?._source?.employeeCount + item?._source?.houseKeepingCount + ( item?._source?.junkCount || 0 ) ) / item?._source?.footfallCount ) * 100 ).toFixed( 0 )} %`,
|
|
2158
|
+
'Status': item._source.status,
|
|
2159
|
+
} );
|
|
2160
|
+
}
|
|
2161
|
+
return await download( exportData, res );
|
|
2162
|
+
}
|
|
2163
|
+
return res.sendSuccess( { result: searchValue, count: count } );
|
|
2164
|
+
} catch ( error ) {
|
|
2165
|
+
const err = error.message || 'Internal Server Error';
|
|
2166
|
+
logger.error( { error: error, messgage: req.query } );
|
|
2167
|
+
return res.sendSuccess( err, 500 );
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
export async function ticketList( req, res ) {
|
|
2172
|
+
try {
|
|
2173
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2174
|
+
const inputData = req.query;
|
|
2175
|
+
const userInfo = req.user;
|
|
2176
|
+
const limit =inputData?.isExport? 10000: inputData?.limit || 10;
|
|
2177
|
+
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
2178
|
+
inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
|
|
2179
|
+
|
|
2180
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
2181
|
+
|
|
2182
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
2183
|
+
|
|
2184
|
+
const searchQuery = {
|
|
2185
|
+
|
|
2186
|
+
size: limit, // or use parseInt(req.query.limit) for dynamic
|
|
2187
|
+
from: offset, // or use parseInt(req.query.offset) for dynamic
|
|
2188
|
+
sort: [ { 'createdAt': { order: 'desc' } } ],
|
|
2189
|
+
|
|
2190
|
+
query: {
|
|
2191
|
+
bool: {
|
|
2192
|
+
must: [
|
|
2193
|
+
{
|
|
2194
|
+
'range': {
|
|
2195
|
+
'dateString': {
|
|
2196
|
+
'gte': inputData.fromDate,
|
|
2197
|
+
'lte': inputData.toDate,
|
|
2198
|
+
'format': 'yyyy-MM-dd',
|
|
2199
|
+
},
|
|
2200
|
+
},
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
terms: {
|
|
2204
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
2205
|
+
inputData.clientId :
|
|
2206
|
+
[ inputData.clientId ],
|
|
2207
|
+
},
|
|
2208
|
+
|
|
2209
|
+
},
|
|
2210
|
+
],
|
|
2211
|
+
},
|
|
2212
|
+
},
|
|
2213
|
+
};
|
|
2214
|
+
|
|
2215
|
+
if ( inputData.sortBy ) {
|
|
2216
|
+
let sortOrder = inputData.sortOrder === 1 ? 'asc' : 'desc';
|
|
2217
|
+
|
|
2218
|
+
|
|
2219
|
+
const stringKeywordFields = [ 'storeName', 'storeId', 'ticketId', 'status', 'type', 'clientId' ];
|
|
2220
|
+
let sortField = inputData.sortBy == 'footfall' ? 'footfallCount' : inputData.sortBy == 'issueDate' ? 'dateString' : inputData.sortBy;
|
|
2221
|
+
if ( stringKeywordFields.includes( sortField ) ) {
|
|
2222
|
+
sortField = `${sortField}.keyword`;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
// Remove default sort so we don't duplicate/conflict
|
|
2226
|
+
searchQuery.sort = [
|
|
2227
|
+
{ [sortField]: { order: sortOrder } },
|
|
2228
|
+
];
|
|
2229
|
+
}
|
|
2230
|
+
// Example: Filtering by storeId if present in the query
|
|
2231
|
+
if ( inputData.storeId ) {
|
|
2232
|
+
inputData.storeId = inputData?.storeId?.split( ',' );
|
|
2233
|
+
searchQuery.query.bool.must.push( {
|
|
293
2234
|
terms: {
|
|
294
|
-
'
|
|
295
|
-
inputData.
|
|
296
|
-
|
|
2235
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
2236
|
+
inputData.storeId :
|
|
2237
|
+
[ inputData.storeId ],
|
|
297
2238
|
},
|
|
298
|
-
}
|
|
299
|
-
|
|
2239
|
+
} );
|
|
2240
|
+
}
|
|
2241
|
+
if ( inputData.status && inputData.status!=='' ) {
|
|
2242
|
+
inputData.status = inputData?.status?.split( ',' );
|
|
2243
|
+
if ( req.user.userType === 'tango' ) {
|
|
2244
|
+
searchQuery.query.bool.must.push( {
|
|
2245
|
+
terms: {
|
|
2246
|
+
'status.keyword': Array.isArray( inputData?.status ) ?
|
|
2247
|
+
inputData?.status :
|
|
2248
|
+
[ inputData?.status ],
|
|
2249
|
+
},
|
|
2250
|
+
} );
|
|
2251
|
+
} else if ( inputData?.permissionType === 'approve' ) {
|
|
2252
|
+
searchQuery.query.bool.must.push( {
|
|
2253
|
+
nested: {
|
|
2254
|
+
path: 'mappingInfo',
|
|
2255
|
+
query: {
|
|
2256
|
+
bool: {
|
|
2257
|
+
must: [
|
|
2258
|
+
{
|
|
2259
|
+
term: {
|
|
2260
|
+
'mappingInfo.type': 'approve',
|
|
2261
|
+
},
|
|
2262
|
+
},
|
|
2263
|
+
{
|
|
2264
|
+
term: {
|
|
2265
|
+
'mappingInfo.status': inputData.status[0],
|
|
2266
|
+
},
|
|
2267
|
+
},
|
|
2268
|
+
],
|
|
2269
|
+
},
|
|
2270
|
+
},
|
|
2271
|
+
},
|
|
2272
|
+
} );
|
|
2273
|
+
} else if ( inputData?.permissionType === 'review' ) {
|
|
2274
|
+
searchQuery.query.bool.must.push( {
|
|
2275
|
+
nested: {
|
|
2276
|
+
path: 'mappingInfo',
|
|
2277
|
+
query: {
|
|
2278
|
+
bool: {
|
|
2279
|
+
must: [
|
|
2280
|
+
{
|
|
2281
|
+
term: {
|
|
2282
|
+
'mappingInfo.type': 'review',
|
|
2283
|
+
},
|
|
2284
|
+
},
|
|
2285
|
+
{
|
|
2286
|
+
term: {
|
|
2287
|
+
'mappingInfo.status': inputData.status[0],
|
|
2288
|
+
},
|
|
2289
|
+
},
|
|
2290
|
+
],
|
|
2291
|
+
},
|
|
2292
|
+
},
|
|
2293
|
+
},
|
|
2294
|
+
} );
|
|
2295
|
+
} else if ( ticketsFeature ) {
|
|
2296
|
+
searchQuery.query.bool.must.push( {
|
|
2297
|
+
nested: {
|
|
2298
|
+
path: 'mappingInfo',
|
|
2299
|
+
query: {
|
|
2300
|
+
bool: {
|
|
2301
|
+
must: [
|
|
2302
|
+
{
|
|
2303
|
+
term: {
|
|
2304
|
+
'mappingInfo.type': 'review',
|
|
2305
|
+
},
|
|
2306
|
+
},
|
|
2307
|
+
{
|
|
2308
|
+
term: {
|
|
2309
|
+
'mappingInfo.status': inputData.status[0],
|
|
2310
|
+
},
|
|
2311
|
+
},
|
|
2312
|
+
],
|
|
2313
|
+
},
|
|
2314
|
+
},
|
|
2315
|
+
},
|
|
2316
|
+
} );
|
|
2317
|
+
} else if ( ticketsApproveFeature ) {
|
|
2318
|
+
searchQuery.query.bool.must.push( {
|
|
2319
|
+
nested: {
|
|
2320
|
+
path: 'mappingInfo',
|
|
2321
|
+
query: {
|
|
2322
|
+
bool: {
|
|
2323
|
+
must: [
|
|
2324
|
+
{
|
|
2325
|
+
term: {
|
|
2326
|
+
'mappingInfo.type': 'approve',
|
|
2327
|
+
},
|
|
2328
|
+
},
|
|
2329
|
+
{
|
|
2330
|
+
term: {
|
|
2331
|
+
'mappingInfo.status': inputData.status[0],
|
|
2332
|
+
},
|
|
2333
|
+
},
|
|
2334
|
+
],
|
|
2335
|
+
},
|
|
2336
|
+
},
|
|
2337
|
+
},
|
|
2338
|
+
} );
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
if ( inputData.filterByStatus && inputData.filterByStatus!=='' ) {
|
|
2343
|
+
inputData.filterByStatus = inputData?.filterByStatus?.split( ',' );
|
|
2344
|
+
|
|
2345
|
+
if ( req?.user?.userType === 'tango' ) {
|
|
2346
|
+
{
|
|
2347
|
+
switch ( inputData?.tangoType ) {
|
|
2348
|
+
case 'store':
|
|
2349
|
+
searchQuery.query.bool.must.push( {
|
|
2350
|
+
nested: {
|
|
2351
|
+
path: 'mappingInfo',
|
|
2352
|
+
query: {
|
|
2353
|
+
bool: {
|
|
2354
|
+
must: [
|
|
2355
|
+
{
|
|
2356
|
+
term: {
|
|
2357
|
+
'mappingInfo.type': 'tangoreview',
|
|
2358
|
+
},
|
|
2359
|
+
},
|
|
2360
|
+
{
|
|
2361
|
+
terms: {
|
|
2362
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2363
|
+
},
|
|
2364
|
+
},
|
|
2365
|
+
],
|
|
2366
|
+
},
|
|
2367
|
+
},
|
|
2368
|
+
},
|
|
2369
|
+
} );
|
|
2370
|
+
break;
|
|
2371
|
+
case 'internal':
|
|
2372
|
+
searchQuery.query.bool.must.push( {
|
|
2373
|
+
'terms': {
|
|
2374
|
+
'status.keyword': Array.isArray( inputData?.filterByStatus ) ?
|
|
2375
|
+
inputData?.filterByStatus :
|
|
2376
|
+
[ inputData?.filterByStatus ],
|
|
2377
|
+
},
|
|
2378
|
+
} );
|
|
2379
|
+
break;
|
|
2380
|
+
defaut:
|
|
2381
|
+
searchQuery.query.bool.must.push( {
|
|
2382
|
+
nested: {
|
|
2383
|
+
path: 'mappingInfo',
|
|
2384
|
+
query: {
|
|
2385
|
+
bool: {
|
|
2386
|
+
must: [
|
|
2387
|
+
{
|
|
2388
|
+
term: {
|
|
2389
|
+
'mappingInfo.type': 'tangoreview',
|
|
2390
|
+
},
|
|
2391
|
+
},
|
|
2392
|
+
{
|
|
2393
|
+
terms: {
|
|
2394
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2395
|
+
},
|
|
2396
|
+
},
|
|
2397
|
+
],
|
|
2398
|
+
},
|
|
2399
|
+
},
|
|
2400
|
+
},
|
|
2401
|
+
} );
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
} else if ( ticketsFeature && !ticketsApproveFeature ) {
|
|
2405
|
+
searchQuery.query.bool.must.push( {
|
|
2406
|
+
nested: {
|
|
2407
|
+
path: 'mappingInfo',
|
|
2408
|
+
query: {
|
|
2409
|
+
bool: {
|
|
2410
|
+
must: [
|
|
2411
|
+
{
|
|
2412
|
+
term: {
|
|
2413
|
+
'mappingInfo.type': 'review',
|
|
2414
|
+
},
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
terms: {
|
|
2418
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2419
|
+
},
|
|
2420
|
+
},
|
|
2421
|
+
],
|
|
2422
|
+
},
|
|
2423
|
+
},
|
|
2424
|
+
},
|
|
2425
|
+
} );
|
|
2426
|
+
} else if ( ticketsFeature && ticketsApproveFeature ) {
|
|
2427
|
+
switch ( inputData.permisisionType ) {
|
|
2428
|
+
case 'review':
|
|
2429
|
+
searchQuery.query.bool.must.push( {
|
|
2430
|
+
nested: {
|
|
2431
|
+
path: 'mappingInfo',
|
|
2432
|
+
query: {
|
|
2433
|
+
bool: {
|
|
2434
|
+
must: [
|
|
2435
|
+
{
|
|
2436
|
+
term: {
|
|
2437
|
+
'mappingInfo.type': 'review',
|
|
2438
|
+
},
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
terms: {
|
|
2442
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2443
|
+
},
|
|
2444
|
+
},
|
|
2445
|
+
],
|
|
2446
|
+
},
|
|
2447
|
+
},
|
|
2448
|
+
},
|
|
2449
|
+
} );
|
|
2450
|
+
break;
|
|
2451
|
+
case 'approve':
|
|
2452
|
+
searchQuery.query.bool.must.push( {
|
|
2453
|
+
nested: {
|
|
2454
|
+
path: 'mappingInfo',
|
|
2455
|
+
query: {
|
|
2456
|
+
bool: {
|
|
2457
|
+
must: [
|
|
2458
|
+
{
|
|
2459
|
+
term: {
|
|
2460
|
+
'mappingInfo.type': 'approve',
|
|
2461
|
+
},
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
terms: {
|
|
2465
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2466
|
+
},
|
|
2467
|
+
},
|
|
2468
|
+
],
|
|
2469
|
+
},
|
|
2470
|
+
},
|
|
2471
|
+
},
|
|
2472
|
+
} );
|
|
2473
|
+
default: searchQuery.query.bool.must.push( {
|
|
2474
|
+
nested: {
|
|
2475
|
+
path: 'mappingInfo',
|
|
2476
|
+
query: {
|
|
2477
|
+
bool: {
|
|
2478
|
+
must: [
|
|
2479
|
+
{
|
|
2480
|
+
term: {
|
|
2481
|
+
'mappingInfo.type': 'approve',
|
|
2482
|
+
},
|
|
2483
|
+
},
|
|
2484
|
+
{
|
|
2485
|
+
terms: {
|
|
2486
|
+
'mappingInfo.status': inputData?.filterByStatus,
|
|
2487
|
+
},
|
|
2488
|
+
},
|
|
2489
|
+
],
|
|
2490
|
+
},
|
|
2491
|
+
},
|
|
2492
|
+
},
|
|
2493
|
+
} );
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
if ( inputData?.filterByStore && inputData?.filterByStore !== '' ) {
|
|
2499
|
+
let percQuery = null;
|
|
2500
|
+
const value = inputData.filterByStore;
|
|
2501
|
+
|
|
2502
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2503
|
+
const percValue = ( val ) => {
|
|
2504
|
+
if ( typeof val === 'string' ) {
|
|
2505
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2506
|
+
}
|
|
2507
|
+
return parseFloat( val );
|
|
2508
|
+
};
|
|
2509
|
+
|
|
2510
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2511
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2512
|
+
// "<90" or "<=90"
|
|
2513
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2514
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2515
|
+
percQuery = {
|
|
2516
|
+
script: {
|
|
2517
|
+
script: {
|
|
2518
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2519
|
+
params: { num },
|
|
2520
|
+
},
|
|
2521
|
+
},
|
|
2522
|
+
};
|
|
2523
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2524
|
+
// ">=90"
|
|
2525
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2526
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2527
|
+
percQuery = {
|
|
2528
|
+
script: {
|
|
2529
|
+
script: {
|
|
2530
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2531
|
+
params: { num },
|
|
2532
|
+
},
|
|
2533
|
+
},
|
|
2534
|
+
};
|
|
2535
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2536
|
+
// "50 to 90"
|
|
2537
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2538
|
+
const from = percValue( match[1] );
|
|
2539
|
+
const to = percValue( match[2] );
|
|
2540
|
+
percQuery = {
|
|
2541
|
+
script: {
|
|
2542
|
+
script: {
|
|
2543
|
+
source:
|
|
2544
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2545
|
+
params: { from, to },
|
|
2546
|
+
},
|
|
2547
|
+
},
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2551
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2552
|
+
percQuery = {
|
|
2553
|
+
script: {
|
|
2554
|
+
script: {
|
|
2555
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2556
|
+
params: { num: percValue( value ) },
|
|
2557
|
+
},
|
|
2558
|
+
},
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
300
2561
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
{
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
2562
|
+
if ( percQuery ) {
|
|
2563
|
+
searchQuery.query.bool.must.push( {
|
|
2564
|
+
nested: {
|
|
2565
|
+
path: 'mappingInfo',
|
|
2566
|
+
query: {
|
|
2567
|
+
bool: {
|
|
2568
|
+
must: [
|
|
2569
|
+
{ term: { 'mappingInfo.type': 'tagging' } },
|
|
2570
|
+
percQuery,
|
|
2571
|
+
],
|
|
2572
|
+
},
|
|
308
2573
|
},
|
|
309
2574
|
},
|
|
310
|
-
|
|
2575
|
+
} );
|
|
2576
|
+
}
|
|
311
2577
|
}
|
|
312
2578
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
2579
|
+
if ( inputData?.filterByReviewer && inputData?.filterByReviewer !== '' ) {
|
|
2580
|
+
let percQuery = null;
|
|
2581
|
+
const value = inputData.filterByReviewer;
|
|
316
2582
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
'
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
2583
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2584
|
+
const percValue = ( val ) => {
|
|
2585
|
+
if ( typeof val === 'string' ) {
|
|
2586
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2587
|
+
}
|
|
2588
|
+
return parseFloat( val );
|
|
2589
|
+
};
|
|
2590
|
+
|
|
2591
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2592
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2593
|
+
// "<90" or "<=90"
|
|
2594
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2595
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2596
|
+
percQuery = {
|
|
2597
|
+
script: {
|
|
2598
|
+
script: {
|
|
2599
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2600
|
+
params: { num },
|
|
326
2601
|
},
|
|
327
2602
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
2603
|
+
};
|
|
2604
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2605
|
+
// ">=90"
|
|
2606
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2607
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2608
|
+
percQuery = {
|
|
2609
|
+
script: {
|
|
2610
|
+
script: {
|
|
2611
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2612
|
+
params: { num },
|
|
333
2613
|
},
|
|
334
2614
|
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
2615
|
+
};
|
|
2616
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2617
|
+
// "50 to 90"
|
|
2618
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2619
|
+
const from = percValue( match[1] );
|
|
2620
|
+
const to = percValue( match[2] );
|
|
2621
|
+
percQuery = {
|
|
2622
|
+
script: {
|
|
2623
|
+
script: {
|
|
2624
|
+
source:
|
|
2625
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2626
|
+
params: { from, to },
|
|
340
2627
|
},
|
|
341
2628
|
},
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2632
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2633
|
+
percQuery = {
|
|
2634
|
+
script: {
|
|
2635
|
+
script: {
|
|
2636
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2637
|
+
params: { num: percValue( value ) },
|
|
2638
|
+
},
|
|
2639
|
+
},
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
if ( percQuery ) {
|
|
2644
|
+
searchQuery.query.bool.must.push( {
|
|
2645
|
+
nested: {
|
|
2646
|
+
path: 'mappingInfo',
|
|
2647
|
+
query: {
|
|
2648
|
+
bool: {
|
|
2649
|
+
must: [
|
|
2650
|
+
{ term: { 'mappingInfo.type': 'review' } },
|
|
2651
|
+
percQuery,
|
|
2652
|
+
],
|
|
346
2653
|
},
|
|
347
2654
|
},
|
|
348
2655
|
},
|
|
2656
|
+
} );
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
349
2659
|
|
|
350
|
-
|
|
351
|
-
|
|
2660
|
+
if ( inputData?.filterByApprover && inputData?.filterByApprover !== '' ) {
|
|
2661
|
+
let percQuery = null;
|
|
2662
|
+
const value = inputData.filterByApprover;
|
|
2663
|
+
|
|
2664
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2665
|
+
const percValue = ( val ) => {
|
|
2666
|
+
if ( typeof val === 'string' ) {
|
|
2667
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2668
|
+
}
|
|
2669
|
+
return parseFloat( val );
|
|
352
2670
|
};
|
|
2671
|
+
|
|
2672
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2673
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2674
|
+
// "<90" or "<=90"
|
|
2675
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2676
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2677
|
+
percQuery = {
|
|
2678
|
+
script: {
|
|
2679
|
+
script: {
|
|
2680
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2681
|
+
params: { num },
|
|
2682
|
+
},
|
|
2683
|
+
},
|
|
2684
|
+
};
|
|
2685
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2686
|
+
// ">=90"
|
|
2687
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2688
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2689
|
+
percQuery = {
|
|
2690
|
+
script: {
|
|
2691
|
+
script: {
|
|
2692
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2693
|
+
params: { num },
|
|
2694
|
+
},
|
|
2695
|
+
},
|
|
2696
|
+
};
|
|
2697
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2698
|
+
// "50 to 90"
|
|
2699
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2700
|
+
const from = percValue( match[1] );
|
|
2701
|
+
const to = percValue( match[2] );
|
|
2702
|
+
percQuery = {
|
|
2703
|
+
script: {
|
|
2704
|
+
script: {
|
|
2705
|
+
source:
|
|
2706
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2707
|
+
params: { from, to },
|
|
2708
|
+
},
|
|
2709
|
+
},
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2713
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2714
|
+
percQuery = {
|
|
2715
|
+
script: {
|
|
2716
|
+
script: {
|
|
2717
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2718
|
+
params: { num: percValue( value ) },
|
|
2719
|
+
},
|
|
2720
|
+
},
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
if ( percQuery ) {
|
|
2725
|
+
searchQuery.query.bool.must.push( {
|
|
2726
|
+
nested: {
|
|
2727
|
+
path: 'mappingInfo',
|
|
2728
|
+
query: {
|
|
2729
|
+
bool: {
|
|
2730
|
+
must: [
|
|
2731
|
+
{ term: { 'mappingInfo.type': 'approve' } },
|
|
2732
|
+
percQuery,
|
|
2733
|
+
],
|
|
2734
|
+
},
|
|
2735
|
+
},
|
|
2736
|
+
},
|
|
2737
|
+
} );
|
|
2738
|
+
}
|
|
353
2739
|
}
|
|
354
2740
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
'storeId',
|
|
359
|
-
'ticketId',
|
|
360
|
-
'createdAt',
|
|
361
|
-
'updatedAt',
|
|
362
|
-
'footfallCount',
|
|
363
|
-
'duplicateCount',
|
|
364
|
-
'employeeCount',
|
|
365
|
-
'houseKeepingCount',
|
|
366
|
-
'junkCount',
|
|
367
|
-
'employeeACCount',
|
|
368
|
-
'duplicateACCount',
|
|
369
|
-
'houseKeepingACCount',
|
|
370
|
-
'junkACCount',
|
|
371
|
-
'status',
|
|
372
|
-
'dateString',
|
|
373
|
-
],
|
|
374
|
-
'from': offset,
|
|
375
|
-
'size': limit,
|
|
376
|
-
'query': {
|
|
377
|
-
'bool': search,
|
|
378
|
-
},
|
|
379
|
-
'sort': [
|
|
380
|
-
{ dateString: { order: 'desc' } },
|
|
381
|
-
],
|
|
382
|
-
};
|
|
2741
|
+
if ( inputData?.filterByTango && inputData?.filterByTango !== '' ) {
|
|
2742
|
+
let percQuery = null;
|
|
2743
|
+
const value = inputData.filterByTango;
|
|
383
2744
|
|
|
384
|
-
|
|
385
|
-
|
|
2745
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2746
|
+
const percValue = ( val ) => {
|
|
2747
|
+
if ( typeof val === 'string' ) {
|
|
2748
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2749
|
+
}
|
|
2750
|
+
return parseFloat( val );
|
|
2751
|
+
};
|
|
386
2752
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
2753
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2754
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2755
|
+
// "<90" or "<=90"
|
|
2756
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2757
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2758
|
+
percQuery = {
|
|
2759
|
+
script: {
|
|
2760
|
+
script: {
|
|
2761
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2762
|
+
params: { num },
|
|
2763
|
+
},
|
|
2764
|
+
},
|
|
2765
|
+
};
|
|
2766
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2767
|
+
// ">=90"
|
|
2768
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2769
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2770
|
+
percQuery = {
|
|
2771
|
+
script: {
|
|
2772
|
+
script: {
|
|
2773
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2774
|
+
params: { num },
|
|
2775
|
+
},
|
|
2776
|
+
},
|
|
2777
|
+
};
|
|
2778
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2779
|
+
// "50 to 90"
|
|
2780
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2781
|
+
const from = percValue( match[1] );
|
|
2782
|
+
const to = percValue( match[2] );
|
|
2783
|
+
percQuery = {
|
|
2784
|
+
script: {
|
|
2785
|
+
script: {
|
|
2786
|
+
source:
|
|
2787
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2788
|
+
params: { from, to },
|
|
2789
|
+
},
|
|
2790
|
+
},
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2794
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2795
|
+
percQuery = {
|
|
2796
|
+
script: {
|
|
2797
|
+
script: {
|
|
2798
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2799
|
+
params: { num: percValue( value ) },
|
|
2800
|
+
},
|
|
2801
|
+
},
|
|
2802
|
+
};
|
|
391
2803
|
}
|
|
392
2804
|
|
|
2805
|
+
if ( percQuery ) {
|
|
2806
|
+
searchQuery.query.bool.must.push( {
|
|
2807
|
+
nested: {
|
|
2808
|
+
path: 'mappingInfo',
|
|
2809
|
+
query: {
|
|
2810
|
+
bool: {
|
|
2811
|
+
must: [
|
|
2812
|
+
{ term: { 'mappingInfo.type': 'tangoreview' } },
|
|
2813
|
+
percQuery,
|
|
2814
|
+
],
|
|
2815
|
+
},
|
|
2816
|
+
},
|
|
2817
|
+
},
|
|
2818
|
+
} );
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
393
2821
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
'
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
'duplicateACCount',
|
|
409
|
-
'houseKeepingACCount',
|
|
410
|
-
'junkACCount',
|
|
411
|
-
'dateString',
|
|
412
|
-
],
|
|
413
|
-
'from': offset,
|
|
414
|
-
'size': limit,
|
|
415
|
-
'query': {
|
|
416
|
-
'bool': search,
|
|
2822
|
+
if ( inputData?.filterByReviewedBy && inputData?.filterByReviewedBy !== '' ) {
|
|
2823
|
+
inputData.filterByReviewedBy = inputData?.filterByReviewedBy?.split( ',' );
|
|
2824
|
+
searchQuery.query.bool.must.push( {
|
|
2825
|
+
nested: {
|
|
2826
|
+
path: 'mappingInfo',
|
|
2827
|
+
query: {
|
|
2828
|
+
bool: {
|
|
2829
|
+
must: [
|
|
2830
|
+
{ term: { 'mappingInfo.type': 'review' } },
|
|
2831
|
+
{ terms: { 'mappingInfo.createdByEmail': inputData?.filterByReviewedBy } },
|
|
2832
|
+
|
|
2833
|
+
],
|
|
2834
|
+
},
|
|
2835
|
+
},
|
|
417
2836
|
},
|
|
418
|
-
|
|
419
|
-
{ [sortByValue]: { order: order === -1 ? 'desc' : 'asc' } },
|
|
420
|
-
],
|
|
421
|
-
};
|
|
2837
|
+
} );
|
|
422
2838
|
}
|
|
423
2839
|
|
|
424
|
-
if ( inputData
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
'
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
'houseKeepingCount',
|
|
439
|
-
'junkCount',
|
|
440
|
-
'junkACCount',
|
|
441
|
-
'status',
|
|
442
|
-
'dateString',
|
|
443
|
-
],
|
|
444
|
-
'from': 0,
|
|
445
|
-
'size': 10000,
|
|
446
|
-
'query': {
|
|
447
|
-
'bool': search,
|
|
2840
|
+
if ( inputData?.fileterByApprovedBy && inputData?.fileterByApprovedBy !== '' ) {
|
|
2841
|
+
inputData.fileterByApprovedBy = inputData?.fileterByApprovedBy?.split( ',' );
|
|
2842
|
+
searchQuery.query.bool.must.push( {
|
|
2843
|
+
nested: {
|
|
2844
|
+
path: 'mappingInfo',
|
|
2845
|
+
query: {
|
|
2846
|
+
bool: {
|
|
2847
|
+
must: [
|
|
2848
|
+
{ term: { 'mappingInfo.type': 'approve' } },
|
|
2849
|
+
{ terms: { 'mappingInfo.createdByEmail': inputData?.fileterByApprovedBy } },
|
|
2850
|
+
|
|
2851
|
+
],
|
|
2852
|
+
},
|
|
2853
|
+
},
|
|
448
2854
|
},
|
|
449
|
-
|
|
450
|
-
{ 'storeName.keyword': { order: 'desc' } },
|
|
451
|
-
],
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
const getData = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
455
|
-
const count = getData?.body?.hits?.total?.value;
|
|
456
|
-
if ( !count || count == 0 ) {
|
|
457
|
-
return res.sendError( 'No data found', 204 );
|
|
458
|
-
}
|
|
459
|
-
const searchValue = getData?.body?.hits?.hits;
|
|
460
|
-
if ( !searchValue || searchValue?.length == 0 ) {
|
|
461
|
-
return res.sendError( 'No data found', 204 );
|
|
2855
|
+
} );
|
|
462
2856
|
}
|
|
463
2857
|
|
|
464
|
-
if ( inputData.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
2858
|
+
if ( req?.user?.userType === 'tango' && inputData.tangoType !== 'internal' ) {
|
|
2859
|
+
searchQuery.query.bool.must.push(
|
|
2860
|
+
{
|
|
2861
|
+
term: {
|
|
2862
|
+
'type.keyword': 'store',
|
|
2863
|
+
},
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
range: {
|
|
2867
|
+
reviced: {
|
|
2868
|
+
lt: 85,
|
|
2869
|
+
},
|
|
2870
|
+
},
|
|
2871
|
+
},
|
|
2872
|
+
{
|
|
2873
|
+
nested: {
|
|
2874
|
+
path: 'mappingInfo',
|
|
2875
|
+
query: {
|
|
2876
|
+
bool: {
|
|
2877
|
+
must: [
|
|
2878
|
+
{
|
|
2879
|
+
term: {
|
|
2880
|
+
'mappingInfo.type': 'tangoreview',
|
|
2881
|
+
},
|
|
2882
|
+
},
|
|
2883
|
+
|
|
2884
|
+
],
|
|
2885
|
+
},
|
|
2886
|
+
},
|
|
2887
|
+
},
|
|
491
2888
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
2889
|
+
},
|
|
2890
|
+
|
|
2891
|
+
|
|
2892
|
+
);
|
|
2893
|
+
} else if ( req?.user?.userType === 'client' ) {
|
|
2894
|
+
searchQuery.query.bool.must.push(
|
|
2895
|
+
{
|
|
2896
|
+
term: {
|
|
2897
|
+
'type.keyword': 'store',
|
|
2898
|
+
},
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
nested: {
|
|
2902
|
+
path: 'mappingInfo',
|
|
2903
|
+
query: {
|
|
2904
|
+
bool: {
|
|
2905
|
+
must: [
|
|
2906
|
+
{
|
|
2907
|
+
term: {
|
|
2908
|
+
'mappingInfo.type': ticketsFeature ? 'review' : ticketsApproveFeature ?
|
|
2909
|
+
'approve' : 'tagging',
|
|
2910
|
+
},
|
|
2911
|
+
},
|
|
2912
|
+
|
|
2913
|
+
],
|
|
2914
|
+
},
|
|
2915
|
+
},
|
|
2916
|
+
},
|
|
2917
|
+
},
|
|
499
2918
|
|
|
500
|
-
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
501
2921
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
2922
|
+
if ( inputData?.permissionType ) {
|
|
2923
|
+
searchQuery.query.bool.must.push(
|
|
2924
|
+
{
|
|
2925
|
+
nested: {
|
|
2926
|
+
path: 'mappingInfo',
|
|
2927
|
+
query: {
|
|
2928
|
+
bool: {
|
|
2929
|
+
must: [
|
|
2930
|
+
{
|
|
2931
|
+
term: {
|
|
2932
|
+
'mappingInfo.type': inputData?.permissionType == 'review' ? 'review' :
|
|
2933
|
+
'approve',
|
|
2934
|
+
},
|
|
2935
|
+
},
|
|
2936
|
+
|
|
2937
|
+
],
|
|
2938
|
+
},
|
|
2939
|
+
},
|
|
2940
|
+
},
|
|
2941
|
+
},
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
506
2944
|
|
|
507
|
-
|
|
2945
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
2946
|
+
searchQuery.query.bool['should'] = [];
|
|
2947
|
+
searchQuery.query.bool.should = [
|
|
508
2948
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
2949
|
+
{
|
|
2950
|
+
'wildcard': {
|
|
2951
|
+
'storeName.keyword': {
|
|
2952
|
+
'value': `*${inputData.searchValue}*`,
|
|
2953
|
+
},
|
|
2954
|
+
},
|
|
2955
|
+
},
|
|
2956
|
+
{
|
|
2957
|
+
'wildcard': {
|
|
2958
|
+
'storeId.keyword': {
|
|
2959
|
+
'value': `*${inputData.searchValue}*`,
|
|
2960
|
+
},
|
|
2961
|
+
},
|
|
2962
|
+
},
|
|
2963
|
+
{
|
|
2964
|
+
'wildcard': {
|
|
2965
|
+
'ticketId.keyword': {
|
|
2966
|
+
'value': `*${inputData.searchValue}*`,
|
|
2967
|
+
},
|
|
2968
|
+
},
|
|
515
2969
|
},
|
|
516
|
-
},
|
|
517
|
-
};
|
|
518
2970
|
|
|
519
|
-
// Example: Filtering by storeId if present in the query
|
|
520
|
-
if ( inputData.storeId ) {
|
|
521
|
-
searchQuery.query.bool.must.push( {
|
|
522
|
-
term: { 'storeId.keyword': inputData.storeId },
|
|
523
|
-
} );
|
|
524
|
-
}
|
|
525
|
-
// Example: Filtering by status if provided
|
|
526
|
-
if ( inputData.status ) {
|
|
527
|
-
searchQuery.query.bool.must.push( {
|
|
528
|
-
term: { 'status.keyword': inputData.status },
|
|
529
|
-
} );
|
|
530
|
-
}
|
|
531
2971
|
|
|
2972
|
+
];
|
|
2973
|
+
searchQuery.query.bool['minimum_should_match'] = 1;
|
|
2974
|
+
}
|
|
532
2975
|
// You can add more filters as needed
|
|
533
|
-
logger.info( { searchQuery, index: openSearch.footfallDirectory } );
|
|
534
2976
|
const searchResult = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
535
|
-
|
|
536
|
-
|
|
537
2977
|
const count = searchResult?.body?.hits?.total?.value || 0;
|
|
2978
|
+
logger.info( { searchResult } );
|
|
538
2979
|
if ( count === 0 ) {
|
|
539
2980
|
return res.sendError( 'no data found', 204 );
|
|
540
2981
|
}
|
|
541
2982
|
const ticketListData = searchResult?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
542
2983
|
|
|
543
|
-
let temp =[];
|
|
544
|
-
if ( req.user.userType
|
|
545
|
-
if ( inputData.
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
2984
|
+
let temp = [];
|
|
2985
|
+
if ( req.user.userType === 'tango' ) {
|
|
2986
|
+
if ( inputData.tangoType === 'store' ) {
|
|
2987
|
+
if ( inputData?.isExport ) {
|
|
2988
|
+
const exportData = [];
|
|
2989
|
+
for ( let item of ticketListData ) {
|
|
2990
|
+
exportData.push( {
|
|
2991
|
+
|
|
2992
|
+
'Ticket ID': item?.ticketId,
|
|
2993
|
+
'store Name': item?.storeName,
|
|
2994
|
+
'store ID': item?.storeId,
|
|
2995
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
|
|
2996
|
+
'Issue Date': item?.dateString,
|
|
2997
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
|
|
2998
|
+
'Actual FF': item?.footfallCount,
|
|
2999
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3000
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3001
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3002
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3003
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3004
|
+
'Comments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
|
|
3005
|
+
'Sub Comments': item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
|
|
3006
|
+
|
|
3007
|
+
} );
|
|
3008
|
+
}
|
|
3009
|
+
return await download( exportData, res );
|
|
3010
|
+
} else {
|
|
3011
|
+
for ( let item of ticketListData ) {
|
|
3012
|
+
temp.push( {
|
|
3013
|
+
|
|
3014
|
+
ticketId: item?.ticketId,
|
|
3015
|
+
storeId: item?.storeId,
|
|
3016
|
+
storeName: item?.storeName,
|
|
3017
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
|
|
3018
|
+
issueDate: item?.dateString,
|
|
3019
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
|
|
3020
|
+
footfall: item?.footfallCount,
|
|
3021
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3022
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3023
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3024
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3025
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3026
|
+
comments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.comments || '--',
|
|
3027
|
+
subComments: item?.mappingInfo?.find( ( f ) => f.type === 'finalRevision' )?.subComments || '--',
|
|
3028
|
+
|
|
3029
|
+
} );
|
|
3030
|
+
}
|
|
564
3031
|
}
|
|
565
3032
|
} else {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
3033
|
+
if ( inputData?.isExport ) {
|
|
3034
|
+
const exportData = [];
|
|
3035
|
+
for ( let item of ticketListData ) {
|
|
3036
|
+
exportData.push( {
|
|
3037
|
+
|
|
3038
|
+
'Ticket ID': item?.ticketId,
|
|
3039
|
+
'Store Name': item?.storeName,
|
|
3040
|
+
'Store ID': item?.storeId,
|
|
3041
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
3042
|
+
'Issue Date': item?.dateString,
|
|
3043
|
+
'Ticket Type': item?.type,
|
|
3044
|
+
'Actual FF': item?.footfallCount,
|
|
3045
|
+
// Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevisoon'
|
|
3046
|
+
'Due Date': ( () => {
|
|
3047
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3048
|
+
const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
|
|
3049
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3050
|
+
}
|
|
3051
|
+
return '';
|
|
3052
|
+
} )(),
|
|
3053
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3054
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3055
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3056
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3057
|
+
'Ticket Status': item?.status,
|
|
3058
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3059
|
+
|
|
3060
|
+
} );
|
|
3061
|
+
}
|
|
3062
|
+
return await download( exportData, res );
|
|
3063
|
+
} else {
|
|
3064
|
+
for ( let item of ticketListData ) {
|
|
3065
|
+
temp.push( {
|
|
3066
|
+
|
|
3067
|
+
ticketId: item?.ticketId,
|
|
3068
|
+
storeId: item?.storeId,
|
|
3069
|
+
storeName: item?.storeName,
|
|
3070
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
3071
|
+
issueDate: item?.dateString,
|
|
3072
|
+
footfall: item?.footfallCount,
|
|
3073
|
+
dueDate: ( () => {
|
|
3074
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3075
|
+
const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
|
|
3076
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3077
|
+
}
|
|
3078
|
+
return '';
|
|
3079
|
+
} )(),
|
|
3080
|
+
type: item?.type || 'store',
|
|
3081
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3082
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3083
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3084
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3085
|
+
status: item?.status,
|
|
3086
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3087
|
+
|
|
3088
|
+
} );
|
|
3089
|
+
}
|
|
586
3090
|
}
|
|
587
3091
|
}
|
|
588
3092
|
} else {
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
3093
|
+
if ( inputData?.permissionType === 'approve' ) {
|
|
3094
|
+
if ( inputData.exportData ) {
|
|
3095
|
+
const exportData = [];
|
|
3096
|
+
for ( let item of ticketListData ) {
|
|
3097
|
+
exportData.push( {
|
|
3098
|
+
|
|
3099
|
+
'Ticket ID': item?.ticketId,
|
|
3100
|
+
'Store Name': item?.storeName,
|
|
3101
|
+
'Store ID': item?.storeId,
|
|
3102
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3103
|
+
'Issue Date': item?.dateString,
|
|
3104
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3105
|
+
'Actual FF': item?.footfallCount,
|
|
3106
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3107
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3108
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3109
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3110
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3111
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3112
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3113
|
+
|
|
3114
|
+
} );
|
|
3115
|
+
}
|
|
3116
|
+
return await download( exportData, res );
|
|
3117
|
+
} else {
|
|
3118
|
+
for ( let item of ticketListData ) {
|
|
3119
|
+
temp.push( {
|
|
3120
|
+
|
|
3121
|
+
ticketId: item?.ticketId,
|
|
3122
|
+
storeId: item?.storeId,
|
|
3123
|
+
storeName: item?.storeName,
|
|
3124
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3125
|
+
issueDate: item?.dateString,
|
|
3126
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3127
|
+
footfall: item?.footfallCount,
|
|
3128
|
+
type: item.type || 'store',
|
|
3129
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3130
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3131
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3132
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3133
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3134
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3135
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3136
|
+
|
|
3137
|
+
} );
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
} else if ( inputData?.permissionType === 'review' ) {
|
|
3141
|
+
if ( inputData?.isExport ) {
|
|
3142
|
+
const exportData = [];
|
|
3143
|
+
for ( let item of ticketListData ) {
|
|
3144
|
+
exportData.push( {
|
|
3145
|
+
|
|
3146
|
+
'Ticket ID': item?.ticketId,
|
|
3147
|
+
'Store Name': item?.storeName,
|
|
3148
|
+
'Store ID': item?.storeId,
|
|
3149
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3150
|
+
'Issue Date': item?.dateString,
|
|
3151
|
+
'Actual FF': item?.footfallCount,
|
|
3152
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3153
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3154
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3155
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3156
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3157
|
+
|
|
3158
|
+
} );
|
|
3159
|
+
}
|
|
3160
|
+
return await download( exportData, res );
|
|
3161
|
+
} else {
|
|
3162
|
+
for ( let item of ticketListData ) {
|
|
3163
|
+
temp.push( {
|
|
3164
|
+
|
|
3165
|
+
ticketId: item?.ticketId,
|
|
3166
|
+
storeId: item?.storeId,
|
|
3167
|
+
storeName: item?.storeName,
|
|
3168
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3169
|
+
issueDate: item?.dateString,
|
|
3170
|
+
footfall: item?.footfallCount,
|
|
3171
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3172
|
+
type: item.type || 'store',
|
|
3173
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3174
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3175
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3176
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3177
|
+
|
|
3178
|
+
} );
|
|
3179
|
+
}
|
|
612
3180
|
}
|
|
613
3181
|
} else if ( req.user.role === 'user' ) {
|
|
614
3182
|
temp = [];
|
|
615
3183
|
} else if ( ticketsFeature ) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
3184
|
+
if ( inputData?.isExport ) {
|
|
3185
|
+
const exportData = [];
|
|
3186
|
+
for ( let item of ticketListData ) {
|
|
3187
|
+
exportData.push( {
|
|
3188
|
+
|
|
3189
|
+
'Ticket ID': item?.ticketId,
|
|
3190
|
+
'Store ID': item?.storeId,
|
|
3191
|
+
'Store Name': item?.storeName,
|
|
3192
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3193
|
+
'Issue Date': item?.dateString,
|
|
3194
|
+
'Actual FF': item?.footfallCount,
|
|
3195
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3196
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3197
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3198
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3199
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3200
|
+
|
|
3201
|
+
} );
|
|
3202
|
+
}
|
|
3203
|
+
return await download( exportData, res );
|
|
3204
|
+
} else {
|
|
3205
|
+
for ( let item of ticketListData ) {
|
|
3206
|
+
temp.push( {
|
|
3207
|
+
|
|
3208
|
+
ticketId: item?.ticketId,
|
|
3209
|
+
storeId: item?.storeId,
|
|
3210
|
+
storeName: item?.storeName,
|
|
3211
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3212
|
+
issueDate: item?.dateString,
|
|
3213
|
+
footfall: item?.footfallCount,
|
|
3214
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3215
|
+
type: item.type || 'store',
|
|
3216
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3217
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3218
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3219
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3220
|
+
|
|
3221
|
+
} );
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
} else if ( ticketsApproveFeature ) {
|
|
3225
|
+
if ( inputData.isExport ) {
|
|
3226
|
+
const exportData = [];
|
|
3227
|
+
for ( let item of ticketListData ) {
|
|
3228
|
+
exportData.push( {
|
|
3229
|
+
|
|
3230
|
+
'Ticket ID': item?.ticketId,
|
|
3231
|
+
'Store Name': item?.storeName,
|
|
3232
|
+
'Store ID': item?.storeId,
|
|
3233
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3234
|
+
'Issue Date': item?.dateString,
|
|
3235
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3236
|
+
'Actual FF': item?.footfallCount,
|
|
3237
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3238
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3239
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3240
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3241
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3242
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3243
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3244
|
+
|
|
3245
|
+
} );
|
|
3246
|
+
}
|
|
3247
|
+
return await download( exportData, res );
|
|
3248
|
+
} else {
|
|
3249
|
+
for ( let item of ticketListData ) {
|
|
3250
|
+
temp.push( {
|
|
3251
|
+
|
|
3252
|
+
ticketId: item?.ticketId,
|
|
3253
|
+
storeId: item?.storeId,
|
|
3254
|
+
storeName: item?.storeName,
|
|
3255
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3256
|
+
issueDate: item?.dateString,
|
|
3257
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3258
|
+
footfall: item?.footfallCount,
|
|
3259
|
+
type: item.type || 'store',
|
|
3260
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3261
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3262
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3263
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3264
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3265
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3266
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3267
|
+
|
|
3268
|
+
} );
|
|
3269
|
+
}
|
|
634
3270
|
}
|
|
635
3271
|
} else {
|
|
636
|
-
temp =[];
|
|
3272
|
+
temp = [];
|
|
637
3273
|
}
|
|
638
3274
|
}
|
|
639
3275
|
|
|
640
|
-
return res.sendSuccess( { result: temp } );
|
|
3276
|
+
return res.sendSuccess( { result: temp, count: count } );
|
|
641
3277
|
} catch ( error ) {
|
|
642
3278
|
const err = error.message || 'Internal Server Error';
|
|
643
3279
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -649,149 +3285,19 @@ export async function getTickets( req, res ) {
|
|
|
649
3285
|
try {
|
|
650
3286
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
651
3287
|
const inputData = req.query;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
logger.info( { inputData: inputData, limit: limit, skip: skip } );
|
|
656
|
-
let source = [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail' ];
|
|
3288
|
+
|
|
3289
|
+
|
|
3290
|
+
let source = [ 'storeId', 'type', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail', 'type' ];
|
|
657
3291
|
let filter = [
|
|
658
3292
|
|
|
659
3293
|
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
gte: inputData.fromDate,
|
|
663
|
-
lte: inputData.toDate,
|
|
664
|
-
format: 'yyyy-MM-dd',
|
|
665
|
-
},
|
|
3294
|
+
term: {
|
|
3295
|
+
'ticketId.keyword': inputData.ticketId,
|
|
666
3296
|
},
|
|
667
3297
|
},
|
|
668
3298
|
];
|
|
669
|
-
if ( inputData?.storeId ) {
|
|
670
|
-
filter.push(
|
|
671
|
-
{
|
|
672
|
-
terms: {
|
|
673
|
-
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
674
|
-
inputData.storeId :
|
|
675
|
-
inputData.storeId,
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
if ( inputData?.dateString ) {
|
|
681
|
-
filter.push(
|
|
682
|
-
{
|
|
683
|
-
term: {
|
|
684
|
-
'dateString': inputData.dateString,
|
|
685
|
-
},
|
|
686
|
-
},
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
if ( inputData.status ) {
|
|
690
|
-
filter.push(
|
|
691
|
-
{
|
|
692
|
-
term: {
|
|
693
|
-
'status.keyword': inputData.status,
|
|
694
|
-
},
|
|
695
|
-
},
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
if (
|
|
699
|
-
inputData.revopsType
|
|
700
|
-
) {
|
|
701
|
-
inputData.revopsType === 'employee' ?
|
|
702
|
-
filter.push( {
|
|
703
|
-
range: {
|
|
704
|
-
employeeCount: {
|
|
705
|
-
gt: 0,
|
|
706
|
-
},
|
|
707
|
-
},
|
|
708
|
-
} ) :
|
|
709
|
-
inputData.revopsType === 'houseKeeping' ?
|
|
710
|
-
filter.push( {
|
|
711
|
-
range: {
|
|
712
|
-
houseKeepingCount: {
|
|
713
|
-
gt: 0,
|
|
714
|
-
},
|
|
715
|
-
},
|
|
716
|
-
} ) :
|
|
717
|
-
inputData.revopsType === 'junk' ?
|
|
718
|
-
filter.push( {
|
|
719
|
-
range: {
|
|
720
|
-
junkCount: {
|
|
721
|
-
gt: 0,
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
} ) :
|
|
725
|
-
filter.push( {
|
|
726
|
-
range: {
|
|
727
|
-
duplicateCount: {
|
|
728
|
-
gt: 0,
|
|
729
|
-
},
|
|
730
|
-
},
|
|
731
|
-
} );
|
|
732
|
-
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'email', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkStatus', 'junkACCount', 'approverRole', 'approverUserName', 'approverEmail' ] :
|
|
733
|
-
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkStatus', 'junkACCount', 'approverRole', 'approverUserName', 'approverEmail' ] :
|
|
734
|
-
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkStatus', 'junkACCount', 'approverRole', 'approverUserName', 'approverEmail' ] :
|
|
735
|
-
inputData.revopsType == 'junk' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'duplicateCount', 'comments', 'junk', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkACCount', 'junkStatus', 'approverRole', 'approverUserName', 'approverEmail' ] : [];
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if ( inputData.action ) {
|
|
739
|
-
filter.push( {
|
|
740
|
-
bool: {
|
|
741
|
-
should: [
|
|
742
|
-
{
|
|
743
|
-
constant_score: {
|
|
744
|
-
filter: { term: { 'houseKeepingStatus.keyword': inputData.action } },
|
|
745
|
-
boost: 1,
|
|
746
|
-
_name: 'matched_housekeeping',
|
|
747
|
-
},
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
constant_score: {
|
|
751
|
-
filter: { term: { 'employeeStatus.keyword': inputData.action } },
|
|
752
|
-
boost: 1,
|
|
753
|
-
_name: 'matched_employee',
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
constant_score: {
|
|
758
|
-
filter: { term: { 'duplicateStatus.keyword': inputData.action } },
|
|
759
|
-
boost: 1,
|
|
760
|
-
_name: 'matched_duplicate',
|
|
761
|
-
},
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
constant_score: {
|
|
765
|
-
filter: { term: { 'junkStatus.keyword': inputData.action } },
|
|
766
|
-
boost: 1,
|
|
767
|
-
_name: 'matched_junk',
|
|
768
|
-
},
|
|
769
|
-
},
|
|
770
|
-
],
|
|
771
|
-
minimum_should_match: 1,
|
|
772
|
-
},
|
|
773
|
-
} );
|
|
774
|
-
}
|
|
775
3299
|
|
|
776
3300
|
|
|
777
|
-
let getRevCount = {};
|
|
778
|
-
if ( inputData.revopsType ) {
|
|
779
|
-
getRevCount = {
|
|
780
|
-
size: 0,
|
|
781
|
-
query: {
|
|
782
|
-
bool: {
|
|
783
|
-
filter: filter,
|
|
784
|
-
},
|
|
785
|
-
},
|
|
786
|
-
aggs: {
|
|
787
|
-
totalCount: {
|
|
788
|
-
sum: {
|
|
789
|
-
field: inputData.revopsType == 'employee' ? 'employeeCount' : inputData.revopsType == 'houseKeeping' ? 'houseKeepingCount' :inputData.revopsType == 'junk' ? 'junkCount': 'duplicateCount',
|
|
790
|
-
},
|
|
791
|
-
},
|
|
792
|
-
},
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
3301
|
const getCount = {
|
|
796
3302
|
query: {
|
|
797
3303
|
bool: {
|
|
@@ -802,20 +3308,14 @@ export async function getTickets( req, res ) {
|
|
|
802
3308
|
|
|
803
3309
|
|
|
804
3310
|
const geteDataCount = await getOpenSearchCount( openSearch.footfallDirectory, getCount );
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
if ( inputData.storeId?.length > 0 ) {
|
|
810
|
-
const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1 } );
|
|
811
|
-
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
812
|
-
}
|
|
813
|
-
return res.sendError( 'No data found', 204 );
|
|
3311
|
+
|
|
3312
|
+
|
|
3313
|
+
if ( !geteDataCount ) {
|
|
3314
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
814
3315
|
}
|
|
815
3316
|
|
|
816
3317
|
const getQuery = {
|
|
817
|
-
size:
|
|
818
|
-
from: skip,
|
|
3318
|
+
size: 1,
|
|
819
3319
|
query: {
|
|
820
3320
|
bool: {
|
|
821
3321
|
filter: filter,
|
|
@@ -825,14 +3325,9 @@ export async function getTickets( req, res ) {
|
|
|
825
3325
|
};
|
|
826
3326
|
|
|
827
3327
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
828
|
-
|
|
829
3328
|
const response = getData?.body?.hits?.hits;
|
|
830
3329
|
if ( !response || response.length == 0 ) {
|
|
831
|
-
|
|
832
|
-
const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1 } );
|
|
833
|
-
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
834
|
-
}
|
|
835
|
-
return res.sendError( 'No data', 204 );
|
|
3330
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
836
3331
|
}
|
|
837
3332
|
let temp = [];
|
|
838
3333
|
if ( inputData?.action ) {
|
|
@@ -870,29 +3365,31 @@ export async function getTickets( req, res ) {
|
|
|
870
3365
|
approverUserName: hit?._source?.approverUserName,
|
|
871
3366
|
approverEmail: hit?._source?.approverEmail,
|
|
872
3367
|
approverRole: hit?._source?.approverRole,
|
|
3368
|
+
type: hit?._source?.type,
|
|
873
3369
|
};
|
|
874
3370
|
let result;
|
|
875
3371
|
|
|
3372
|
+
|
|
876
3373
|
const matched = hit.matched_queries;
|
|
877
3374
|
// Add only matched data array
|
|
878
|
-
if ( matched.includes( 'matched_employee' )&& ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
3375
|
+
if ( matched.includes( 'matched_employee' ) && ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
879
3376
|
result = defaultData;
|
|
880
3377
|
result.employee = hit?._source?.employee;
|
|
881
3378
|
// result.type = 'employee';
|
|
882
3379
|
result.matched = matched;
|
|
883
3380
|
}
|
|
884
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
3381
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
885
3382
|
result = defaultData;
|
|
886
3383
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
887
3384
|
result.matched = matched;
|
|
888
3385
|
}
|
|
889
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
3386
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
890
3387
|
result = defaultData;
|
|
891
3388
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
892
3389
|
result.matched = matched;
|
|
893
3390
|
}
|
|
894
3391
|
|
|
895
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
3392
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
896
3393
|
result = defaultData;
|
|
897
3394
|
result.junk = hit?._source?.junk;
|
|
898
3395
|
result.matched = matched;
|
|
@@ -911,15 +3408,264 @@ export async function getTickets( req, res ) {
|
|
|
911
3408
|
}
|
|
912
3409
|
} );
|
|
913
3410
|
}
|
|
3411
|
+
|
|
914
3412
|
const finalResponse = inputData.action ? temp : response;
|
|
3413
|
+
const getRevopQuery = {
|
|
3414
|
+
size: 10000,
|
|
3415
|
+
query: {
|
|
3416
|
+
bool: {
|
|
3417
|
+
must: [
|
|
3418
|
+
{ term: { 'storeId.keyword': response?.[0]?._source?.storeId } }, // assuming inputData.storeId is an array
|
|
3419
|
+
{ term: { 'dateString': response?.[0]?._source?.dateString } },
|
|
3420
|
+
],
|
|
3421
|
+
},
|
|
3422
|
+
},
|
|
3423
|
+
};
|
|
3424
|
+
|
|
3425
|
+
|
|
3426
|
+
const revopResp = await getOpenSearchData( openSearch.revop, getRevopQuery );
|
|
3427
|
+
|
|
3428
|
+
|
|
3429
|
+
// Map revopResp.body.hits.hits to revopSources
|
|
3430
|
+
const revopSources = Array.isArray( revopResp?.body?.hits?.hits ) ?
|
|
3431
|
+
revopResp?.body?.hits?.hits?.map( ( hit ) => hit._source ) :
|
|
3432
|
+
[];
|
|
3433
|
+
|
|
3434
|
+
// Create a map of revopSources by id for quick lookup
|
|
3435
|
+
const revopSourcesMap = new Map();
|
|
3436
|
+
revopSources.forEach( ( item ) => {
|
|
3437
|
+
if ( item?.id ) {
|
|
3438
|
+
revopSourcesMap.set( String( item.id ), item );
|
|
3439
|
+
}
|
|
3440
|
+
} );
|
|
3441
|
+
|
|
3442
|
+
// Process revopSources to replace duplicateImage entries with full objects from the array
|
|
3443
|
+
const processedRevopSources = revopSources.map( ( item ) => {
|
|
3444
|
+
// Check if this is a duplicate parent item
|
|
3445
|
+
if ( item?.revopsType === 'duplicate' && item?.isParent === true && Array.isArray( item?.duplicateImage ) ) {
|
|
3446
|
+
// Map each duplicateImage entry to the full object from revopSources
|
|
3447
|
+
const updatedDuplicateImage = item.duplicateImage.map( ( duplicateImg ) => {
|
|
3448
|
+
const duplicateId = String( duplicateImg?.id );
|
|
3449
|
+
// Find the full object in revopSources that matches this id
|
|
3450
|
+
const fullObject = revopSourcesMap.get( duplicateId );
|
|
3451
|
+
// Return the full object if found, otherwise return the original duplicateImg
|
|
3452
|
+
return fullObject || duplicateImg;
|
|
3453
|
+
} );
|
|
3454
|
+
|
|
3455
|
+
return {
|
|
3456
|
+
...item,
|
|
3457
|
+
duplicateImage: updatedDuplicateImage,
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
// Return item as-is if it doesn't meet the criteria
|
|
3461
|
+
return item;
|
|
3462
|
+
} );
|
|
3463
|
+
|
|
915
3464
|
if ( finalResponse?.length == 0 || !finalResponse ) {
|
|
916
3465
|
if ( inputData.storeId?.length > 0 ) {
|
|
917
|
-
const getStoreName = await findOneStore( { storeId:
|
|
3466
|
+
const getStoreName = await findOneStore( { storeId: response?.[0]?._source?.storeId }, { storeName: 1 } );
|
|
918
3467
|
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
919
3468
|
}
|
|
920
3469
|
return res.sendError( 'No Data found', 204 );
|
|
921
3470
|
}
|
|
922
|
-
|
|
3471
|
+
|
|
3472
|
+
|
|
3473
|
+
// replace the mappingInfo.revisedDetail with processedRevopSources
|
|
3474
|
+
if ( Array.isArray( finalResponse ) ) {
|
|
3475
|
+
for ( let item of finalResponse ) {
|
|
3476
|
+
if (
|
|
3477
|
+
item &&
|
|
3478
|
+
item._source &&
|
|
3479
|
+
item._source.mappingInfo
|
|
3480
|
+
|
|
3481
|
+
) {
|
|
3482
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3483
|
+
const vmsCommentsLogIndex = openSearch.vmsCommentsLog || 'vms-comments-log-dev';
|
|
3484
|
+
let commentsResponse = [];
|
|
3485
|
+
const commentsFilter = [
|
|
3486
|
+
{ term: { 'storeId.keyword': item?._source?.storeId } },
|
|
3487
|
+
{ term: { dateString: item?._source?.dateString } },
|
|
3488
|
+
|
|
3489
|
+
];
|
|
3490
|
+
|
|
3491
|
+
const commentsQuery = {
|
|
3492
|
+
size: 10000,
|
|
3493
|
+
sort: [
|
|
3494
|
+
{ createdAt: { order: 'desc' } }, // Sort descending by createdAt
|
|
3495
|
+
],
|
|
3496
|
+
query: {
|
|
3497
|
+
bool: {
|
|
3498
|
+
filter: commentsFilter,
|
|
3499
|
+
},
|
|
3500
|
+
},
|
|
3501
|
+
};
|
|
3502
|
+
|
|
3503
|
+
const commentsRes = await getOpenSearchData( vmsCommentsLogIndex, commentsQuery );
|
|
3504
|
+
// If mappingInfo is an array, update revisedDetail for each mappingInfo object
|
|
3505
|
+
if ( Array.isArray( item._source.mappingInfo ) ) {
|
|
3506
|
+
item._source.mappingInfo.forEach( ( mappingObj ) => {
|
|
3507
|
+
commentsResponse = commentsRes?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
3508
|
+
|
|
3509
|
+
// Check if duplicate condition exists in commentsResponse
|
|
3510
|
+
|
|
3511
|
+
// Structure comments output
|
|
3512
|
+
let commentsDetails = [];
|
|
3513
|
+
|
|
3514
|
+
|
|
3515
|
+
const types = [ 'tagging', 'review', 'approve' ];
|
|
3516
|
+
|
|
3517
|
+
// Process each type
|
|
3518
|
+
types.forEach( ( typeValue ) => {
|
|
3519
|
+
if ( typeValue === 'tagging' ) {
|
|
3520
|
+
// For tagging, group by category and create separate objects for each category
|
|
3521
|
+
const taggingComments = commentsResponse.filter( ( c ) => c.type === typeValue );
|
|
3522
|
+
|
|
3523
|
+
// Group by category
|
|
3524
|
+
const categoryGroups = {};
|
|
3525
|
+
taggingComments.forEach( ( c ) => {
|
|
3526
|
+
const category = c.category || 'other';
|
|
3527
|
+
if ( !categoryGroups[category] ) {
|
|
3528
|
+
categoryGroups[category] = [];
|
|
3529
|
+
}
|
|
3530
|
+
categoryGroups[category].push( c );
|
|
3531
|
+
} );
|
|
3532
|
+
|
|
3533
|
+
// Create separate objects for each category
|
|
3534
|
+
Object.keys( categoryGroups ).forEach( ( category ) => {
|
|
3535
|
+
const categoryComments = categoryGroups[category];
|
|
3536
|
+
let parent = null;
|
|
3537
|
+
|
|
3538
|
+
const comms = categoryComments.map( ( c ) => {
|
|
3539
|
+
if ( category === 'duplicate' ) {
|
|
3540
|
+
if ( !parent && c.parent ) {
|
|
3541
|
+
parent = c.parent;
|
|
3542
|
+
}
|
|
3543
|
+
return {
|
|
3544
|
+
createdByEmail: c.createdByEmail,
|
|
3545
|
+
createdByUserName: c.createdByUserName,
|
|
3546
|
+
createdByRole: c.createdByRole,
|
|
3547
|
+
message: c.message,
|
|
3548
|
+
};
|
|
3549
|
+
} else {
|
|
3550
|
+
return {
|
|
3551
|
+
id: c.id,
|
|
3552
|
+
tempId: c.tempId,
|
|
3553
|
+
timeRange: c.timeRange,
|
|
3554
|
+
entryTime: c.entryTime,
|
|
3555
|
+
exitTime: c.exitTime,
|
|
3556
|
+
filePath: c.filePath,
|
|
3557
|
+
isChecked: c.isChecked,
|
|
3558
|
+
createdAt: c.createdAt,
|
|
3559
|
+
message: c.message,
|
|
3560
|
+
createdByEmail: c.createdByEmail,
|
|
3561
|
+
createdByUserName: c.createdByUserName,
|
|
3562
|
+
createdByRole: c.createdByRole,
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
} );
|
|
3566
|
+
|
|
3567
|
+
const taggingObj = {
|
|
3568
|
+
category: category,
|
|
3569
|
+
type: typeValue,
|
|
3570
|
+
comments: comms,
|
|
3571
|
+
};
|
|
3572
|
+
|
|
3573
|
+
// Add parent only for duplicate category
|
|
3574
|
+
if ( category === 'duplicate' && parent !== null ) {
|
|
3575
|
+
taggingObj.parent = parent;
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
commentsDetails.push( taggingObj );
|
|
3579
|
+
} );
|
|
3580
|
+
} else if ( typeValue === 'review' || typeValue === 'approve' ) {
|
|
3581
|
+
// For review and approve, keep existing structure
|
|
3582
|
+
const comms = commentsResponse
|
|
3583
|
+
.filter( ( c ) => c.type === typeValue )
|
|
3584
|
+
.map( ( c ) => {
|
|
3585
|
+
if ( c.category === 'duplicate' ) {
|
|
3586
|
+
return {
|
|
3587
|
+
parent: c?.taggedImages[0]?._source?.parent,
|
|
3588
|
+
category: c.category,
|
|
3589
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
3590
|
+
c.taggedImages.map( ( img ) => ( {
|
|
3591
|
+
id: img?._source?.id,
|
|
3592
|
+
tempId: img?._source?.tempId,
|
|
3593
|
+
timeRange: img?._source?.timeRange,
|
|
3594
|
+
entryTime: img?._source?.entryTime,
|
|
3595
|
+
exitTime: img?._source?.exitTime,
|
|
3596
|
+
filePath: img?._source?.filePath,
|
|
3597
|
+
isChecked: img?._source?.isChecked,
|
|
3598
|
+
} ) ) :
|
|
3599
|
+
[],
|
|
3600
|
+
createdByEmail: c.createdByEmail,
|
|
3601
|
+
createdByUserName: c.createdByUserName,
|
|
3602
|
+
createdByRole: c.createdByRole,
|
|
3603
|
+
status: c.status,
|
|
3604
|
+
message: c.message,
|
|
3605
|
+
};
|
|
3606
|
+
} else {
|
|
3607
|
+
return {
|
|
3608
|
+
category: c.category,
|
|
3609
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
3610
|
+
c.taggedImages.map( ( img ) => ( {
|
|
3611
|
+
id: img?._source?.id,
|
|
3612
|
+
tempId: img?._source?.tempId,
|
|
3613
|
+
timeRange: img?._source?.timeRange,
|
|
3614
|
+
entryTime: img?._source?.entryTime,
|
|
3615
|
+
exitTime: img?._source?.exitTime,
|
|
3616
|
+
filePath: img?._source?.filePath,
|
|
3617
|
+
isChecked: img?._source?.isChecked,
|
|
3618
|
+
} ) ) :
|
|
3619
|
+
[],
|
|
3620
|
+
createdByEmail: c.createdByEmail,
|
|
3621
|
+
createdByUserName: c.createdByUserName,
|
|
3622
|
+
createdByRole: c.createdByRole,
|
|
3623
|
+
status: c.status,
|
|
3624
|
+
message: c.message,
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
} );
|
|
3628
|
+
|
|
3629
|
+
// Only add if there are comments
|
|
3630
|
+
if ( comms.length > 0 ) {
|
|
3631
|
+
commentsDetails.push( {
|
|
3632
|
+
type: typeValue,
|
|
3633
|
+
comments: comms,
|
|
3634
|
+
} );
|
|
3635
|
+
} else {
|
|
3636
|
+
// Add empty comments array if no comments
|
|
3637
|
+
commentsDetails.push( {
|
|
3638
|
+
type: typeValue,
|
|
3639
|
+
comments: [],
|
|
3640
|
+
} );
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
} );
|
|
3644
|
+
|
|
3645
|
+
|
|
3646
|
+
item._source.commentsDetails = commentsDetails;
|
|
3647
|
+
|
|
3648
|
+
if (
|
|
3649
|
+
Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) &&
|
|
3650
|
+
mappingObj.type !== 'tangoreview'
|
|
3651
|
+
) {
|
|
3652
|
+
mappingObj.revisedDetail = processedRevopSources;
|
|
3653
|
+
}
|
|
3654
|
+
} );
|
|
3655
|
+
} else {
|
|
3656
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
} else if (
|
|
3661
|
+
finalResponse &&
|
|
3662
|
+
finalResponse._source &&
|
|
3663
|
+
finalResponse._source.mappingInfo
|
|
3664
|
+
) {
|
|
3665
|
+
finalResponse._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
return res.sendSuccess( { result: finalResponse } );
|
|
923
3669
|
} catch ( error ) {
|
|
924
3670
|
const err = error.message || 'Internal Server Error';
|
|
925
3671
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -991,7 +3737,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
991
3737
|
item.isChecked == true ? tempId.push( { tempId: item.tempId, timeRange: item.timeRange } ) : null;
|
|
992
3738
|
bulkBody.push(
|
|
993
3739
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${item.timeRange}_${item.tempId}` } },
|
|
994
|
-
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true? 'approved':'rejected' } },
|
|
3740
|
+
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true ? 'approved' : 'rejected' } },
|
|
995
3741
|
);
|
|
996
3742
|
} );
|
|
997
3743
|
}
|
|
@@ -1006,11 +3752,11 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1006
3752
|
updateData.employee = updatedEmployee;
|
|
1007
3753
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: updateData } );
|
|
1008
3754
|
for ( let employee of updateData?.employee ) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
3755
|
+
( employee.isChecked == true ) ? tempId.push( { tempId: employee.tempId, timeRange: employee.timeRange } ) : null;
|
|
3756
|
+
bulkBody.push(
|
|
3757
|
+
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${employee.timeRange}_${employee.tempId}` } },
|
|
3758
|
+
{ doc: { isChecked: employee.isChecked, status: employee?.isChecked == true ? 'approved' : 'rejected' } },
|
|
3759
|
+
);
|
|
1014
3760
|
}
|
|
1015
3761
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { employeeACCount: tempId?.length || 0 } } );
|
|
1016
3762
|
}
|
|
@@ -1029,7 +3775,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1029
3775
|
houseKeeping.isChecked == true ? tempId.push( { tempId: houseKeeping.tempId, timeRange: houseKeeping.timeRange } ) : null;
|
|
1030
3776
|
bulkBody.push(
|
|
1031
3777
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${houseKeeping.timeRange}_${houseKeeping.tempId}` } },
|
|
1032
|
-
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true? 'approved':'rejected' } },
|
|
3778
|
+
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1033
3779
|
);
|
|
1034
3780
|
}
|
|
1035
3781
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { houseKeepingACCount: tempId?.length || 0 } } );
|
|
@@ -1050,7 +3796,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1050
3796
|
junk.isChecked == true ? tempId.push( { tempId: junk.tempId, timeRange: junk.timeRange } ) : null;
|
|
1051
3797
|
bulkBody.push(
|
|
1052
3798
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${junk.timeRange}_${junk.tempId}` } },
|
|
1053
|
-
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true? 'approved':'rejected' } },
|
|
3799
|
+
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1054
3800
|
);
|
|
1055
3801
|
}
|
|
1056
3802
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { junkACCount: tempId?.length || 0 } } );
|
|
@@ -1075,7 +3821,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1075
3821
|
let getUpdateExistingOne = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
1076
3822
|
const tempIdList = await extractCheckedTempIds( getUpdateExistingOne?.body );
|
|
1077
3823
|
const isSendMessge = await sendSqsMessage( data, tempIdList, getStoreType, storeId );
|
|
1078
|
-
if ( isSendMessge ==true ) {
|
|
3824
|
+
if ( isSendMessge == true ) {
|
|
1079
3825
|
return true; // res.sendSuccess( 'Ticket has been updated successfully' );
|
|
1080
3826
|
} else {
|
|
1081
3827
|
return false; // res.sendError( 'No SQS message sent', 500 );
|
|
@@ -1138,7 +3884,7 @@ function updateEmployeeCheckFlags( existingEmployees, inputEmployees, status ) {
|
|
|
1138
3884
|
// Step 2: Loop through all existing and update isChecked accordingly
|
|
1139
3885
|
const updatedEmployees = existingEmployees.map( ( emp ) => ( {
|
|
1140
3886
|
...emp,
|
|
1141
|
-
isChecked: status === 'rejected'? !checkedTempIds.has( emp.tempId ):checkedTempIds.has( emp.tempId ),
|
|
3887
|
+
isChecked: status === 'rejected' ? !checkedTempIds.has( emp.tempId ) : checkedTempIds.has( emp.tempId ),
|
|
1142
3888
|
} ) );
|
|
1143
3889
|
|
|
1144
3890
|
return updatedEmployees;
|
|
@@ -1188,7 +3934,7 @@ function mergeDuplicateImagesWithUncheck( existingData, inputData, status ) {
|
|
|
1188
3934
|
export async function sendSqsMessage( inputData, tempId, getStoreType, storeId ) {
|
|
1189
3935
|
const sqs = JSON.parse( process.env.SQS );
|
|
1190
3936
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1191
|
-
const sqsName = getStoreType > 0? sqs.revopTrackTicket: sqs.revopTicket;
|
|
3937
|
+
const sqsName = getStoreType > 0 ? sqs.revopTrackTicket : sqs.revopTicket;
|
|
1192
3938
|
const sqsProduceQueue = getStoreType > 0 ? {
|
|
1193
3939
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1194
3940
|
MessageBody: JSON.stringify( {
|
|
@@ -1216,12 +3962,11 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1216
3962
|
|
|
1217
3963
|
} ),
|
|
1218
3964
|
};
|
|
1219
|
-
const sqsQueue = getStoreType > 0? await sendMessageToFIFOQueue( sqsProduceQueue ):
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
);
|
|
3965
|
+
const sqsQueue = getStoreType > 0 ? await sendMessageToFIFOQueue( sqsProduceQueue ) :
|
|
3966
|
+
await sendMessageToQueue(
|
|
3967
|
+
sqsProduceQueue.QueueUrl,
|
|
3968
|
+
sqsProduceQueue.MessageBody,
|
|
3969
|
+
);
|
|
1225
3970
|
if ( sqsQueue.statusCode ) {
|
|
1226
3971
|
logger.error( {
|
|
1227
3972
|
error: `${sqsQueue}`,
|
|
@@ -1229,7 +3974,7 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1229
3974
|
} );
|
|
1230
3975
|
return false;
|
|
1231
3976
|
} else {
|
|
1232
|
-
const id
|
|
3977
|
+
const id = `${storeId}_${inputData.dateString.split( '-' ).reverse().join( '-' )}_${getStoreType > 0 ? 'live' : 'reduction'}_${Date.now()}`;
|
|
1233
3978
|
const logs = {
|
|
1234
3979
|
QueueUrl: sqsProduceQueue.QueueUrl,
|
|
1235
3980
|
MessageBody: sqsProduceQueue.MessageBody,
|
|
@@ -1279,13 +4024,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1279
4024
|
},
|
|
1280
4025
|
},
|
|
1281
4026
|
];
|
|
1282
|
-
|
|
1283
|
-
// filter.push(
|
|
1284
|
-
// {
|
|
1285
|
-
// terms: { 'storeId.keyword': req.stores },
|
|
1286
|
-
// },
|
|
1287
|
-
// );
|
|
1288
|
-
// }
|
|
4027
|
+
|
|
1289
4028
|
filter.push(
|
|
1290
4029
|
{
|
|
1291
4030
|
terms: { 'storeId.keyword': req?.stores || [] },
|
|
@@ -1336,7 +4075,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1336
4075
|
+ (doc.containsKey('employeeCount') && !doc['employeeCount'].empty ? doc['employeeCount'].value : 0)
|
|
1337
4076
|
+ (doc.containsKey('junkCount') && !doc['junkCount'].empty ? doc['junkCount'].value : 0);
|
|
1338
4077
|
`,
|
|
1339
|
-
lang: '
|
|
4078
|
+
lang: 'scripting',
|
|
1340
4079
|
},
|
|
1341
4080
|
},
|
|
1342
4081
|
},
|
|
@@ -1400,8 +4139,8 @@ export async function downloadTickets( req, res ) {
|
|
|
1400
4139
|
{
|
|
1401
4140
|
terms: {
|
|
1402
4141
|
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1403
|
-
|
|
1404
|
-
|
|
4142
|
+
inputData.storeId :
|
|
4143
|
+
inputData.storeId,
|
|
1405
4144
|
},
|
|
1406
4145
|
},
|
|
1407
4146
|
);
|
|
@@ -1449,26 +4188,26 @@ export async function downloadTickets( req, res ) {
|
|
|
1449
4188
|
},
|
|
1450
4189
|
|
|
1451
4190
|
} ) :
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
4191
|
+
inputData.revopsType === 'junk' ?
|
|
4192
|
+
filter.push( {
|
|
4193
|
+
range: {
|
|
4194
|
+
junkCount: {
|
|
4195
|
+
gt: 0,
|
|
4196
|
+
},
|
|
1457
4197
|
},
|
|
1458
|
-
}
|
|
1459
|
-
} ):
|
|
4198
|
+
} ) :
|
|
1460
4199
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
4200
|
+
filter.push( {
|
|
4201
|
+
range: {
|
|
4202
|
+
duplicateCount: {
|
|
4203
|
+
gt: 0,
|
|
4204
|
+
},
|
|
1465
4205
|
},
|
|
1466
|
-
}
|
|
1467
|
-
} );
|
|
4206
|
+
} );
|
|
1468
4207
|
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1469
4208
|
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1470
4209
|
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1471
|
-
|
|
4210
|
+
inputData.revopsType == 'junk' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'junkCount', 'comments', 'junk', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] : [];
|
|
1472
4211
|
}
|
|
1473
4212
|
|
|
1474
4213
|
if ( inputData.action ) {
|
|
@@ -1545,7 +4284,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1545
4284
|
let temp = [];
|
|
1546
4285
|
if ( inputData?.action ) {
|
|
1547
4286
|
response.map( ( hit ) => {
|
|
1548
|
-
const defaultData ={
|
|
4287
|
+
const defaultData = {
|
|
1549
4288
|
storeId: hit._source.storeId,
|
|
1550
4289
|
dateString: hit?._source?.dateString,
|
|
1551
4290
|
ticketName: hit?._source?.ticketName,
|
|
@@ -1575,19 +4314,19 @@ export async function downloadTickets( req, res ) {
|
|
|
1575
4314
|
// result.type = 'employee';
|
|
1576
4315
|
result.matched = matched;
|
|
1577
4316
|
}
|
|
1578
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
4317
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1579
4318
|
logger.info( { revop: inputData.revopsType } );
|
|
1580
4319
|
result = defaultData;
|
|
1581
4320
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
1582
4321
|
result.matched = matched;
|
|
1583
4322
|
}
|
|
1584
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
4323
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1585
4324
|
logger.info( { revop: inputData.revopsType } );
|
|
1586
4325
|
result = defaultData;
|
|
1587
4326
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
1588
4327
|
result.matched = matched;
|
|
1589
4328
|
}
|
|
1590
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
4329
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1591
4330
|
result = defaultData;
|
|
1592
4331
|
result.junk = hit?._source?.junk;
|
|
1593
4332
|
result.matched = matched;
|
|
@@ -1631,7 +4370,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1631
4370
|
revopsType: inputData?.revopsType,
|
|
1632
4371
|
type: 'get-tickets',
|
|
1633
4372
|
};
|
|
1634
|
-
const record={
|
|
4373
|
+
const record = {
|
|
1635
4374
|
stores: inputData?.storeId,
|
|
1636
4375
|
fromDate: inputData?.fromDate,
|
|
1637
4376
|
toDate: inputData?.toDate,
|
|
@@ -1649,7 +4388,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1649
4388
|
const sqs = JSON.parse( process.env.SQS );
|
|
1650
4389
|
const sqsName = sqs.revopDownload;
|
|
1651
4390
|
|
|
1652
|
-
const sqsProduceQueue ={
|
|
4391
|
+
const sqsProduceQueue = {
|
|
1653
4392
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1654
4393
|
MessageBody: JSON.stringify( {
|
|
1655
4394
|
_id: getId?._id,
|
|
@@ -1678,7 +4417,6 @@ export async function downloadTickets( req, res ) {
|
|
|
1678
4417
|
}
|
|
1679
4418
|
|
|
1680
4419
|
async function extractTempIds( document ) {
|
|
1681
|
-
logger.info( { document: document } );
|
|
1682
4420
|
const source = document?._source || {};
|
|
1683
4421
|
const result = [];
|
|
1684
4422
|
|
|
@@ -1728,7 +4466,7 @@ export async function reviewerList( req, res ) {
|
|
|
1728
4466
|
featureName: 'FootfallDirectory',
|
|
1729
4467
|
modules: {
|
|
1730
4468
|
$elemMatch: {
|
|
1731
|
-
name: '
|
|
4469
|
+
name: inputData?.type === 'review' ? 'reviewer' : 'approver',
|
|
1732
4470
|
$or: [ { isAdd: true }, { isEdit: true } ],
|
|
1733
4471
|
},
|
|
1734
4472
|
},
|
|
@@ -1737,7 +4475,7 @@ export async function reviewerList( req, res ) {
|
|
|
1737
4475
|
};
|
|
1738
4476
|
|
|
1739
4477
|
const getUserlist = await findUser( reviewerRoleQuery, { userName: 1, email: 1, role: 1 } );
|
|
1740
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
4478
|
+
return res.sendSuccess( getUserlist || [] );
|
|
1741
4479
|
} catch ( error ) {
|
|
1742
4480
|
const err = error.message || 'Internal Server Error';
|
|
1743
4481
|
return res.sendError( err, 500 );
|
|
@@ -1747,6 +4485,7 @@ export async function reviewerList( req, res ) {
|
|
|
1747
4485
|
export async function openTicketList( req, res ) {
|
|
1748
4486
|
try {
|
|
1749
4487
|
const inputData = req.body;
|
|
4488
|
+
logger.info( { inputData } );
|
|
1750
4489
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1751
4490
|
|
|
1752
4491
|
// INSERT_YOUR_CODE
|
|
@@ -1759,6 +4498,29 @@ export async function openTicketList( req, res ) {
|
|
|
1759
4498
|
clientId: Array.isArray( clientId ) ? clientId : [ clientId ],
|
|
1760
4499
|
},
|
|
1761
4500
|
},
|
|
4501
|
+
{
|
|
4502
|
+
nested: {
|
|
4503
|
+
path: 'mappingInfo',
|
|
4504
|
+
query: {
|
|
4505
|
+
bool: {
|
|
4506
|
+
must: [
|
|
4507
|
+
{
|
|
4508
|
+
term: {
|
|
4509
|
+
'mappingInfo.type': inputData.type,
|
|
4510
|
+
},
|
|
4511
|
+
},
|
|
4512
|
+
{
|
|
4513
|
+
term: {
|
|
4514
|
+
'mappingInfo.status': 'Open',
|
|
4515
|
+
},
|
|
4516
|
+
},
|
|
4517
|
+
|
|
4518
|
+
],
|
|
4519
|
+
},
|
|
4520
|
+
},
|
|
4521
|
+
},
|
|
4522
|
+
},
|
|
4523
|
+
|
|
1762
4524
|
{
|
|
1763
4525
|
range: {
|
|
1764
4526
|
dateString: {
|
|
@@ -1770,6 +4532,7 @@ export async function openTicketList( req, res ) {
|
|
|
1770
4532
|
},
|
|
1771
4533
|
];
|
|
1772
4534
|
|
|
4535
|
+
|
|
1773
4536
|
const openSearchQuery = {
|
|
1774
4537
|
size: 10000,
|
|
1775
4538
|
query: {
|
|
@@ -1777,14 +4540,44 @@ export async function openTicketList( req, res ) {
|
|
|
1777
4540
|
filter: filter,
|
|
1778
4541
|
},
|
|
1779
4542
|
},
|
|
1780
|
-
_source: [ 'ticketId', 'storeName', 'revicedFootfall', 'footfallCount', 'revicedPerc' ],
|
|
4543
|
+
_source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
|
|
1781
4544
|
};
|
|
1782
4545
|
|
|
4546
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
4547
|
+
openSearchQuery.query.bool['should'] = [];
|
|
4548
|
+
openSearchQuery.query.bool.should = [
|
|
4549
|
+
|
|
4550
|
+
{
|
|
4551
|
+
'wildcard': {
|
|
4552
|
+
'storeName.keyword': {
|
|
4553
|
+
'value': `*${inputData.searchValue}*`,
|
|
4554
|
+
},
|
|
4555
|
+
},
|
|
4556
|
+
},
|
|
4557
|
+
{
|
|
4558
|
+
'wildcard': {
|
|
4559
|
+
'ticketId.keyword': {
|
|
4560
|
+
'value': `*${inputData.searchValue}*`,
|
|
4561
|
+
},
|
|
4562
|
+
},
|
|
4563
|
+
},
|
|
4564
|
+
|
|
4565
|
+
|
|
4566
|
+
];
|
|
4567
|
+
openSearchQuery.query.bool['minimum_should_match'] = 1;
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
// INSERT_YOUR_CODE
|
|
4571
|
+
// Add sorting by revicedPerc descending (highest revised accuracy first)
|
|
4572
|
+
openSearchQuery.sort = [
|
|
4573
|
+
{ 'revicedPerc.keyword': { order: inputData?.sortOrder === 1 ? 'asc' : 'desc' } },
|
|
4574
|
+
];
|
|
4575
|
+
|
|
1783
4576
|
|
|
1784
4577
|
// Assuming getOpenSearchData and openSearch.footfallDirectoryTagging are available
|
|
1785
4578
|
const result = await getOpenSearchData( openSearch.footfallDirectory, openSearchQuery );
|
|
1786
4579
|
const getUserlist = result?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
1787
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
4580
|
+
return res.sendSuccess( getUserlist || [] );
|
|
1788
4581
|
} catch ( error ) {
|
|
1789
4582
|
const err = error.message || 'Internal Server Error';
|
|
1790
4583
|
logger.error( { error: error, function: 'openTicketList' } );
|
|
@@ -1797,73 +4590,32 @@ export async function assignTicket( req, res ) {
|
|
|
1797
4590
|
const inputData = req.body;
|
|
1798
4591
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1799
4592
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
const { email, userName, role, actionType } = inputData;
|
|
1803
|
-
|
|
1804
|
-
// INSERT_YOUR_CODE
|
|
1805
|
-
|
|
1806
|
-
// Find and update mappingInfo fields for the provided ticketId and actionType
|
|
1807
|
-
// Requires ticketId in inputData
|
|
1808
|
-
const { ticketId } = inputData;
|
|
1809
|
-
if ( !ticketId ) {
|
|
1810
|
-
return res.sendError( 'ticketId is required', 400 );
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
// Build the OpenSearch update-by-query body
|
|
1814
|
-
const updateBody = {
|
|
1815
|
-
script: {
|
|
1816
|
-
source: `
|
|
1817
|
-
if (ctx._source.mappingInfo != null) {
|
|
1818
|
-
for (int i = 0; i < ctx._source.mappingInfo.length; i++) {
|
|
1819
|
-
if (ctx._source.mappingInfo[i].type == params.actionType) {
|
|
1820
|
-
ctx._source.mappingInfo[i].createdByEmail = params.email;
|
|
1821
|
-
ctx._source.mappingInfo[i].createdByUserName = params.userName;
|
|
1822
|
-
ctx._source.mappingInfo[i].createdByRole = params.role;
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
`,
|
|
1827
|
-
lang: 'painless',
|
|
1828
|
-
params: {
|
|
1829
|
-
email,
|
|
1830
|
-
userName,
|
|
1831
|
-
role,
|
|
1832
|
-
actionType,
|
|
1833
|
-
},
|
|
1834
|
-
},
|
|
1835
|
-
query: {
|
|
1836
|
-
bool: {
|
|
1837
|
-
must: [
|
|
1838
|
-
{ term: { 'ticketId.keyword': ticketId } },
|
|
1839
|
-
{
|
|
1840
|
-
nested: {
|
|
1841
|
-
path: 'mappingInfo',
|
|
1842
|
-
query: {
|
|
1843
|
-
bool: {
|
|
1844
|
-
must: [
|
|
1845
|
-
{ match: { 'mappingInfo.type': actionType } },
|
|
1846
|
-
],
|
|
1847
|
-
},
|
|
1848
|
-
},
|
|
1849
|
-
},
|
|
1850
|
-
},
|
|
1851
|
-
],
|
|
1852
|
-
},
|
|
1853
|
-
},
|
|
1854
|
-
};
|
|
1855
|
-
|
|
1856
|
-
// Call OpenSearch _update_by_query to update doc(s) where ticketId and mappingInfo[i].type == actionType
|
|
1857
|
-
const response = await upsertOpenSearchData(
|
|
1858
|
-
openSearch.footfallDirectory,
|
|
1859
|
-
'11-1716_2025-11-20_footfall-directory-tagging',
|
|
1860
|
-
updateBody, // custom arg to indicate passthrough for update-by-query, depends on helper implementation
|
|
1861
|
-
);
|
|
1862
|
-
|
|
4593
|
+
const { email, userName, role, storeId, dateString } = inputData;
|
|
4594
|
+
const _id = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
1863
4595
|
|
|
1864
|
-
|
|
4596
|
+
const getTicket = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
4597
|
+
if ( !getTicket ) {
|
|
4598
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
4599
|
+
}
|
|
4600
|
+
const source = getTicket?.body?._source;
|
|
4601
|
+
const mappingInfo = Array.isArray( source.mappingInfo ) ? source.mappingInfo : [];
|
|
4602
|
+
if ( mappingInfo.length === 0 ) {
|
|
4603
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
4604
|
+
}
|
|
4605
|
+
const lastIndex = mappingInfo.length - 1;
|
|
4606
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
4607
|
+
if ( String( lastEntry.status ) !== 'In-Progress' ) {
|
|
4608
|
+
return res.sendError( 'Ticket is not in progress', 400 );
|
|
4609
|
+
}
|
|
1865
4610
|
|
|
1866
|
-
|
|
4611
|
+
const currentTime = new Date();
|
|
4612
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
4613
|
+
updatedMappingInfo[lastIndex] = { ...lastEntry, createdByEmail: email, updatedAt: currentTime, createdByUserName: userName, createdByRole: role };
|
|
4614
|
+
const updateResult = await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { mappingInfo: updatedMappingInfo } } );
|
|
4615
|
+
if ( !updateResult ) {
|
|
4616
|
+
return res.sendError( 'Failed to update ticket', 400 );
|
|
4617
|
+
}
|
|
4618
|
+
return res.sendSuccess( { message: 'Ticket assigned successfully' } );
|
|
1867
4619
|
} catch ( error ) {
|
|
1868
4620
|
const err = error.message || 'Internal Server Error';
|
|
1869
4621
|
logger.error( { error: error, function: 'assignTicket' } );
|
|
@@ -1874,11 +4626,13 @@ export async function assignTicket( req, res ) {
|
|
|
1874
4626
|
export async function updateTempStatus( req, res ) {
|
|
1875
4627
|
try {
|
|
1876
4628
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1877
|
-
const
|
|
4629
|
+
const inputData = req.body;
|
|
4630
|
+
const { id, type, status, comments } = inputData;
|
|
1878
4631
|
|
|
1879
4632
|
// Use bulk update API via bucketing (batch update) -- fetch docs, then bulk-update
|
|
1880
4633
|
// 1. Search for all documents matching the ticket IDs
|
|
1881
4634
|
const searchBody = {
|
|
4635
|
+
size: 10000,
|
|
1882
4636
|
query: {
|
|
1883
4637
|
bool: {
|
|
1884
4638
|
must: [
|
|
@@ -1897,16 +4651,14 @@ export async function updateTempStatus( req, res ) {
|
|
|
1897
4651
|
openSearch.revop,
|
|
1898
4652
|
searchBody,
|
|
1899
4653
|
);
|
|
1900
|
-
|
|
4654
|
+
|
|
1901
4655
|
// Extract bulk IDs to update
|
|
1902
4656
|
const hits = searchResp?.body?.hits?.hits ?? [];
|
|
1903
|
-
|
|
4657
|
+
|
|
1904
4658
|
if ( !hits.length ) {
|
|
1905
4659
|
return res.sendError( 'no data', 204 );
|
|
1906
4660
|
}
|
|
1907
4661
|
|
|
1908
|
-
// 2. Build bulk update commands
|
|
1909
|
-
// Each doc: { update: { _id: ..., _index: ... } }, { doc: { status: status } }
|
|
1910
4662
|
|
|
1911
4663
|
// 1. Get all IDs from hits
|
|
1912
4664
|
const docIdToIndex = {};
|
|
@@ -1914,7 +4666,6 @@ export async function updateTempStatus( req, res ) {
|
|
|
1914
4666
|
docIdToIndex[doc._id] = doc._index;
|
|
1915
4667
|
} );
|
|
1916
4668
|
const docIds = hits.map( ( doc ) => doc._id );
|
|
1917
|
-
logger.info( { docIds } );
|
|
1918
4669
|
// 2. Fetch all docs by ID to get 'actions' (in chunks if large)
|
|
1919
4670
|
const getBody = [];
|
|
1920
4671
|
for ( const doc of hits ) {
|
|
@@ -1922,71 +4673,76 @@ export async function updateTempStatus( req, res ) {
|
|
|
1922
4673
|
}
|
|
1923
4674
|
|
|
1924
4675
|
let mgetResp;
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
4676
|
+
|
|
4677
|
+
mgetResp = await getOpenSearchData(
|
|
4678
|
+
openSearch.revop,
|
|
4679
|
+
{
|
|
4680
|
+
size: 10000,
|
|
4681
|
+
query: {
|
|
4682
|
+
ids: {
|
|
4683
|
+
values: docIds,
|
|
1933
4684
|
},
|
|
1934
|
-
_source: true,
|
|
1935
4685
|
},
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
mgetResp = undefined;
|
|
1940
|
-
}
|
|
1941
|
-
logger.info( { mgetResp } );
|
|
4686
|
+
_source: true,
|
|
4687
|
+
},
|
|
4688
|
+
);
|
|
1942
4689
|
// (If you have a utility for multi-get, you may want to use that. Else, you might need to fetch each by ID.)
|
|
1943
4690
|
// For fallback, fetch all source fields via another search
|
|
1944
|
-
let fullDocs = [];
|
|
1945
|
-
if ( mgetResp && mgetResp.body && mgetResp.body.docs && Array.isArray( mgetResp.body.docs ) ) {
|
|
1946
|
-
fullDocs = mgetResp.body.docs;
|
|
1947
|
-
} else if ( searchResp.body && searchResp.body.hits && searchResp.body.hits.hits ) {
|
|
1948
|
-
// fallback: use searchResp docs (request _source above)
|
|
1949
|
-
fullDocs = searchResp.body.hits.hits;
|
|
1950
|
-
}
|
|
4691
|
+
let fullDocs = mgetResp?.body?.hits?.hits || searchResp?.body?.hits?.hits || [];
|
|
1951
4692
|
|
|
1952
4693
|
// 3. Prepare the new actions array for each doc, and set up bulk update payloads
|
|
1953
4694
|
const reviewActions = [ 'approved', 'rejected' ];
|
|
1954
4695
|
const docsToUpdate = [];
|
|
1955
|
-
logger.info( { fullDocs: fullDocs } );
|
|
1956
4696
|
for ( const doc of fullDocs ) {
|
|
1957
4697
|
const source = doc._source || doc.fields || {}; // support mget and search hits
|
|
1958
4698
|
let actions = Array.isArray( source.actions ) ? [ ...source.actions ] : [];
|
|
1959
4699
|
if ( reviewActions.includes( status ) ) {
|
|
1960
4700
|
// for review: update or push 'review'
|
|
1961
4701
|
let found = false;
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
4702
|
+
switch ( type ) {
|
|
4703
|
+
case 'review':
|
|
4704
|
+
actions = actions.map( ( item ) => {
|
|
4705
|
+
if ( item.actionType === 'review' ) {
|
|
4706
|
+
found = true;
|
|
4707
|
+
return { ...item, action: status };
|
|
4708
|
+
}
|
|
4709
|
+
return item;
|
|
4710
|
+
} );
|
|
4711
|
+
if ( !found ) {
|
|
4712
|
+
actions.push( { actionType: 'review', action: status } );
|
|
4713
|
+
}
|
|
4714
|
+
break;
|
|
4715
|
+
case 'approve':
|
|
4716
|
+
actions = actions.map( ( item ) => {
|
|
4717
|
+
if ( item.actionType === 'approve' ) {
|
|
4718
|
+
found = true;
|
|
4719
|
+
return { ...item, action: status };
|
|
4720
|
+
}
|
|
4721
|
+
return item;
|
|
4722
|
+
} );
|
|
4723
|
+
if ( !found ) {
|
|
4724
|
+
actions.push( { actionType: 'approve', action: status } );
|
|
4725
|
+
}
|
|
4726
|
+
break;
|
|
4727
|
+
default:
|
|
4728
|
+
return res.sendError( 'wrong vaue', 400 );
|
|
1984
4729
|
}
|
|
1985
4730
|
}
|
|
4731
|
+
let isChecked = true;
|
|
4732
|
+
switch ( type ) {
|
|
4733
|
+
case 'review':
|
|
4734
|
+
isChecked = status === 'approved' ? true : false;
|
|
4735
|
+
break;
|
|
4736
|
+
case 'approve':
|
|
4737
|
+
isChecked = status === 'approved' ? doc?._source?.isChecked : !doc?._source?.isChecked;
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
1986
4740
|
docsToUpdate.push( {
|
|
1987
4741
|
_index: doc._index || docIdToIndex[doc._id],
|
|
1988
4742
|
_id: doc._id,
|
|
1989
4743
|
actions,
|
|
4744
|
+
isChecked: isChecked,
|
|
4745
|
+
comments: comments || '',
|
|
1990
4746
|
} );
|
|
1991
4747
|
}
|
|
1992
4748
|
const bulkPayload = [];
|
|
@@ -1996,11 +4752,9 @@ export async function updateTempStatus( req, res ) {
|
|
|
1996
4752
|
update: { _index: doc._index, _id: doc._id },
|
|
1997
4753
|
} );
|
|
1998
4754
|
bulkPayload.push( {
|
|
1999
|
-
doc: { actions: doc.actions },
|
|
4755
|
+
doc: { actions: doc.actions, isChecked: doc?.isChecked, comments: doc?.comments || '' },
|
|
2000
4756
|
} );
|
|
2001
4757
|
}
|
|
2002
|
-
logger.info( { bulkPayload: bulkPayload } );
|
|
2003
|
-
|
|
2004
4758
|
|
|
2005
4759
|
// 3. Execute bulk update
|
|
2006
4760
|
const bulkResp = await bulkUpdate( bulkPayload );
|
|
@@ -2008,8 +4762,50 @@ export async function updateTempStatus( req, res ) {
|
|
|
2008
4762
|
// Count successes
|
|
2009
4763
|
const updatedCount = bulkResp?.body?.items?.filter( ( item ) => item?.update?.result === 'updated' || item?.update?.result === 'noop' ).length ?? 0;
|
|
2010
4764
|
|
|
2011
|
-
|
|
4765
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
4766
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
4767
|
+
const searchBody1 = {
|
|
4768
|
+
size: 10000,
|
|
4769
|
+
query: {
|
|
4770
|
+
bool: {
|
|
4771
|
+
must: [
|
|
4772
|
+
{
|
|
4773
|
+
terms: {
|
|
4774
|
+
'_id': docIds,
|
|
4775
|
+
},
|
|
4776
|
+
},
|
|
4777
|
+
{
|
|
4778
|
+
term: {
|
|
4779
|
+
isParent: false,
|
|
4780
|
+
},
|
|
4781
|
+
},
|
|
4782
|
+
],
|
|
4783
|
+
},
|
|
4784
|
+
},
|
|
4785
|
+
};
|
|
4786
|
+
|
|
4787
|
+
const getSearchResp = await getOpenSearchData(
|
|
4788
|
+
openSearch.revop,
|
|
4789
|
+
searchBody1,
|
|
4790
|
+
);
|
|
4791
|
+
|
|
2012
4792
|
|
|
4793
|
+
const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0 ? getSearchResp?.body?.hits?.hits : [];
|
|
4794
|
+
const logs = {
|
|
4795
|
+
type: inputData.type,
|
|
4796
|
+
storeId: taggedImages?.[0]?._source?.storeId,
|
|
4797
|
+
dateString: taggedImages?.[0]?._source?.dateString,
|
|
4798
|
+
category: taggedImages?.[0]?._source?.revopsType || '',
|
|
4799
|
+
taggedImages: taggedImages,
|
|
4800
|
+
status: inputData?.status,
|
|
4801
|
+
createdByEmail: req?.user?.email,
|
|
4802
|
+
createdByUserName: req?.user?.userName,
|
|
4803
|
+
createdByRole: req?.user?.role,
|
|
4804
|
+
message: inputData.comments || '',
|
|
4805
|
+
createdAt: new Date(),
|
|
4806
|
+
};
|
|
4807
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
4808
|
+
}
|
|
2013
4809
|
return res.sendSuccess( { updated: updatedCount } );
|
|
2014
4810
|
} catch ( error ) {
|
|
2015
4811
|
const err = error.message;
|
|
@@ -2018,3 +4814,283 @@ export async function updateTempStatus( req, res ) {
|
|
|
2018
4814
|
}
|
|
2019
4815
|
}
|
|
2020
4816
|
|
|
4817
|
+
export async function updateUserTicketStatus( req, res ) {
|
|
4818
|
+
try {
|
|
4819
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
4820
|
+
const { storeId, dateString } = req.body || {};
|
|
4821
|
+
|
|
4822
|
+
if ( !storeId || !dateString ) {
|
|
4823
|
+
return res.sendError( 'storeId and dateString are required', 400 );
|
|
4824
|
+
}
|
|
4825
|
+
|
|
4826
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
4827
|
+
|
|
4828
|
+
// Fetch existing ticket so we can validate mappingInfo state
|
|
4829
|
+
const existingDoc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
4830
|
+
const ticketSource = existingDoc?.body?._source;
|
|
4831
|
+
|
|
4832
|
+
if ( !ticketSource ) {
|
|
4833
|
+
return res.sendError( 'Ticket not found', 404 );
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
const mappingInfo = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [];
|
|
4837
|
+
if ( mappingInfo.length === 0 ) {
|
|
4838
|
+
return res.sendError( 'mappingInfo is missing for this ticket', 400 );
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
const lastIndex = mappingInfo.length - 1;
|
|
4842
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
4843
|
+
|
|
4844
|
+
if ( !lastEntry ) {
|
|
4845
|
+
return res.sendError( 'Unable to determine current ticket status', 400 );
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4848
|
+
if ( String( lastEntry.status ).toLowerCase() !== 'open' ) {
|
|
4849
|
+
return res.sendError( 'Ticket is already picked by another user', 409 );
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4852
|
+
const currentTime = new Date();
|
|
4853
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
4854
|
+
updatedMappingInfo[lastIndex] = {
|
|
4855
|
+
...lastEntry,
|
|
4856
|
+
status: 'In-Progress',
|
|
4857
|
+
updatedAt: currentTime,
|
|
4858
|
+
createdAt: currentTime,
|
|
4859
|
+
createdByRole: req?.user?.role || '',
|
|
4860
|
+
createdByEmail: req?.user?.email || '',
|
|
4861
|
+
createdByUserName: req?.user?.userName || '',
|
|
4862
|
+
};
|
|
4863
|
+
|
|
4864
|
+
const updatePayload = {
|
|
4865
|
+
doc: {
|
|
4866
|
+
status: lastEntry?.type == 'review'? 'Reviewer In progress':lastEntry?.type == 'approve'? 'Approver In progress':ticketSource?.status,
|
|
4867
|
+
mappingInfo: updatedMappingInfo,
|
|
4868
|
+
updatedAt: currentTime,
|
|
4869
|
+
},
|
|
4870
|
+
};
|
|
4871
|
+
|
|
4872
|
+
const updateResult = await updateOpenSearchData(
|
|
4873
|
+
openSearch.footfallDirectory,
|
|
4874
|
+
docId,
|
|
4875
|
+
updatePayload,
|
|
4876
|
+
);
|
|
4877
|
+
|
|
4878
|
+
|
|
4879
|
+
if ( !updateResult || !( updateResult.statusCode === 200 || updateResult.statusCode === 201 ) ) {
|
|
4880
|
+
return res.sendError( 'Failed to update ticket status', 500 );
|
|
4881
|
+
}
|
|
4882
|
+
return res.sendSuccess( 'Ticket status updated successfully' );
|
|
4883
|
+
} catch ( error ) {
|
|
4884
|
+
const err = error.message;
|
|
4885
|
+
logger.info( { error: err, function: 'updateUserTicketStatus' } );
|
|
4886
|
+
return res.sendError( err, 500 );
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
export async function multiCloseTicket( req, res ) {
|
|
4891
|
+
try {
|
|
4892
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
4893
|
+
const inputData = req.body;
|
|
4894
|
+
|
|
4895
|
+
// inputData structure should include an array of items to close
|
|
4896
|
+
// Accept both array or single ticket update
|
|
4897
|
+
const tickets = Array.isArray( inputData.ticketList ) ? inputData.ticketList : [ inputData.ticketList ];
|
|
4898
|
+
// const mode = inputData.mode || '';
|
|
4899
|
+
|
|
4900
|
+
if ( !tickets.length ) {
|
|
4901
|
+
return res.sendError( 'No tickets provided', 400 );
|
|
4902
|
+
}
|
|
4903
|
+
|
|
4904
|
+
const results = [];
|
|
4905
|
+
for ( const ticket of tickets ) {
|
|
4906
|
+
const { storeId, dateString } = ticket || {};
|
|
4907
|
+
if ( !storeId || !dateString ) {
|
|
4908
|
+
results.push( { storeId, dateString, success: false, error: 'Missing storeId or dateString' } );
|
|
4909
|
+
continue;
|
|
4910
|
+
}
|
|
4911
|
+
// 1. Update the ticket document in footfallDirectory index
|
|
4912
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
4913
|
+
|
|
4914
|
+
// Fetch existing doc to update mappingInfo
|
|
4915
|
+
|
|
4916
|
+
const doc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
4917
|
+
|
|
4918
|
+
|
|
4919
|
+
const ticketSource = doc?.body?._source;
|
|
4920
|
+
if ( !ticketSource || !ticketSource.mappingInfo ) {
|
|
4921
|
+
results.push( { storeId, dateString, success: false, error: 'Ticket or mappingInfo missing' } );
|
|
4922
|
+
continue;
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4925
|
+
let mappingInfoArray = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [ ticketSource.mappingInfo ];
|
|
4926
|
+
// Find all mappingInfo items matching type 'approve'
|
|
4927
|
+
let updated = false;
|
|
4928
|
+
let newMappingInfoArray = mappingInfoArray.map( ( mi, i ) => {
|
|
4929
|
+
if ( mi?.type === 'approve' && mi?.status === 'Open' ) {
|
|
4930
|
+
updated = true;
|
|
4931
|
+
return {
|
|
4932
|
+
...mi,
|
|
4933
|
+
status: 'Approver-Closed',
|
|
4934
|
+
status: 'Closed', // following the user's instruction to set sttaus property (assumed typo, but explicitly used)
|
|
4935
|
+
updatedAt: new Date(),
|
|
4936
|
+
};
|
|
4937
|
+
}
|
|
4938
|
+
return mi;
|
|
4939
|
+
} );
|
|
4940
|
+
|
|
4941
|
+
if ( !updated ) {
|
|
4942
|
+
// None found to update
|
|
4943
|
+
results.push( { storeId, dateString, success: false, error: `coudn't approve this store` } );
|
|
4944
|
+
continue;
|
|
4945
|
+
}
|
|
4946
|
+
|
|
4947
|
+
// Write update to footfallDirectory
|
|
4948
|
+
const ticketUpdatePayload = {
|
|
4949
|
+
doc: {
|
|
4950
|
+
mappingInfo: newMappingInfoArray,
|
|
4951
|
+
status: 'Approver-Closed', // status updated at top level as well
|
|
4952
|
+
updatedAt: new Date(),
|
|
4953
|
+
},
|
|
4954
|
+
};
|
|
4955
|
+
let ticketUpdateResult;
|
|
4956
|
+
try {
|
|
4957
|
+
ticketUpdateResult = await updateOpenSearchData( openSearch.footfallDirectory, docId, ticketUpdatePayload );
|
|
4958
|
+
if ( !( ticketUpdateResult && ( ticketUpdateResult.statusCode === 200 || ticketUpdateResult.statusCode === 201 ) ) ) {
|
|
4959
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
4960
|
+
continue;
|
|
4961
|
+
}
|
|
4962
|
+
} catch ( err ) {
|
|
4963
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
4964
|
+
continue;
|
|
4965
|
+
}
|
|
4966
|
+
|
|
4967
|
+
|
|
4968
|
+
// For each ticket, update actions array for all matching image docs (storeId & dateString)
|
|
4969
|
+
|
|
4970
|
+
// Query to find all matching docs in revopTagging index with storeId and dateString
|
|
4971
|
+
const revopImageQuery = {
|
|
4972
|
+
size: 10000, // assume there won't be more than 1000 images per ticket
|
|
4973
|
+
query: {
|
|
4974
|
+
bool: {
|
|
4975
|
+
must: [
|
|
4976
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
4977
|
+
{ term: { dateString: dateString } },
|
|
4978
|
+
],
|
|
4979
|
+
},
|
|
4980
|
+
},
|
|
4981
|
+
};
|
|
4982
|
+
|
|
4983
|
+
// Fetch matching docs
|
|
4984
|
+
const searchRes = await getOpenSearchData( openSearch.revop, revopImageQuery );
|
|
4985
|
+
const revopHits = searchRes?.body?.hits?.hits || [];
|
|
4986
|
+
|
|
4987
|
+
// Optimized: batch and parallelize image-doc updates to avoid long sequential waits
|
|
4988
|
+
const BATCH_SIZE = 100;
|
|
4989
|
+
const now = new Date();
|
|
4990
|
+
|
|
4991
|
+
for ( let i = 0; i < revopHits.length; i += BATCH_SIZE ) {
|
|
4992
|
+
const batch = revopHits.slice( i, i + BATCH_SIZE );
|
|
4993
|
+
|
|
4994
|
+
const updatePromises = batch.map( async ( hit ) => {
|
|
4995
|
+
const imageDocId = hit._id;
|
|
4996
|
+
const imageSource = hit._source || {};
|
|
4997
|
+
const imageActionsArray = Array.isArray( imageSource.actions ) ? [ ...imageSource.actions ] : [];
|
|
4998
|
+
|
|
4999
|
+
imageActionsArray.push( {
|
|
5000
|
+
actionType: 'approve',
|
|
5001
|
+
action: 'approved',
|
|
5002
|
+
} );
|
|
5003
|
+
|
|
5004
|
+
const imageUpdatePayload = {
|
|
5005
|
+
doc: {
|
|
5006
|
+
actions: imageActionsArray,
|
|
5007
|
+
updatedAt: now,
|
|
5008
|
+
},
|
|
5009
|
+
};
|
|
5010
|
+
|
|
5011
|
+
return updateOpenSearchData( openSearch.revop, imageDocId, imageUpdatePayload );
|
|
5012
|
+
} );
|
|
5013
|
+
|
|
5014
|
+
// Wait for this batch to finish before starting the next one
|
|
5015
|
+
await Promise.all( updatePromises );
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
if ( results && results?.length > 0 ) {
|
|
5019
|
+
return res.sendError( results, 500 );
|
|
5020
|
+
}
|
|
5021
|
+
// Return batch summary
|
|
5022
|
+
} catch ( error ) {
|
|
5023
|
+
const err = error.message;
|
|
5024
|
+
logger.info( { error: err, function: 'multiCloseTicket' } );
|
|
5025
|
+
return res.sendError( err, 500 );
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
|
|
5029
|
+
|
|
5030
|
+
export async function checkTicketExists( req, res ) {
|
|
5031
|
+
try {
|
|
5032
|
+
let inputData = req.body;
|
|
5033
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
5034
|
+
let findQuery = {
|
|
5035
|
+
size: 10000,
|
|
5036
|
+
query: {
|
|
5037
|
+
bool: {
|
|
5038
|
+
must: [
|
|
5039
|
+
{
|
|
5040
|
+
term: {
|
|
5041
|
+
'storeId.keyword': inputData.storeId,
|
|
5042
|
+
},
|
|
5043
|
+
},
|
|
5044
|
+
{
|
|
5045
|
+
term: {
|
|
5046
|
+
'dateString': inputData.dateString,
|
|
5047
|
+
},
|
|
5048
|
+
},
|
|
5049
|
+
],
|
|
5050
|
+
},
|
|
5051
|
+
},
|
|
5052
|
+
};
|
|
5053
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
5054
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
5055
|
+
|
|
5056
|
+
|
|
5057
|
+
res.sendSuccess( Ticket );
|
|
5058
|
+
} catch ( error ) {
|
|
5059
|
+
const err = error.message;
|
|
5060
|
+
logger.info( { error: err, function: 'checkTicketExists' } );
|
|
5061
|
+
return res.sendError( err, 500 );
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
|
|
5065
|
+
|
|
5066
|
+
export async function getAccuracyIssues( req, res ) {
|
|
5067
|
+
try {
|
|
5068
|
+
const inputData = req.query;
|
|
5069
|
+
|
|
5070
|
+
|
|
5071
|
+
const getIsues = await findStoreAccuracIssues( { clientId: inputData.clientId, isActive: true }, { issueName: 1 } );
|
|
5072
|
+
// const mode = inputData.mode || '';
|
|
5073
|
+
res.sendSuccess( { result: getIsues|| [] } );
|
|
5074
|
+
// Return batch summary
|
|
5075
|
+
} catch ( error ) {
|
|
5076
|
+
const err = error.message;
|
|
5077
|
+
logger.info( { error: err, function: 'getAccuracyIssues' } );
|
|
5078
|
+
return res.sendError( err, 500 );
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
|
|
5082
|
+
export async function updateAccuracyIssues( req, res ) {
|
|
5083
|
+
try {
|
|
5084
|
+
const inputData = req.query;
|
|
5085
|
+
|
|
5086
|
+
|
|
5087
|
+
const getIsues = await upsertStoreAccuracIssues( { clientId: inputData.clientId, issueName: inputData?.issueName }, { issueName: inputData?.issueName, isActive: true } );
|
|
5088
|
+
// const mode = inputData.mode || '';
|
|
5089
|
+
res.sendSuccess( { result: getIsues|| [] } );
|
|
5090
|
+
// Return batch summary
|
|
5091
|
+
} catch ( error ) {
|
|
5092
|
+
const err = error.message;
|
|
5093
|
+
logger.info( { error: err, function: 'putAccuracyIssues' } );
|
|
5094
|
+
return res.sendError( err, 500 );
|
|
5095
|
+
}
|
|
5096
|
+
}
|