tango-app-api-infra 3.9.5-vms.7 → 3.9.5-vms.70
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.
|
@@ -1,13 +1,14 @@
|
|
|
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';
|
|
11
12
|
dayjs.extend( utc );
|
|
12
13
|
dayjs.extend( timezone );
|
|
13
14
|
|
|
@@ -60,6 +61,395 @@ export async function createTicket( req, res ) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
export async function createinternalTicket( req, res ) {
|
|
65
|
+
try {
|
|
66
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
67
|
+
let inputData = req.body;
|
|
68
|
+
let record = {
|
|
69
|
+
|
|
70
|
+
storeId: inputData.storeId,
|
|
71
|
+
type: 'internal',
|
|
72
|
+
dateString: inputData.dateString,
|
|
73
|
+
storeName: inputData?.storeName,
|
|
74
|
+
ticketName: inputData.ticketName || 'footfall-directory',
|
|
75
|
+
footfallCount: inputData.footfallCount,
|
|
76
|
+
clientId: inputData?.clientId,
|
|
77
|
+
ticketId: 'TE_FDT_' + new Date().valueOf(),
|
|
78
|
+
createdAt: new Date(),
|
|
79
|
+
updatedAt: new Date(),
|
|
80
|
+
status: 'open',
|
|
81
|
+
comments: inputData?.comments || '',
|
|
82
|
+
createdByEmail: req?.user?.email,
|
|
83
|
+
createdByUserName: req?.user?.userName,
|
|
84
|
+
createdByRole: req?.user?.role,
|
|
85
|
+
mappingInfo: [],
|
|
86
|
+
};
|
|
87
|
+
const id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
88
|
+
const insertResult = await insertWithId( openSearch.footfallDirectory, id, record );
|
|
89
|
+
if ( insertResult && insertResult.statusCode === 201 ) {
|
|
90
|
+
return res.sendSuccess( 'Ticket raised successfully' );
|
|
91
|
+
}
|
|
92
|
+
} catch ( error ) {
|
|
93
|
+
const err = error.message || 'Internal Server Error';
|
|
94
|
+
logger.error( { error: error, funtion: 'createinternalTicket' } );
|
|
95
|
+
return res.sendError( err, 500 );
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export async function tangoReviewTicket( req, res ) {
|
|
99
|
+
try {
|
|
100
|
+
const inputData = req.body;
|
|
101
|
+
|
|
102
|
+
// get store info by the storeId into mongo db
|
|
103
|
+
const getstoreName = await findOneStore( { storeId: inputData.storeId, status: 'active' }, { storeId: 1, storeName: 1, clientId: 1 } );
|
|
104
|
+
|
|
105
|
+
if ( !getstoreName || getstoreName == null ) {
|
|
106
|
+
return res.sendError( 'The store ID is either inActive or not found', 400 );
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// get the footfall count from opensearch
|
|
110
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
111
|
+
const dateString = `${inputData.storeId}_${inputData.dateString}`;
|
|
112
|
+
const getQuery = {
|
|
113
|
+
query: {
|
|
114
|
+
terms: {
|
|
115
|
+
_id: [ dateString ],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
_source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
119
|
+
sort: [
|
|
120
|
+
{
|
|
121
|
+
date_iso: {
|
|
122
|
+
order: 'desc',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const getFootfallCount = await getOpenSearchData( openSearch.footfall, getQuery );
|
|
129
|
+
const hits = getFootfallCount?.body?.hits?.hits || [];
|
|
130
|
+
if ( hits?.[0]?._source?.footfall_count <= 0 ) {
|
|
131
|
+
return res.sendError( 'You can’t create a ticket because this store has 0 footfall data' );
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// get category details from the client level configuration
|
|
135
|
+
const getConfig = await findOneClient( { clientId: getstoreName.clientId }, { footfallDirectoryConfigs: 1 } );
|
|
136
|
+
if ( !getConfig || getConfig == null ) {
|
|
137
|
+
return res.sendError( 'The Client ID is either not configured or not found', 400 );
|
|
138
|
+
}
|
|
139
|
+
let findQuery = {
|
|
140
|
+
size: 10000,
|
|
141
|
+
query: {
|
|
142
|
+
bool: {
|
|
143
|
+
must: [
|
|
144
|
+
{
|
|
145
|
+
term: {
|
|
146
|
+
'storeId.keyword': inputData.storeId,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
term: {
|
|
151
|
+
'dateString': inputData.dateString,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
159
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
160
|
+
|
|
161
|
+
if ( Ticket.length === 0 ) {
|
|
162
|
+
return res.sendError( 'Ticket not found', 400 );
|
|
163
|
+
}
|
|
164
|
+
const getTicket = {
|
|
165
|
+
size: 10000,
|
|
166
|
+
query: {
|
|
167
|
+
bool: {
|
|
168
|
+
must: [
|
|
169
|
+
{
|
|
170
|
+
term: {
|
|
171
|
+
'storeId.keyword': inputData.storeId,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
term: {
|
|
176
|
+
'dateString': inputData.dateString,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
if ( Ticket[0]?._source?.type != 'internal' ) {
|
|
185
|
+
getTicket.query.bool.must.push(
|
|
186
|
+
{
|
|
187
|
+
nested: {
|
|
188
|
+
path: 'mappingInfo',
|
|
189
|
+
query: {
|
|
190
|
+
bool: {
|
|
191
|
+
must: [
|
|
192
|
+
{
|
|
193
|
+
term: {
|
|
194
|
+
'mappingInfo.type': 'tangoreview',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const getFootfallticketData = await getOpenSearchData( openSearch.footfallDirectory, getTicket );
|
|
206
|
+
const ticketData = getFootfallticketData?.body?.hits?.hits;
|
|
207
|
+
|
|
208
|
+
if ( !ticketData || ticketData?.length == 0 ) {
|
|
209
|
+
return res.sendError( 'You don’t have any tagged images right now', 400 );
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const record = {
|
|
213
|
+
|
|
214
|
+
status: parseInt( inputData?.mappingInfo?.[0]?.revicedPerc || 0 ) < parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview|| 0 ) ? 'Open - Accuracy Issue' : 'Closed',
|
|
215
|
+
revicedFootfall: inputData.mappingInfo?.[0]?.revicedFootfall,
|
|
216
|
+
revicedPerc: inputData.mappingInfo?.[0]?.revicedPerc,
|
|
217
|
+
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
218
|
+
createdByEmail: req?.user?.email,
|
|
219
|
+
createdByUserName: req?.user?.userName,
|
|
220
|
+
createdByRole: req?.user?.role,
|
|
221
|
+
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
// Retrieve client footfallDirectoryConfigs revision
|
|
226
|
+
let isAutoCloseEnable = getConfig.footfallDirectoryConfigs.isAutoCloseEnable;
|
|
227
|
+
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
|
|
228
|
+
|
|
229
|
+
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
230
|
+
|
|
231
|
+
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
232
|
+
let revisedPercentage = inputData.mappingInfo?.revicedPerc;
|
|
233
|
+
const revised = Number( revisedPercentage?.split( '%' )[0] );
|
|
234
|
+
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] );
|
|
235
|
+
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
236
|
+
if (
|
|
237
|
+
isAutoCloseEnable === true &&
|
|
238
|
+
revisedPercentage >= autoCloseAccuracyValue
|
|
239
|
+
) {
|
|
240
|
+
record.status = 'Closed';
|
|
241
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
242
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
243
|
+
const temp = record.mappingInfo
|
|
244
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
245
|
+
.map( ( item ) => ( {
|
|
246
|
+
...item,
|
|
247
|
+
|
|
248
|
+
mode: inputData.mappingInfo?.mode,
|
|
249
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
250
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
251
|
+
count: inputData.mappingInfo?.count,
|
|
252
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
253
|
+
status: 'Closed',
|
|
254
|
+
createdByEmail: req?.user?.email,
|
|
255
|
+
createdByUserName: req?.user?.userName,
|
|
256
|
+
createdByRole: req?.user?.role,
|
|
257
|
+
} ) );
|
|
258
|
+
|
|
259
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
260
|
+
...temp ];
|
|
261
|
+
// If updating the mapping config to mark [i].status as 'Closed'
|
|
262
|
+
// Make sure all relevant mappingInfo items of type 'approve' are set to status 'Closed'
|
|
263
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
264
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
265
|
+
return {
|
|
266
|
+
...item,
|
|
267
|
+
status: 'Closed',
|
|
268
|
+
};
|
|
269
|
+
} );
|
|
270
|
+
}
|
|
271
|
+
// If no review mapping existed, push a new one
|
|
272
|
+
// if ( record.mappingInfo.length === 0 ) {
|
|
273
|
+
// record.mappingInfo.push( {
|
|
274
|
+
// type: 'tangoreview',
|
|
275
|
+
// mode: inputData.mappingInfo?.mode,
|
|
276
|
+
// revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
277
|
+
// revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
278
|
+
// count: inputData.mappingInfo?.count,
|
|
279
|
+
// revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
280
|
+
// status: 'Closed',
|
|
281
|
+
// createdByEmail: req?.user?.email,
|
|
282
|
+
// createdByUserName: req?.user?.userName,
|
|
283
|
+
// createdByRole: req?.user?.role,
|
|
284
|
+
// } );
|
|
285
|
+
// }
|
|
286
|
+
}
|
|
287
|
+
record.mappingInfo.push(
|
|
288
|
+
{
|
|
289
|
+
type: 'finalRevision',
|
|
290
|
+
mode: inputData.mappingInfo?.mode,
|
|
291
|
+
revicedFootfall: revisedFootfall,
|
|
292
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
293
|
+
count: inputData.mappingInfo?.count,
|
|
294
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
295
|
+
status: 'Closed',
|
|
296
|
+
createdByEmail: req?.user?.email,
|
|
297
|
+
createdByUserName: req?.user?.userName,
|
|
298
|
+
createdByRole: req?.user?.role,
|
|
299
|
+
createdAt: new Date(),
|
|
300
|
+
},
|
|
301
|
+
);
|
|
302
|
+
} else if ( revised < tangoReview ) {
|
|
303
|
+
// If ticket is closed, do not proceed with revision mapping
|
|
304
|
+
|
|
305
|
+
record.status = 'Closed - Accuracy Issue';
|
|
306
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
307
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
308
|
+
const temp = record.mappingInfo
|
|
309
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
310
|
+
.map( ( item ) => ( {
|
|
311
|
+
...item,
|
|
312
|
+
mode: inputData.mappingInfo?.mode,
|
|
313
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
314
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
315
|
+
count: inputData.mappingInfo?.count,
|
|
316
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
317
|
+
status: 'Closed',
|
|
318
|
+
createdByEmail: req?.user?.email,
|
|
319
|
+
createdByUserName: req?.user?.userName,
|
|
320
|
+
createdByRole: req?.user?.role,
|
|
321
|
+
} ) );
|
|
322
|
+
|
|
323
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
324
|
+
...temp ];
|
|
325
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
326
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
327
|
+
return {
|
|
328
|
+
...item,
|
|
329
|
+
status: 'Closed',
|
|
330
|
+
};
|
|
331
|
+
} );
|
|
332
|
+
}
|
|
333
|
+
// If no review mapping existed, push a new one
|
|
334
|
+
// if ( record.mappingInfo.length === 0 ) {
|
|
335
|
+
// record.mappingInfo.push( {
|
|
336
|
+
// type: 'tangoreview',
|
|
337
|
+
// mode: inputData.mappingInfo?.mode,
|
|
338
|
+
// revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
339
|
+
// revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
340
|
+
// count: inputData.mappingInfo?.count,
|
|
341
|
+
// revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
342
|
+
// status: 'Closed',
|
|
343
|
+
// createdByEmail: req?.user?.email,
|
|
344
|
+
// createdByUserName: req?.user?.userName,
|
|
345
|
+
// createdByRole: req?.user?.role,
|
|
346
|
+
// } );
|
|
347
|
+
// }
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
351
|
+
const temp = record.mappingInfo
|
|
352
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
353
|
+
.map( ( item ) => ( {
|
|
354
|
+
...item,
|
|
355
|
+
mode: inputData.mappingInfo?.mode,
|
|
356
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
357
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
358
|
+
count: inputData.mappingInfo?.count,
|
|
359
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
360
|
+
status: 'Closed',
|
|
361
|
+
createdByEmail: req?.user?.email,
|
|
362
|
+
createdByUserName: req?.user?.userName,
|
|
363
|
+
createdByRole: req?.user?.role,
|
|
364
|
+
} ) );
|
|
365
|
+
|
|
366
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
367
|
+
...temp ];
|
|
368
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
369
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
370
|
+
return {
|
|
371
|
+
...item,
|
|
372
|
+
status: 'Closed',
|
|
373
|
+
};
|
|
374
|
+
} );
|
|
375
|
+
}
|
|
376
|
+
// If no review mapping existed, push a new one
|
|
377
|
+
// if ( record.mappingInfo.length === 0 ) {
|
|
378
|
+
// record.mappingInfo.push( {
|
|
379
|
+
// type: 'tangoreview',
|
|
380
|
+
// mode: inputData.mappingInfo?.mode,
|
|
381
|
+
// revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
382
|
+
// revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
383
|
+
// count: inputData.mappingInfo?.count,
|
|
384
|
+
// revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
385
|
+
// status: 'Closed',
|
|
386
|
+
// createdByEmail: req?.user?.email,
|
|
387
|
+
// createdByUserName: req?.user?.userName,
|
|
388
|
+
// createdByRole: req?.user?.role,
|
|
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
|
+
count: inputData.mappingInfo?.count,
|
|
399
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
400
|
+
status: 'Closed',
|
|
401
|
+
createdByEmail: req?.user?.email,
|
|
402
|
+
createdByUserName: req?.user?.userName,
|
|
403
|
+
createdByRole: req?.user?.role,
|
|
404
|
+
createdAt: new Date(),
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if ( Ticket[0]?._source?.type==='store' ) {
|
|
410
|
+
let findTagging = Ticket[0]?._source?.mappingInfo.filter( ( data ) => data.type==='tagging' );
|
|
411
|
+
if ( findTagging?.length>0&&findTagging[0].createdByEmail!='' ) {
|
|
412
|
+
let userData = await findOneUser( { email: findTagging[0]?.createdByEmail } );
|
|
413
|
+
let title = `Received response for the Footfall ticket raised.`;
|
|
414
|
+
let createdOn = dayjs( Ticket[0]?._source?.dateString ).format( 'DD MMM YYYY' );
|
|
415
|
+
let description = `Raised on ${createdOn}`;
|
|
416
|
+
|
|
417
|
+
let Data = {
|
|
418
|
+
'title': title,
|
|
419
|
+
'body': description,
|
|
420
|
+
'type': 'closed',
|
|
421
|
+
'date': Ticket[0]?._source?.dateString,
|
|
422
|
+
'storeId': Ticket[0]?._source?.storeId,
|
|
423
|
+
'clientId': Ticket[0]?._source?.clientId,
|
|
424
|
+
'ticketId': Ticket[0]?._source?.ticketId,
|
|
425
|
+
};
|
|
426
|
+
if ( userData && userData.fcmToken ) {
|
|
427
|
+
const fcmToken = userData.fcmToken;
|
|
428
|
+
await sendPushNotification( title, description, fcmToken, Data );
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// return;
|
|
433
|
+
|
|
434
|
+
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
435
|
+
if ( inputData.ticketType === 'internal' ) {
|
|
436
|
+
id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
440
|
+
|
|
441
|
+
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
442
|
+
return res.sendSuccess( 'Ticket closed successfully' );
|
|
443
|
+
} else {
|
|
444
|
+
return res.sendError( 'Internal Server Error', 500 );
|
|
445
|
+
}
|
|
446
|
+
} catch ( error ) {
|
|
447
|
+
const err = error.message || 'Internal Server Error';
|
|
448
|
+
logger.error( { error: err, funtion: 'tangoReviewTicket' } );
|
|
449
|
+
return res.sendError( err, 500 );
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
63
453
|
async function bulkUpdateStatusToPending( indexName, inputData ) {
|
|
64
454
|
const bulkBody = [];
|
|
65
455
|
|
|
@@ -195,7 +585,6 @@ export async function ticketSummary1( req, res ) {
|
|
|
195
585
|
|
|
196
586
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
197
587
|
const aggs = getData?.body?.aggregations;
|
|
198
|
-
logger.info( { aggs: aggs, body: getData?.body } );
|
|
199
588
|
|
|
200
589
|
const result = {
|
|
201
590
|
totalTickets: aggs.totalTicketCount.value,
|
|
@@ -220,10 +609,10 @@ export async function ticketSummary( req, res ) {
|
|
|
220
609
|
try {
|
|
221
610
|
let result = '';
|
|
222
611
|
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 ) ) ) );
|
|
612
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
224
613
|
|
|
225
|
-
if ( req.user.userType =='tango' ) {
|
|
226
|
-
result ={
|
|
614
|
+
if ( req.user.userType == 'tango' ) {
|
|
615
|
+
result = {
|
|
227
616
|
totalTickets: 0,
|
|
228
617
|
averageAccuracyOverAll: 0,
|
|
229
618
|
openTickets: 0,
|
|
@@ -234,30 +623,30 @@ export async function ticketSummary( req, res ) {
|
|
|
234
623
|
ticketAccuracyBelow: '0%',
|
|
235
624
|
};
|
|
236
625
|
} else {
|
|
237
|
-
result = req.user.role === 'superadmin'?
|
|
238
|
-
{
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} :
|
|
249
|
-
req.user.role === 'user'? 'NA':
|
|
250
|
-
ticketsFeature?
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}: 'NA';
|
|
626
|
+
result = req.user.role === 'superadmin' ?
|
|
627
|
+
{
|
|
628
|
+
totalTickets: 0,
|
|
629
|
+
openTickets: 0,
|
|
630
|
+
inprogress: 0,
|
|
631
|
+
closedTickets: 0,
|
|
632
|
+
dueToday: 0,
|
|
633
|
+
Expired: 0,
|
|
634
|
+
underTangoReview: 0,
|
|
635
|
+
avgTicket: '0%',
|
|
636
|
+
avgAccuracy: '0%',
|
|
637
|
+
} :
|
|
638
|
+
req.user.role === 'user' ? 'NA' :
|
|
639
|
+
ticketsFeature ?
|
|
640
|
+
{
|
|
641
|
+
totalTickets: 0,
|
|
642
|
+
openTickets: 0,
|
|
643
|
+
inprogress: 0,
|
|
644
|
+
closedTickets: 0,
|
|
645
|
+
dueToday: 0,
|
|
646
|
+
Expired: 0,
|
|
647
|
+
avgTicket: '0%',
|
|
648
|
+
avgAccuracy: '0%',
|
|
649
|
+
} : 'NA';
|
|
261
650
|
}
|
|
262
651
|
|
|
263
652
|
return res.sendSuccess( { result: result } );
|
|
@@ -293,7 +682,7 @@ export async function ticketList1( req, res ) {
|
|
|
293
682
|
terms: {
|
|
294
683
|
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
295
684
|
inputData.clientId :
|
|
296
|
-
|
|
685
|
+
[ inputData.clientId ],
|
|
297
686
|
},
|
|
298
687
|
},
|
|
299
688
|
];
|
|
@@ -470,12 +859,12 @@ export async function ticketList1( req, res ) {
|
|
|
470
859
|
'Ticket raised on': dayjs( item._source.createdAt ).format( 'DD MMM, YYYY' ),
|
|
471
860
|
'Issue Date': dayjs( item._source.dateString ).format( 'DD MMM, YYYY' ),
|
|
472
861
|
'Total Footfalls': item._source.footfallCount,
|
|
473
|
-
'Duplicates': item?._source?.status === 'closed'? item._source.duplicateACCount : item._source.duplicateCount,
|
|
474
|
-
'Employee/Staff': item?._source?.status === 'closed'? item._source.employeeACCount : item._source.employeeCount,
|
|
475
|
-
'HouseKeeping': item?._source?.status === 'closed'? item._source.houseKeepingACCount : item._source.houseKeepingCount,
|
|
476
|
-
'Junk': item?._source?.status === 'closed'? ( item._source.junkACCount || 0 ) : ( item._source.junkCount|| 0 ),
|
|
477
|
-
'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 ) ),
|
|
478
|
-
'Ticket%': item?._source?.status === 'closed'
|
|
862
|
+
'Duplicates': item?._source?.status === 'closed' ? item._source.duplicateACCount : item._source.duplicateCount,
|
|
863
|
+
'Employee/Staff': item?._source?.status === 'closed' ? item._source.employeeACCount : item._source.employeeCount,
|
|
864
|
+
'HouseKeeping': item?._source?.status === 'closed' ? item._source.houseKeepingACCount : item._source.houseKeepingCount,
|
|
865
|
+
'Junk': item?._source?.status === 'closed' ? ( item._source.junkACCount || 0 ) : ( item._source.junkCount || 0 ),
|
|
866
|
+
'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 ) ),
|
|
867
|
+
'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 )} %`,
|
|
479
868
|
'Status': item._source.status,
|
|
480
869
|
} );
|
|
481
870
|
}
|
|
@@ -491,825 +880,463 @@ export async function ticketList1( req, res ) {
|
|
|
491
880
|
|
|
492
881
|
export async function ticketList( req, res ) {
|
|
493
882
|
try {
|
|
494
|
-
|
|
495
|
-
const inputData= req.query;
|
|
883
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
884
|
+
const inputData = req.query;
|
|
496
885
|
const userInfo = req.user;
|
|
497
|
-
const
|
|
886
|
+
const limit = inputData?.limit || 10;
|
|
887
|
+
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
888
|
+
inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
|
|
498
889
|
|
|
499
|
-
if ( req.user.userType =='tango' ) {
|
|
500
|
-
result =inputData.tangotype == 'store'?
|
|
501
890
|
|
|
891
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
502
892
|
|
|
503
|
-
|
|
504
|
-
{
|
|
505
|
-
ticketId: 'TE_FDT_1763539990306',
|
|
506
|
-
storeId: '11-1716',
|
|
507
|
-
storeName: 'LKST1916',
|
|
508
|
-
ticketRaised: '2025-11-16',
|
|
509
|
-
issueDate: '2025-11-16',
|
|
510
|
-
dueDate: '2025-11-18',
|
|
511
|
-
footfall: 1200,
|
|
512
|
-
storeRevisedAccuracy: '98%',
|
|
513
|
-
reviewerRevisedAccuracy: '97%',
|
|
514
|
-
approverRevisedAccuracy: '98%',
|
|
515
|
-
tangoRevisedAccuracy: '98%',
|
|
516
|
-
status: 'Closed',
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
ticketId: 'TE_FDT_1763860421803',
|
|
520
|
-
storeId: '11-1716',
|
|
521
|
-
storeName: 'LKST1916',
|
|
522
|
-
ticketRaised: '2025-11-21',
|
|
523
|
-
issueDate: '2025-11-20',
|
|
524
|
-
dueDate: '2025-11-26',
|
|
525
|
-
footfall: 94,
|
|
526
|
-
storeRevisedAccuracy: '90%',
|
|
527
|
-
reviewerRevisedAccuracy: '--',
|
|
528
|
-
approverRevisedAccuracy: '--',
|
|
529
|
-
tangoRevisedAccuracy: '--',
|
|
530
|
-
status: 'Open',
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
ticketId: 'TE_FDT_1763711403163',
|
|
534
|
-
storeId: '11-1716',
|
|
535
|
-
storeName: 'LKST1916',
|
|
536
|
-
ticketRaised: '2025-11-20',
|
|
537
|
-
issueDate: '2025-11-19',
|
|
538
|
-
dueDate: 'Due Today',
|
|
539
|
-
footfall: 94,
|
|
540
|
-
storeRevisedAccuracy: '95%',
|
|
541
|
-
reviewerRevisedAccuracy: '--',
|
|
542
|
-
approverRevisedAccuracy: '--',
|
|
543
|
-
tangoRevisedAccuracy: '--',
|
|
544
|
-
status: 'Open',
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
ticketId: 'TE_FDT_1763539990309',
|
|
548
|
-
storeId: '11-2000',
|
|
549
|
-
storeName: 'LKST2368',
|
|
550
|
-
ticketRaised: '2025-11-13',
|
|
551
|
-
issueDate: '2025-11-13',
|
|
552
|
-
dueDate: '2025-11-15',
|
|
553
|
-
footfall: 1280,
|
|
554
|
-
storeRevisedAccuracy: '98%',
|
|
555
|
-
reviewerRevisedAccuracy: '98%',
|
|
556
|
-
approverRevisedAccuracy: '97%',
|
|
557
|
-
tangoRevisedAccuracy: '97%',
|
|
558
|
-
status: 'Closed',
|
|
559
|
-
},
|
|
560
|
-
{
|
|
561
|
-
ticketId: 'TE_FDT_1763539990310',
|
|
562
|
-
storeId: '11-2000',
|
|
563
|
-
storeName: 'LKST2368',
|
|
564
|
-
ticketRaised: '2025-11-14',
|
|
565
|
-
issueDate: '2025-11-14',
|
|
566
|
-
dueDate: '2025-11-16',
|
|
567
|
-
footfall: 1300,
|
|
568
|
-
storeRevisedAccuracy: '96%',
|
|
569
|
-
reviewerRevisedAccuracy: '95%',
|
|
570
|
-
approverRevisedAccuracy: '96%',
|
|
571
|
-
tangoRevisedAccuracy: '95%',
|
|
572
|
-
status: 'Open',
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
ticketId: 'TE_FDT_1763539990311',
|
|
576
|
-
storeId: '11-2000',
|
|
577
|
-
storeName: 'LKST2368',
|
|
578
|
-
ticketRaised: '2025-11-15',
|
|
579
|
-
issueDate: '2025-11-15',
|
|
580
|
-
dueDate: '2025-11-17',
|
|
581
|
-
footfall: 1350,
|
|
582
|
-
storeRevisedAccuracy: '99%',
|
|
583
|
-
reviewerRevisedAccuracy: '98%',
|
|
584
|
-
approverRevisedAccuracy: '98%',
|
|
585
|
-
tangoRevisedAccuracy: '99%',
|
|
586
|
-
status: 'Closed',
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
ticketId: 'TE_FDT_1763539990312',
|
|
590
|
-
storeId: '11-2000',
|
|
591
|
-
storeName: 'LKST2368',
|
|
592
|
-
ticketRaised: '2025-11-16',
|
|
593
|
-
issueDate: '2025-11-16',
|
|
594
|
-
dueDate: '2025-11-18',
|
|
595
|
-
footfall: 1400,
|
|
596
|
-
storeRevisedAccuracy: '97%',
|
|
597
|
-
reviewerRevisedAccuracy: '96%',
|
|
598
|
-
approverRevisedAccuracy: '97%',
|
|
599
|
-
tangoRevisedAccuracy: '96%',
|
|
600
|
-
status: 'Closed',
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
ticketId: 'TE_FDT_1763539990313',
|
|
604
|
-
storeId: '11-10',
|
|
605
|
-
storeName: 'LKST80',
|
|
606
|
-
ticketRaised: '2023-11-14',
|
|
607
|
-
issueDate: '2023-11-14',
|
|
608
|
-
dueDate: '2023-11-16',
|
|
609
|
-
footfall: 900,
|
|
610
|
-
storeRevisedAccuracy: '95%',
|
|
611
|
-
reviewerRevisedAccuracy: '94%',
|
|
612
|
-
approverRevisedAccuracy: '95%',
|
|
613
|
-
tangoRevisedAccuracy: '95%',
|
|
614
|
-
status: 'Inprogress',
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
ticketId: 'TE_FDT_1763539990314',
|
|
618
|
-
storeId: '11-10',
|
|
619
|
-
storeName: 'LKST80',
|
|
620
|
-
ticketRaised: '2023-11-15',
|
|
621
|
-
issueDate: '2023-11-15',
|
|
622
|
-
dueDate: '2023-11-17',
|
|
623
|
-
footfall: 1000,
|
|
624
|
-
storeRevisedAccuracy: '98%',
|
|
625
|
-
reviewerRevisedAccuracy: '97%',
|
|
626
|
-
approverRevisedAccuracy: '97%',
|
|
627
|
-
tangoRevisedAccuracy: '98%',
|
|
628
|
-
status: 'Closed',
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
ticketId: 'TE_FDT_1763539990315',
|
|
632
|
-
storeId: '11-10',
|
|
633
|
-
storeName: 'LKST80',
|
|
634
|
-
ticketRaised: '2023-11-16',
|
|
635
|
-
issueDate: '2023-11-16',
|
|
636
|
-
dueDate: '2023-11-18',
|
|
637
|
-
footfall: 1050,
|
|
638
|
-
storeRevisedAccuracy: '96%',
|
|
639
|
-
reviewerRevisedAccuracy: '96%',
|
|
640
|
-
approverRevisedAccuracy: '96%',
|
|
641
|
-
tangoRevisedAccuracy: '97%',
|
|
642
|
-
status: 'Open',
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
ticketId: 'TE_FDT_1763539990316',
|
|
646
|
-
storeId: '11-10',
|
|
647
|
-
storeName: 'LKST80',
|
|
648
|
-
ticketRaised: '2023-11-17',
|
|
649
|
-
issueDate: '2023-11-17',
|
|
650
|
-
dueDate: '2023-11-19',
|
|
651
|
-
footfall: 1100,
|
|
652
|
-
storeRevisedAccuracy: '97%',
|
|
653
|
-
reviewerRevisedAccuracy: '97%',
|
|
654
|
-
approverRevisedAccuracy: '97%',
|
|
655
|
-
tangoRevisedAccuracy: '97%',
|
|
656
|
-
status: 'Closed',
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
ticketId: 'TE_FDT_1763539990317',
|
|
660
|
-
storeId: '12-1111',
|
|
661
|
-
storeName: 'LKST3030',
|
|
662
|
-
ticketRaised: '2025-10-12',
|
|
663
|
-
issueDate: '2025-10-12',
|
|
664
|
-
dueDate: '2025-10-14',
|
|
665
|
-
footfall: 1200,
|
|
666
|
-
storeRevisedAccuracy: '97%',
|
|
667
|
-
reviewerRevisedAccuracy: '97%',
|
|
668
|
-
approverRevisedAccuracy: '97%',
|
|
669
|
-
tangoRevisedAccuracy: '96%',
|
|
670
|
-
status: 'Open',
|
|
671
|
-
},
|
|
672
|
-
{
|
|
673
|
-
ticketId: 'TE_FDT_176353999018',
|
|
674
|
-
storeId: '12-1111',
|
|
675
|
-
storeName: 'LKST3030',
|
|
676
|
-
ticketRaised: '2025-10-13',
|
|
677
|
-
issueDate: '2025-10-13',
|
|
678
|
-
dueDate: '2025-10-15',
|
|
679
|
-
footfall: 1095,
|
|
680
|
-
storeRevisedAccuracy: '95%',
|
|
681
|
-
reviewerRevisedAccuracy: '96%',
|
|
682
|
-
approverRevisedAccuracy: '97%',
|
|
683
|
-
tangoRevisedAccuracy: '95%',
|
|
684
|
-
status: 'Open-Accuracy Issue',
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
ticketId: 'TE_FDT_1763539990319',
|
|
688
|
-
storeId: '12-1111',
|
|
689
|
-
storeName: 'LKST3030',
|
|
690
|
-
ticketRaised: '2025-10-14',
|
|
691
|
-
issueDate: '2025-10-14',
|
|
692
|
-
dueDate: '2025-10-16',
|
|
693
|
-
footfall: 987,
|
|
694
|
-
storeRevisedAccuracy: '98%',
|
|
695
|
-
reviewerRevisedAccuracy: '99%',
|
|
696
|
-
approverRevisedAccuracy: '99%',
|
|
697
|
-
tangoRevisedAccuracy: '99%',
|
|
698
|
-
status: 'closed-Accuracy Issue',
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
ticketId: 'TE_FDT_1763539990320',
|
|
702
|
-
storeId: '14-8002',
|
|
703
|
-
storeName: 'LKST4590',
|
|
704
|
-
ticketRaised: '2025-09-12',
|
|
705
|
-
issueDate: '2025-09-12',
|
|
706
|
-
dueDate: '2025-09-14',
|
|
707
|
-
footfall: 1080,
|
|
708
|
-
storeRevisedAccuracy: '98%',
|
|
709
|
-
reviewerRevisedAccuracy: '97%',
|
|
710
|
-
approverRevisedAccuracy: '99%',
|
|
711
|
-
tangoRevisedAccuracy: '99%',
|
|
712
|
-
status: 'Closed',
|
|
713
|
-
},
|
|
893
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
714
894
|
|
|
715
|
-
|
|
716
|
-
[
|
|
895
|
+
const searchQuery = {
|
|
717
896
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
storeName: 'LKST1916',
|
|
722
|
-
ticketRaised: '2025-11-21',
|
|
723
|
-
issueDate: '2025-11-20',
|
|
724
|
-
footfall: 94,
|
|
725
|
-
type: 'store',
|
|
726
|
-
storeRevisedAccuracy: '95%',
|
|
727
|
-
reviewerRevisedAccuracy: '--',
|
|
728
|
-
approverRevisedAccuracy: '--',
|
|
729
|
-
tangoRevisedAccuracy: '--',
|
|
730
|
-
status: 'Open',
|
|
731
|
-
tangoStatus: 'Open',
|
|
732
|
-
},
|
|
733
|
-
{
|
|
734
|
-
ticketId: 'TE_FDT_1763539990307',
|
|
735
|
-
storeId: '11-1716',
|
|
736
|
-
storeName: 'LKST1916',
|
|
737
|
-
ticketRaised: '2025-11-13',
|
|
738
|
-
issueDate: '2025-11-13',
|
|
739
|
-
footfall: 1150,
|
|
740
|
-
type: 'store',
|
|
741
|
-
storeRevisedAccuracy: '99%',
|
|
742
|
-
reviewerRevisedAccuracy: '99%',
|
|
743
|
-
approverRevisedAccuracy: '98%',
|
|
744
|
-
tangoRevisedAccuracy: '97%',
|
|
745
|
-
status: 'Closed',
|
|
746
|
-
tangoStatus: 'open',
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
ticketId: 'TE_FDT_1763539990308',
|
|
750
|
-
storeId: '11-1716',
|
|
751
|
-
storeName: 'LKST1916',
|
|
752
|
-
ticketRaised: '2025-11-14',
|
|
753
|
-
issueDate: '2025-11-14',
|
|
754
|
-
footfall: 1100,
|
|
755
|
-
type: 'store',
|
|
756
|
-
storeRevisedAccuracy: '97%',
|
|
757
|
-
reviewerRevisedAccuracy: '96%',
|
|
758
|
-
approverRevisedAccuracy: '97%',
|
|
759
|
-
status: 'Closed',
|
|
760
|
-
tangoStatus: 'In-Progress',
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
ticketId: 'TE_FDT_1763539990309',
|
|
764
|
-
storeId: '11-2000',
|
|
765
|
-
storeName: 'LKST2368',
|
|
766
|
-
ticketRaised: '2025-11-13',
|
|
767
|
-
issueDate: '2025-11-13',
|
|
768
|
-
footfall: 1280,
|
|
769
|
-
type: 'internal',
|
|
770
|
-
storeRevisedAccuracy: '98%',
|
|
771
|
-
reviewerRevisedAccuracy: '98%',
|
|
772
|
-
approverRevisedAccuracy: '97%',
|
|
773
|
-
tangoRevisedAccuracy: '97%',
|
|
774
|
-
status: 'Closed-Accuracy Issue',
|
|
775
|
-
tangoStatus: 'Open',
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
ticketId: 'TE_FDT_1763539990310',
|
|
779
|
-
storeId: '11-2000',
|
|
780
|
-
storeName: 'LKST2368',
|
|
781
|
-
ticketRaised: '2025-11-14',
|
|
782
|
-
issueDate: '2025-11-14',
|
|
783
|
-
footfall: 300,
|
|
784
|
-
type: 'store',
|
|
785
|
-
storeRevisedAccuracy: '96%',
|
|
786
|
-
reviewerRevisedAccuracy: '95%',
|
|
787
|
-
approverRevisedAccuracy: '96%',
|
|
788
|
-
tangoRevisedAccuracy: '95%',
|
|
789
|
-
status: 'Closed',
|
|
790
|
-
tangoStatus: 'In-Progress',
|
|
791
|
-
},
|
|
792
|
-
{
|
|
793
|
-
ticketId: 'TE_FDT_1763539990311',
|
|
794
|
-
storeId: '11-2000',
|
|
795
|
-
storeName: 'LKST2368',
|
|
796
|
-
ticketRaised: '2025-11-15',
|
|
797
|
-
issueDate: '2025-11-15',
|
|
798
|
-
footfall: 350,
|
|
799
|
-
type: 'internal',
|
|
800
|
-
storeRevisedAccuracy: '99%',
|
|
801
|
-
reviewerRevisedAccuracy: '98%',
|
|
802
|
-
approverRevisedAccuracy: '98%',
|
|
803
|
-
tangoRevisedAccuracy: '99%',
|
|
804
|
-
status: 'Closed',
|
|
805
|
-
tangoStatus: 'In-Progress',
|
|
806
|
-
},
|
|
807
|
-
{
|
|
808
|
-
ticketId: 'TE_FDT_1763539990312',
|
|
809
|
-
storeId: '11-2000',
|
|
810
|
-
storeName: 'LKST2368',
|
|
811
|
-
ticketRaised: '2025-11-16',
|
|
812
|
-
issueDate: '2025-11-16',
|
|
813
|
-
footfall: 400,
|
|
814
|
-
type: 'internal',
|
|
815
|
-
storeRevisedAccuracy: '97%',
|
|
816
|
-
reviewerRevisedAccuracy: '96%',
|
|
817
|
-
approverRevisedAccuracy: '97%',
|
|
818
|
-
tangoRevisedAccuracy: '96%',
|
|
819
|
-
status: 'Open-Accuracy Issue',
|
|
820
|
-
tangoStatus: 'Open',
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
ticketId: 'TE_FDT_1763539990313',
|
|
824
|
-
storeId: '11-10',
|
|
825
|
-
storeName: 'LKST80',
|
|
826
|
-
ticketRaised: '2023-11-14',
|
|
827
|
-
issueDate: '2023-11-14',
|
|
828
|
-
footfall: 900,
|
|
829
|
-
type: 'internal',
|
|
830
|
-
storeRevisedAccuracy: '95%',
|
|
831
|
-
reviewerRevisedAccuracy: '94%',
|
|
832
|
-
approverRevisedAccuracy: '95%',
|
|
833
|
-
tangoRevisedAccuracy: '95%',
|
|
834
|
-
status: 'Closed',
|
|
835
|
-
tangoStatus: 'Closed',
|
|
836
|
-
},
|
|
837
|
-
{
|
|
838
|
-
ticketId: 'TE_FDT_1763539990314',
|
|
839
|
-
storeId: '11-10',
|
|
840
|
-
storeName: 'LKST80',
|
|
841
|
-
ticketRaised: '2023-11-15',
|
|
842
|
-
issueDate: '2023-11-15',
|
|
843
|
-
type: 'store',
|
|
844
|
-
footfall: 100,
|
|
845
|
-
storeRevisedAccuracy: '98%',
|
|
846
|
-
reviewerRevisedAccuracy: '97%',
|
|
847
|
-
approverRevisedAccuracy: '97%',
|
|
848
|
-
tangoRevisedAccuracy: '98%',
|
|
849
|
-
status: 'closed-Accuracy Issue',
|
|
850
|
-
tangoStatus: 'Closed',
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
ticketId: 'TE_FDT_1763539990320',
|
|
854
|
-
storeId: '14-8002',
|
|
855
|
-
storeName: 'LKST4590',
|
|
856
|
-
ticketRaised: '2025-09-12',
|
|
857
|
-
issueDate: '2025-09-12',
|
|
858
|
-
type: 'internal',
|
|
859
|
-
footfall: 80,
|
|
860
|
-
storeRevisedAccuracy: '98%',
|
|
861
|
-
reviewerRevisedAccuracy: '97%',
|
|
862
|
-
approverRevisedAccuracy: '99%',
|
|
863
|
-
tangoRevisedAccuracy: '99%',
|
|
864
|
-
status: 'Closed-Accuracy Issue',
|
|
865
|
-
tangoStatus: 'Closed',
|
|
866
|
-
},
|
|
867
|
-
];
|
|
868
|
-
} else {
|
|
869
|
-
result = req.user.role === 'superadmin'?
|
|
870
|
-
[
|
|
871
|
-
{
|
|
872
|
-
ticketId: 'TE_FDT_1763539990320',
|
|
873
|
-
storeId: '11-1716',
|
|
874
|
-
storeName: 'LKST1916',
|
|
875
|
-
ticketRaised: '2025-11-13',
|
|
876
|
-
issueDate: '2025-11-12',
|
|
877
|
-
dueDate: '2025-11-14',
|
|
878
|
-
footfall: 60,
|
|
879
|
-
storeRevisedAccuracy: '45%',
|
|
880
|
-
reviewerRevisedAccuracy: '67%',
|
|
881
|
-
approverRevisedAccuracy: '87%',
|
|
882
|
-
tangoRevisedAccuracy: '93%',
|
|
883
|
-
status: 'Open',
|
|
884
|
-
approvedBy: '',
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
ticketId: 'TE_FDT_1763539990321',
|
|
888
|
-
storeId: '11-1716',
|
|
889
|
-
storeName: 'LKST1916',
|
|
890
|
-
ticketRaised: '2025-11-11',
|
|
891
|
-
issueDate: '2025-11-10',
|
|
892
|
-
dueDate: '2025-11-12',
|
|
893
|
-
footfall: 69,
|
|
894
|
-
storeRevisedAccuracy: '79%',
|
|
895
|
-
reviewerRevisedAccuracy: '80%',
|
|
896
|
-
approverRevisedAccuracy: '90%',
|
|
897
|
-
tangoRevisedAccuracy: '90%',
|
|
898
|
-
status: 'In-Progress',
|
|
899
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
ticketId: 'TE_FDT_1763860421803',
|
|
903
|
-
storeId: '11-1716',
|
|
904
|
-
storeName: 'LKST1916',
|
|
905
|
-
ticketRaised: '2025-11-21',
|
|
906
|
-
issueDate: '2025-11-20',
|
|
907
|
-
dueDate: '2025-11-26',
|
|
908
|
-
footfall: 94,
|
|
909
|
-
storeRevisedAccuracy: '90%',
|
|
910
|
-
reviewerRevisedAccuracy: '90%',
|
|
911
|
-
approverRevisedAccuracy: '90%',
|
|
912
|
-
tangoRevisedAccuracy: '90%',
|
|
913
|
-
status: 'Closed',
|
|
914
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
ticketId: 'TE_FDT_1763711403163',
|
|
918
|
-
storeId: '11-1716',
|
|
919
|
-
storeName: 'LKST1916',
|
|
920
|
-
ticketRaised: '2025-11-20',
|
|
921
|
-
issueDate: '2025-11-19',
|
|
922
|
-
dueDate: 'Due Today',
|
|
923
|
-
footfall: 94,
|
|
924
|
-
storeRevisedAccuracy: '95%',
|
|
925
|
-
reviewerRevisedAccuracy: '--',
|
|
926
|
-
approverRevisedAccuracy: '--',
|
|
927
|
-
tangoRevisedAccuracy: '--',
|
|
928
|
-
status: 'In-Progress',
|
|
929
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
930
|
-
},
|
|
931
|
-
{
|
|
932
|
-
ticketId: 'TE_FDT_1763539990320',
|
|
933
|
-
storeId: '11-1716',
|
|
934
|
-
storeName: 'LKST1916',
|
|
935
|
-
ticketRaised: '2025-11-15',
|
|
936
|
-
issueDate: '2025-11-14',
|
|
937
|
-
dueDate: '2025-11-17',
|
|
938
|
-
footfall: 110,
|
|
939
|
-
storeRevisedAccuracy: '90%',
|
|
940
|
-
reviewerRevisedAccuracy: '--',
|
|
941
|
-
approverRevisedAccuracy: '--',
|
|
942
|
-
tangoRevisedAccuracy: '--',
|
|
943
|
-
status: 'Open',
|
|
944
|
-
approvedBy: '',
|
|
945
|
-
},
|
|
946
|
-
{
|
|
947
|
-
ticketId: 'TE_FDT_1763539990323',
|
|
948
|
-
storeId: '11-10',
|
|
949
|
-
storeName: 'LKST80',
|
|
950
|
-
ticketRaised: '2025-11-13',
|
|
951
|
-
issueDate: '2025-11-12',
|
|
952
|
-
dueDate: '2025-11-14',
|
|
953
|
-
footfall: 170,
|
|
954
|
-
storeRevisedAccuracy: '90%',
|
|
955
|
-
reviewerRevisedAccuracy: '90%',
|
|
956
|
-
approverRevisedAccuracy: '--',
|
|
957
|
-
tangoRevisedAccuracy: '--',
|
|
958
|
-
status: 'Open',
|
|
959
|
-
approvedBy: '',
|
|
960
|
-
},
|
|
961
|
-
{
|
|
962
|
-
ticketId: 'TE_FDT_1763539990328',
|
|
963
|
-
storeId: '11-10',
|
|
964
|
-
storeName: 'LKST80',
|
|
965
|
-
ticketRaised: '2025-11-12',
|
|
966
|
-
issueDate: '2025-11-11',
|
|
967
|
-
dueDate: '2025-11-14',
|
|
968
|
-
footfall: 170,
|
|
969
|
-
storeRevisedAccuracy: '90%',
|
|
970
|
-
reviewerRevisedAccuracy: '90%',
|
|
971
|
-
approverRevisedAccuracy: '90%',
|
|
972
|
-
tangoRevisedAccuracy: '--',
|
|
973
|
-
status: 'Expired',
|
|
974
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
ticketId: 'TE_FDT_1763539990330',
|
|
978
|
-
storeId: '11-10',
|
|
979
|
-
storeName: 'LKST80',
|
|
980
|
-
ticketRaised: '2025-11-18',
|
|
981
|
-
issueDate: '2025-11-15',
|
|
982
|
-
dueDate: 'Due Today',
|
|
983
|
-
footfall: 230,
|
|
984
|
-
storeRevisedAccuracy: '90%',
|
|
985
|
-
reviewerRevisedAccuracy: '90%',
|
|
986
|
-
approverRevisedAccuracy: '90%',
|
|
987
|
-
tangoRevisedAccuracy: '90%',
|
|
988
|
-
status: 'Closed',
|
|
989
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
990
|
-
},
|
|
991
|
-
{
|
|
992
|
-
ticketId: 'TE_FDT_1763539990332',
|
|
993
|
-
storeId: '11-10',
|
|
994
|
-
storeName: 'LKST80',
|
|
995
|
-
ticketRaised: '2025-11-17',
|
|
996
|
-
issueDate: '2025-11-16',
|
|
997
|
-
dueDate: '2025-11-20',
|
|
998
|
-
footfall: 812,
|
|
999
|
-
storeRevisedAccuracy: '80%',
|
|
1000
|
-
reviewerRevisedAccuracy: '80%',
|
|
1001
|
-
approverRevisedAccuracy: '80%',
|
|
1002
|
-
tangoRevisedAccuracy: '--',
|
|
1003
|
-
status: 'Open',
|
|
1004
|
-
approvedBy: 'mu_mu@yopmail.com',
|
|
1005
|
-
},
|
|
1006
|
-
{
|
|
1007
|
-
ticketId: 'TE_FDT_176353999034',
|
|
1008
|
-
storeId: '11-2000',
|
|
1009
|
-
storeName: 'LKST2368',
|
|
1010
|
-
ticketRaised: '2025-11-15',
|
|
1011
|
-
issueDate: '2025-11-14',
|
|
1012
|
-
dueDate: '2025-11-19',
|
|
1013
|
-
footfall: '',
|
|
1014
|
-
storeRevisedAccuracy: '--',
|
|
1015
|
-
reviewerRevisedAccuracy: '--',
|
|
1016
|
-
approverRevisedAccuracy: '--',
|
|
1017
|
-
tangoRevisedAccuracy: '--',
|
|
1018
|
-
status: 'Open',
|
|
1019
|
-
approvedBy: '',
|
|
1020
|
-
},
|
|
1021
|
-
] :
|
|
1022
|
-
req.user.role === 'user'? 'NA':
|
|
1023
|
-
ticketsFeature?
|
|
1024
|
-
[
|
|
1025
|
-
{
|
|
1026
|
-
ticketId: 'TE_FDT_1763860421803',
|
|
1027
|
-
storeId: '11-1716',
|
|
1028
|
-
storeName: 'LKST1916',
|
|
1029
|
-
ticketRaised: '2025-11-21',
|
|
1030
|
-
issueDate: '2025-11-20',
|
|
1031
|
-
dueDate: 'Due Today',
|
|
1032
|
-
footfall: 90,
|
|
1033
|
-
storeRevisedAccuracy: '90%',
|
|
1034
|
-
reviewerRevisedAccuracy: '0%',
|
|
1035
|
-
status: 'Open',
|
|
1036
|
-
ReviewedBy: '',
|
|
1037
|
-
},
|
|
1038
|
-
{
|
|
1039
|
-
ticketId: 'TE_FDT_1763539990346',
|
|
1040
|
-
storeId: '11-2000',
|
|
1041
|
-
storeName: 'LKST2368',
|
|
1042
|
-
ticketRaised: '2025-11-21',
|
|
1043
|
-
issueDate: '2025-11-20',
|
|
1044
|
-
dueDate: '2025-11-26',
|
|
1045
|
-
footfall: 90,
|
|
1046
|
-
storeRevisedAccuracy: '90%',
|
|
1047
|
-
reviewerRevisedAccuracy: '--',
|
|
1048
|
-
status: 'In-Progress',
|
|
1049
|
-
ReviewedBy: 'mu_mu@yopmail.com',
|
|
1050
|
-
},
|
|
1051
|
-
{
|
|
1052
|
-
ticketId: 'TE_FDT_176353999048',
|
|
1053
|
-
storeId: '11-2000',
|
|
1054
|
-
storeName: 'LKST2368',
|
|
1055
|
-
ticketRaised: '2025-11-16',
|
|
1056
|
-
issueDate: '2025-11-15',
|
|
1057
|
-
dueDate: '2025-11-19',
|
|
1058
|
-
footfall: 100,
|
|
1059
|
-
storeRevisedAccuracy: '90%',
|
|
1060
|
-
reviewerRevisedAccuracy: '90%',
|
|
1061
|
-
status: 'Closed',
|
|
1062
|
-
ReviewedBy: 'ayyanar@yopmail.com',
|
|
1063
|
-
},
|
|
1064
|
-
{
|
|
1065
|
-
ticketId: 'TE_FDT_176353999048',
|
|
1066
|
-
storeId: '11-10',
|
|
1067
|
-
storeName: 'LKST80',
|
|
1068
|
-
ticketRaised: '2025-11-08',
|
|
1069
|
-
issueDate: '2025-11-06',
|
|
1070
|
-
dueDate: '2025-11-09',
|
|
1071
|
-
footfall: 120,
|
|
1072
|
-
storeRevisedAccuracy: '90%',
|
|
1073
|
-
reviewerRevisedAccuracy: '0%',
|
|
1074
|
-
status: 'Expired',
|
|
1075
|
-
ReviewedBy: '',
|
|
1076
|
-
},
|
|
1077
|
-
{
|
|
1078
|
-
ticketId: 'TE_FDT_1763539990341',
|
|
1079
|
-
storeId: '11-2000',
|
|
1080
|
-
storeName: 'LKST2368',
|
|
1081
|
-
ticketRaised: '2025-11-6',
|
|
1082
|
-
issueDate: '2025-11-15',
|
|
1083
|
-
dueDate: 'Due Today',
|
|
1084
|
-
footfall: 510,
|
|
1085
|
-
storeRevisedAccuracy: '90%',
|
|
1086
|
-
reviewerRevisedAccuracy: '--',
|
|
1087
|
-
status: 'Open',
|
|
1088
|
-
ReviewedBy: '',
|
|
1089
|
-
},
|
|
1090
|
-
{
|
|
1091
|
-
ticketId: 'TE_FDT_1763539990340',
|
|
1092
|
-
storeId: '11-10',
|
|
1093
|
-
storeName: 'LKST80',
|
|
1094
|
-
ticketRaised: '2025-11-14',
|
|
1095
|
-
issueDate: '2025-11-12',
|
|
1096
|
-
dueDate: '2025-11-15',
|
|
1097
|
-
footfall: 100,
|
|
1098
|
-
storeRevisedAccuracy: '0%',
|
|
1099
|
-
reviewerRevisedAccuracy: '0%',
|
|
1100
|
-
status: 'Expired',
|
|
1101
|
-
ReviewedBy: '',
|
|
1102
|
-
},
|
|
1103
|
-
{
|
|
1104
|
-
ticketId: 'TE_FDT_1763539990339',
|
|
1105
|
-
storeId: '11-2000',
|
|
1106
|
-
storeName: 'LKST2368',
|
|
1107
|
-
ticketRaised: '2025-11-16',
|
|
1108
|
-
issueDate: '2025-11-15',
|
|
1109
|
-
dueDate: '2025-11-17',
|
|
1110
|
-
footfall: 140,
|
|
1111
|
-
storeRevisedAccuracy: '90%',
|
|
1112
|
-
reviewerRevisedAccuracy: '90%',
|
|
1113
|
-
status: 'Closed',
|
|
1114
|
-
ReviewedBy: 'sornanithya@yopmail.com',
|
|
1115
|
-
},
|
|
1116
|
-
{
|
|
1117
|
-
ticketId: 'TE_FDT_1763539990337',
|
|
1118
|
-
storeId: '11-10',
|
|
1119
|
-
storeName: 'LKST80',
|
|
1120
|
-
ticketRaised: '2025-11-16',
|
|
1121
|
-
issueDate: '2025-11-15',
|
|
1122
|
-
dueDate: '2025-11-18',
|
|
1123
|
-
footfall: '',
|
|
1124
|
-
storeRevisedAccuracy: '90%',
|
|
1125
|
-
reviewerRevisedAccuracy: '--',
|
|
1126
|
-
status: 'Expired',
|
|
1127
|
-
ReviewedBy: '',
|
|
1128
|
-
},
|
|
1129
|
-
{
|
|
1130
|
-
ticketId: 'TE_FDT_1763539990338',
|
|
1131
|
-
storeId: '11-2000',
|
|
1132
|
-
storeName: 'LKST2368',
|
|
1133
|
-
ticketRaised: '2025-11-17',
|
|
1134
|
-
issueDate: '2025-11-16',
|
|
1135
|
-
dueDate: 'Due today',
|
|
1136
|
-
footfall: 110,
|
|
1137
|
-
storeRevisedAccuracy: '90%',
|
|
1138
|
-
reviewerRevisedAccuracy: '--',
|
|
1139
|
-
status: 'In-Progress',
|
|
1140
|
-
ReviewedBy: 'vinoth@yopmail.com',
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
ticketId: 'TE_FDT_1763539990335',
|
|
1144
|
-
storeId: '11-2000',
|
|
1145
|
-
storeName: 'LKST2368',
|
|
1146
|
-
ticketRaised: '2025-11-17',
|
|
1147
|
-
issueDate: '2025-11-16',
|
|
1148
|
-
dueDate: 'Due today',
|
|
1149
|
-
footfall: 100,
|
|
1150
|
-
storeRevisedAccuracy: '90%',
|
|
1151
|
-
reviewerRevisedAccuracy: '0%',
|
|
1152
|
-
status: 'In-Progress',
|
|
1153
|
-
ReviewedBy: 'sornanithya@yopmail.com',
|
|
1154
|
-
},
|
|
1155
|
-
]: 'NA';
|
|
1156
|
-
}
|
|
897
|
+
size: limit, // or use parseInt(req.query.limit) for dynamic
|
|
898
|
+
from: offset, // or use parseInt(req.query.offset) for dynamic
|
|
899
|
+
sort: [ { 'createdAt': { order: 'desc' } } ],
|
|
1157
900
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
901
|
+
query: {
|
|
902
|
+
bool: {
|
|
903
|
+
must: [
|
|
904
|
+
{
|
|
905
|
+
'range': {
|
|
906
|
+
'dateString': {
|
|
907
|
+
'gte': inputData.fromDate,
|
|
908
|
+
'lte': inputData.toDate,
|
|
909
|
+
'format': 'yyyy-MM-dd',
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
terms: {
|
|
915
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
916
|
+
inputData.clientId :
|
|
917
|
+
[ inputData.clientId ],
|
|
918
|
+
},
|
|
1176
919
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
dateString: {
|
|
1180
|
-
gte: inputData.fromDate,
|
|
1181
|
-
lte: inputData.toDate,
|
|
1182
|
-
format: 'yyyy-MM-dd',
|
|
1183
|
-
},
|
|
920
|
+
},
|
|
921
|
+
],
|
|
1184
922
|
},
|
|
1185
923
|
},
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
if ( inputData.sortBy ) {
|
|
927
|
+
let sortOrder = inputData.sortOrder === 1 ? 'asc' : 'desc';
|
|
928
|
+
|
|
929
|
+
// Remove default sort so we don't duplicate/conflict
|
|
930
|
+
// INSERT_YOUR_CODE
|
|
931
|
+
// If sortBy is present, check if the field needs ".keyword" (for string fields like storeName, storeId, ticketId)
|
|
932
|
+
// This avoids OpenSearch errors about sorting on text fields.
|
|
933
|
+
const stringKeywordFields = [ 'storeName', 'storeId', 'ticketId', 'status', 'type', 'clientId' ];
|
|
934
|
+
let sortField = inputData.sortBy == 'footfall' ? 'footfallCount' : inputData.sortBy == 'issueDate' ? 'dateString' : inputData.sortBy;
|
|
935
|
+
if ( stringKeywordFields.includes( sortField ) ) {
|
|
936
|
+
sortField = `${sortField}.keyword`;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// Remove default sort so we don't duplicate/conflict
|
|
940
|
+
searchQuery.sort = [
|
|
941
|
+
{ [sortField]: { order: sortOrder } },
|
|
942
|
+
];
|
|
1197
943
|
}
|
|
1198
|
-
if
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
944
|
+
// Example: Filtering by storeId if present in the query
|
|
945
|
+
if ( inputData.storeId ) {
|
|
946
|
+
inputData.storeId = inputData?.storeId?.split( ',' );
|
|
947
|
+
searchQuery.query.bool.must.push( {
|
|
948
|
+
terms: {
|
|
949
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
950
|
+
inputData.storeId :
|
|
951
|
+
[ inputData.storeId ],
|
|
952
|
+
},
|
|
953
|
+
} );
|
|
1206
954
|
}
|
|
1207
|
-
if ( inputData.status ) {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
955
|
+
if ( inputData.status && inputData.status!=='' ) {
|
|
956
|
+
inputData.status = inputData?.status?.split( ',' );
|
|
957
|
+
if ( req.user.userType === 'tango' ) {
|
|
958
|
+
searchQuery.query.bool.must.push( {
|
|
959
|
+
terms: {
|
|
960
|
+
'status': Array.isArray( inputData?.status ) ?
|
|
961
|
+
inputData?.status :
|
|
962
|
+
[ inputData?.status ],
|
|
1213
963
|
},
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
964
|
+
} );
|
|
965
|
+
} else if ( inputData?.permissionType === 'approve' ) {
|
|
966
|
+
searchQuery.query.bool.must.push( {
|
|
967
|
+
nested: {
|
|
968
|
+
path: 'mappingInfo',
|
|
969
|
+
query: {
|
|
970
|
+
bool: {
|
|
971
|
+
must: [
|
|
972
|
+
{
|
|
973
|
+
term: {
|
|
974
|
+
'mappingInfo.type': 'approve',
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
term: {
|
|
979
|
+
'mappingInfo.status': inputData.status[0],
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
],
|
|
983
|
+
},
|
|
1224
984
|
},
|
|
1225
985
|
},
|
|
1226
|
-
} )
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
986
|
+
} );
|
|
987
|
+
} else if ( inputData?.permissionType === 'review' ) {
|
|
988
|
+
searchQuery.query.bool.must.push( {
|
|
989
|
+
nested: {
|
|
990
|
+
path: 'mappingInfo',
|
|
991
|
+
query: {
|
|
992
|
+
bool: {
|
|
993
|
+
must: [
|
|
994
|
+
{
|
|
995
|
+
term: {
|
|
996
|
+
'mappingInfo.type': 'review',
|
|
997
|
+
},
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
term: {
|
|
1001
|
+
'mappingInfo.status': inputData.status[0],
|
|
1002
|
+
},
|
|
1003
|
+
},
|
|
1004
|
+
],
|
|
1232
1005
|
},
|
|
1233
1006
|
},
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1007
|
+
},
|
|
1008
|
+
} );
|
|
1009
|
+
} else if ( ticketsFeature ) {
|
|
1010
|
+
searchQuery.query.bool.must.push( {
|
|
1011
|
+
nested: {
|
|
1012
|
+
path: 'mappingInfo',
|
|
1013
|
+
query: {
|
|
1014
|
+
bool: {
|
|
1015
|
+
must: [
|
|
1016
|
+
{
|
|
1017
|
+
term: {
|
|
1018
|
+
'mappingInfo.type': 'review',
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
term: {
|
|
1023
|
+
'mappingInfo.status': inputData.status[0],
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
1026
|
+
],
|
|
1240
1027
|
},
|
|
1241
1028
|
},
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1029
|
+
},
|
|
1030
|
+
} );
|
|
1031
|
+
} else if ( ticketsApproveFeature ) {
|
|
1032
|
+
searchQuery.query.bool.must.push( {
|
|
1033
|
+
nested: {
|
|
1034
|
+
path: 'mappingInfo',
|
|
1035
|
+
query: {
|
|
1036
|
+
bool: {
|
|
1037
|
+
must: [
|
|
1038
|
+
{
|
|
1039
|
+
term: {
|
|
1040
|
+
'mappingInfo.type': 'approve',
|
|
1041
|
+
},
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
term: {
|
|
1045
|
+
'mappingInfo.status': inputData.status[0],
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
],
|
|
1247
1049
|
},
|
|
1248
1050
|
},
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
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' ] :
|
|
1253
|
-
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' ] : [];
|
|
1051
|
+
},
|
|
1052
|
+
} );
|
|
1053
|
+
}
|
|
1254
1054
|
}
|
|
1255
1055
|
|
|
1256
|
-
if ( inputData.
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
constant_score: {
|
|
1262
|
-
filter: { term: { 'houseKeepingStatus.keyword': inputData.action } },
|
|
1263
|
-
boost: 1,
|
|
1264
|
-
_name: 'matched_housekeeping',
|
|
1265
|
-
},
|
|
1056
|
+
if ( req?.user?.userType == 'tango' && inputData.tangoType !== 'internal' ) {
|
|
1057
|
+
searchQuery.query.bool.must.push(
|
|
1058
|
+
{
|
|
1059
|
+
term: {
|
|
1060
|
+
'type.keyword': 'store',
|
|
1266
1061
|
},
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
nested: {
|
|
1065
|
+
path: 'mappingInfo',
|
|
1066
|
+
query: {
|
|
1067
|
+
bool: {
|
|
1068
|
+
must: [
|
|
1069
|
+
{
|
|
1070
|
+
term: {
|
|
1071
|
+
'mappingInfo.type': 'tangoreview',
|
|
1072
|
+
},
|
|
1073
|
+
},
|
|
1074
|
+
|
|
1075
|
+
],
|
|
1076
|
+
},
|
|
1272
1077
|
},
|
|
1273
1078
|
},
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1079
|
+
|
|
1080
|
+
},
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
);
|
|
1084
|
+
} else if ( req?.user?.userType == 'client' && inputData.tangoType !== 'internal' ) {
|
|
1085
|
+
searchQuery.query.bool.must.push(
|
|
1086
|
+
{
|
|
1087
|
+
term: {
|
|
1088
|
+
'type.keyword': 'store',
|
|
1089
|
+
},
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
nested: {
|
|
1093
|
+
path: 'mappingInfo',
|
|
1094
|
+
query: {
|
|
1095
|
+
bool: {
|
|
1096
|
+
must: [
|
|
1097
|
+
{
|
|
1098
|
+
term: {
|
|
1099
|
+
'mappingInfo.type': ticketsFeature ? 'review' : ticketsApproveFeature ?
|
|
1100
|
+
'approve' : 'tagging',
|
|
1101
|
+
},
|
|
1102
|
+
},
|
|
1103
|
+
|
|
1104
|
+
],
|
|
1105
|
+
},
|
|
1279
1106
|
},
|
|
1280
1107
|
},
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1108
|
+
},
|
|
1109
|
+
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if ( inputData?.permissionType ) {
|
|
1114
|
+
searchQuery.query.bool.must.push(
|
|
1115
|
+
{
|
|
1116
|
+
nested: {
|
|
1117
|
+
path: 'mappingInfo',
|
|
1118
|
+
query: {
|
|
1119
|
+
bool: {
|
|
1120
|
+
must: [
|
|
1121
|
+
{
|
|
1122
|
+
term: {
|
|
1123
|
+
'mappingInfo.type': inputData?.permissionType == 'review' ? 'review' :
|
|
1124
|
+
'approve',
|
|
1125
|
+
},
|
|
1126
|
+
},
|
|
1127
|
+
|
|
1128
|
+
],
|
|
1129
|
+
},
|
|
1286
1130
|
},
|
|
1287
1131
|
},
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
},
|
|
1291
|
-
} );
|
|
1132
|
+
},
|
|
1133
|
+
);
|
|
1292
1134
|
}
|
|
1293
1135
|
|
|
1136
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
1137
|
+
searchQuery.query.bool['should'] = [];
|
|
1138
|
+
searchQuery.query.bool.should = [
|
|
1294
1139
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
bool: {
|
|
1301
|
-
filter: filter,
|
|
1140
|
+
{
|
|
1141
|
+
'wildcard': {
|
|
1142
|
+
'storeName.keyword': {
|
|
1143
|
+
'value': `*${inputData.searchValue}*`,
|
|
1144
|
+
},
|
|
1302
1145
|
},
|
|
1303
1146
|
},
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1147
|
+
{
|
|
1148
|
+
'wildcard': {
|
|
1149
|
+
'storeId.keyword': {
|
|
1150
|
+
'value': `*${inputData.searchValue}*`,
|
|
1308
1151
|
},
|
|
1309
1152
|
},
|
|
1310
1153
|
},
|
|
1311
|
-
|
|
1154
|
+
{
|
|
1155
|
+
'wildcard': {
|
|
1156
|
+
'ticketId.keyword': {
|
|
1157
|
+
'value': `*${inputData.searchValue}*`,
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
];
|
|
1164
|
+
searchQuery.query.bool['minimum_should_match'] = 1;
|
|
1165
|
+
}
|
|
1166
|
+
// You can add more filters as needed
|
|
1167
|
+
const searchResult = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
1168
|
+
const count = searchResult?.body?.hits?.total?.value || 0;
|
|
1169
|
+
|
|
1170
|
+
if ( count === 0 ) {
|
|
1171
|
+
return res.sendError( 'no data found', 204 );
|
|
1172
|
+
}
|
|
1173
|
+
const ticketListData = searchResult?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
1174
|
+
|
|
1175
|
+
let temp = [];
|
|
1176
|
+
if ( req.user.userType == 'tango' ) {
|
|
1177
|
+
if ( inputData.tangotype == 'store' ) {
|
|
1178
|
+
for ( let item of ticketListData ) {
|
|
1179
|
+
temp.push( {
|
|
1180
|
+
|
|
1181
|
+
ticketId: item?.ticketId,
|
|
1182
|
+
storeId: item?.storeId,
|
|
1183
|
+
storeName: item?.storeName,
|
|
1184
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1185
|
+
issueDate: item?.dateString,
|
|
1186
|
+
dueDate: '',
|
|
1187
|
+
footfall: item?.footfallCount,
|
|
1188
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1189
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1190
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1191
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1192
|
+
status: item?.status,
|
|
1193
|
+
|
|
1194
|
+
} );
|
|
1195
|
+
}
|
|
1196
|
+
} else {
|
|
1197
|
+
for ( let item of ticketListData ) {
|
|
1198
|
+
temp.push( {
|
|
1199
|
+
|
|
1200
|
+
ticketId: item?.ticketId,
|
|
1201
|
+
storeId: item?.storeId,
|
|
1202
|
+
storeName: item?.storeName,
|
|
1203
|
+
|
|
1204
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1205
|
+
issueDate: item?.dateString,
|
|
1206
|
+
footfall: item?.footfallCount,
|
|
1207
|
+
dueDate: '',
|
|
1208
|
+
type: item?.type || 'store',
|
|
1209
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1210
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1211
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1212
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1213
|
+
status: item?.status,
|
|
1214
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1215
|
+
|
|
1216
|
+
} );
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
} else {
|
|
1220
|
+
if ( inputData?.permissionType === 'approve' ) {
|
|
1221
|
+
for ( let item of ticketListData ) {
|
|
1222
|
+
temp.push( {
|
|
1223
|
+
|
|
1224
|
+
ticketId: item?.ticketId,
|
|
1225
|
+
storeId: item?.storeId,
|
|
1226
|
+
storeName: item?.storeName,
|
|
1227
|
+
|
|
1228
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1229
|
+
issueDate: item?.dateString,
|
|
1230
|
+
dueDate: '',
|
|
1231
|
+
footfall: item?.footfallCount,
|
|
1232
|
+
|
|
1233
|
+
type: item.type || 'store',
|
|
1234
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1235
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1236
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1237
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1238
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
1239
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1240
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1241
|
+
|
|
1242
|
+
} );
|
|
1243
|
+
}
|
|
1244
|
+
} else if ( inputData?.permissionType === 'review' ) {
|
|
1245
|
+
for ( let item of ticketListData ) {
|
|
1246
|
+
temp.push( {
|
|
1247
|
+
|
|
1248
|
+
ticketId: item?.ticketId,
|
|
1249
|
+
storeId: item?.storeId,
|
|
1250
|
+
storeName: item?.storeName,
|
|
1251
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1252
|
+
issueDate: item?.dateString,
|
|
1253
|
+
footfall: item?.footfallCount,
|
|
1254
|
+
dueDate: '',
|
|
1255
|
+
type: item.type || 'store',
|
|
1256
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1257
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1258
|
+
|
|
1259
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
1260
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1261
|
+
|
|
1262
|
+
} );
|
|
1263
|
+
}
|
|
1264
|
+
} else if ( req.user.role === 'user' ) {
|
|
1265
|
+
temp = [];
|
|
1266
|
+
} else if ( ticketsFeature ) {
|
|
1267
|
+
for ( let item of ticketListData ) {
|
|
1268
|
+
temp.push( {
|
|
1269
|
+
|
|
1270
|
+
ticketId: item?.ticketId,
|
|
1271
|
+
storeId: item?.storeId,
|
|
1272
|
+
storeName: item?.storeName,
|
|
1273
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1274
|
+
issueDate: item?.dateString,
|
|
1275
|
+
footfall: item?.footfallCount,
|
|
1276
|
+
dueDate: '',
|
|
1277
|
+
type: item.type || 'store',
|
|
1278
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1279
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1280
|
+
|
|
1281
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
1282
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1283
|
+
|
|
1284
|
+
} );
|
|
1285
|
+
}
|
|
1286
|
+
} else if ( ticketsApproveFeature ) {
|
|
1287
|
+
for ( let item of ticketListData ) {
|
|
1288
|
+
temp.push( {
|
|
1289
|
+
|
|
1290
|
+
ticketId: item?.ticketId,
|
|
1291
|
+
storeId: item?.storeId,
|
|
1292
|
+
storeName: item?.storeName,
|
|
1293
|
+
|
|
1294
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1295
|
+
issueDate: item?.dateString,
|
|
1296
|
+
dueDate: '',
|
|
1297
|
+
footfall: item?.footfallCount,
|
|
1298
|
+
|
|
1299
|
+
type: item.type || 'store',
|
|
1300
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1301
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1302
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1303
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1304
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
1305
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1306
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1307
|
+
|
|
1308
|
+
} );
|
|
1309
|
+
}
|
|
1310
|
+
} else {
|
|
1311
|
+
temp = [];
|
|
1312
|
+
}
|
|
1312
1313
|
}
|
|
1314
|
+
|
|
1315
|
+
return res.sendSuccess( { result: temp, count: count } );
|
|
1316
|
+
} catch ( error ) {
|
|
1317
|
+
const err = error.message || 'Internal Server Error';
|
|
1318
|
+
logger.error( { error: error, messgage: req.query } );
|
|
1319
|
+
return res.sendSuccess( err, 500 );
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
export async function getTickets( req, res ) {
|
|
1324
|
+
try {
|
|
1325
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1326
|
+
const inputData = req.query;
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
let source = [ 'storeId', 'type', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail', 'type' ];
|
|
1330
|
+
let filter = [
|
|
1331
|
+
|
|
1332
|
+
{
|
|
1333
|
+
term: {
|
|
1334
|
+
'ticketId.keyword': inputData.ticketId,
|
|
1335
|
+
},
|
|
1336
|
+
},
|
|
1337
|
+
];
|
|
1338
|
+
|
|
1339
|
+
|
|
1313
1340
|
const getCount = {
|
|
1314
1341
|
query: {
|
|
1315
1342
|
bool: {
|
|
@@ -1320,20 +1347,14 @@ export async function getTickets( req, res ) {
|
|
|
1320
1347
|
|
|
1321
1348
|
|
|
1322
1349
|
const geteDataCount = await getOpenSearchCount( openSearch.footfallDirectory, getCount );
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
if ( inputData.storeId?.length > 0 ) {
|
|
1328
|
-
const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1 } );
|
|
1329
|
-
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
1330
|
-
}
|
|
1331
|
-
return res.sendError( 'No data found', 204 );
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
if ( !geteDataCount ) {
|
|
1353
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
1332
1354
|
}
|
|
1333
1355
|
|
|
1334
1356
|
const getQuery = {
|
|
1335
|
-
size:
|
|
1336
|
-
from: skip,
|
|
1357
|
+
size: 1,
|
|
1337
1358
|
query: {
|
|
1338
1359
|
bool: {
|
|
1339
1360
|
filter: filter,
|
|
@@ -1343,14 +1364,9 @@ export async function getTickets( req, res ) {
|
|
|
1343
1364
|
};
|
|
1344
1365
|
|
|
1345
1366
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
1346
|
-
|
|
1347
1367
|
const response = getData?.body?.hits?.hits;
|
|
1348
1368
|
if ( !response || response.length == 0 ) {
|
|
1349
|
-
|
|
1350
|
-
const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1 } );
|
|
1351
|
-
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
1352
|
-
}
|
|
1353
|
-
return res.sendError( 'No data', 204 );
|
|
1369
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
1354
1370
|
}
|
|
1355
1371
|
let temp = [];
|
|
1356
1372
|
if ( inputData?.action ) {
|
|
@@ -1388,56 +1404,307 @@ export async function getTickets( req, res ) {
|
|
|
1388
1404
|
approverUserName: hit?._source?.approverUserName,
|
|
1389
1405
|
approverEmail: hit?._source?.approverEmail,
|
|
1390
1406
|
approverRole: hit?._source?.approverRole,
|
|
1407
|
+
type: hit?._source?.type,
|
|
1391
1408
|
};
|
|
1392
1409
|
let result;
|
|
1393
1410
|
|
|
1411
|
+
|
|
1394
1412
|
const matched = hit.matched_queries;
|
|
1395
1413
|
// Add only matched data array
|
|
1396
|
-
if ( matched.includes( 'matched_employee' )&& ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
1414
|
+
if ( matched.includes( 'matched_employee' ) && ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
1397
1415
|
result = defaultData;
|
|
1398
1416
|
result.employee = hit?._source?.employee;
|
|
1399
1417
|
// result.type = 'employee';
|
|
1400
1418
|
result.matched = matched;
|
|
1401
1419
|
}
|
|
1402
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1420
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1403
1421
|
result = defaultData;
|
|
1404
1422
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
1405
1423
|
result.matched = matched;
|
|
1406
1424
|
}
|
|
1407
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1425
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1408
1426
|
result = defaultData;
|
|
1409
1427
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
1410
1428
|
result.matched = matched;
|
|
1411
1429
|
}
|
|
1412
1430
|
|
|
1413
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1414
|
-
result = defaultData;
|
|
1415
|
-
result.junk = hit?._source?.junk;
|
|
1416
|
-
result.matched = matched;
|
|
1417
|
-
}
|
|
1431
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1432
|
+
result = defaultData;
|
|
1433
|
+
result.junk = hit?._source?.junk;
|
|
1434
|
+
result.matched = matched;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
if ( result ) {
|
|
1438
|
+
const nested = [
|
|
1439
|
+
{
|
|
1440
|
+
_id: hit._id,
|
|
1441
|
+
_index: hit._index,
|
|
1442
|
+
_score: 0,
|
|
1443
|
+
_source: result,
|
|
1444
|
+
},
|
|
1445
|
+
];
|
|
1446
|
+
temp.push( ...nested );
|
|
1447
|
+
}
|
|
1448
|
+
} );
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
const finalResponse = inputData.action ? temp : response;
|
|
1452
|
+
const getRevopQuery = {
|
|
1453
|
+
size: 10000,
|
|
1454
|
+
query: {
|
|
1455
|
+
bool: {
|
|
1456
|
+
must: [
|
|
1457
|
+
{ term: { 'storeId.keyword': response?.[0]?._source?.storeId } }, // assuming inputData.storeId is an array
|
|
1458
|
+
{ term: { 'dateString': response?.[0]?._source?.dateString } },
|
|
1459
|
+
],
|
|
1460
|
+
},
|
|
1461
|
+
},
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
const revopResp = await getOpenSearchData( openSearch.revop, getRevopQuery );
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
// Map revopResp.body.hits.hits to revopSources
|
|
1469
|
+
const revopSources = Array.isArray( revopResp?.body?.hits?.hits ) ?
|
|
1470
|
+
revopResp?.body?.hits?.hits?.map( ( hit ) => hit._source ) :
|
|
1471
|
+
[];
|
|
1472
|
+
|
|
1473
|
+
// Create a map of revopSources by id for quick lookup
|
|
1474
|
+
const revopSourcesMap = new Map();
|
|
1475
|
+
revopSources.forEach( ( item ) => {
|
|
1476
|
+
if ( item?.id ) {
|
|
1477
|
+
revopSourcesMap.set( String( item.id ), item );
|
|
1478
|
+
}
|
|
1479
|
+
} );
|
|
1480
|
+
|
|
1481
|
+
// Process revopSources to replace duplicateImage entries with full objects from the array
|
|
1482
|
+
const processedRevopSources = revopSources.map( ( item ) => {
|
|
1483
|
+
// Check if this is a duplicate parent item
|
|
1484
|
+
if ( item?.revopsType === 'duplicate' && item?.isParent === true && Array.isArray( item?.duplicateImage ) ) {
|
|
1485
|
+
// Map each duplicateImage entry to the full object from revopSources
|
|
1486
|
+
const updatedDuplicateImage = item.duplicateImage.map( ( duplicateImg ) => {
|
|
1487
|
+
const duplicateId = String( duplicateImg?.id );
|
|
1488
|
+
// Find the full object in revopSources that matches this id
|
|
1489
|
+
const fullObject = revopSourcesMap.get( duplicateId );
|
|
1490
|
+
// Return the full object if found, otherwise return the original duplicateImg
|
|
1491
|
+
return fullObject || duplicateImg;
|
|
1492
|
+
} );
|
|
1493
|
+
|
|
1494
|
+
return {
|
|
1495
|
+
...item,
|
|
1496
|
+
duplicateImage: updatedDuplicateImage,
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
// Return item as-is if it doesn't meet the criteria
|
|
1500
|
+
return item;
|
|
1501
|
+
} );
|
|
1418
1502
|
|
|
1419
|
-
if ( result ) {
|
|
1420
|
-
const nested = [
|
|
1421
|
-
{
|
|
1422
|
-
_id: hit._id,
|
|
1423
|
-
_index: hit._index,
|
|
1424
|
-
_score: 0,
|
|
1425
|
-
_source: result,
|
|
1426
|
-
},
|
|
1427
|
-
];
|
|
1428
|
-
temp.push( ...nested );
|
|
1429
|
-
}
|
|
1430
|
-
} );
|
|
1431
|
-
}
|
|
1432
|
-
const finalResponse = inputData.action ? temp : response;
|
|
1433
1503
|
if ( finalResponse?.length == 0 || !finalResponse ) {
|
|
1434
1504
|
if ( inputData.storeId?.length > 0 ) {
|
|
1435
|
-
const getStoreName = await findOneStore( { storeId:
|
|
1505
|
+
const getStoreName = await findOneStore( { storeId: response?.[0]?._source?.storeId }, { storeName: 1 } );
|
|
1436
1506
|
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
1437
1507
|
}
|
|
1438
1508
|
return res.sendError( 'No Data found', 204 );
|
|
1439
1509
|
}
|
|
1440
|
-
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
// replace the mappingInfo.revisedDetail with processedRevopSources
|
|
1513
|
+
if ( Array.isArray( finalResponse ) ) {
|
|
1514
|
+
for ( let item of finalResponse ) {
|
|
1515
|
+
if (
|
|
1516
|
+
item &&
|
|
1517
|
+
item._source &&
|
|
1518
|
+
item._source.mappingInfo
|
|
1519
|
+
|
|
1520
|
+
) {
|
|
1521
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1522
|
+
const vmsCommentsLogIndex = openSearch.vmsCommentsLog || 'vms-comments-log-dev';
|
|
1523
|
+
let commentsResponse = [];
|
|
1524
|
+
const commentsFilter = [
|
|
1525
|
+
{ term: { 'storeId.keyword': item?._source?.storeId } },
|
|
1526
|
+
{ term: { dateString: item?._source?.dateString } },
|
|
1527
|
+
|
|
1528
|
+
];
|
|
1529
|
+
|
|
1530
|
+
const commentsQuery = {
|
|
1531
|
+
size: 10000,
|
|
1532
|
+
sort: [
|
|
1533
|
+
{ createdAt: { order: 'desc' } }, // Sort descending by createdAt
|
|
1534
|
+
],
|
|
1535
|
+
query: {
|
|
1536
|
+
bool: {
|
|
1537
|
+
filter: commentsFilter,
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
const commentsRes = await getOpenSearchData( vmsCommentsLogIndex, commentsQuery );
|
|
1543
|
+
// If mappingInfo is an array, update revisedDetail for each mappingInfo object
|
|
1544
|
+
if ( Array.isArray( item._source.mappingInfo ) ) {
|
|
1545
|
+
item._source.mappingInfo.forEach( ( mappingObj ) => {
|
|
1546
|
+
commentsResponse = commentsRes?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
1547
|
+
|
|
1548
|
+
// Check if duplicate condition exists in commentsResponse
|
|
1549
|
+
|
|
1550
|
+
// Structure comments output
|
|
1551
|
+
let commentsDetails = [];
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
const types = [ 'tagging', 'review', 'approve' ];
|
|
1555
|
+
|
|
1556
|
+
// Process each type
|
|
1557
|
+
types.forEach( ( typeValue ) => {
|
|
1558
|
+
if ( typeValue === 'tagging' ) {
|
|
1559
|
+
// For tagging, group by category and create separate objects for each category
|
|
1560
|
+
const taggingComments = commentsResponse.filter( ( c ) => c.type === typeValue );
|
|
1561
|
+
|
|
1562
|
+
// Group by category
|
|
1563
|
+
const categoryGroups = {};
|
|
1564
|
+
taggingComments.forEach( ( c ) => {
|
|
1565
|
+
const category = c.category || 'other';
|
|
1566
|
+
if ( !categoryGroups[category] ) {
|
|
1567
|
+
categoryGroups[category] = [];
|
|
1568
|
+
}
|
|
1569
|
+
categoryGroups[category].push( c );
|
|
1570
|
+
} );
|
|
1571
|
+
|
|
1572
|
+
// Create separate objects for each category
|
|
1573
|
+
Object.keys( categoryGroups ).forEach( ( category ) => {
|
|
1574
|
+
const categoryComments = categoryGroups[category];
|
|
1575
|
+
let parent = null;
|
|
1576
|
+
|
|
1577
|
+
const comms = categoryComments.map( ( c ) => {
|
|
1578
|
+
if ( category === 'duplicate' ) {
|
|
1579
|
+
if ( !parent && c.parent ) {
|
|
1580
|
+
parent = c.parent;
|
|
1581
|
+
}
|
|
1582
|
+
return {
|
|
1583
|
+
createdByEmail: c.createdByEmail,
|
|
1584
|
+
createdByUserName: c.createdByUserName,
|
|
1585
|
+
createdByRole: c.createdByRole,
|
|
1586
|
+
message: c.message,
|
|
1587
|
+
};
|
|
1588
|
+
} else {
|
|
1589
|
+
return {
|
|
1590
|
+
id: c.id,
|
|
1591
|
+
tempId: c.tempId,
|
|
1592
|
+
timeRange: c.timeRange,
|
|
1593
|
+
entryTime: c.entryTime,
|
|
1594
|
+
exitTime: c.exitTime,
|
|
1595
|
+
filePath: c.filePath,
|
|
1596
|
+
isChecked: c.isChecked,
|
|
1597
|
+
createdAt: c.createdAt,
|
|
1598
|
+
message: c.message,
|
|
1599
|
+
createdByEmail: c.createdByEmail,
|
|
1600
|
+
createdByUserName: c.createdByUserName,
|
|
1601
|
+
createdByRole: c.createdByRole,
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
} );
|
|
1605
|
+
|
|
1606
|
+
const taggingObj = {
|
|
1607
|
+
category: category,
|
|
1608
|
+
type: typeValue,
|
|
1609
|
+
comments: comms,
|
|
1610
|
+
};
|
|
1611
|
+
|
|
1612
|
+
// Add parent only for duplicate category
|
|
1613
|
+
if ( category === 'duplicate' && parent !== null ) {
|
|
1614
|
+
taggingObj.parent = parent;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
commentsDetails.push( taggingObj );
|
|
1618
|
+
} );
|
|
1619
|
+
} else if ( typeValue === 'review' || typeValue === 'approve' ) {
|
|
1620
|
+
// For review and approve, keep existing structure
|
|
1621
|
+
const comms = commentsResponse
|
|
1622
|
+
.filter( ( c ) => c.type === typeValue )
|
|
1623
|
+
.map( ( c ) => {
|
|
1624
|
+
if ( c.category === 'duplicate' ) {
|
|
1625
|
+
return {
|
|
1626
|
+
parent: c?.taggedImages[0]?._source?.parent,
|
|
1627
|
+
category: c.category,
|
|
1628
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
1629
|
+
c.taggedImages.map( ( img ) => ( {
|
|
1630
|
+
id: img?._source?.id,
|
|
1631
|
+
tempId: img?._source?.tempId,
|
|
1632
|
+
timeRange: img?._source?.timeRange,
|
|
1633
|
+
entryTime: img?._source?.entryTime,
|
|
1634
|
+
exitTime: img?._source?.exitTime,
|
|
1635
|
+
filePath: img?._source?.filePath,
|
|
1636
|
+
isChecked: img?._source?.isChecked,
|
|
1637
|
+
} ) ) :
|
|
1638
|
+
[],
|
|
1639
|
+
createdByEmail: c.createdByEmail,
|
|
1640
|
+
createdByUserName: c.createdByUserName,
|
|
1641
|
+
createdByRole: c.createdByRole,
|
|
1642
|
+
status: c.status,
|
|
1643
|
+
message: c.message,
|
|
1644
|
+
};
|
|
1645
|
+
} else {
|
|
1646
|
+
return {
|
|
1647
|
+
category: c.category,
|
|
1648
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
1649
|
+
c.taggedImages.map( ( img ) => ( {
|
|
1650
|
+
id: img?._source?.id,
|
|
1651
|
+
tempId: img?._source?.tempId,
|
|
1652
|
+
timeRange: img?._source?.timeRange,
|
|
1653
|
+
entryTime: img?._source?.entryTime,
|
|
1654
|
+
exitTime: img?._source?.exitTime,
|
|
1655
|
+
filePath: img?._source?.filePath,
|
|
1656
|
+
isChecked: img?._source?.isChecked,
|
|
1657
|
+
} ) ) :
|
|
1658
|
+
[],
|
|
1659
|
+
createdByEmail: c.createdByEmail,
|
|
1660
|
+
createdByUserName: c.createdByUserName,
|
|
1661
|
+
createdByRole: c.createdByRole,
|
|
1662
|
+
status: c.status,
|
|
1663
|
+
message: c.message,
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
} );
|
|
1667
|
+
|
|
1668
|
+
// Only add if there are comments
|
|
1669
|
+
if ( comms.length > 0 ) {
|
|
1670
|
+
commentsDetails.push( {
|
|
1671
|
+
type: typeValue,
|
|
1672
|
+
comments: comms,
|
|
1673
|
+
} );
|
|
1674
|
+
} else {
|
|
1675
|
+
// Add empty comments array if no comments
|
|
1676
|
+
commentsDetails.push( {
|
|
1677
|
+
type: typeValue,
|
|
1678
|
+
comments: [],
|
|
1679
|
+
} );
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
} );
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
item._source.commentsDetails = commentsDetails;
|
|
1686
|
+
|
|
1687
|
+
if (
|
|
1688
|
+
Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) &&
|
|
1689
|
+
mappingObj.type !== 'tangoreview'
|
|
1690
|
+
) {
|
|
1691
|
+
mappingObj.revisedDetail = processedRevopSources;
|
|
1692
|
+
}
|
|
1693
|
+
} );
|
|
1694
|
+
} else {
|
|
1695
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
} else if (
|
|
1700
|
+
finalResponse &&
|
|
1701
|
+
finalResponse._source &&
|
|
1702
|
+
finalResponse._source.mappingInfo
|
|
1703
|
+
) {
|
|
1704
|
+
finalResponse._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
return res.sendSuccess( { result: finalResponse } );
|
|
1441
1708
|
} catch ( error ) {
|
|
1442
1709
|
const err = error.message || 'Internal Server Error';
|
|
1443
1710
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -1509,7 +1776,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1509
1776
|
item.isChecked == true ? tempId.push( { tempId: item.tempId, timeRange: item.timeRange } ) : null;
|
|
1510
1777
|
bulkBody.push(
|
|
1511
1778
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${item.timeRange}_${item.tempId}` } },
|
|
1512
|
-
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true? 'approved':'rejected' } },
|
|
1779
|
+
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1513
1780
|
);
|
|
1514
1781
|
} );
|
|
1515
1782
|
}
|
|
@@ -1524,11 +1791,11 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1524
1791
|
updateData.employee = updatedEmployee;
|
|
1525
1792
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: updateData } );
|
|
1526
1793
|
for ( let employee of updateData?.employee ) {
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1794
|
+
( employee.isChecked == true ) ? tempId.push( { tempId: employee.tempId, timeRange: employee.timeRange } ) : null;
|
|
1795
|
+
bulkBody.push(
|
|
1796
|
+
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${employee.timeRange}_${employee.tempId}` } },
|
|
1797
|
+
{ doc: { isChecked: employee.isChecked, status: employee?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1798
|
+
);
|
|
1532
1799
|
}
|
|
1533
1800
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { employeeACCount: tempId?.length || 0 } } );
|
|
1534
1801
|
}
|
|
@@ -1547,7 +1814,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1547
1814
|
houseKeeping.isChecked == true ? tempId.push( { tempId: houseKeeping.tempId, timeRange: houseKeeping.timeRange } ) : null;
|
|
1548
1815
|
bulkBody.push(
|
|
1549
1816
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${houseKeeping.timeRange}_${houseKeeping.tempId}` } },
|
|
1550
|
-
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true? 'approved':'rejected' } },
|
|
1817
|
+
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1551
1818
|
);
|
|
1552
1819
|
}
|
|
1553
1820
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { houseKeepingACCount: tempId?.length || 0 } } );
|
|
@@ -1568,7 +1835,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1568
1835
|
junk.isChecked == true ? tempId.push( { tempId: junk.tempId, timeRange: junk.timeRange } ) : null;
|
|
1569
1836
|
bulkBody.push(
|
|
1570
1837
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${junk.timeRange}_${junk.tempId}` } },
|
|
1571
|
-
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true? 'approved':'rejected' } },
|
|
1838
|
+
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1572
1839
|
);
|
|
1573
1840
|
}
|
|
1574
1841
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { junkACCount: tempId?.length || 0 } } );
|
|
@@ -1593,7 +1860,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1593
1860
|
let getUpdateExistingOne = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
1594
1861
|
const tempIdList = await extractCheckedTempIds( getUpdateExistingOne?.body );
|
|
1595
1862
|
const isSendMessge = await sendSqsMessage( data, tempIdList, getStoreType, storeId );
|
|
1596
|
-
if ( isSendMessge ==true ) {
|
|
1863
|
+
if ( isSendMessge == true ) {
|
|
1597
1864
|
return true; // res.sendSuccess( 'Ticket has been updated successfully' );
|
|
1598
1865
|
} else {
|
|
1599
1866
|
return false; // res.sendError( 'No SQS message sent', 500 );
|
|
@@ -1656,7 +1923,7 @@ function updateEmployeeCheckFlags( existingEmployees, inputEmployees, status ) {
|
|
|
1656
1923
|
// Step 2: Loop through all existing and update isChecked accordingly
|
|
1657
1924
|
const updatedEmployees = existingEmployees.map( ( emp ) => ( {
|
|
1658
1925
|
...emp,
|
|
1659
|
-
isChecked: status === 'rejected'? !checkedTempIds.has( emp.tempId ):checkedTempIds.has( emp.tempId ),
|
|
1926
|
+
isChecked: status === 'rejected' ? !checkedTempIds.has( emp.tempId ) : checkedTempIds.has( emp.tempId ),
|
|
1660
1927
|
} ) );
|
|
1661
1928
|
|
|
1662
1929
|
return updatedEmployees;
|
|
@@ -1706,7 +1973,7 @@ function mergeDuplicateImagesWithUncheck( existingData, inputData, status ) {
|
|
|
1706
1973
|
export async function sendSqsMessage( inputData, tempId, getStoreType, storeId ) {
|
|
1707
1974
|
const sqs = JSON.parse( process.env.SQS );
|
|
1708
1975
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1709
|
-
const sqsName = getStoreType > 0? sqs.revopTrackTicket: sqs.revopTicket;
|
|
1976
|
+
const sqsName = getStoreType > 0 ? sqs.revopTrackTicket : sqs.revopTicket;
|
|
1710
1977
|
const sqsProduceQueue = getStoreType > 0 ? {
|
|
1711
1978
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1712
1979
|
MessageBody: JSON.stringify( {
|
|
@@ -1734,12 +2001,11 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1734
2001
|
|
|
1735
2002
|
} ),
|
|
1736
2003
|
};
|
|
1737
|
-
const sqsQueue = getStoreType > 0? await sendMessageToFIFOQueue( sqsProduceQueue ):
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
);
|
|
2004
|
+
const sqsQueue = getStoreType > 0 ? await sendMessageToFIFOQueue( sqsProduceQueue ) :
|
|
2005
|
+
await sendMessageToQueue(
|
|
2006
|
+
sqsProduceQueue.QueueUrl,
|
|
2007
|
+
sqsProduceQueue.MessageBody,
|
|
2008
|
+
);
|
|
1743
2009
|
if ( sqsQueue.statusCode ) {
|
|
1744
2010
|
logger.error( {
|
|
1745
2011
|
error: `${sqsQueue}`,
|
|
@@ -1747,7 +2013,7 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1747
2013
|
} );
|
|
1748
2014
|
return false;
|
|
1749
2015
|
} else {
|
|
1750
|
-
const id
|
|
2016
|
+
const id = `${storeId}_${inputData.dateString.split( '-' ).reverse().join( '-' )}_${getStoreType > 0 ? 'live' : 'reduction'}_${Date.now()}`;
|
|
1751
2017
|
const logs = {
|
|
1752
2018
|
QueueUrl: sqsProduceQueue.QueueUrl,
|
|
1753
2019
|
MessageBody: sqsProduceQueue.MessageBody,
|
|
@@ -1797,13 +2063,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1797
2063
|
},
|
|
1798
2064
|
},
|
|
1799
2065
|
];
|
|
1800
|
-
|
|
1801
|
-
// filter.push(
|
|
1802
|
-
// {
|
|
1803
|
-
// terms: { 'storeId.keyword': req.stores },
|
|
1804
|
-
// },
|
|
1805
|
-
// );
|
|
1806
|
-
// }
|
|
2066
|
+
|
|
1807
2067
|
filter.push(
|
|
1808
2068
|
{
|
|
1809
2069
|
terms: { 'storeId.keyword': req?.stores || [] },
|
|
@@ -1918,8 +2178,8 @@ export async function downloadTickets( req, res ) {
|
|
|
1918
2178
|
{
|
|
1919
2179
|
terms: {
|
|
1920
2180
|
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1921
|
-
|
|
1922
|
-
|
|
2181
|
+
inputData.storeId :
|
|
2182
|
+
inputData.storeId,
|
|
1923
2183
|
},
|
|
1924
2184
|
},
|
|
1925
2185
|
);
|
|
@@ -1967,26 +2227,26 @@ export async function downloadTickets( req, res ) {
|
|
|
1967
2227
|
},
|
|
1968
2228
|
|
|
1969
2229
|
} ) :
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2230
|
+
inputData.revopsType === 'junk' ?
|
|
2231
|
+
filter.push( {
|
|
2232
|
+
range: {
|
|
2233
|
+
junkCount: {
|
|
2234
|
+
gt: 0,
|
|
2235
|
+
},
|
|
1975
2236
|
},
|
|
1976
|
-
}
|
|
1977
|
-
} ):
|
|
2237
|
+
} ) :
|
|
1978
2238
|
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2239
|
+
filter.push( {
|
|
2240
|
+
range: {
|
|
2241
|
+
duplicateCount: {
|
|
2242
|
+
gt: 0,
|
|
2243
|
+
},
|
|
1983
2244
|
},
|
|
1984
|
-
}
|
|
1985
|
-
} );
|
|
2245
|
+
} );
|
|
1986
2246
|
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1987
2247
|
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1988
2248
|
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1989
|
-
|
|
2249
|
+
inputData.revopsType == 'junk' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'junkCount', 'comments', 'junk', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] : [];
|
|
1990
2250
|
}
|
|
1991
2251
|
|
|
1992
2252
|
if ( inputData.action ) {
|
|
@@ -2063,7 +2323,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2063
2323
|
let temp = [];
|
|
2064
2324
|
if ( inputData?.action ) {
|
|
2065
2325
|
response.map( ( hit ) => {
|
|
2066
|
-
const defaultData ={
|
|
2326
|
+
const defaultData = {
|
|
2067
2327
|
storeId: hit._source.storeId,
|
|
2068
2328
|
dateString: hit?._source?.dateString,
|
|
2069
2329
|
ticketName: hit?._source?.ticketName,
|
|
@@ -2093,19 +2353,19 @@ export async function downloadTickets( req, res ) {
|
|
|
2093
2353
|
// result.type = 'employee';
|
|
2094
2354
|
result.matched = matched;
|
|
2095
2355
|
}
|
|
2096
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
2356
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
2097
2357
|
logger.info( { revop: inputData.revopsType } );
|
|
2098
2358
|
result = defaultData;
|
|
2099
2359
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
2100
2360
|
result.matched = matched;
|
|
2101
2361
|
}
|
|
2102
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
2362
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
2103
2363
|
logger.info( { revop: inputData.revopsType } );
|
|
2104
2364
|
result = defaultData;
|
|
2105
2365
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
2106
2366
|
result.matched = matched;
|
|
2107
2367
|
}
|
|
2108
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
2368
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
2109
2369
|
result = defaultData;
|
|
2110
2370
|
result.junk = hit?._source?.junk;
|
|
2111
2371
|
result.matched = matched;
|
|
@@ -2149,7 +2409,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2149
2409
|
revopsType: inputData?.revopsType,
|
|
2150
2410
|
type: 'get-tickets',
|
|
2151
2411
|
};
|
|
2152
|
-
const record={
|
|
2412
|
+
const record = {
|
|
2153
2413
|
stores: inputData?.storeId,
|
|
2154
2414
|
fromDate: inputData?.fromDate,
|
|
2155
2415
|
toDate: inputData?.toDate,
|
|
@@ -2167,7 +2427,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2167
2427
|
const sqs = JSON.parse( process.env.SQS );
|
|
2168
2428
|
const sqsName = sqs.revopDownload;
|
|
2169
2429
|
|
|
2170
|
-
const sqsProduceQueue ={
|
|
2430
|
+
const sqsProduceQueue = {
|
|
2171
2431
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
2172
2432
|
MessageBody: JSON.stringify( {
|
|
2173
2433
|
_id: getId?._id,
|
|
@@ -2196,7 +2456,6 @@ export async function downloadTickets( req, res ) {
|
|
|
2196
2456
|
}
|
|
2197
2457
|
|
|
2198
2458
|
async function extractTempIds( document ) {
|
|
2199
|
-
logger.info( { document: document } );
|
|
2200
2459
|
const source = document?._source || {};
|
|
2201
2460
|
const result = [];
|
|
2202
2461
|
|
|
@@ -2246,7 +2505,7 @@ export async function reviewerList( req, res ) {
|
|
|
2246
2505
|
featureName: 'FootfallDirectory',
|
|
2247
2506
|
modules: {
|
|
2248
2507
|
$elemMatch: {
|
|
2249
|
-
name: '
|
|
2508
|
+
name: inputData?.type === 'review' ? 'reviewer' : 'approver',
|
|
2250
2509
|
$or: [ { isAdd: true }, { isEdit: true } ],
|
|
2251
2510
|
},
|
|
2252
2511
|
},
|
|
@@ -2255,7 +2514,7 @@ export async function reviewerList( req, res ) {
|
|
|
2255
2514
|
};
|
|
2256
2515
|
|
|
2257
2516
|
const getUserlist = await findUser( reviewerRoleQuery, { userName: 1, email: 1, role: 1 } );
|
|
2258
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
2517
|
+
return res.sendSuccess( getUserlist || [] );
|
|
2259
2518
|
} catch ( error ) {
|
|
2260
2519
|
const err = error.message || 'Internal Server Error';
|
|
2261
2520
|
return res.sendError( err, 500 );
|
|
@@ -2277,6 +2536,16 @@ export async function openTicketList( req, res ) {
|
|
|
2277
2536
|
clientId: Array.isArray( clientId ) ? clientId : [ clientId ],
|
|
2278
2537
|
},
|
|
2279
2538
|
},
|
|
2539
|
+
{
|
|
2540
|
+
term: {
|
|
2541
|
+
'mappingInfo.type': inputData.type,
|
|
2542
|
+
},
|
|
2543
|
+
},
|
|
2544
|
+
{
|
|
2545
|
+
term: {
|
|
2546
|
+
'mappingInfo.status.keyword': 'Open',
|
|
2547
|
+
},
|
|
2548
|
+
},
|
|
2280
2549
|
{
|
|
2281
2550
|
range: {
|
|
2282
2551
|
dateString: {
|
|
@@ -2295,14 +2564,19 @@ export async function openTicketList( req, res ) {
|
|
|
2295
2564
|
filter: filter,
|
|
2296
2565
|
},
|
|
2297
2566
|
},
|
|
2298
|
-
_source: [ 'ticketId', 'storeName', 'revicedFootfall', 'footfallCount', 'revicedPerc' ],
|
|
2567
|
+
_source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
|
|
2299
2568
|
};
|
|
2569
|
+
// INSERT_YOUR_CODE
|
|
2570
|
+
// Add sorting by revicedPerc descending (highest revised accuracy first)
|
|
2571
|
+
openSearchQuery.sort = [
|
|
2572
|
+
{ 'revicedPerc.keyword': { order: inputData?.sortOrder === 1 ? 'asc' : 'desc' } },
|
|
2573
|
+
];
|
|
2300
2574
|
|
|
2301
2575
|
|
|
2302
2576
|
// Assuming getOpenSearchData and openSearch.footfallDirectoryTagging are available
|
|
2303
2577
|
const result = await getOpenSearchData( openSearch.footfallDirectory, openSearchQuery );
|
|
2304
2578
|
const getUserlist = result?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
2305
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
2579
|
+
return res.sendSuccess( getUserlist || [] );
|
|
2306
2580
|
} catch ( error ) {
|
|
2307
2581
|
const err = error.message || 'Internal Server Error';
|
|
2308
2582
|
logger.error( { error: error, function: 'openTicketList' } );
|
|
@@ -2315,73 +2589,32 @@ export async function assignTicket( req, res ) {
|
|
|
2315
2589
|
const inputData = req.body;
|
|
2316
2590
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2317
2591
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
const { email, userName, role, actionType } = inputData;
|
|
2321
|
-
|
|
2322
|
-
// INSERT_YOUR_CODE
|
|
2323
|
-
|
|
2324
|
-
// Find and update mappingInfo fields for the provided ticketId and actionType
|
|
2325
|
-
// Requires ticketId in inputData
|
|
2326
|
-
const { ticketId } = inputData;
|
|
2327
|
-
if ( !ticketId ) {
|
|
2328
|
-
return res.sendError( 'ticketId is required', 400 );
|
|
2329
|
-
}
|
|
2330
|
-
|
|
2331
|
-
// Build the OpenSearch update-by-query body
|
|
2332
|
-
const updateBody = {
|
|
2333
|
-
script: {
|
|
2334
|
-
source: `
|
|
2335
|
-
if (ctx._source.mappingInfo != null) {
|
|
2336
|
-
for (int i = 0; i < ctx._source.mappingInfo.length; i++) {
|
|
2337
|
-
if (ctx._source.mappingInfo[i].type == params.actionType) {
|
|
2338
|
-
ctx._source.mappingInfo[i].createdByEmail = params.email;
|
|
2339
|
-
ctx._source.mappingInfo[i].createdByUserName = params.userName;
|
|
2340
|
-
ctx._source.mappingInfo[i].createdByRole = params.role;
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
`,
|
|
2345
|
-
lang: 'painless',
|
|
2346
|
-
params: {
|
|
2347
|
-
email,
|
|
2348
|
-
userName,
|
|
2349
|
-
role,
|
|
2350
|
-
actionType,
|
|
2351
|
-
},
|
|
2352
|
-
},
|
|
2353
|
-
query: {
|
|
2354
|
-
bool: {
|
|
2355
|
-
must: [
|
|
2356
|
-
{ term: { 'ticketId.keyword': ticketId } },
|
|
2357
|
-
{
|
|
2358
|
-
nested: {
|
|
2359
|
-
path: 'mappingInfo',
|
|
2360
|
-
query: {
|
|
2361
|
-
bool: {
|
|
2362
|
-
must: [
|
|
2363
|
-
{ match: { 'mappingInfo.type': actionType } },
|
|
2364
|
-
],
|
|
2365
|
-
},
|
|
2366
|
-
},
|
|
2367
|
-
},
|
|
2368
|
-
},
|
|
2369
|
-
],
|
|
2370
|
-
},
|
|
2371
|
-
},
|
|
2372
|
-
};
|
|
2373
|
-
|
|
2374
|
-
// Call OpenSearch _update_by_query to update doc(s) where ticketId and mappingInfo[i].type == actionType
|
|
2375
|
-
const response = await upsertOpenSearchData(
|
|
2376
|
-
openSearch.footfallDirectory,
|
|
2377
|
-
'11-1716_2025-11-20_footfall-directory-tagging',
|
|
2378
|
-
updateBody, // custom arg to indicate passthrough for update-by-query, depends on helper implementation
|
|
2379
|
-
);
|
|
2380
|
-
|
|
2592
|
+
const { email, userName, role, storeId, dateString } = inputData;
|
|
2593
|
+
const _id = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2381
2594
|
|
|
2382
|
-
|
|
2595
|
+
const getTicket = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
2596
|
+
if ( !getTicket ) {
|
|
2597
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
2598
|
+
}
|
|
2599
|
+
const source = getTicket?.body?._source;
|
|
2600
|
+
const mappingInfo = Array.isArray( source.mappingInfo ) ? source.mappingInfo : [];
|
|
2601
|
+
if ( mappingInfo.length === 0 ) {
|
|
2602
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
2603
|
+
}
|
|
2604
|
+
const lastIndex = mappingInfo.length - 1;
|
|
2605
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
2606
|
+
if ( String( lastEntry.status ) !== 'In-Progress' ) {
|
|
2607
|
+
return res.sendError( 'Ticket is not in progress', 400 );
|
|
2608
|
+
}
|
|
2383
2609
|
|
|
2384
|
-
|
|
2610
|
+
const currentTime = new Date();
|
|
2611
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
2612
|
+
updatedMappingInfo[lastIndex] = { ...lastEntry, createdByEmail: email, updatedAt: currentTime, createdByUserName: userName, createdByRole: role };
|
|
2613
|
+
const updateResult = await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { mappingInfo: updatedMappingInfo } } );
|
|
2614
|
+
if ( !updateResult ) {
|
|
2615
|
+
return res.sendError( 'Failed to update ticket', 400 );
|
|
2616
|
+
}
|
|
2617
|
+
return res.sendSuccess( { message: 'Ticket assigned successfully' } );
|
|
2385
2618
|
} catch ( error ) {
|
|
2386
2619
|
const err = error.message || 'Internal Server Error';
|
|
2387
2620
|
logger.error( { error: error, function: 'assignTicket' } );
|
|
@@ -2392,11 +2625,13 @@ export async function assignTicket( req, res ) {
|
|
|
2392
2625
|
export async function updateTempStatus( req, res ) {
|
|
2393
2626
|
try {
|
|
2394
2627
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2395
|
-
const
|
|
2628
|
+
const inputData = req.body;
|
|
2629
|
+
const { id, type, status, comments } = inputData;
|
|
2396
2630
|
|
|
2397
2631
|
// Use bulk update API via bucketing (batch update) -- fetch docs, then bulk-update
|
|
2398
2632
|
// 1. Search for all documents matching the ticket IDs
|
|
2399
2633
|
const searchBody = {
|
|
2634
|
+
size: 10000,
|
|
2400
2635
|
query: {
|
|
2401
2636
|
bool: {
|
|
2402
2637
|
must: [
|
|
@@ -2415,16 +2650,14 @@ export async function updateTempStatus( req, res ) {
|
|
|
2415
2650
|
openSearch.revop,
|
|
2416
2651
|
searchBody,
|
|
2417
2652
|
);
|
|
2418
|
-
|
|
2653
|
+
|
|
2419
2654
|
// Extract bulk IDs to update
|
|
2420
2655
|
const hits = searchResp?.body?.hits?.hits ?? [];
|
|
2421
|
-
|
|
2656
|
+
|
|
2422
2657
|
if ( !hits.length ) {
|
|
2423
2658
|
return res.sendError( 'no data', 204 );
|
|
2424
2659
|
}
|
|
2425
2660
|
|
|
2426
|
-
// 2. Build bulk update commands
|
|
2427
|
-
// Each doc: { update: { _id: ..., _index: ... } }, { doc: { status: status } }
|
|
2428
2661
|
|
|
2429
2662
|
// 1. Get all IDs from hits
|
|
2430
2663
|
const docIdToIndex = {};
|
|
@@ -2432,7 +2665,6 @@ export async function updateTempStatus( req, res ) {
|
|
|
2432
2665
|
docIdToIndex[doc._id] = doc._index;
|
|
2433
2666
|
} );
|
|
2434
2667
|
const docIds = hits.map( ( doc ) => doc._id );
|
|
2435
|
-
logger.info( { docIds } );
|
|
2436
2668
|
// 2. Fetch all docs by ID to get 'actions' (in chunks if large)
|
|
2437
2669
|
const getBody = [];
|
|
2438
2670
|
for ( const doc of hits ) {
|
|
@@ -2440,71 +2672,76 @@ export async function updateTempStatus( req, res ) {
|
|
|
2440
2672
|
}
|
|
2441
2673
|
|
|
2442
2674
|
let mgetResp;
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2675
|
+
|
|
2676
|
+
mgetResp = await getOpenSearchData(
|
|
2677
|
+
openSearch.revop,
|
|
2678
|
+
{
|
|
2679
|
+
size: 10000,
|
|
2680
|
+
query: {
|
|
2681
|
+
ids: {
|
|
2682
|
+
values: docIds,
|
|
2451
2683
|
},
|
|
2452
|
-
_source: true,
|
|
2453
2684
|
},
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
mgetResp = undefined;
|
|
2458
|
-
}
|
|
2459
|
-
logger.info( { mgetResp } );
|
|
2685
|
+
_source: true,
|
|
2686
|
+
},
|
|
2687
|
+
);
|
|
2460
2688
|
// (If you have a utility for multi-get, you may want to use that. Else, you might need to fetch each by ID.)
|
|
2461
2689
|
// For fallback, fetch all source fields via another search
|
|
2462
|
-
let fullDocs = [];
|
|
2463
|
-
if ( mgetResp && mgetResp.body && mgetResp.body.docs && Array.isArray( mgetResp.body.docs ) ) {
|
|
2464
|
-
fullDocs = mgetResp.body.docs;
|
|
2465
|
-
} else if ( searchResp.body && searchResp.body.hits && searchResp.body.hits.hits ) {
|
|
2466
|
-
// fallback: use searchResp docs (request _source above)
|
|
2467
|
-
fullDocs = searchResp.body.hits.hits;
|
|
2468
|
-
}
|
|
2690
|
+
let fullDocs = mgetResp?.body?.hits?.hits || searchResp?.body?.hits?.hits || [];
|
|
2469
2691
|
|
|
2470
2692
|
// 3. Prepare the new actions array for each doc, and set up bulk update payloads
|
|
2471
2693
|
const reviewActions = [ 'approved', 'rejected' ];
|
|
2472
2694
|
const docsToUpdate = [];
|
|
2473
|
-
logger.info( { fullDocs: fullDocs } );
|
|
2474
2695
|
for ( const doc of fullDocs ) {
|
|
2475
2696
|
const source = doc._source || doc.fields || {}; // support mget and search hits
|
|
2476
2697
|
let actions = Array.isArray( source.actions ) ? [ ...source.actions ] : [];
|
|
2477
2698
|
if ( reviewActions.includes( status ) ) {
|
|
2478
2699
|
// for review: update or push 'review'
|
|
2479
2700
|
let found = false;
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2701
|
+
switch ( type ) {
|
|
2702
|
+
case 'review':
|
|
2703
|
+
actions = actions.map( ( item ) => {
|
|
2704
|
+
if ( item.actionType === 'review' ) {
|
|
2705
|
+
found = true;
|
|
2706
|
+
return { ...item, action: status };
|
|
2707
|
+
}
|
|
2708
|
+
return item;
|
|
2709
|
+
} );
|
|
2710
|
+
if ( !found ) {
|
|
2711
|
+
actions.push( { actionType: 'review', action: status } );
|
|
2712
|
+
}
|
|
2713
|
+
break;
|
|
2714
|
+
case 'approve':
|
|
2715
|
+
actions = actions.map( ( item ) => {
|
|
2716
|
+
if ( item.actionType === 'approve' ) {
|
|
2717
|
+
found = true;
|
|
2718
|
+
return { ...item, action: status };
|
|
2719
|
+
}
|
|
2720
|
+
return item;
|
|
2721
|
+
} );
|
|
2722
|
+
if ( !found ) {
|
|
2723
|
+
actions.push( { actionType: 'approve', action: status } );
|
|
2724
|
+
}
|
|
2725
|
+
break;
|
|
2726
|
+
default:
|
|
2727
|
+
return res.sendError( 'wrong vaue', 400 );
|
|
2502
2728
|
}
|
|
2503
2729
|
}
|
|
2730
|
+
let isChecked = true;
|
|
2731
|
+
switch ( type ) {
|
|
2732
|
+
case 'review':
|
|
2733
|
+
isChecked = status === 'approved' ? true : false;
|
|
2734
|
+
break;
|
|
2735
|
+
case 'approve':
|
|
2736
|
+
isChecked = status === 'approved' ? doc?._source?.isChecked : !doc?._source?.isChecked;
|
|
2737
|
+
break;
|
|
2738
|
+
}
|
|
2504
2739
|
docsToUpdate.push( {
|
|
2505
2740
|
_index: doc._index || docIdToIndex[doc._id],
|
|
2506
2741
|
_id: doc._id,
|
|
2507
2742
|
actions,
|
|
2743
|
+
isChecked: isChecked,
|
|
2744
|
+
comments: comments || '',
|
|
2508
2745
|
} );
|
|
2509
2746
|
}
|
|
2510
2747
|
const bulkPayload = [];
|
|
@@ -2514,11 +2751,9 @@ export async function updateTempStatus( req, res ) {
|
|
|
2514
2751
|
update: { _index: doc._index, _id: doc._id },
|
|
2515
2752
|
} );
|
|
2516
2753
|
bulkPayload.push( {
|
|
2517
|
-
doc: { actions: doc.actions },
|
|
2754
|
+
doc: { actions: doc.actions, isChecked: doc?.isChecked, comments: doc?.comments || '' },
|
|
2518
2755
|
} );
|
|
2519
2756
|
}
|
|
2520
|
-
logger.info( { bulkPayload: bulkPayload } );
|
|
2521
|
-
|
|
2522
2757
|
|
|
2523
2758
|
// 3. Execute bulk update
|
|
2524
2759
|
const bulkResp = await bulkUpdate( bulkPayload );
|
|
@@ -2526,8 +2761,50 @@ export async function updateTempStatus( req, res ) {
|
|
|
2526
2761
|
// Count successes
|
|
2527
2762
|
const updatedCount = bulkResp?.body?.items?.filter( ( item ) => item?.update?.result === 'updated' || item?.update?.result === 'noop' ).length ?? 0;
|
|
2528
2763
|
|
|
2529
|
-
|
|
2764
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
2765
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
2766
|
+
const searchBody1 = {
|
|
2767
|
+
size: 10000,
|
|
2768
|
+
query: {
|
|
2769
|
+
bool: {
|
|
2770
|
+
must: [
|
|
2771
|
+
{
|
|
2772
|
+
terms: {
|
|
2773
|
+
'_id': docIds,
|
|
2774
|
+
},
|
|
2775
|
+
},
|
|
2776
|
+
{
|
|
2777
|
+
term: {
|
|
2778
|
+
isParent: false,
|
|
2779
|
+
},
|
|
2780
|
+
},
|
|
2781
|
+
],
|
|
2782
|
+
},
|
|
2783
|
+
},
|
|
2784
|
+
};
|
|
2785
|
+
|
|
2786
|
+
const getSearchResp = await getOpenSearchData(
|
|
2787
|
+
openSearch.revop,
|
|
2788
|
+
searchBody1,
|
|
2789
|
+
);
|
|
2790
|
+
|
|
2530
2791
|
|
|
2792
|
+
const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0 ? getSearchResp?.body?.hits?.hits : [];
|
|
2793
|
+
const logs = {
|
|
2794
|
+
type: inputData.type,
|
|
2795
|
+
storeId: taggedImages?.[0]?._source?.storeId,
|
|
2796
|
+
dateString: taggedImages?.[0]?._source?.dateString,
|
|
2797
|
+
category: taggedImages?.[0]?._source?.revopsType || '',
|
|
2798
|
+
taggedImages: taggedImages,
|
|
2799
|
+
status: inputData?.status,
|
|
2800
|
+
createdByEmail: req?.user?.email,
|
|
2801
|
+
createdByUserName: req?.user?.userName,
|
|
2802
|
+
createdByRole: req?.user?.role,
|
|
2803
|
+
message: inputData.comments || '',
|
|
2804
|
+
createdAt: new Date(),
|
|
2805
|
+
};
|
|
2806
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
2807
|
+
}
|
|
2531
2808
|
return res.sendSuccess( { updated: updatedCount } );
|
|
2532
2809
|
} catch ( error ) {
|
|
2533
2810
|
const err = error.message;
|
|
@@ -2536,3 +2813,249 @@ export async function updateTempStatus( req, res ) {
|
|
|
2536
2813
|
}
|
|
2537
2814
|
}
|
|
2538
2815
|
|
|
2816
|
+
export async function updateUserTicketStatus( req, res ) {
|
|
2817
|
+
try {
|
|
2818
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2819
|
+
const { storeId, dateString } = req.body || {};
|
|
2820
|
+
|
|
2821
|
+
if ( !storeId || !dateString ) {
|
|
2822
|
+
return res.sendError( 'storeId and dateString are required', 400 );
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2826
|
+
|
|
2827
|
+
// Fetch existing ticket so we can validate mappingInfo state
|
|
2828
|
+
const existingDoc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
2829
|
+
const ticketSource = existingDoc?.body?._source;
|
|
2830
|
+
|
|
2831
|
+
if ( !ticketSource ) {
|
|
2832
|
+
return res.sendError( 'Ticket not found', 404 );
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
const mappingInfo = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [];
|
|
2836
|
+
if ( mappingInfo.length === 0 ) {
|
|
2837
|
+
return res.sendError( 'mappingInfo is missing for this ticket', 400 );
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
const lastIndex = mappingInfo.length - 1;
|
|
2841
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
2842
|
+
|
|
2843
|
+
if ( !lastEntry ) {
|
|
2844
|
+
return res.sendError( 'Unable to determine current ticket status', 400 );
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
if ( String( lastEntry.status ).toLowerCase() !== 'open' ) {
|
|
2848
|
+
return res.sendError( 'Ticket is already picked by another user', 409 );
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
const currentTime = new Date();
|
|
2852
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
2853
|
+
updatedMappingInfo[lastIndex] = {
|
|
2854
|
+
...lastEntry,
|
|
2855
|
+
status: 'In-Progress',
|
|
2856
|
+
updatedAt: currentTime,
|
|
2857
|
+
createdByRole: req?.user?.role || '',
|
|
2858
|
+
createdByEmail: req?.user?.email || '',
|
|
2859
|
+
createdByUserName: req?.user?.userName || '',
|
|
2860
|
+
};
|
|
2861
|
+
|
|
2862
|
+
const updatePayload = {
|
|
2863
|
+
doc: {
|
|
2864
|
+
status: 'In-Progress',
|
|
2865
|
+
mappingInfo: updatedMappingInfo,
|
|
2866
|
+
updatedAt: currentTime,
|
|
2867
|
+
},
|
|
2868
|
+
};
|
|
2869
|
+
|
|
2870
|
+
const updateResult = await updateOpenSearchData(
|
|
2871
|
+
openSearch.footfallDirectory,
|
|
2872
|
+
docId,
|
|
2873
|
+
updatePayload,
|
|
2874
|
+
);
|
|
2875
|
+
|
|
2876
|
+
|
|
2877
|
+
if ( !updateResult || !( updateResult.statusCode === 200 || updateResult.statusCode === 201 ) ) {
|
|
2878
|
+
return res.sendError( 'Failed to update ticket status', 500 );
|
|
2879
|
+
}
|
|
2880
|
+
return res.sendSuccess( 'Ticket status updated successfully' );
|
|
2881
|
+
} catch ( error ) {
|
|
2882
|
+
const err = error.message;
|
|
2883
|
+
logger.info( { error: err, function: 'updateUserTicketStatus' } );
|
|
2884
|
+
return res.sendError( err, 500 );
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
export async function multiCloseTicket( req, res ) {
|
|
2889
|
+
try {
|
|
2890
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2891
|
+
const inputData = req.body;
|
|
2892
|
+
|
|
2893
|
+
// inputData structure should include an array of items to close
|
|
2894
|
+
// Accept both array or single ticket update
|
|
2895
|
+
const tickets = Array.isArray( inputData.ticketList ) ? inputData.ticketList : [ inputData.ticketList ];
|
|
2896
|
+
// const mode = inputData.mode || '';
|
|
2897
|
+
|
|
2898
|
+
if ( !tickets.length ) {
|
|
2899
|
+
return res.sendError( 'No tickets provided', 400 );
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
const results = [];
|
|
2903
|
+
for ( const ticket of tickets ) {
|
|
2904
|
+
const { storeId, dateString } = ticket || {};
|
|
2905
|
+
if ( !storeId || !dateString ) {
|
|
2906
|
+
results.push( { storeId, dateString, success: false, error: 'Missing storeId or dateString' } );
|
|
2907
|
+
continue;
|
|
2908
|
+
}
|
|
2909
|
+
// 1. Update the ticket document in footfallDirectory index
|
|
2910
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2911
|
+
|
|
2912
|
+
// Fetch existing doc to update mappingInfo
|
|
2913
|
+
|
|
2914
|
+
const doc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
2915
|
+
|
|
2916
|
+
|
|
2917
|
+
const ticketSource = doc?.body?._source;
|
|
2918
|
+
if ( !ticketSource || !ticketSource.mappingInfo ) {
|
|
2919
|
+
results.push( { storeId, dateString, success: false, error: 'Ticket or mappingInfo missing' } );
|
|
2920
|
+
continue;
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
let mappingInfoArray = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [ ticketSource.mappingInfo ];
|
|
2924
|
+
// Find all mappingInfo items matching type 'approve'
|
|
2925
|
+
let updated = false;
|
|
2926
|
+
let newMappingInfoArray = mappingInfoArray.map( ( mi, i ) => {
|
|
2927
|
+
if ( mi?.type === 'approve' && mi?.status === 'Open' ) {
|
|
2928
|
+
updated = true;
|
|
2929
|
+
return {
|
|
2930
|
+
...mi,
|
|
2931
|
+
status: 'Approver-Closed',
|
|
2932
|
+
status: 'Closed', // following the user's instruction to set sttaus property (assumed typo, but explicitly used)
|
|
2933
|
+
updatedAt: new Date(),
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
return mi;
|
|
2937
|
+
} );
|
|
2938
|
+
|
|
2939
|
+
if ( !updated ) {
|
|
2940
|
+
// None found to update
|
|
2941
|
+
results.push( { storeId, dateString, success: false, error: `coudn't approve this store` } );
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
// Write update to footfallDirectory
|
|
2946
|
+
const ticketUpdatePayload = {
|
|
2947
|
+
doc: {
|
|
2948
|
+
mappingInfo: newMappingInfoArray,
|
|
2949
|
+
status: 'Approver-Closed', // status updated at top level as well
|
|
2950
|
+
updatedAt: new Date(),
|
|
2951
|
+
},
|
|
2952
|
+
};
|
|
2953
|
+
let ticketUpdateResult;
|
|
2954
|
+
try {
|
|
2955
|
+
ticketUpdateResult = await updateOpenSearchData( openSearch.footfallDirectory, docId, ticketUpdatePayload );
|
|
2956
|
+
if ( !( ticketUpdateResult && ( ticketUpdateResult.statusCode === 200 || ticketUpdateResult.statusCode === 201 ) ) ) {
|
|
2957
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2960
|
+
} catch ( err ) {
|
|
2961
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
2962
|
+
continue;
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
|
|
2966
|
+
// For each ticket, update actions array for all matching image docs (storeId & dateString)
|
|
2967
|
+
|
|
2968
|
+
// Query to find all matching docs in revopTagging index with storeId and dateString
|
|
2969
|
+
const revopImageQuery = {
|
|
2970
|
+
size: 10000, // assume there won't be more than 1000 images per ticket
|
|
2971
|
+
query: {
|
|
2972
|
+
bool: {
|
|
2973
|
+
must: [
|
|
2974
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
2975
|
+
{ term: { dateString: dateString } },
|
|
2976
|
+
],
|
|
2977
|
+
},
|
|
2978
|
+
},
|
|
2979
|
+
};
|
|
2980
|
+
|
|
2981
|
+
// Fetch matching docs
|
|
2982
|
+
const searchRes = await getOpenSearchData( openSearch.revop, revopImageQuery );
|
|
2983
|
+
const revopHits = searchRes?.body?.hits?.hits || [];
|
|
2984
|
+
|
|
2985
|
+
// Optimized: batch and parallelize image-doc updates to avoid long sequential waits
|
|
2986
|
+
const BATCH_SIZE = 100;
|
|
2987
|
+
const now = new Date();
|
|
2988
|
+
|
|
2989
|
+
for ( let i = 0; i < revopHits.length; i += BATCH_SIZE ) {
|
|
2990
|
+
const batch = revopHits.slice( i, i + BATCH_SIZE );
|
|
2991
|
+
|
|
2992
|
+
const updatePromises = batch.map( async ( hit ) => {
|
|
2993
|
+
const imageDocId = hit._id;
|
|
2994
|
+
const imageSource = hit._source || {};
|
|
2995
|
+
const imageActionsArray = Array.isArray( imageSource.actions ) ? [ ...imageSource.actions ] : [];
|
|
2996
|
+
|
|
2997
|
+
imageActionsArray.push( {
|
|
2998
|
+
actionType: 'approve',
|
|
2999
|
+
action: 'approved',
|
|
3000
|
+
} );
|
|
3001
|
+
|
|
3002
|
+
const imageUpdatePayload = {
|
|
3003
|
+
doc: {
|
|
3004
|
+
actions: imageActionsArray,
|
|
3005
|
+
updatedAt: now,
|
|
3006
|
+
},
|
|
3007
|
+
};
|
|
3008
|
+
|
|
3009
|
+
return updateOpenSearchData( openSearch.revop, imageDocId, imageUpdatePayload );
|
|
3010
|
+
} );
|
|
3011
|
+
|
|
3012
|
+
// Wait for this batch to finish before starting the next one
|
|
3013
|
+
await Promise.all( updatePromises );
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
if ( results && results?.length > 0 ) {
|
|
3017
|
+
return res.sendError( results, 500 );
|
|
3018
|
+
}
|
|
3019
|
+
// Return batch summary
|
|
3020
|
+
} catch ( error ) {
|
|
3021
|
+
const err = error.message;
|
|
3022
|
+
logger.info( { error: err, function: 'multiCloseTicket' } );
|
|
3023
|
+
return res.sendError( err, 500 );
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
|
|
3028
|
+
export async function checkTicketExists( req, res ) {
|
|
3029
|
+
try {
|
|
3030
|
+
let inputData = req.body;
|
|
3031
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
3032
|
+
let findQuery = {
|
|
3033
|
+
size: 10000,
|
|
3034
|
+
query: {
|
|
3035
|
+
bool: {
|
|
3036
|
+
must: [
|
|
3037
|
+
{
|
|
3038
|
+
term: {
|
|
3039
|
+
'storeId.keyword': inputData.storeId,
|
|
3040
|
+
},
|
|
3041
|
+
},
|
|
3042
|
+
{
|
|
3043
|
+
term: {
|
|
3044
|
+
'dateString': inputData.dateString,
|
|
3045
|
+
},
|
|
3046
|
+
},
|
|
3047
|
+
],
|
|
3048
|
+
},
|
|
3049
|
+
},
|
|
3050
|
+
};
|
|
3051
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
3052
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
3053
|
+
|
|
3054
|
+
|
|
3055
|
+
res.sendSuccess( Ticket );
|
|
3056
|
+
} catch ( error ) {
|
|
3057
|
+
const err = error.message;
|
|
3058
|
+
logger.info( { error: err, function: 'checkTicketExists' } );
|
|
3059
|
+
return res.sendError( err, 500 );
|
|
3060
|
+
}
|
|
3061
|
+
}
|