tango-app-api-infra 3.9.5-vms.9 → 3.9.5-vms.91
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 +3780 -693
- package/src/dtos/footfallDirectory.dtos.js +189 -60
- package/src/routes/footfallDirectory.routes.js +15 -5
- package/src/services/storeAccuracyIssues.service.js +9 -0
- package/src/validations/footfallDirectory.validation.js +1803 -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,2426 @@ 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
|
-
},
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
terms: {
|
|
294
|
-
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
295
|
-
inputData.clientId :
|
|
296
|
-
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
];
|
|
1969
|
+
},
|
|
1970
|
+
},
|
|
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( {
|
|
2234
|
+
terms: {
|
|
2235
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
2236
|
+
inputData.storeId :
|
|
2237
|
+
[ inputData.storeId ],
|
|
2238
|
+
},
|
|
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
|
+
}
|
|
2561
|
+
|
|
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
|
+
},
|
|
2573
|
+
},
|
|
2574
|
+
},
|
|
2575
|
+
} );
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
if ( inputData?.filterByReviewer && inputData?.filterByReviewer !== '' ) {
|
|
2580
|
+
let percQuery = null;
|
|
2581
|
+
const value = inputData.filterByReviewer;
|
|
2582
|
+
|
|
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 },
|
|
2601
|
+
},
|
|
2602
|
+
},
|
|
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 },
|
|
2613
|
+
},
|
|
2614
|
+
},
|
|
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 },
|
|
2627
|
+
},
|
|
2628
|
+
},
|
|
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
|
+
}
|
|
300
2642
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
{
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+
],
|
|
2653
|
+
},
|
|
308
2654
|
},
|
|
309
2655
|
},
|
|
310
|
-
|
|
2656
|
+
} );
|
|
2657
|
+
}
|
|
311
2658
|
}
|
|
312
2659
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
2660
|
+
if ( inputData?.filterByApprover && inputData?.filterByApprover !== '' ) {
|
|
2661
|
+
let percQuery = null;
|
|
2662
|
+
const value = inputData.filterByApprover;
|
|
316
2663
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
'
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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 );
|
|
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 },
|
|
326
2682
|
},
|
|
327
2683
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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 },
|
|
333
2694
|
},
|
|
334
2695
|
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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 },
|
|
340
2708
|
},
|
|
341
2709
|
},
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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 ) },
|
|
347
2719
|
},
|
|
348
2720
|
},
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
349
2723
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
console.log( item );
|
|
3037
|
+
exportData.push( {
|
|
3038
|
+
|
|
3039
|
+
'Ticket ID': item?.ticketId,
|
|
3040
|
+
'Store Name': item?.storeName,
|
|
3041
|
+
'Store ID': item?.storeId,
|
|
3042
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
3043
|
+
'Issue Date': item?.dateString,
|
|
3044
|
+
'Ticket Type': item?.type,
|
|
3045
|
+
'Actual FF': item?.footfallCount,
|
|
3046
|
+
// Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevisoon'
|
|
3047
|
+
'Due Date': ( () => {
|
|
3048
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3049
|
+
const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
|
|
3050
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3051
|
+
}
|
|
3052
|
+
return '';
|
|
3053
|
+
} )(),
|
|
3054
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3055
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3056
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3057
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3058
|
+
'Ticket Status': item?.type === 'store'? item?.status : '',
|
|
3059
|
+
'Tango Status': item?.type === 'store'? item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--' : item.status,
|
|
3060
|
+
|
|
3061
|
+
} );
|
|
3062
|
+
}
|
|
3063
|
+
return await download( exportData, res );
|
|
3064
|
+
} else {
|
|
3065
|
+
for ( let item of ticketListData ) {
|
|
3066
|
+
console.log( '🚀 ~ ticketList ~ item:', item );
|
|
3067
|
+
temp.push( {
|
|
3068
|
+
|
|
3069
|
+
ticketId: item?.ticketId,
|
|
3070
|
+
storeId: item?.storeId,
|
|
3071
|
+
storeName: item?.storeName,
|
|
3072
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f?.type === 'tagging' )?.createdAt,
|
|
3073
|
+
issueDate: item?.dateString,
|
|
3074
|
+
footfall: item?.footfallCount,
|
|
3075
|
+
dueDate: ( () => {
|
|
3076
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3077
|
+
const filtered = item.mappingInfo.filter( ( f ) => f?.dueDate && f?.type !== 'finalRevisoon' );
|
|
3078
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3079
|
+
}
|
|
3080
|
+
return '';
|
|
3081
|
+
} )(),
|
|
3082
|
+
type: item?.type || 'store',
|
|
3083
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3084
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3085
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3086
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3087
|
+
status: item?.status,
|
|
3088
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3089
|
+
|
|
3090
|
+
} );
|
|
3091
|
+
}
|
|
586
3092
|
}
|
|
587
3093
|
}
|
|
588
3094
|
} 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
|
-
}
|
|
3095
|
+
if ( inputData?.permissionType === 'approve' ) {
|
|
3096
|
+
if ( inputData.exportData ) {
|
|
3097
|
+
const exportData = [];
|
|
3098
|
+
for ( let item of ticketListData ) {
|
|
3099
|
+
exportData.push( {
|
|
3100
|
+
|
|
3101
|
+
'Ticket ID': item?.ticketId,
|
|
3102
|
+
'Store Name': item?.storeName,
|
|
3103
|
+
'Store ID': item?.storeId,
|
|
3104
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3105
|
+
'Issue Date': item?.dateString,
|
|
3106
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3107
|
+
'Actual FF': item?.footfallCount,
|
|
3108
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3109
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3110
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3111
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3112
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3113
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3114
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3115
|
+
|
|
3116
|
+
} );
|
|
3117
|
+
}
|
|
3118
|
+
return await download( exportData, res );
|
|
3119
|
+
} else {
|
|
3120
|
+
for ( let item of ticketListData ) {
|
|
3121
|
+
temp.push( {
|
|
3122
|
+
|
|
3123
|
+
ticketId: item?.ticketId,
|
|
3124
|
+
storeId: item?.storeId,
|
|
3125
|
+
storeName: item?.storeName,
|
|
3126
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3127
|
+
issueDate: item?.dateString,
|
|
3128
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3129
|
+
footfall: item?.footfallCount,
|
|
3130
|
+
type: item.type || 'store',
|
|
3131
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3132
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3133
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3134
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3135
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3136
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3137
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3138
|
+
|
|
3139
|
+
} );
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
} else if ( inputData?.permissionType === 'review' ) {
|
|
3143
|
+
if ( inputData?.isExport ) {
|
|
3144
|
+
const exportData = [];
|
|
3145
|
+
for ( let item of ticketListData ) {
|
|
3146
|
+
exportData.push( {
|
|
3147
|
+
|
|
3148
|
+
'Ticket ID': item?.ticketId,
|
|
3149
|
+
'Store Name': item?.storeName,
|
|
3150
|
+
'Store ID': item?.storeId,
|
|
3151
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3152
|
+
'Issue Date': item?.dateString,
|
|
3153
|
+
'Actual FF': item?.footfallCount,
|
|
3154
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3155
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3156
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3157
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3158
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3159
|
+
|
|
3160
|
+
} );
|
|
3161
|
+
}
|
|
3162
|
+
return await download( exportData, res );
|
|
3163
|
+
} else {
|
|
3164
|
+
for ( let item of ticketListData ) {
|
|
3165
|
+
temp.push( {
|
|
3166
|
+
|
|
3167
|
+
ticketId: item?.ticketId,
|
|
3168
|
+
storeId: item?.storeId,
|
|
3169
|
+
storeName: item?.storeName,
|
|
3170
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3171
|
+
issueDate: item?.dateString,
|
|
3172
|
+
footfall: item?.footfallCount,
|
|
3173
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3174
|
+
type: item.type || 'store',
|
|
3175
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3176
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3177
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3178
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3179
|
+
|
|
3180
|
+
} );
|
|
3181
|
+
}
|
|
612
3182
|
}
|
|
613
3183
|
} else if ( req.user.role === 'user' ) {
|
|
614
3184
|
temp = [];
|
|
615
3185
|
} else if ( ticketsFeature ) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
3186
|
+
if ( inputData?.isExport ) {
|
|
3187
|
+
const exportData = [];
|
|
3188
|
+
for ( let item of ticketListData ) {
|
|
3189
|
+
exportData.push( {
|
|
3190
|
+
|
|
3191
|
+
'Ticket ID': item?.ticketId,
|
|
3192
|
+
'Store ID': item?.storeId,
|
|
3193
|
+
'Store Name': item?.storeName,
|
|
3194
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3195
|
+
'Issue Date': item?.dateString,
|
|
3196
|
+
'Actual FF': item?.footfallCount,
|
|
3197
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3198
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3199
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3200
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3201
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3202
|
+
|
|
3203
|
+
} );
|
|
3204
|
+
}
|
|
3205
|
+
return await download( exportData, res );
|
|
3206
|
+
} else {
|
|
3207
|
+
for ( let item of ticketListData ) {
|
|
3208
|
+
temp.push( {
|
|
3209
|
+
|
|
3210
|
+
ticketId: item?.ticketId,
|
|
3211
|
+
storeId: item?.storeId,
|
|
3212
|
+
storeName: item?.storeName,
|
|
3213
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3214
|
+
issueDate: item?.dateString,
|
|
3215
|
+
footfall: item?.footfallCount,
|
|
3216
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3217
|
+
type: item.type || 'store',
|
|
3218
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3219
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3220
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3221
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3222
|
+
|
|
3223
|
+
} );
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
} else if ( ticketsApproveFeature ) {
|
|
3227
|
+
if ( inputData.isExport ) {
|
|
3228
|
+
const exportData = [];
|
|
3229
|
+
for ( let item of ticketListData ) {
|
|
3230
|
+
exportData.push( {
|
|
3231
|
+
|
|
3232
|
+
'Ticket ID': item?.ticketId,
|
|
3233
|
+
'Store Name': item?.storeName,
|
|
3234
|
+
'Store ID': item?.storeId,
|
|
3235
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3236
|
+
'Issue Date': item?.dateString,
|
|
3237
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3238
|
+
'Actual FF': item?.footfallCount,
|
|
3239
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3240
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3241
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3242
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3243
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3244
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3245
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3246
|
+
|
|
3247
|
+
} );
|
|
3248
|
+
}
|
|
3249
|
+
return await download( exportData, res );
|
|
3250
|
+
} else {
|
|
3251
|
+
for ( let item of ticketListData ) {
|
|
3252
|
+
temp.push( {
|
|
3253
|
+
|
|
3254
|
+
ticketId: item?.ticketId,
|
|
3255
|
+
storeId: item?.storeId,
|
|
3256
|
+
storeName: item?.storeName,
|
|
3257
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3258
|
+
issueDate: item?.dateString,
|
|
3259
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3260
|
+
footfall: item?.footfallCount,
|
|
3261
|
+
type: item.type || 'store',
|
|
3262
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3263
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3264
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3265
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3266
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3267
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3268
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3269
|
+
|
|
3270
|
+
} );
|
|
3271
|
+
}
|
|
634
3272
|
}
|
|
635
3273
|
} else {
|
|
636
|
-
temp =[];
|
|
3274
|
+
temp = [];
|
|
637
3275
|
}
|
|
638
3276
|
}
|
|
639
3277
|
|
|
640
|
-
return res.sendSuccess( { result: temp } );
|
|
3278
|
+
return res.sendSuccess( { result: temp, count: count } );
|
|
641
3279
|
} catch ( error ) {
|
|
642
3280
|
const err = error.message || 'Internal Server Error';
|
|
643
3281
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -649,149 +3287,19 @@ export async function getTickets( req, res ) {
|
|
|
649
3287
|
try {
|
|
650
3288
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
651
3289
|
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' ];
|
|
3290
|
+
|
|
3291
|
+
|
|
3292
|
+
let source = [ 'storeId', 'createdByEmail', '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
3293
|
let filter = [
|
|
658
3294
|
|
|
659
3295
|
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
gte: inputData.fromDate,
|
|
663
|
-
lte: inputData.toDate,
|
|
664
|
-
format: 'yyyy-MM-dd',
|
|
665
|
-
},
|
|
3296
|
+
term: {
|
|
3297
|
+
'ticketId.keyword': inputData.ticketId,
|
|
666
3298
|
},
|
|
667
3299
|
},
|
|
668
3300
|
];
|
|
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
3301
|
|
|
776
3302
|
|
|
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
3303
|
const getCount = {
|
|
796
3304
|
query: {
|
|
797
3305
|
bool: {
|
|
@@ -802,20 +3310,14 @@ export async function getTickets( req, res ) {
|
|
|
802
3310
|
|
|
803
3311
|
|
|
804
3312
|
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 );
|
|
3313
|
+
|
|
3314
|
+
|
|
3315
|
+
if ( !geteDataCount ) {
|
|
3316
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
814
3317
|
}
|
|
815
3318
|
|
|
816
3319
|
const getQuery = {
|
|
817
|
-
size:
|
|
818
|
-
from: skip,
|
|
3320
|
+
size: 1,
|
|
819
3321
|
query: {
|
|
820
3322
|
bool: {
|
|
821
3323
|
filter: filter,
|
|
@@ -825,20 +3327,16 @@ export async function getTickets( req, res ) {
|
|
|
825
3327
|
};
|
|
826
3328
|
|
|
827
3329
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
828
|
-
|
|
829
3330
|
const response = getData?.body?.hits?.hits;
|
|
830
3331
|
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 );
|
|
3332
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
836
3333
|
}
|
|
837
3334
|
let temp = [];
|
|
838
3335
|
if ( inputData?.action ) {
|
|
839
3336
|
response.map( ( hit ) => {
|
|
840
3337
|
const defaultData = {
|
|
841
3338
|
storeId: hit._source.storeId,
|
|
3339
|
+
createdByEmail: hit?._source?.createdByEmail,
|
|
842
3340
|
dateString: hit?._source?.dateString,
|
|
843
3341
|
ticketName: hit?._source?.ticketName,
|
|
844
3342
|
status: hit?._source?.status?.revicedFootfall,
|
|
@@ -870,29 +3368,31 @@ export async function getTickets( req, res ) {
|
|
|
870
3368
|
approverUserName: hit?._source?.approverUserName,
|
|
871
3369
|
approverEmail: hit?._source?.approverEmail,
|
|
872
3370
|
approverRole: hit?._source?.approverRole,
|
|
3371
|
+
type: hit?._source?.type,
|
|
873
3372
|
};
|
|
874
3373
|
let result;
|
|
875
3374
|
|
|
3375
|
+
|
|
876
3376
|
const matched = hit.matched_queries;
|
|
877
3377
|
// Add only matched data array
|
|
878
|
-
if ( matched.includes( 'matched_employee' )&& ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
3378
|
+
if ( matched.includes( 'matched_employee' ) && ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
879
3379
|
result = defaultData;
|
|
880
3380
|
result.employee = hit?._source?.employee;
|
|
881
3381
|
// result.type = 'employee';
|
|
882
3382
|
result.matched = matched;
|
|
883
3383
|
}
|
|
884
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
3384
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
885
3385
|
result = defaultData;
|
|
886
3386
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
887
3387
|
result.matched = matched;
|
|
888
3388
|
}
|
|
889
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
3389
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
890
3390
|
result = defaultData;
|
|
891
3391
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
892
3392
|
result.matched = matched;
|
|
893
3393
|
}
|
|
894
3394
|
|
|
895
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
3395
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
896
3396
|
result = defaultData;
|
|
897
3397
|
result.junk = hit?._source?.junk;
|
|
898
3398
|
result.matched = matched;
|
|
@@ -911,15 +3411,264 @@ export async function getTickets( req, res ) {
|
|
|
911
3411
|
}
|
|
912
3412
|
} );
|
|
913
3413
|
}
|
|
3414
|
+
|
|
914
3415
|
const finalResponse = inputData.action ? temp : response;
|
|
3416
|
+
const getRevopQuery = {
|
|
3417
|
+
size: 10000,
|
|
3418
|
+
query: {
|
|
3419
|
+
bool: {
|
|
3420
|
+
must: [
|
|
3421
|
+
{ term: { 'storeId.keyword': response?.[0]?._source?.storeId } }, // assuming inputData.storeId is an array
|
|
3422
|
+
{ term: { 'dateString': response?.[0]?._source?.dateString } },
|
|
3423
|
+
],
|
|
3424
|
+
},
|
|
3425
|
+
},
|
|
3426
|
+
};
|
|
3427
|
+
|
|
3428
|
+
|
|
3429
|
+
const revopResp = await getOpenSearchData( openSearch.revop, getRevopQuery );
|
|
3430
|
+
|
|
3431
|
+
|
|
3432
|
+
// Map revopResp.body.hits.hits to revopSources
|
|
3433
|
+
const revopSources = Array.isArray( revopResp?.body?.hits?.hits ) ?
|
|
3434
|
+
revopResp?.body?.hits?.hits?.map( ( hit ) => hit._source ) :
|
|
3435
|
+
[];
|
|
3436
|
+
|
|
3437
|
+
// Create a map of revopSources by id for quick lookup
|
|
3438
|
+
const revopSourcesMap = new Map();
|
|
3439
|
+
revopSources.forEach( ( item ) => {
|
|
3440
|
+
if ( item?.id ) {
|
|
3441
|
+
revopSourcesMap.set( String( item.id ), item );
|
|
3442
|
+
}
|
|
3443
|
+
} );
|
|
3444
|
+
|
|
3445
|
+
// Process revopSources to replace duplicateImage entries with full objects from the array
|
|
3446
|
+
const processedRevopSources = revopSources.map( ( item ) => {
|
|
3447
|
+
// Check if this is a duplicate parent item
|
|
3448
|
+
if ( item?.revopsType === 'duplicate' && item?.isParent === true && Array.isArray( item?.duplicateImage ) ) {
|
|
3449
|
+
// Map each duplicateImage entry to the full object from revopSources
|
|
3450
|
+
const updatedDuplicateImage = item.duplicateImage.map( ( duplicateImg ) => {
|
|
3451
|
+
const duplicateId = String( duplicateImg?.id );
|
|
3452
|
+
// Find the full object in revopSources that matches this id
|
|
3453
|
+
const fullObject = revopSourcesMap.get( duplicateId );
|
|
3454
|
+
// Return the full object if found, otherwise return the original duplicateImg
|
|
3455
|
+
return fullObject || duplicateImg;
|
|
3456
|
+
} );
|
|
3457
|
+
|
|
3458
|
+
return {
|
|
3459
|
+
...item,
|
|
3460
|
+
duplicateImage: updatedDuplicateImage,
|
|
3461
|
+
};
|
|
3462
|
+
}
|
|
3463
|
+
// Return item as-is if it doesn't meet the criteria
|
|
3464
|
+
return item;
|
|
3465
|
+
} );
|
|
3466
|
+
|
|
915
3467
|
if ( finalResponse?.length == 0 || !finalResponse ) {
|
|
916
3468
|
if ( inputData.storeId?.length > 0 ) {
|
|
917
|
-
const getStoreName = await findOneStore( { storeId:
|
|
3469
|
+
const getStoreName = await findOneStore( { storeId: response?.[0]?._source?.storeId }, { storeName: 1 } );
|
|
918
3470
|
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
919
3471
|
}
|
|
920
3472
|
return res.sendError( 'No Data found', 204 );
|
|
921
3473
|
}
|
|
922
|
-
|
|
3474
|
+
|
|
3475
|
+
|
|
3476
|
+
// replace the mappingInfo.revisedDetail with processedRevopSources
|
|
3477
|
+
if ( Array.isArray( finalResponse ) ) {
|
|
3478
|
+
for ( let item of finalResponse ) {
|
|
3479
|
+
if (
|
|
3480
|
+
item &&
|
|
3481
|
+
item._source &&
|
|
3482
|
+
item._source.mappingInfo
|
|
3483
|
+
|
|
3484
|
+
) {
|
|
3485
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3486
|
+
const vmsCommentsLogIndex = openSearch.vmsCommentsLog || 'vms-comments-log-dev';
|
|
3487
|
+
let commentsResponse = [];
|
|
3488
|
+
const commentsFilter = [
|
|
3489
|
+
{ term: { 'storeId.keyword': item?._source?.storeId } },
|
|
3490
|
+
{ term: { dateString: item?._source?.dateString } },
|
|
3491
|
+
|
|
3492
|
+
];
|
|
3493
|
+
|
|
3494
|
+
const commentsQuery = {
|
|
3495
|
+
size: 10000,
|
|
3496
|
+
sort: [
|
|
3497
|
+
{ createdAt: { order: 'desc' } }, // Sort descending by createdAt
|
|
3498
|
+
],
|
|
3499
|
+
query: {
|
|
3500
|
+
bool: {
|
|
3501
|
+
filter: commentsFilter,
|
|
3502
|
+
},
|
|
3503
|
+
},
|
|
3504
|
+
};
|
|
3505
|
+
|
|
3506
|
+
const commentsRes = await getOpenSearchData( vmsCommentsLogIndex, commentsQuery );
|
|
3507
|
+
// If mappingInfo is an array, update revisedDetail for each mappingInfo object
|
|
3508
|
+
if ( Array.isArray( item._source.mappingInfo ) ) {
|
|
3509
|
+
item._source.mappingInfo.forEach( ( mappingObj ) => {
|
|
3510
|
+
commentsResponse = commentsRes?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
3511
|
+
|
|
3512
|
+
// Check if duplicate condition exists in commentsResponse
|
|
3513
|
+
|
|
3514
|
+
// Structure comments output
|
|
3515
|
+
let commentsDetails = [];
|
|
3516
|
+
|
|
3517
|
+
|
|
3518
|
+
const types = [ 'tagging', 'review', 'approve' ];
|
|
3519
|
+
|
|
3520
|
+
// Process each type
|
|
3521
|
+
types.forEach( ( typeValue ) => {
|
|
3522
|
+
if ( typeValue === 'tagging' ) {
|
|
3523
|
+
// For tagging, group by category and create separate objects for each category
|
|
3524
|
+
const taggingComments = commentsResponse.filter( ( c ) => c.type === typeValue );
|
|
3525
|
+
|
|
3526
|
+
// Group by category
|
|
3527
|
+
const categoryGroups = {};
|
|
3528
|
+
taggingComments.forEach( ( c ) => {
|
|
3529
|
+
const category = c.category || 'other';
|
|
3530
|
+
if ( !categoryGroups[category] ) {
|
|
3531
|
+
categoryGroups[category] = [];
|
|
3532
|
+
}
|
|
3533
|
+
categoryGroups[category].push( c );
|
|
3534
|
+
} );
|
|
3535
|
+
|
|
3536
|
+
// Create separate objects for each category
|
|
3537
|
+
Object.keys( categoryGroups ).forEach( ( category ) => {
|
|
3538
|
+
const categoryComments = categoryGroups[category];
|
|
3539
|
+
let parent = null;
|
|
3540
|
+
|
|
3541
|
+
const comms = categoryComments.map( ( c ) => {
|
|
3542
|
+
if ( category === 'duplicate' ) {
|
|
3543
|
+
if ( !parent && c.parent ) {
|
|
3544
|
+
parent = c.parent;
|
|
3545
|
+
}
|
|
3546
|
+
return {
|
|
3547
|
+
createdByEmail: c.createdByEmail,
|
|
3548
|
+
createdByUserName: c.createdByUserName,
|
|
3549
|
+
createdByRole: c.createdByRole,
|
|
3550
|
+
message: c.message,
|
|
3551
|
+
};
|
|
3552
|
+
} else {
|
|
3553
|
+
return {
|
|
3554
|
+
id: c.id,
|
|
3555
|
+
tempId: c.tempId,
|
|
3556
|
+
timeRange: c.timeRange,
|
|
3557
|
+
entryTime: c.entryTime,
|
|
3558
|
+
exitTime: c.exitTime,
|
|
3559
|
+
filePath: c.filePath,
|
|
3560
|
+
isChecked: c.isChecked,
|
|
3561
|
+
createdAt: c.createdAt,
|
|
3562
|
+
message: c.message,
|
|
3563
|
+
createdByEmail: c.createdByEmail,
|
|
3564
|
+
createdByUserName: c.createdByUserName,
|
|
3565
|
+
createdByRole: c.createdByRole,
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
} );
|
|
3569
|
+
|
|
3570
|
+
const taggingObj = {
|
|
3571
|
+
category: category,
|
|
3572
|
+
type: typeValue,
|
|
3573
|
+
comments: comms,
|
|
3574
|
+
};
|
|
3575
|
+
|
|
3576
|
+
// Add parent only for duplicate category
|
|
3577
|
+
if ( category === 'duplicate' && parent !== null ) {
|
|
3578
|
+
taggingObj.parent = parent;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
commentsDetails.push( taggingObj );
|
|
3582
|
+
} );
|
|
3583
|
+
} else if ( typeValue === 'review' || typeValue === 'approve' ) {
|
|
3584
|
+
// For review and approve, keep existing structure
|
|
3585
|
+
const comms = commentsResponse
|
|
3586
|
+
.filter( ( c ) => c.type === typeValue )
|
|
3587
|
+
.map( ( c ) => {
|
|
3588
|
+
if ( c.category === 'duplicate' ) {
|
|
3589
|
+
return {
|
|
3590
|
+
parent: c?.taggedImages[0]?._source?.parent,
|
|
3591
|
+
category: c.category,
|
|
3592
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
3593
|
+
c.taggedImages.map( ( img ) => ( {
|
|
3594
|
+
id: img?._source?.id,
|
|
3595
|
+
tempId: img?._source?.tempId,
|
|
3596
|
+
timeRange: img?._source?.timeRange,
|
|
3597
|
+
entryTime: img?._source?.entryTime,
|
|
3598
|
+
exitTime: img?._source?.exitTime,
|
|
3599
|
+
filePath: img?._source?.filePath,
|
|
3600
|
+
isChecked: img?._source?.isChecked,
|
|
3601
|
+
} ) ) :
|
|
3602
|
+
[],
|
|
3603
|
+
createdByEmail: c.createdByEmail,
|
|
3604
|
+
createdByUserName: c.createdByUserName,
|
|
3605
|
+
createdByRole: c.createdByRole,
|
|
3606
|
+
status: c.status,
|
|
3607
|
+
message: c.message,
|
|
3608
|
+
};
|
|
3609
|
+
} else {
|
|
3610
|
+
return {
|
|
3611
|
+
category: c.category,
|
|
3612
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
3613
|
+
c.taggedImages.map( ( img ) => ( {
|
|
3614
|
+
id: img?._source?.id,
|
|
3615
|
+
tempId: img?._source?.tempId,
|
|
3616
|
+
timeRange: img?._source?.timeRange,
|
|
3617
|
+
entryTime: img?._source?.entryTime,
|
|
3618
|
+
exitTime: img?._source?.exitTime,
|
|
3619
|
+
filePath: img?._source?.filePath,
|
|
3620
|
+
isChecked: img?._source?.isChecked,
|
|
3621
|
+
} ) ) :
|
|
3622
|
+
[],
|
|
3623
|
+
createdByEmail: c.createdByEmail,
|
|
3624
|
+
createdByUserName: c.createdByUserName,
|
|
3625
|
+
createdByRole: c.createdByRole,
|
|
3626
|
+
status: c.status,
|
|
3627
|
+
message: c.message,
|
|
3628
|
+
};
|
|
3629
|
+
}
|
|
3630
|
+
} );
|
|
3631
|
+
|
|
3632
|
+
// Only add if there are comments
|
|
3633
|
+
if ( comms.length > 0 ) {
|
|
3634
|
+
commentsDetails.push( {
|
|
3635
|
+
type: typeValue,
|
|
3636
|
+
comments: comms,
|
|
3637
|
+
} );
|
|
3638
|
+
} else {
|
|
3639
|
+
// Add empty comments array if no comments
|
|
3640
|
+
commentsDetails.push( {
|
|
3641
|
+
type: typeValue,
|
|
3642
|
+
comments: [],
|
|
3643
|
+
} );
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
} );
|
|
3647
|
+
|
|
3648
|
+
|
|
3649
|
+
item._source.commentsDetails = commentsDetails;
|
|
3650
|
+
|
|
3651
|
+
if (
|
|
3652
|
+
Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) &&
|
|
3653
|
+
mappingObj.type !== 'tangoreview'
|
|
3654
|
+
) {
|
|
3655
|
+
mappingObj.revisedDetail = processedRevopSources;
|
|
3656
|
+
}
|
|
3657
|
+
} );
|
|
3658
|
+
} else {
|
|
3659
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
} else if (
|
|
3664
|
+
finalResponse &&
|
|
3665
|
+
finalResponse._source &&
|
|
3666
|
+
finalResponse._source.mappingInfo
|
|
3667
|
+
) {
|
|
3668
|
+
finalResponse._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
return res.sendSuccess( { result: finalResponse } );
|
|
923
3672
|
} catch ( error ) {
|
|
924
3673
|
const err = error.message || 'Internal Server Error';
|
|
925
3674
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -991,7 +3740,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
991
3740
|
item.isChecked == true ? tempId.push( { tempId: item.tempId, timeRange: item.timeRange } ) : null;
|
|
992
3741
|
bulkBody.push(
|
|
993
3742
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${item.timeRange}_${item.tempId}` } },
|
|
994
|
-
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true? 'approved':'rejected' } },
|
|
3743
|
+
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true ? 'approved' : 'rejected' } },
|
|
995
3744
|
);
|
|
996
3745
|
} );
|
|
997
3746
|
}
|
|
@@ -1006,11 +3755,11 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1006
3755
|
updateData.employee = updatedEmployee;
|
|
1007
3756
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: updateData } );
|
|
1008
3757
|
for ( let employee of updateData?.employee ) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
3758
|
+
( employee.isChecked == true ) ? tempId.push( { tempId: employee.tempId, timeRange: employee.timeRange } ) : null;
|
|
3759
|
+
bulkBody.push(
|
|
3760
|
+
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${employee.timeRange}_${employee.tempId}` } },
|
|
3761
|
+
{ doc: { isChecked: employee.isChecked, status: employee?.isChecked == true ? 'approved' : 'rejected' } },
|
|
3762
|
+
);
|
|
1014
3763
|
}
|
|
1015
3764
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { employeeACCount: tempId?.length || 0 } } );
|
|
1016
3765
|
}
|
|
@@ -1029,7 +3778,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1029
3778
|
houseKeeping.isChecked == true ? tempId.push( { tempId: houseKeeping.tempId, timeRange: houseKeeping.timeRange } ) : null;
|
|
1030
3779
|
bulkBody.push(
|
|
1031
3780
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${houseKeeping.timeRange}_${houseKeeping.tempId}` } },
|
|
1032
|
-
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true? 'approved':'rejected' } },
|
|
3781
|
+
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1033
3782
|
);
|
|
1034
3783
|
}
|
|
1035
3784
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { houseKeepingACCount: tempId?.length || 0 } } );
|
|
@@ -1050,7 +3799,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1050
3799
|
junk.isChecked == true ? tempId.push( { tempId: junk.tempId, timeRange: junk.timeRange } ) : null;
|
|
1051
3800
|
bulkBody.push(
|
|
1052
3801
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${junk.timeRange}_${junk.tempId}` } },
|
|
1053
|
-
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true? 'approved':'rejected' } },
|
|
3802
|
+
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1054
3803
|
);
|
|
1055
3804
|
}
|
|
1056
3805
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { junkACCount: tempId?.length || 0 } } );
|
|
@@ -1075,7 +3824,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1075
3824
|
let getUpdateExistingOne = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
1076
3825
|
const tempIdList = await extractCheckedTempIds( getUpdateExistingOne?.body );
|
|
1077
3826
|
const isSendMessge = await sendSqsMessage( data, tempIdList, getStoreType, storeId );
|
|
1078
|
-
if ( isSendMessge ==true ) {
|
|
3827
|
+
if ( isSendMessge == true ) {
|
|
1079
3828
|
return true; // res.sendSuccess( 'Ticket has been updated successfully' );
|
|
1080
3829
|
} else {
|
|
1081
3830
|
return false; // res.sendError( 'No SQS message sent', 500 );
|
|
@@ -1138,7 +3887,7 @@ function updateEmployeeCheckFlags( existingEmployees, inputEmployees, status ) {
|
|
|
1138
3887
|
// Step 2: Loop through all existing and update isChecked accordingly
|
|
1139
3888
|
const updatedEmployees = existingEmployees.map( ( emp ) => ( {
|
|
1140
3889
|
...emp,
|
|
1141
|
-
isChecked: status === 'rejected'? !checkedTempIds.has( emp.tempId ):checkedTempIds.has( emp.tempId ),
|
|
3890
|
+
isChecked: status === 'rejected' ? !checkedTempIds.has( emp.tempId ) : checkedTempIds.has( emp.tempId ),
|
|
1142
3891
|
} ) );
|
|
1143
3892
|
|
|
1144
3893
|
return updatedEmployees;
|
|
@@ -1188,7 +3937,7 @@ function mergeDuplicateImagesWithUncheck( existingData, inputData, status ) {
|
|
|
1188
3937
|
export async function sendSqsMessage( inputData, tempId, getStoreType, storeId ) {
|
|
1189
3938
|
const sqs = JSON.parse( process.env.SQS );
|
|
1190
3939
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1191
|
-
const sqsName = getStoreType > 0? sqs.revopTrackTicket: sqs.revopTicket;
|
|
3940
|
+
const sqsName = getStoreType > 0 ? sqs.revopTrackTicket : sqs.revopTicket;
|
|
1192
3941
|
const sqsProduceQueue = getStoreType > 0 ? {
|
|
1193
3942
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1194
3943
|
MessageBody: JSON.stringify( {
|
|
@@ -1216,12 +3965,11 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1216
3965
|
|
|
1217
3966
|
} ),
|
|
1218
3967
|
};
|
|
1219
|
-
const sqsQueue = getStoreType > 0? await sendMessageToFIFOQueue( sqsProduceQueue ):
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
);
|
|
3968
|
+
const sqsQueue = getStoreType > 0 ? await sendMessageToFIFOQueue( sqsProduceQueue ) :
|
|
3969
|
+
await sendMessageToQueue(
|
|
3970
|
+
sqsProduceQueue.QueueUrl,
|
|
3971
|
+
sqsProduceQueue.MessageBody,
|
|
3972
|
+
);
|
|
1225
3973
|
if ( sqsQueue.statusCode ) {
|
|
1226
3974
|
logger.error( {
|
|
1227
3975
|
error: `${sqsQueue}`,
|
|
@@ -1229,7 +3977,7 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1229
3977
|
} );
|
|
1230
3978
|
return false;
|
|
1231
3979
|
} else {
|
|
1232
|
-
const id
|
|
3980
|
+
const id = `${storeId}_${inputData.dateString.split( '-' ).reverse().join( '-' )}_${getStoreType > 0 ? 'live' : 'reduction'}_${Date.now()}`;
|
|
1233
3981
|
const logs = {
|
|
1234
3982
|
QueueUrl: sqsProduceQueue.QueueUrl,
|
|
1235
3983
|
MessageBody: sqsProduceQueue.MessageBody,
|
|
@@ -1279,13 +4027,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1279
4027
|
},
|
|
1280
4028
|
},
|
|
1281
4029
|
];
|
|
1282
|
-
|
|
1283
|
-
// filter.push(
|
|
1284
|
-
// {
|
|
1285
|
-
// terms: { 'storeId.keyword': req.stores },
|
|
1286
|
-
// },
|
|
1287
|
-
// );
|
|
1288
|
-
// }
|
|
4030
|
+
|
|
1289
4031
|
filter.push(
|
|
1290
4032
|
{
|
|
1291
4033
|
terms: { 'storeId.keyword': req?.stores || [] },
|
|
@@ -1336,7 +4078,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1336
4078
|
+ (doc.containsKey('employeeCount') && !doc['employeeCount'].empty ? doc['employeeCount'].value : 0)
|
|
1337
4079
|
+ (doc.containsKey('junkCount') && !doc['junkCount'].empty ? doc['junkCount'].value : 0);
|
|
1338
4080
|
`,
|
|
1339
|
-
lang: '
|
|
4081
|
+
lang: 'scripting',
|
|
1340
4082
|
},
|
|
1341
4083
|
},
|
|
1342
4084
|
},
|
|
@@ -1400,8 +4142,8 @@ export async function downloadTickets( req, res ) {
|
|
|
1400
4142
|
{
|
|
1401
4143
|
terms: {
|
|
1402
4144
|
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1403
|
-
|
|
1404
|
-
|
|
4145
|
+
inputData.storeId :
|
|
4146
|
+
inputData.storeId,
|
|
1405
4147
|
},
|
|
1406
4148
|
},
|
|
1407
4149
|
);
|
|
@@ -1447,28 +4189,28 @@ export async function downloadTickets( req, res ) {
|
|
|
1447
4189
|
gt: 0,
|
|
1448
4190
|
},
|
|
1449
4191
|
},
|
|
1450
|
-
|
|
1451
|
-
} ) :
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
4192
|
+
|
|
4193
|
+
} ) :
|
|
4194
|
+
inputData.revopsType === 'junk' ?
|
|
4195
|
+
filter.push( {
|
|
4196
|
+
range: {
|
|
4197
|
+
junkCount: {
|
|
4198
|
+
gt: 0,
|
|
4199
|
+
},
|
|
4200
|
+
},
|
|
4201
|
+
} ) :
|
|
4202
|
+
|
|
4203
|
+
filter.push( {
|
|
4204
|
+
range: {
|
|
4205
|
+
duplicateCount: {
|
|
4206
|
+
gt: 0,
|
|
4207
|
+
},
|
|
4208
|
+
},
|
|
4209
|
+
} );
|
|
1468
4210
|
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1469
4211
|
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1470
4212
|
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1471
|
-
|
|
4213
|
+
inputData.revopsType == 'junk' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'junkCount', 'comments', 'junk', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] : [];
|
|
1472
4214
|
}
|
|
1473
4215
|
|
|
1474
4216
|
if ( inputData.action ) {
|
|
@@ -1545,7 +4287,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1545
4287
|
let temp = [];
|
|
1546
4288
|
if ( inputData?.action ) {
|
|
1547
4289
|
response.map( ( hit ) => {
|
|
1548
|
-
const defaultData ={
|
|
4290
|
+
const defaultData = {
|
|
1549
4291
|
storeId: hit._source.storeId,
|
|
1550
4292
|
dateString: hit?._source?.dateString,
|
|
1551
4293
|
ticketName: hit?._source?.ticketName,
|
|
@@ -1575,19 +4317,19 @@ export async function downloadTickets( req, res ) {
|
|
|
1575
4317
|
// result.type = 'employee';
|
|
1576
4318
|
result.matched = matched;
|
|
1577
4319
|
}
|
|
1578
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
4320
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1579
4321
|
logger.info( { revop: inputData.revopsType } );
|
|
1580
4322
|
result = defaultData;
|
|
1581
4323
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
1582
4324
|
result.matched = matched;
|
|
1583
4325
|
}
|
|
1584
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
4326
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1585
4327
|
logger.info( { revop: inputData.revopsType } );
|
|
1586
4328
|
result = defaultData;
|
|
1587
4329
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
1588
4330
|
result.matched = matched;
|
|
1589
4331
|
}
|
|
1590
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
4332
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1591
4333
|
result = defaultData;
|
|
1592
4334
|
result.junk = hit?._source?.junk;
|
|
1593
4335
|
result.matched = matched;
|
|
@@ -1631,7 +4373,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1631
4373
|
revopsType: inputData?.revopsType,
|
|
1632
4374
|
type: 'get-tickets',
|
|
1633
4375
|
};
|
|
1634
|
-
const record={
|
|
4376
|
+
const record = {
|
|
1635
4377
|
stores: inputData?.storeId,
|
|
1636
4378
|
fromDate: inputData?.fromDate,
|
|
1637
4379
|
toDate: inputData?.toDate,
|
|
@@ -1649,7 +4391,7 @@ export async function downloadTickets( req, res ) {
|
|
|
1649
4391
|
const sqs = JSON.parse( process.env.SQS );
|
|
1650
4392
|
const sqsName = sqs.revopDownload;
|
|
1651
4393
|
|
|
1652
|
-
const sqsProduceQueue ={
|
|
4394
|
+
const sqsProduceQueue = {
|
|
1653
4395
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1654
4396
|
MessageBody: JSON.stringify( {
|
|
1655
4397
|
_id: getId?._id,
|
|
@@ -1678,7 +4420,6 @@ export async function downloadTickets( req, res ) {
|
|
|
1678
4420
|
}
|
|
1679
4421
|
|
|
1680
4422
|
async function extractTempIds( document ) {
|
|
1681
|
-
logger.info( { document: document } );
|
|
1682
4423
|
const source = document?._source || {};
|
|
1683
4424
|
const result = [];
|
|
1684
4425
|
|
|
@@ -1719,6 +4460,14 @@ async function extractTempIds( document ) {
|
|
|
1719
4460
|
export async function reviewerList( req, res ) {
|
|
1720
4461
|
try {
|
|
1721
4462
|
const inputData = req.query;
|
|
4463
|
+
|
|
4464
|
+
console.log( '🚀 ~ reviewerList ~ inputData.tangotype:', inputData.tangotype );
|
|
4465
|
+
if ( inputData.tangotype!=''&&inputData.tangotype==='internal' ) {
|
|
4466
|
+
const getUserlist = await findUser( { userType: 'tango' }, { userName: 1, email: 1, role: 1 } );
|
|
4467
|
+
return res.sendSuccess( getUserlist || [] );
|
|
4468
|
+
}
|
|
4469
|
+
|
|
4470
|
+
|
|
1722
4471
|
// Build the query for users who have rolespermission with featureName "FootfallDirectory",
|
|
1723
4472
|
// and a module "Reviewer" where isAdd or isEdit is true.
|
|
1724
4473
|
const reviewerRoleQuery = {
|
|
@@ -1728,7 +4477,7 @@ export async function reviewerList( req, res ) {
|
|
|
1728
4477
|
featureName: 'FootfallDirectory',
|
|
1729
4478
|
modules: {
|
|
1730
4479
|
$elemMatch: {
|
|
1731
|
-
name: '
|
|
4480
|
+
name: inputData?.type === 'review' ? 'reviewer' : 'approver',
|
|
1732
4481
|
$or: [ { isAdd: true }, { isEdit: true } ],
|
|
1733
4482
|
},
|
|
1734
4483
|
},
|
|
@@ -1737,7 +4486,7 @@ export async function reviewerList( req, res ) {
|
|
|
1737
4486
|
};
|
|
1738
4487
|
|
|
1739
4488
|
const getUserlist = await findUser( reviewerRoleQuery, { userName: 1, email: 1, role: 1 } );
|
|
1740
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
4489
|
+
return res.sendSuccess( getUserlist || [] );
|
|
1741
4490
|
} catch ( error ) {
|
|
1742
4491
|
const err = error.message || 'Internal Server Error';
|
|
1743
4492
|
return res.sendError( err, 500 );
|
|
@@ -1747,6 +4496,7 @@ export async function reviewerList( req, res ) {
|
|
|
1747
4496
|
export async function openTicketList( req, res ) {
|
|
1748
4497
|
try {
|
|
1749
4498
|
const inputData = req.body;
|
|
4499
|
+
logger.info( { inputData } );
|
|
1750
4500
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1751
4501
|
|
|
1752
4502
|
// INSERT_YOUR_CODE
|
|
@@ -1759,6 +4509,29 @@ export async function openTicketList( req, res ) {
|
|
|
1759
4509
|
clientId: Array.isArray( clientId ) ? clientId : [ clientId ],
|
|
1760
4510
|
},
|
|
1761
4511
|
},
|
|
4512
|
+
{
|
|
4513
|
+
nested: {
|
|
4514
|
+
path: 'mappingInfo',
|
|
4515
|
+
query: {
|
|
4516
|
+
bool: {
|
|
4517
|
+
must: [
|
|
4518
|
+
{
|
|
4519
|
+
term: {
|
|
4520
|
+
'mappingInfo.type': inputData.type,
|
|
4521
|
+
},
|
|
4522
|
+
},
|
|
4523
|
+
{
|
|
4524
|
+
term: {
|
|
4525
|
+
'mappingInfo.status': 'Open',
|
|
4526
|
+
},
|
|
4527
|
+
},
|
|
4528
|
+
|
|
4529
|
+
],
|
|
4530
|
+
},
|
|
4531
|
+
},
|
|
4532
|
+
},
|
|
4533
|
+
},
|
|
4534
|
+
|
|
1762
4535
|
{
|
|
1763
4536
|
range: {
|
|
1764
4537
|
dateString: {
|
|
@@ -1770,6 +4543,7 @@ export async function openTicketList( req, res ) {
|
|
|
1770
4543
|
},
|
|
1771
4544
|
];
|
|
1772
4545
|
|
|
4546
|
+
|
|
1773
4547
|
const openSearchQuery = {
|
|
1774
4548
|
size: 10000,
|
|
1775
4549
|
query: {
|
|
@@ -1777,14 +4551,44 @@ export async function openTicketList( req, res ) {
|
|
|
1777
4551
|
filter: filter,
|
|
1778
4552
|
},
|
|
1779
4553
|
},
|
|
1780
|
-
_source: [ 'ticketId', 'storeName', 'revicedFootfall', 'footfallCount', 'revicedPerc' ],
|
|
4554
|
+
_source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
|
|
1781
4555
|
};
|
|
1782
4556
|
|
|
4557
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
4558
|
+
openSearchQuery.query.bool['should'] = [];
|
|
4559
|
+
openSearchQuery.query.bool.should = [
|
|
4560
|
+
|
|
4561
|
+
{
|
|
4562
|
+
'wildcard': {
|
|
4563
|
+
'storeName.keyword': {
|
|
4564
|
+
'value': `*${inputData.searchValue}*`,
|
|
4565
|
+
},
|
|
4566
|
+
},
|
|
4567
|
+
},
|
|
4568
|
+
{
|
|
4569
|
+
'wildcard': {
|
|
4570
|
+
'ticketId.keyword': {
|
|
4571
|
+
'value': `*${inputData.searchValue}*`,
|
|
4572
|
+
},
|
|
4573
|
+
},
|
|
4574
|
+
},
|
|
4575
|
+
|
|
4576
|
+
|
|
4577
|
+
];
|
|
4578
|
+
openSearchQuery.query.bool['minimum_should_match'] = 1;
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4581
|
+
// INSERT_YOUR_CODE
|
|
4582
|
+
// Add sorting by revicedPerc descending (highest revised accuracy first)
|
|
4583
|
+
openSearchQuery.sort = [
|
|
4584
|
+
{ 'revicedPerc.keyword': { order: inputData?.sortOrder === 1 ? 'asc' : 'desc' } },
|
|
4585
|
+
];
|
|
4586
|
+
|
|
1783
4587
|
|
|
1784
4588
|
// Assuming getOpenSearchData and openSearch.footfallDirectoryTagging are available
|
|
1785
4589
|
const result = await getOpenSearchData( openSearch.footfallDirectory, openSearchQuery );
|
|
1786
4590
|
const getUserlist = result?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
1787
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
4591
|
+
return res.sendSuccess( getUserlist || [] );
|
|
1788
4592
|
} catch ( error ) {
|
|
1789
4593
|
const err = error.message || 'Internal Server Error';
|
|
1790
4594
|
logger.error( { error: error, function: 'openTicketList' } );
|
|
@@ -1797,73 +4601,32 @@ export async function assignTicket( req, res ) {
|
|
|
1797
4601
|
const inputData = req.body;
|
|
1798
4602
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1799
4603
|
|
|
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
|
-
|
|
4604
|
+
const { email, userName, role, storeId, dateString } = inputData;
|
|
4605
|
+
const _id = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
1863
4606
|
|
|
1864
|
-
|
|
4607
|
+
const getTicket = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
4608
|
+
if ( !getTicket ) {
|
|
4609
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
4610
|
+
}
|
|
4611
|
+
const source = getTicket?.body?._source;
|
|
4612
|
+
const mappingInfo = Array.isArray( source.mappingInfo ) ? source.mappingInfo : [];
|
|
4613
|
+
if ( mappingInfo.length === 0 ) {
|
|
4614
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
4615
|
+
}
|
|
4616
|
+
const lastIndex = mappingInfo.length - 1;
|
|
4617
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
4618
|
+
if ( String( lastEntry.status ) !== 'In-Progress' ) {
|
|
4619
|
+
return res.sendError( 'Ticket is not in progress', 400 );
|
|
4620
|
+
}
|
|
1865
4621
|
|
|
1866
|
-
|
|
4622
|
+
const currentTime = new Date();
|
|
4623
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
4624
|
+
updatedMappingInfo[lastIndex] = { ...lastEntry, createdByEmail: email, updatedAt: currentTime, createdByUserName: userName, createdByRole: role };
|
|
4625
|
+
const updateResult = await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { mappingInfo: updatedMappingInfo } } );
|
|
4626
|
+
if ( !updateResult ) {
|
|
4627
|
+
return res.sendError( 'Failed to update ticket', 400 );
|
|
4628
|
+
}
|
|
4629
|
+
return res.sendSuccess( { message: 'Ticket assigned successfully' } );
|
|
1867
4630
|
} catch ( error ) {
|
|
1868
4631
|
const err = error.message || 'Internal Server Error';
|
|
1869
4632
|
logger.error( { error: error, function: 'assignTicket' } );
|
|
@@ -1874,11 +4637,13 @@ export async function assignTicket( req, res ) {
|
|
|
1874
4637
|
export async function updateTempStatus( req, res ) {
|
|
1875
4638
|
try {
|
|
1876
4639
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1877
|
-
const
|
|
4640
|
+
const inputData = req.body;
|
|
4641
|
+
const { id, type, status, comments } = inputData;
|
|
1878
4642
|
|
|
1879
4643
|
// Use bulk update API via bucketing (batch update) -- fetch docs, then bulk-update
|
|
1880
4644
|
// 1. Search for all documents matching the ticket IDs
|
|
1881
4645
|
const searchBody = {
|
|
4646
|
+
size: 10000,
|
|
1882
4647
|
query: {
|
|
1883
4648
|
bool: {
|
|
1884
4649
|
must: [
|
|
@@ -1897,16 +4662,14 @@ export async function updateTempStatus( req, res ) {
|
|
|
1897
4662
|
openSearch.revop,
|
|
1898
4663
|
searchBody,
|
|
1899
4664
|
);
|
|
1900
|
-
|
|
4665
|
+
|
|
1901
4666
|
// Extract bulk IDs to update
|
|
1902
4667
|
const hits = searchResp?.body?.hits?.hits ?? [];
|
|
1903
|
-
|
|
4668
|
+
|
|
1904
4669
|
if ( !hits.length ) {
|
|
1905
4670
|
return res.sendError( 'no data', 204 );
|
|
1906
4671
|
}
|
|
1907
4672
|
|
|
1908
|
-
// 2. Build bulk update commands
|
|
1909
|
-
// Each doc: { update: { _id: ..., _index: ... } }, { doc: { status: status } }
|
|
1910
4673
|
|
|
1911
4674
|
// 1. Get all IDs from hits
|
|
1912
4675
|
const docIdToIndex = {};
|
|
@@ -1914,7 +4677,6 @@ export async function updateTempStatus( req, res ) {
|
|
|
1914
4677
|
docIdToIndex[doc._id] = doc._index;
|
|
1915
4678
|
} );
|
|
1916
4679
|
const docIds = hits.map( ( doc ) => doc._id );
|
|
1917
|
-
logger.info( { docIds } );
|
|
1918
4680
|
// 2. Fetch all docs by ID to get 'actions' (in chunks if large)
|
|
1919
4681
|
const getBody = [];
|
|
1920
4682
|
for ( const doc of hits ) {
|
|
@@ -1922,71 +4684,76 @@ export async function updateTempStatus( req, res ) {
|
|
|
1922
4684
|
}
|
|
1923
4685
|
|
|
1924
4686
|
let mgetResp;
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
4687
|
+
|
|
4688
|
+
mgetResp = await getOpenSearchData(
|
|
4689
|
+
openSearch.revop,
|
|
4690
|
+
{
|
|
4691
|
+
size: 10000,
|
|
4692
|
+
query: {
|
|
4693
|
+
ids: {
|
|
4694
|
+
values: docIds,
|
|
1933
4695
|
},
|
|
1934
|
-
_source: true,
|
|
1935
4696
|
},
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
mgetResp = undefined;
|
|
1940
|
-
}
|
|
1941
|
-
logger.info( { mgetResp } );
|
|
4697
|
+
_source: true,
|
|
4698
|
+
},
|
|
4699
|
+
);
|
|
1942
4700
|
// (If you have a utility for multi-get, you may want to use that. Else, you might need to fetch each by ID.)
|
|
1943
4701
|
// 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
|
-
}
|
|
4702
|
+
let fullDocs = mgetResp?.body?.hits?.hits || searchResp?.body?.hits?.hits || [];
|
|
1951
4703
|
|
|
1952
4704
|
// 3. Prepare the new actions array for each doc, and set up bulk update payloads
|
|
1953
4705
|
const reviewActions = [ 'approved', 'rejected' ];
|
|
1954
4706
|
const docsToUpdate = [];
|
|
1955
|
-
logger.info( { fullDocs: fullDocs } );
|
|
1956
4707
|
for ( const doc of fullDocs ) {
|
|
1957
4708
|
const source = doc._source || doc.fields || {}; // support mget and search hits
|
|
1958
4709
|
let actions = Array.isArray( source.actions ) ? [ ...source.actions ] : [];
|
|
1959
4710
|
if ( reviewActions.includes( status ) ) {
|
|
1960
4711
|
// for review: update or push 'review'
|
|
1961
4712
|
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
|
-
|
|
4713
|
+
switch ( type ) {
|
|
4714
|
+
case 'review':
|
|
4715
|
+
actions = actions.map( ( item ) => {
|
|
4716
|
+
if ( item.actionType === 'review' ) {
|
|
4717
|
+
found = true;
|
|
4718
|
+
return { ...item, action: status };
|
|
4719
|
+
}
|
|
4720
|
+
return item;
|
|
4721
|
+
} );
|
|
4722
|
+
if ( !found ) {
|
|
4723
|
+
actions.push( { actionType: 'review', action: status } );
|
|
4724
|
+
}
|
|
4725
|
+
break;
|
|
4726
|
+
case 'approve':
|
|
4727
|
+
actions = actions.map( ( item ) => {
|
|
4728
|
+
if ( item.actionType === 'approve' ) {
|
|
4729
|
+
found = true;
|
|
4730
|
+
return { ...item, action: status };
|
|
4731
|
+
}
|
|
4732
|
+
return item;
|
|
4733
|
+
} );
|
|
4734
|
+
if ( !found ) {
|
|
4735
|
+
actions.push( { actionType: 'approve', action: status } );
|
|
4736
|
+
}
|
|
4737
|
+
break;
|
|
4738
|
+
default:
|
|
4739
|
+
return res.sendError( 'wrong vaue', 400 );
|
|
1984
4740
|
}
|
|
1985
4741
|
}
|
|
4742
|
+
let isChecked = true;
|
|
4743
|
+
switch ( type ) {
|
|
4744
|
+
case 'review':
|
|
4745
|
+
isChecked = status === 'approved' ? true : false;
|
|
4746
|
+
break;
|
|
4747
|
+
case 'approve':
|
|
4748
|
+
isChecked = status === 'approved' ? doc?._source?.isChecked : !doc?._source?.isChecked;
|
|
4749
|
+
break;
|
|
4750
|
+
}
|
|
1986
4751
|
docsToUpdate.push( {
|
|
1987
4752
|
_index: doc._index || docIdToIndex[doc._id],
|
|
1988
4753
|
_id: doc._id,
|
|
1989
4754
|
actions,
|
|
4755
|
+
isChecked: isChecked,
|
|
4756
|
+
comments: comments || '',
|
|
1990
4757
|
} );
|
|
1991
4758
|
}
|
|
1992
4759
|
const bulkPayload = [];
|
|
@@ -1996,11 +4763,9 @@ export async function updateTempStatus( req, res ) {
|
|
|
1996
4763
|
update: { _index: doc._index, _id: doc._id },
|
|
1997
4764
|
} );
|
|
1998
4765
|
bulkPayload.push( {
|
|
1999
|
-
doc: { actions: doc.actions },
|
|
4766
|
+
doc: { actions: doc.actions, isChecked: doc?.isChecked, comments: doc?.comments || '' },
|
|
2000
4767
|
} );
|
|
2001
4768
|
}
|
|
2002
|
-
logger.info( { bulkPayload: bulkPayload } );
|
|
2003
|
-
|
|
2004
4769
|
|
|
2005
4770
|
// 3. Execute bulk update
|
|
2006
4771
|
const bulkResp = await bulkUpdate( bulkPayload );
|
|
@@ -2008,8 +4773,50 @@ export async function updateTempStatus( req, res ) {
|
|
|
2008
4773
|
// Count successes
|
|
2009
4774
|
const updatedCount = bulkResp?.body?.items?.filter( ( item ) => item?.update?.result === 'updated' || item?.update?.result === 'noop' ).length ?? 0;
|
|
2010
4775
|
|
|
2011
|
-
|
|
4776
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
4777
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
4778
|
+
const searchBody1 = {
|
|
4779
|
+
size: 10000,
|
|
4780
|
+
query: {
|
|
4781
|
+
bool: {
|
|
4782
|
+
must: [
|
|
4783
|
+
{
|
|
4784
|
+
terms: {
|
|
4785
|
+
'_id': docIds,
|
|
4786
|
+
},
|
|
4787
|
+
},
|
|
4788
|
+
{
|
|
4789
|
+
term: {
|
|
4790
|
+
isParent: false,
|
|
4791
|
+
},
|
|
4792
|
+
},
|
|
4793
|
+
],
|
|
4794
|
+
},
|
|
4795
|
+
},
|
|
4796
|
+
};
|
|
4797
|
+
|
|
4798
|
+
const getSearchResp = await getOpenSearchData(
|
|
4799
|
+
openSearch.revop,
|
|
4800
|
+
searchBody1,
|
|
4801
|
+
);
|
|
4802
|
+
|
|
2012
4803
|
|
|
4804
|
+
const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0 ? getSearchResp?.body?.hits?.hits : [];
|
|
4805
|
+
const logs = {
|
|
4806
|
+
type: inputData.type,
|
|
4807
|
+
storeId: taggedImages?.[0]?._source?.storeId,
|
|
4808
|
+
dateString: taggedImages?.[0]?._source?.dateString,
|
|
4809
|
+
category: taggedImages?.[0]?._source?.revopsType || '',
|
|
4810
|
+
taggedImages: taggedImages,
|
|
4811
|
+
status: inputData?.status,
|
|
4812
|
+
createdByEmail: req?.user?.email,
|
|
4813
|
+
createdByUserName: req?.user?.userName,
|
|
4814
|
+
createdByRole: req?.user?.role,
|
|
4815
|
+
message: inputData.comments || '',
|
|
4816
|
+
createdAt: new Date(),
|
|
4817
|
+
};
|
|
4818
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
4819
|
+
}
|
|
2013
4820
|
return res.sendSuccess( { updated: updatedCount } );
|
|
2014
4821
|
} catch ( error ) {
|
|
2015
4822
|
const err = error.message;
|
|
@@ -2018,3 +4825,283 @@ export async function updateTempStatus( req, res ) {
|
|
|
2018
4825
|
}
|
|
2019
4826
|
}
|
|
2020
4827
|
|
|
4828
|
+
export async function updateUserTicketStatus( req, res ) {
|
|
4829
|
+
try {
|
|
4830
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
4831
|
+
const { storeId, dateString } = req.body || {};
|
|
4832
|
+
|
|
4833
|
+
if ( !storeId || !dateString ) {
|
|
4834
|
+
return res.sendError( 'storeId and dateString are required', 400 );
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
4838
|
+
|
|
4839
|
+
// Fetch existing ticket so we can validate mappingInfo state
|
|
4840
|
+
const existingDoc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
4841
|
+
const ticketSource = existingDoc?.body?._source;
|
|
4842
|
+
|
|
4843
|
+
if ( !ticketSource ) {
|
|
4844
|
+
return res.sendError( 'Ticket not found', 404 );
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
const mappingInfo = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [];
|
|
4848
|
+
if ( mappingInfo.length === 0 ) {
|
|
4849
|
+
return res.sendError( 'mappingInfo is missing for this ticket', 400 );
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4852
|
+
const lastIndex = mappingInfo.length - 1;
|
|
4853
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
4854
|
+
|
|
4855
|
+
if ( !lastEntry ) {
|
|
4856
|
+
return res.sendError( 'Unable to determine current ticket status', 400 );
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4859
|
+
if ( String( lastEntry.status ).toLowerCase() !== 'open' ) {
|
|
4860
|
+
return res.sendError( 'Ticket is already picked by another user', 409 );
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4863
|
+
const currentTime = new Date();
|
|
4864
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
4865
|
+
updatedMappingInfo[lastIndex] = {
|
|
4866
|
+
...lastEntry,
|
|
4867
|
+
status: 'In-Progress',
|
|
4868
|
+
updatedAt: currentTime,
|
|
4869
|
+
createdAt: currentTime,
|
|
4870
|
+
createdByRole: req?.user?.role || '',
|
|
4871
|
+
createdByEmail: req?.user?.email || '',
|
|
4872
|
+
createdByUserName: req?.user?.userName || '',
|
|
4873
|
+
};
|
|
4874
|
+
|
|
4875
|
+
const updatePayload = {
|
|
4876
|
+
doc: {
|
|
4877
|
+
status: lastEntry?.type == 'review'? 'Reviewer In progress':lastEntry?.type == 'approve'? 'Approver In progress':ticketSource?.status,
|
|
4878
|
+
mappingInfo: updatedMappingInfo,
|
|
4879
|
+
updatedAt: currentTime,
|
|
4880
|
+
},
|
|
4881
|
+
};
|
|
4882
|
+
|
|
4883
|
+
const updateResult = await updateOpenSearchData(
|
|
4884
|
+
openSearch.footfallDirectory,
|
|
4885
|
+
docId,
|
|
4886
|
+
updatePayload,
|
|
4887
|
+
);
|
|
4888
|
+
|
|
4889
|
+
|
|
4890
|
+
if ( !updateResult || !( updateResult.statusCode === 200 || updateResult.statusCode === 201 ) ) {
|
|
4891
|
+
return res.sendError( 'Failed to update ticket status', 500 );
|
|
4892
|
+
}
|
|
4893
|
+
return res.sendSuccess( 'Ticket status updated successfully' );
|
|
4894
|
+
} catch ( error ) {
|
|
4895
|
+
const err = error.message;
|
|
4896
|
+
logger.info( { error: err, function: 'updateUserTicketStatus' } );
|
|
4897
|
+
return res.sendError( err, 500 );
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
export async function multiCloseTicket( req, res ) {
|
|
4902
|
+
try {
|
|
4903
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
4904
|
+
const inputData = req.body;
|
|
4905
|
+
|
|
4906
|
+
// inputData structure should include an array of items to close
|
|
4907
|
+
// Accept both array or single ticket update
|
|
4908
|
+
const tickets = Array.isArray( inputData.ticketList ) ? inputData.ticketList : [ inputData.ticketList ];
|
|
4909
|
+
// const mode = inputData.mode || '';
|
|
4910
|
+
|
|
4911
|
+
if ( !tickets.length ) {
|
|
4912
|
+
return res.sendError( 'No tickets provided', 400 );
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4915
|
+
const results = [];
|
|
4916
|
+
for ( const ticket of tickets ) {
|
|
4917
|
+
const { storeId, dateString } = ticket || {};
|
|
4918
|
+
if ( !storeId || !dateString ) {
|
|
4919
|
+
results.push( { storeId, dateString, success: false, error: 'Missing storeId or dateString' } );
|
|
4920
|
+
continue;
|
|
4921
|
+
}
|
|
4922
|
+
// 1. Update the ticket document in footfallDirectory index
|
|
4923
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
4924
|
+
|
|
4925
|
+
// Fetch existing doc to update mappingInfo
|
|
4926
|
+
|
|
4927
|
+
const doc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
4928
|
+
|
|
4929
|
+
|
|
4930
|
+
const ticketSource = doc?.body?._source;
|
|
4931
|
+
if ( !ticketSource || !ticketSource.mappingInfo ) {
|
|
4932
|
+
results.push( { storeId, dateString, success: false, error: 'Ticket or mappingInfo missing' } );
|
|
4933
|
+
continue;
|
|
4934
|
+
}
|
|
4935
|
+
|
|
4936
|
+
let mappingInfoArray = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [ ticketSource.mappingInfo ];
|
|
4937
|
+
// Find all mappingInfo items matching type 'approve'
|
|
4938
|
+
let updated = false;
|
|
4939
|
+
let newMappingInfoArray = mappingInfoArray.map( ( mi, i ) => {
|
|
4940
|
+
if ( mi?.type === 'approve' && mi?.status === 'Open' ) {
|
|
4941
|
+
updated = true;
|
|
4942
|
+
return {
|
|
4943
|
+
...mi,
|
|
4944
|
+
status: 'Approver-Closed',
|
|
4945
|
+
status: 'Closed', // following the user's instruction to set sttaus property (assumed typo, but explicitly used)
|
|
4946
|
+
updatedAt: new Date(),
|
|
4947
|
+
};
|
|
4948
|
+
}
|
|
4949
|
+
return mi;
|
|
4950
|
+
} );
|
|
4951
|
+
|
|
4952
|
+
if ( !updated ) {
|
|
4953
|
+
// None found to update
|
|
4954
|
+
results.push( { storeId, dateString, success: false, error: `coudn't approve this store` } );
|
|
4955
|
+
continue;
|
|
4956
|
+
}
|
|
4957
|
+
|
|
4958
|
+
// Write update to footfallDirectory
|
|
4959
|
+
const ticketUpdatePayload = {
|
|
4960
|
+
doc: {
|
|
4961
|
+
mappingInfo: newMappingInfoArray,
|
|
4962
|
+
status: 'Approver-Closed', // status updated at top level as well
|
|
4963
|
+
updatedAt: new Date(),
|
|
4964
|
+
},
|
|
4965
|
+
};
|
|
4966
|
+
let ticketUpdateResult;
|
|
4967
|
+
try {
|
|
4968
|
+
ticketUpdateResult = await updateOpenSearchData( openSearch.footfallDirectory, docId, ticketUpdatePayload );
|
|
4969
|
+
if ( !( ticketUpdateResult && ( ticketUpdateResult.statusCode === 200 || ticketUpdateResult.statusCode === 201 ) ) ) {
|
|
4970
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
4971
|
+
continue;
|
|
4972
|
+
}
|
|
4973
|
+
} catch ( err ) {
|
|
4974
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
4975
|
+
continue;
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
|
|
4979
|
+
// For each ticket, update actions array for all matching image docs (storeId & dateString)
|
|
4980
|
+
|
|
4981
|
+
// Query to find all matching docs in revopTagging index with storeId and dateString
|
|
4982
|
+
const revopImageQuery = {
|
|
4983
|
+
size: 10000, // assume there won't be more than 1000 images per ticket
|
|
4984
|
+
query: {
|
|
4985
|
+
bool: {
|
|
4986
|
+
must: [
|
|
4987
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
4988
|
+
{ term: { dateString: dateString } },
|
|
4989
|
+
],
|
|
4990
|
+
},
|
|
4991
|
+
},
|
|
4992
|
+
};
|
|
4993
|
+
|
|
4994
|
+
// Fetch matching docs
|
|
4995
|
+
const searchRes = await getOpenSearchData( openSearch.revop, revopImageQuery );
|
|
4996
|
+
const revopHits = searchRes?.body?.hits?.hits || [];
|
|
4997
|
+
|
|
4998
|
+
// Optimized: batch and parallelize image-doc updates to avoid long sequential waits
|
|
4999
|
+
const BATCH_SIZE = 100;
|
|
5000
|
+
const now = new Date();
|
|
5001
|
+
|
|
5002
|
+
for ( let i = 0; i < revopHits.length; i += BATCH_SIZE ) {
|
|
5003
|
+
const batch = revopHits.slice( i, i + BATCH_SIZE );
|
|
5004
|
+
|
|
5005
|
+
const updatePromises = batch.map( async ( hit ) => {
|
|
5006
|
+
const imageDocId = hit._id;
|
|
5007
|
+
const imageSource = hit._source || {};
|
|
5008
|
+
const imageActionsArray = Array.isArray( imageSource.actions ) ? [ ...imageSource.actions ] : [];
|
|
5009
|
+
|
|
5010
|
+
imageActionsArray.push( {
|
|
5011
|
+
actionType: 'approve',
|
|
5012
|
+
action: 'approved',
|
|
5013
|
+
} );
|
|
5014
|
+
|
|
5015
|
+
const imageUpdatePayload = {
|
|
5016
|
+
doc: {
|
|
5017
|
+
actions: imageActionsArray,
|
|
5018
|
+
updatedAt: now,
|
|
5019
|
+
},
|
|
5020
|
+
};
|
|
5021
|
+
|
|
5022
|
+
return updateOpenSearchData( openSearch.revop, imageDocId, imageUpdatePayload );
|
|
5023
|
+
} );
|
|
5024
|
+
|
|
5025
|
+
// Wait for this batch to finish before starting the next one
|
|
5026
|
+
await Promise.all( updatePromises );
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
if ( results && results?.length > 0 ) {
|
|
5030
|
+
return res.sendError( results, 500 );
|
|
5031
|
+
}
|
|
5032
|
+
// Return batch summary
|
|
5033
|
+
} catch ( error ) {
|
|
5034
|
+
const err = error.message;
|
|
5035
|
+
logger.info( { error: err, function: 'multiCloseTicket' } );
|
|
5036
|
+
return res.sendError( err, 500 );
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
|
|
5041
|
+
export async function checkTicketExists( req, res ) {
|
|
5042
|
+
try {
|
|
5043
|
+
let inputData = req.body;
|
|
5044
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
5045
|
+
let findQuery = {
|
|
5046
|
+
size: 10000,
|
|
5047
|
+
query: {
|
|
5048
|
+
bool: {
|
|
5049
|
+
must: [
|
|
5050
|
+
{
|
|
5051
|
+
term: {
|
|
5052
|
+
'storeId.keyword': inputData.storeId,
|
|
5053
|
+
},
|
|
5054
|
+
},
|
|
5055
|
+
{
|
|
5056
|
+
term: {
|
|
5057
|
+
'dateString': inputData.dateString,
|
|
5058
|
+
},
|
|
5059
|
+
},
|
|
5060
|
+
],
|
|
5061
|
+
},
|
|
5062
|
+
},
|
|
5063
|
+
};
|
|
5064
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
5065
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
5066
|
+
|
|
5067
|
+
|
|
5068
|
+
res.sendSuccess( Ticket );
|
|
5069
|
+
} catch ( error ) {
|
|
5070
|
+
const err = error.message;
|
|
5071
|
+
logger.info( { error: err, function: 'checkTicketExists' } );
|
|
5072
|
+
return res.sendError( err, 500 );
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
|
|
5076
|
+
|
|
5077
|
+
export async function getAccuracyIssues( req, res ) {
|
|
5078
|
+
try {
|
|
5079
|
+
const inputData = req.query;
|
|
5080
|
+
|
|
5081
|
+
|
|
5082
|
+
const getIsues = await findStoreAccuracIssues( { clientId: inputData.clientId, isActive: true }, { issueName: 1 } );
|
|
5083
|
+
// const mode = inputData.mode || '';
|
|
5084
|
+
res.sendSuccess( { result: getIsues|| [] } );
|
|
5085
|
+
// Return batch summary
|
|
5086
|
+
} catch ( error ) {
|
|
5087
|
+
const err = error.message;
|
|
5088
|
+
logger.info( { error: err, function: 'getAccuracyIssues' } );
|
|
5089
|
+
return res.sendError( err, 500 );
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
|
|
5093
|
+
export async function updateAccuracyIssues( req, res ) {
|
|
5094
|
+
try {
|
|
5095
|
+
const inputData = req.query;
|
|
5096
|
+
|
|
5097
|
+
|
|
5098
|
+
const getIsues = await upsertStoreAccuracIssues( { clientId: inputData.clientId, issueName: inputData?.issueName }, { issueName: inputData?.issueName, isActive: true } );
|
|
5099
|
+
// const mode = inputData.mode || '';
|
|
5100
|
+
res.sendSuccess( { result: getIsues|| [] } );
|
|
5101
|
+
// Return batch summary
|
|
5102
|
+
} catch ( error ) {
|
|
5103
|
+
const err = error.message;
|
|
5104
|
+
logger.info( { error: err, function: 'putAccuracyIssues' } );
|
|
5105
|
+
return res.sendError( err, 500 );
|
|
5106
|
+
}
|
|
5107
|
+
}
|