tango-app-api-infra 3.9.5-vms.6 → 3.9.5-vms.61
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,5 +1,5 @@
|
|
|
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';
|
|
@@ -7,6 +7,7 @@ import dayjs from 'dayjs';
|
|
|
7
7
|
import utc from 'dayjs/plugin/utc.js';
|
|
8
8
|
import timezone from 'dayjs/plugin/timezone.js';
|
|
9
9
|
import { findUser } from '../services/user.service.js';
|
|
10
|
+
import { findOneClient } from '../services/client.service.js';
|
|
10
11
|
|
|
11
12
|
dayjs.extend( utc );
|
|
12
13
|
dayjs.extend( timezone );
|
|
@@ -60,6 +61,355 @@ 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
|
+
if ( Ticket.length === 0 ) {
|
|
161
|
+
return res.sendError( 'Ticket not found', 400 );
|
|
162
|
+
}
|
|
163
|
+
const getTicket = {
|
|
164
|
+
size: 10000,
|
|
165
|
+
query: {
|
|
166
|
+
bool: {
|
|
167
|
+
must: [
|
|
168
|
+
{
|
|
169
|
+
term: {
|
|
170
|
+
'storeId.keyword': inputData.storeId,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
term: {
|
|
175
|
+
'dateString': inputData.dateString,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
if ( Ticket[0]?._source?.type != 'internal' ) {
|
|
184
|
+
getTicket.query.bool.must.push( {
|
|
185
|
+
term: {
|
|
186
|
+
'mappingInfo.type': 'tangoreview',
|
|
187
|
+
},
|
|
188
|
+
} );
|
|
189
|
+
}
|
|
190
|
+
const getFootfallticketData = await getOpenSearchData( openSearch.footfallDirectory, getTicket );
|
|
191
|
+
const ticketData = getFootfallticketData?.body?.hits?.hits;
|
|
192
|
+
|
|
193
|
+
if ( !ticketData || ticketData?.length == 0 ) {
|
|
194
|
+
return res.sendError( 'You don’t have any tagged images right now', 400 );
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const record = {
|
|
198
|
+
|
|
199
|
+
status: 'Closed',
|
|
200
|
+
revicedFootfall: inputData.mappingInfo?.[0]?.revicedFootfall,
|
|
201
|
+
revicedPerc: inputData.mappingInfo?.[0]?.revicedPerc,
|
|
202
|
+
mappingInfo: ticketData?.[0]?._source?.mappingInfo,
|
|
203
|
+
createdByEmail: req?.user?.email,
|
|
204
|
+
createdByUserName: req?.user?.userName,
|
|
205
|
+
createdByRole: req?.user?.role,
|
|
206
|
+
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
// Retrieve client footfallDirectoryConfigs revision
|
|
211
|
+
let isAutoCloseEnable = getConfig.footfallDirectoryConfigs.isAutoCloseEnable;
|
|
212
|
+
let autoCloseAccuracy = getConfig?.footfallDirectoryConfigs?.autoCloseAccuracy;
|
|
213
|
+
|
|
214
|
+
const getNumber = autoCloseAccuracy.split( '%' )[0];
|
|
215
|
+
|
|
216
|
+
let autoCloseAccuracyValue = parseFloat( ( autoCloseAccuracy || getNumber ).replace( '%', '' ) );
|
|
217
|
+
let revisedPercentage = inputData.mappingInfo?.revicedPerc;
|
|
218
|
+
const revised = Number( revisedPercentage?.split( '%' )[0] );
|
|
219
|
+
const tangoReview = Number( getConfig?.footfallDirectoryConfigs?.tangoReview?.split( '%' )[0] );
|
|
220
|
+
// If autoclose enabled and revisedPercentage meets/exceeds threshold, close ticket and skip revision
|
|
221
|
+
if (
|
|
222
|
+
isAutoCloseEnable === true &&
|
|
223
|
+
revisedPercentage >= autoCloseAccuracyValue
|
|
224
|
+
) {
|
|
225
|
+
record.status = 'Closed';
|
|
226
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
227
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
228
|
+
const temp = record.mappingInfo
|
|
229
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
230
|
+
.map( ( item ) => ( {
|
|
231
|
+
...item,
|
|
232
|
+
|
|
233
|
+
mode: inputData.mappingInfo?.mode,
|
|
234
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
235
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
236
|
+
count: inputData.mappingInfo?.count,
|
|
237
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
238
|
+
status: 'Closed',
|
|
239
|
+
createdByEmail: req?.user?.email,
|
|
240
|
+
createdByUserName: req?.user?.userName,
|
|
241
|
+
createdByRole: req?.user?.role,
|
|
242
|
+
} ) );
|
|
243
|
+
|
|
244
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
245
|
+
...temp ];
|
|
246
|
+
// If updating the mapping config to mark [i].status as 'Closed'
|
|
247
|
+
// Make sure all relevant mappingInfo items of type 'approve' are set to status 'Closed'
|
|
248
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
249
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
250
|
+
return {
|
|
251
|
+
...item,
|
|
252
|
+
status: 'Closed',
|
|
253
|
+
};
|
|
254
|
+
} );
|
|
255
|
+
}
|
|
256
|
+
// If no review mapping existed, push a new one
|
|
257
|
+
if ( record.mappingInfo.length === 0 ) {
|
|
258
|
+
record.mappingInfo.push( {
|
|
259
|
+
type: 'tangoreview',
|
|
260
|
+
mode: inputData.mappingInfo?.mode,
|
|
261
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
262
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
263
|
+
count: inputData.mappingInfo?.count,
|
|
264
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
265
|
+
status: 'Closed',
|
|
266
|
+
createdByEmail: req?.user?.email,
|
|
267
|
+
createdByUserName: req?.user?.userName,
|
|
268
|
+
createdByRole: req?.user?.role,
|
|
269
|
+
} );
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
record.mappingInfo.push(
|
|
273
|
+
{
|
|
274
|
+
type: 'finalRevision',
|
|
275
|
+
mode: inputData.mappingInfo?.mode,
|
|
276
|
+
revicedFootfall: revisedFootfall,
|
|
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
|
+
createdAt: new Date(),
|
|
285
|
+
},
|
|
286
|
+
);
|
|
287
|
+
} else if ( revised < tangoReview ) {
|
|
288
|
+
// If ticket is closed, do not proceed with revision mapping
|
|
289
|
+
|
|
290
|
+
record.status = 'Closed - Accuracy Issue';
|
|
291
|
+
// Only keep or modify mappingInfo items with type "review"
|
|
292
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
293
|
+
const temp = record.mappingInfo
|
|
294
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
295
|
+
.map( ( item ) => ( {
|
|
296
|
+
...item,
|
|
297
|
+
mode: inputData.mappingInfo?.mode,
|
|
298
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
299
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
300
|
+
count: inputData.mappingInfo?.count,
|
|
301
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
302
|
+
status: 'Closed',
|
|
303
|
+
createdByEmail: req?.user?.email,
|
|
304
|
+
createdByUserName: req?.user?.userName,
|
|
305
|
+
createdByRole: req?.user?.role,
|
|
306
|
+
} ) );
|
|
307
|
+
|
|
308
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
309
|
+
...temp ];
|
|
310
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
311
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
312
|
+
return {
|
|
313
|
+
...item,
|
|
314
|
+
status: 'Closed',
|
|
315
|
+
};
|
|
316
|
+
} );
|
|
317
|
+
}
|
|
318
|
+
// If no review mapping existed, push a new one
|
|
319
|
+
if ( record.mappingInfo.length === 0 ) {
|
|
320
|
+
record.mappingInfo.push( {
|
|
321
|
+
type: 'tangoreview',
|
|
322
|
+
mode: inputData.mappingInfo?.mode,
|
|
323
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
324
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
325
|
+
count: inputData.mappingInfo?.count,
|
|
326
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
327
|
+
status: 'Closed',
|
|
328
|
+
createdByEmail: req?.user?.email,
|
|
329
|
+
createdByUserName: req?.user?.userName,
|
|
330
|
+
createdByRole: req?.user?.role,
|
|
331
|
+
} );
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
336
|
+
const temp = record.mappingInfo
|
|
337
|
+
.filter( ( item ) => item.type === 'tangoreview' )
|
|
338
|
+
.map( ( item ) => ( {
|
|
339
|
+
...item,
|
|
340
|
+
mode: inputData.mappingInfo?.mode,
|
|
341
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
342
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
343
|
+
count: inputData.mappingInfo?.count,
|
|
344
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
345
|
+
status: 'Closed',
|
|
346
|
+
createdByEmail: req?.user?.email,
|
|
347
|
+
createdByUserName: req?.user?.userName,
|
|
348
|
+
createdByRole: req?.user?.role,
|
|
349
|
+
} ) );
|
|
350
|
+
|
|
351
|
+
record.mappingInfo = [ ...ticketData?.[0]?._source?.mappingInfo.slice( 0, -1 ),
|
|
352
|
+
...temp ];
|
|
353
|
+
if ( Array.isArray( record.mappingInfo ) ) {
|
|
354
|
+
record.mappingInfo = record.mappingInfo.map( ( item ) => {
|
|
355
|
+
return {
|
|
356
|
+
...item,
|
|
357
|
+
status: 'Closed',
|
|
358
|
+
};
|
|
359
|
+
} );
|
|
360
|
+
}
|
|
361
|
+
// If no review mapping existed, push a new one
|
|
362
|
+
if ( record.mappingInfo.length === 0 ) {
|
|
363
|
+
record.mappingInfo.push( {
|
|
364
|
+
type: 'tangoreview',
|
|
365
|
+
mode: inputData.mappingInfo?.mode,
|
|
366
|
+
revicedFootfall: inputData.mappingInfo?.revicedFootfall,
|
|
367
|
+
revicedPerc: inputData.mappingInfo?.revicedPerc,
|
|
368
|
+
count: inputData.mappingInfo?.count,
|
|
369
|
+
revisedDetail: inputData.mappingInfo?.revisedDetail,
|
|
370
|
+
status: 'Closed',
|
|
371
|
+
createdByEmail: req?.user?.email,
|
|
372
|
+
createdByUserName: req?.user?.userName,
|
|
373
|
+
createdByRole: req?.user?.role,
|
|
374
|
+
} );
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
record.mappingInfo.push(
|
|
378
|
+
{
|
|
379
|
+
type: 'finalRevision',
|
|
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
|
+
createdAt: new Date(),
|
|
390
|
+
},
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
395
|
+
if ( inputData.ticketType === 'internal' ) {
|
|
396
|
+
id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
400
|
+
|
|
401
|
+
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
402
|
+
return res.sendSuccess( 'Ticket closed successfully' );
|
|
403
|
+
} else {
|
|
404
|
+
return res.sendError( 'Internal Server Error', 500 );
|
|
405
|
+
}
|
|
406
|
+
} catch ( error ) {
|
|
407
|
+
const err = error.message || 'Internal Server Error';
|
|
408
|
+
logger.error( { error: err, funtion: 'tangoReviewTicket' } );
|
|
409
|
+
return res.sendError( err, 500 );
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
63
413
|
async function bulkUpdateStatusToPending( indexName, inputData ) {
|
|
64
414
|
const bulkBody = [];
|
|
65
415
|
|
|
@@ -195,7 +545,6 @@ export async function ticketSummary1( req, res ) {
|
|
|
195
545
|
|
|
196
546
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
197
547
|
const aggs = getData?.body?.aggregations;
|
|
198
|
-
logger.info( { aggs: aggs, body: getData?.body } );
|
|
199
548
|
|
|
200
549
|
const result = {
|
|
201
550
|
totalTickets: aggs.totalTicketCount.value,
|
|
@@ -220,10 +569,10 @@ export async function ticketSummary( req, res ) {
|
|
|
220
569
|
try {
|
|
221
570
|
let result = '';
|
|
222
571
|
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 ) ) ) );
|
|
572
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
224
573
|
|
|
225
|
-
if ( req.user.userType =='tango' ) {
|
|
226
|
-
result ={
|
|
574
|
+
if ( req.user.userType == 'tango' ) {
|
|
575
|
+
result = {
|
|
227
576
|
totalTickets: 0,
|
|
228
577
|
averageAccuracyOverAll: 0,
|
|
229
578
|
openTickets: 0,
|
|
@@ -234,30 +583,30 @@ export async function ticketSummary( req, res ) {
|
|
|
234
583
|
ticketAccuracyBelow: '0%',
|
|
235
584
|
};
|
|
236
585
|
} 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';
|
|
586
|
+
result = req.user.role === 'superadmin' ?
|
|
587
|
+
{
|
|
588
|
+
totalTickets: 0,
|
|
589
|
+
openTickets: 0,
|
|
590
|
+
inprogress: 0,
|
|
591
|
+
closedTickets: 0,
|
|
592
|
+
dueToday: 0,
|
|
593
|
+
Expired: 0,
|
|
594
|
+
underTangoReview: 0,
|
|
595
|
+
avgTicket: '0%',
|
|
596
|
+
avgAccuracy: '0%',
|
|
597
|
+
} :
|
|
598
|
+
req.user.role === 'user' ? 'NA' :
|
|
599
|
+
ticketsFeature ?
|
|
600
|
+
{
|
|
601
|
+
totalTickets: 0,
|
|
602
|
+
openTickets: 0,
|
|
603
|
+
inprogress: 0,
|
|
604
|
+
closedTickets: 0,
|
|
605
|
+
dueToday: 0,
|
|
606
|
+
Expired: 0,
|
|
607
|
+
avgTicket: '0%',
|
|
608
|
+
avgAccuracy: '0%',
|
|
609
|
+
} : 'NA';
|
|
261
610
|
}
|
|
262
611
|
|
|
263
612
|
return res.sendSuccess( { result: result } );
|
|
@@ -293,7 +642,7 @@ export async function ticketList1( req, res ) {
|
|
|
293
642
|
terms: {
|
|
294
643
|
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
295
644
|
inputData.clientId :
|
|
296
|
-
|
|
645
|
+
[ inputData.clientId ],
|
|
297
646
|
},
|
|
298
647
|
},
|
|
299
648
|
];
|
|
@@ -470,12 +819,12 @@ export async function ticketList1( req, res ) {
|
|
|
470
819
|
'Ticket raised on': dayjs( item._source.createdAt ).format( 'DD MMM, YYYY' ),
|
|
471
820
|
'Issue Date': dayjs( item._source.dateString ).format( 'DD MMM, YYYY' ),
|
|
472
821
|
'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'
|
|
822
|
+
'Duplicates': item?._source?.status === 'closed' ? item._source.duplicateACCount : item._source.duplicateCount,
|
|
823
|
+
'Employee/Staff': item?._source?.status === 'closed' ? item._source.employeeACCount : item._source.employeeCount,
|
|
824
|
+
'HouseKeeping': item?._source?.status === 'closed' ? item._source.houseKeepingACCount : item._source.houseKeepingCount,
|
|
825
|
+
'Junk': item?._source?.status === 'closed' ? ( item._source.junkACCount || 0 ) : ( item._source.junkCount || 0 ),
|
|
826
|
+
'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 ) ),
|
|
827
|
+
'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
828
|
'Status': item._source.status,
|
|
480
829
|
} );
|
|
481
830
|
}
|
|
@@ -491,671 +840,296 @@ export async function ticketList1( req, res ) {
|
|
|
491
840
|
|
|
492
841
|
export async function ticketList( req, res ) {
|
|
493
842
|
try {
|
|
494
|
-
|
|
495
|
-
const inputData= req.query;
|
|
843
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
844
|
+
const inputData = req.query;
|
|
496
845
|
const userInfo = req.user;
|
|
497
|
-
const
|
|
846
|
+
const limit = inputData?.limit || 10;
|
|
847
|
+
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
848
|
+
inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
|
|
498
849
|
|
|
499
|
-
|
|
500
|
-
result =inputData.tangotyep == 'store'?
|
|
850
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
501
851
|
|
|
852
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
502
853
|
|
|
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
|
-
},
|
|
854
|
+
const searchQuery = {
|
|
714
855
|
|
|
715
|
-
|
|
716
|
-
|
|
856
|
+
size: limit, // or use parseInt(req.query.limit) for dynamic
|
|
857
|
+
from: offset, // or use parseInt(req.query.offset) for dynamic
|
|
858
|
+
sort: [ { 'createdAt': { order: 'desc' } } ],
|
|
717
859
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
860
|
+
query: {
|
|
861
|
+
bool: {
|
|
862
|
+
must: [
|
|
863
|
+
{
|
|
864
|
+
'range': {
|
|
865
|
+
'dateString': {
|
|
866
|
+
'gte': inputData.fromDate,
|
|
867
|
+
'lte': inputData.toDate,
|
|
868
|
+
'format': 'yyyy-MM-dd',
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
terms: {
|
|
874
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
875
|
+
inputData.clientId :
|
|
876
|
+
[ inputData.clientId ],
|
|
877
|
+
},
|
|
878
|
+
|
|
879
|
+
},
|
|
880
|
+
],
|
|
732
881
|
},
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
882
|
+
},
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
if ( inputData.sortBy ) {
|
|
886
|
+
let sortOrder = inputData.sortOrder === 1? 'asc': 'desc';
|
|
887
|
+
|
|
888
|
+
// Remove default sort so we don't duplicate/conflict
|
|
889
|
+
// INSERT_YOUR_CODE
|
|
890
|
+
// If sortBy is present, check if the field needs ".keyword" (for string fields like storeName, storeId, ticketId)
|
|
891
|
+
// This avoids OpenSearch errors about sorting on text fields.
|
|
892
|
+
const stringKeywordFields = [ 'storeName', 'storeId', 'ticketId', 'status', 'type', 'clientId' ];
|
|
893
|
+
let sortField = inputData.sortBy == 'footfall'? 'footfallCount' :inputData.sortBy == 'issueDate'?'dateString':inputData.sortBy;
|
|
894
|
+
if ( stringKeywordFields.includes( sortField ) ) {
|
|
895
|
+
sortField = `${sortField}.keyword`;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Remove default sort so we don't duplicate/conflict
|
|
899
|
+
searchQuery.sort = [
|
|
900
|
+
{ [sortField]: { order: sortOrder } },
|
|
901
|
+
];
|
|
902
|
+
}
|
|
903
|
+
// Example: Filtering by storeId if present in the query
|
|
904
|
+
if ( inputData.storeId ) {
|
|
905
|
+
inputData.storeId = inputData?.storeId?.split( ',' );
|
|
906
|
+
searchQuery.query.bool.must.push( {
|
|
907
|
+
terms: {
|
|
908
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
909
|
+
inputData.storeId :
|
|
910
|
+
[ inputData.storeId ],
|
|
747
911
|
},
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
type: 'store',
|
|
756
|
-
storeRevisedAccuracy: '97%',
|
|
757
|
-
reviewerRevisedAccuracy: '96%',
|
|
758
|
-
approverRevisedAccuracy: '97%',
|
|
759
|
-
status: 'Closed',
|
|
760
|
-
tangoStatus: 'In-Progress',
|
|
912
|
+
} );
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if ( req?.user?.userType == 'tango' && inputData.tangoType !== 'internal' ) {
|
|
916
|
+
searchQuery.query.bool.must.push( {
|
|
917
|
+
term: {
|
|
918
|
+
'mappingInfo.type': 'tangoreview',
|
|
761
919
|
},
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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',
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
term: {
|
|
923
|
+
'type.keyword': 'store',
|
|
776
924
|
},
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
type: '
|
|
785
|
-
|
|
786
|
-
reviewerRevisedAccuracy: '95%',
|
|
787
|
-
approverRevisedAccuracy: '96%',
|
|
788
|
-
tangoRevisedAccuracy: '95%',
|
|
789
|
-
status: 'Closed',
|
|
790
|
-
tangoStatus: 'In-Progress',
|
|
925
|
+
},
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
);
|
|
929
|
+
} else if ( inputData.tangoType !== 'internal' ) {
|
|
930
|
+
searchQuery.query.bool.must.push( {
|
|
931
|
+
term: {
|
|
932
|
+
'mappingInfo.type': ticketsFeature ? 'review' : ticketsApproveFeature ?
|
|
933
|
+
'approve' :'tagging',
|
|
791
934
|
},
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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',
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
term: {
|
|
938
|
+
'type.keyword': 'store',
|
|
806
939
|
},
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
storeRevisedAccuracy: '97%',
|
|
816
|
-
reviewerRevisedAccuracy: '96%',
|
|
817
|
-
approverRevisedAccuracy: '97%',
|
|
818
|
-
tangoRevisedAccuracy: '96%',
|
|
819
|
-
status: 'Open-Accuracy Issue',
|
|
820
|
-
tangoStatus: 'Open',
|
|
940
|
+
} );
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if ( inputData?.permissionType ) {
|
|
944
|
+
searchQuery.query.bool.must.push( {
|
|
945
|
+
term: {
|
|
946
|
+
'mappingInfo.type': inputData?.permissionType == 'review' ? 'review' :
|
|
947
|
+
'approve',
|
|
821
948
|
},
|
|
949
|
+
} );
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
953
|
+
searchQuery.query.bool['should'] =[];
|
|
954
|
+
searchQuery.query.bool.should=[
|
|
955
|
+
|
|
822
956
|
{
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
footfall: 900,
|
|
829
|
-
type: 'internal',
|
|
830
|
-
storeRevisedAccuracy: '95%',
|
|
831
|
-
reviewerRevisedAccuracy: '94%',
|
|
832
|
-
approverRevisedAccuracy: '95%',
|
|
833
|
-
tangoRevisedAccuracy: '95%',
|
|
834
|
-
status: 'Closed',
|
|
835
|
-
tangoStatus: 'Closed',
|
|
957
|
+
'wildcard': {
|
|
958
|
+
'storeName.keyword': {
|
|
959
|
+
'value': `*${inputData.searchValue}*`,
|
|
960
|
+
},
|
|
961
|
+
},
|
|
836
962
|
},
|
|
837
963
|
{
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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',
|
|
964
|
+
'wildcard': {
|
|
965
|
+
'storeId.keyword': {
|
|
966
|
+
'value': `*${inputData.searchValue}*`,
|
|
967
|
+
},
|
|
968
|
+
},
|
|
851
969
|
},
|
|
852
970
|
{
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
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',
|
|
971
|
+
'wildcard': {
|
|
972
|
+
'ticketId.keyword': {
|
|
973
|
+
'value': `*${inputData.searchValue}*`,
|
|
974
|
+
},
|
|
975
|
+
},
|
|
866
976
|
},
|
|
977
|
+
|
|
978
|
+
|
|
867
979
|
];
|
|
980
|
+
searchQuery.query.bool['minimum_should_match'] = 1;
|
|
981
|
+
}
|
|
982
|
+
// You can add more filters as needed
|
|
983
|
+
const searchResult = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
984
|
+
logger.info( { searchResult } );
|
|
985
|
+
const count = searchResult?.body?.hits?.total?.value || 0;
|
|
986
|
+
|
|
987
|
+
if ( count === 0 ) {
|
|
988
|
+
return res.sendError( 'no data found', 204 );
|
|
989
|
+
}
|
|
990
|
+
const ticketListData = searchResult?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
991
|
+
|
|
992
|
+
let temp = [];
|
|
993
|
+
if ( req.user.userType == 'tango' ) {
|
|
994
|
+
if ( inputData.tangotype == 'store' ) {
|
|
995
|
+
for ( let item of ticketListData ) {
|
|
996
|
+
temp.push( {
|
|
997
|
+
|
|
998
|
+
ticketId: item?.ticketId,
|
|
999
|
+
storeId: item?.storeId,
|
|
1000
|
+
storeName: item?.storeName,
|
|
1001
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1002
|
+
issueDate: item?.dateString,
|
|
1003
|
+
dueDate: '',
|
|
1004
|
+
footfall: item?.footfallCount,
|
|
1005
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1006
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1007
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1008
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1009
|
+
status: item?.status,
|
|
1010
|
+
|
|
1011
|
+
} );
|
|
1012
|
+
}
|
|
1013
|
+
} else {
|
|
1014
|
+
for ( let item of ticketListData ) {
|
|
1015
|
+
temp.push( {
|
|
1016
|
+
|
|
1017
|
+
ticketId: item?.ticketId,
|
|
1018
|
+
storeId: item?.storeId,
|
|
1019
|
+
storeName: item?.storeName,
|
|
1020
|
+
|
|
1021
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1022
|
+
issueDate: item?.dateString,
|
|
1023
|
+
footfall: item?.footfallCount,
|
|
1024
|
+
dueDate: '',
|
|
1025
|
+
type: item?.type || 'store',
|
|
1026
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1027
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1028
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1029
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1030
|
+
status: item?.status,
|
|
1031
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1032
|
+
|
|
1033
|
+
} );
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
868
1036
|
} else {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
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_17635399903490',
|
|
1027
|
-
storeId: '11-1716',
|
|
1028
|
-
storeName: 'LKST1916',
|
|
1029
|
-
ticketRaised: '2025-11-16',
|
|
1030
|
-
issueDate: '2025-11-15',
|
|
1031
|
-
dueDate: 'Due Today',
|
|
1032
|
-
footfall: 213,
|
|
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-16',
|
|
1043
|
-
issueDate: '2025-11-14',
|
|
1044
|
-
dueDate: '2025-11-18',
|
|
1045
|
-
footfall: 90,
|
|
1046
|
-
storeRevisedAccuracy: '90%',
|
|
1047
|
-
reviewerRevisedAccuracy: '--',
|
|
1048
|
-
status: 'Expired',
|
|
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';
|
|
1037
|
+
if ( inputData?.permissionType ==='approve' ) {
|
|
1038
|
+
for ( let item of ticketListData ) {
|
|
1039
|
+
temp.push( {
|
|
1040
|
+
|
|
1041
|
+
ticketId: item?.ticketId,
|
|
1042
|
+
storeId: item?.storeId,
|
|
1043
|
+
storeName: item?.storeName,
|
|
1044
|
+
|
|
1045
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1046
|
+
issueDate: item?.dateString,
|
|
1047
|
+
dueDate: '',
|
|
1048
|
+
footfall: item?.footfallCount,
|
|
1049
|
+
|
|
1050
|
+
type: item.type || 'store',
|
|
1051
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1052
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1053
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1054
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1055
|
+
status: item?.status,
|
|
1056
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1057
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1058
|
+
|
|
1059
|
+
} );
|
|
1060
|
+
}
|
|
1061
|
+
} else if ( inputData?.permissionType ==='review' ) {
|
|
1062
|
+
for ( let item of ticketListData ) {
|
|
1063
|
+
temp.push( {
|
|
1064
|
+
|
|
1065
|
+
ticketId: item?.ticketId,
|
|
1066
|
+
storeId: item?.storeId,
|
|
1067
|
+
storeName: item?.storeName,
|
|
1068
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1069
|
+
issueDate: item?.dateString,
|
|
1070
|
+
footfall: item?.footfallCount,
|
|
1071
|
+
dueDate: '',
|
|
1072
|
+
type: item.type || 'store',
|
|
1073
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1074
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1075
|
+
|
|
1076
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
1077
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1078
|
+
|
|
1079
|
+
} );
|
|
1080
|
+
}
|
|
1081
|
+
} else if ( req.user.role === 'user' ) {
|
|
1082
|
+
temp = [];
|
|
1083
|
+
} else if ( ticketsFeature ) {
|
|
1084
|
+
for ( let item of ticketListData ) {
|
|
1085
|
+
temp.push( {
|
|
1086
|
+
|
|
1087
|
+
ticketId: item?.ticketId,
|
|
1088
|
+
storeId: item?.storeId,
|
|
1089
|
+
storeName: item?.storeName,
|
|
1090
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1091
|
+
issueDate: item?.dateString,
|
|
1092
|
+
footfall: item?.footfallCount,
|
|
1093
|
+
dueDate: '',
|
|
1094
|
+
type: item.type || 'store',
|
|
1095
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1096
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1097
|
+
|
|
1098
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
1099
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1100
|
+
|
|
1101
|
+
} );
|
|
1102
|
+
}
|
|
1103
|
+
} else if ( ticketsApproveFeature ) {
|
|
1104
|
+
for ( let item of ticketListData ) {
|
|
1105
|
+
temp.push( {
|
|
1106
|
+
|
|
1107
|
+
ticketId: item?.ticketId,
|
|
1108
|
+
storeId: item?.storeId,
|
|
1109
|
+
storeName: item?.storeName,
|
|
1110
|
+
|
|
1111
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
1112
|
+
issueDate: item?.dateString,
|
|
1113
|
+
dueDate: '',
|
|
1114
|
+
footfall: item?.footfallCount,
|
|
1115
|
+
|
|
1116
|
+
type: item.type || 'store',
|
|
1117
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
1118
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
1119
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
1120
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1121
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
1122
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
1123
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1124
|
+
|
|
1125
|
+
} );
|
|
1126
|
+
}
|
|
1127
|
+
} else {
|
|
1128
|
+
temp = [];
|
|
1129
|
+
}
|
|
1156
1130
|
}
|
|
1157
1131
|
|
|
1158
|
-
return res.sendSuccess( { result:
|
|
1132
|
+
return res.sendSuccess( { result: temp, count: count } );
|
|
1159
1133
|
} catch ( error ) {
|
|
1160
1134
|
const err = error.message || 'Internal Server Error';
|
|
1161
1135
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -1167,149 +1141,19 @@ export async function getTickets( req, res ) {
|
|
|
1167
1141
|
try {
|
|
1168
1142
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1169
1143
|
const inputData = req.query;
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
logger.info( { inputData: inputData, limit: limit, skip: skip } );
|
|
1174
|
-
let source = [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'junkCount', 'junkACCount', 'comments', 'employee', 'houseKeeping', 'junk', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'approverRole', 'approverUserName', 'approverEmail' ];
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
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' ];
|
|
1175
1147
|
let filter = [
|
|
1176
1148
|
|
|
1177
1149
|
{
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
gte: inputData.fromDate,
|
|
1181
|
-
lte: inputData.toDate,
|
|
1182
|
-
format: 'yyyy-MM-dd',
|
|
1183
|
-
},
|
|
1150
|
+
term: {
|
|
1151
|
+
'ticketId.keyword': inputData.ticketId,
|
|
1184
1152
|
},
|
|
1185
1153
|
},
|
|
1186
1154
|
];
|
|
1187
|
-
if ( inputData?.storeId ) {
|
|
1188
|
-
filter.push(
|
|
1189
|
-
{
|
|
1190
|
-
terms: {
|
|
1191
|
-
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1192
|
-
inputData.storeId :
|
|
1193
|
-
inputData.storeId,
|
|
1194
|
-
},
|
|
1195
|
-
},
|
|
1196
|
-
);
|
|
1197
|
-
}
|
|
1198
|
-
if ( inputData?.dateString ) {
|
|
1199
|
-
filter.push(
|
|
1200
|
-
{
|
|
1201
|
-
term: {
|
|
1202
|
-
'dateString': inputData.dateString,
|
|
1203
|
-
},
|
|
1204
|
-
},
|
|
1205
|
-
);
|
|
1206
|
-
}
|
|
1207
|
-
if ( inputData.status ) {
|
|
1208
|
-
filter.push(
|
|
1209
|
-
{
|
|
1210
|
-
term: {
|
|
1211
|
-
'status.keyword': inputData.status,
|
|
1212
|
-
},
|
|
1213
|
-
},
|
|
1214
|
-
);
|
|
1215
|
-
}
|
|
1216
|
-
if (
|
|
1217
|
-
inputData.revopsType
|
|
1218
|
-
) {
|
|
1219
|
-
inputData.revopsType === 'employee' ?
|
|
1220
|
-
filter.push( {
|
|
1221
|
-
range: {
|
|
1222
|
-
employeeCount: {
|
|
1223
|
-
gt: 0,
|
|
1224
|
-
},
|
|
1225
|
-
},
|
|
1226
|
-
} ) :
|
|
1227
|
-
inputData.revopsType === 'houseKeeping' ?
|
|
1228
|
-
filter.push( {
|
|
1229
|
-
range: {
|
|
1230
|
-
houseKeepingCount: {
|
|
1231
|
-
gt: 0,
|
|
1232
|
-
},
|
|
1233
|
-
},
|
|
1234
|
-
} ) :
|
|
1235
|
-
inputData.revopsType === 'junk' ?
|
|
1236
|
-
filter.push( {
|
|
1237
|
-
range: {
|
|
1238
|
-
junkCount: {
|
|
1239
|
-
gt: 0,
|
|
1240
|
-
},
|
|
1241
|
-
},
|
|
1242
|
-
} ) :
|
|
1243
|
-
filter.push( {
|
|
1244
|
-
range: {
|
|
1245
|
-
duplicateCount: {
|
|
1246
|
-
gt: 0,
|
|
1247
|
-
},
|
|
1248
|
-
},
|
|
1249
|
-
} );
|
|
1250
|
-
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'email', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkStatus', 'junkACCount', 'approverRole', 'approverUserName', 'approverEmail' ] :
|
|
1251
|
-
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'revicedFootfall', 'revicedPerc', 'mappingInfo', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'email', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'houseKeepingACCount', 'houseKeepingCount', 'employeeCount', 'employeeACCount', 'duplicateCount', 'duplicateACCount', 'junkCount', 'junkStatus', 'junkACCount', 'approverRole', 'approverUserName', 'approverEmail' ] :
|
|
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' ] : [];
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
if ( inputData.action ) {
|
|
1257
|
-
filter.push( {
|
|
1258
|
-
bool: {
|
|
1259
|
-
should: [
|
|
1260
|
-
{
|
|
1261
|
-
constant_score: {
|
|
1262
|
-
filter: { term: { 'houseKeepingStatus.keyword': inputData.action } },
|
|
1263
|
-
boost: 1,
|
|
1264
|
-
_name: 'matched_housekeeping',
|
|
1265
|
-
},
|
|
1266
|
-
},
|
|
1267
|
-
{
|
|
1268
|
-
constant_score: {
|
|
1269
|
-
filter: { term: { 'employeeStatus.keyword': inputData.action } },
|
|
1270
|
-
boost: 1,
|
|
1271
|
-
_name: 'matched_employee',
|
|
1272
|
-
},
|
|
1273
|
-
},
|
|
1274
|
-
{
|
|
1275
|
-
constant_score: {
|
|
1276
|
-
filter: { term: { 'duplicateStatus.keyword': inputData.action } },
|
|
1277
|
-
boost: 1,
|
|
1278
|
-
_name: 'matched_duplicate',
|
|
1279
|
-
},
|
|
1280
|
-
},
|
|
1281
|
-
{
|
|
1282
|
-
constant_score: {
|
|
1283
|
-
filter: { term: { 'junkStatus.keyword': inputData.action } },
|
|
1284
|
-
boost: 1,
|
|
1285
|
-
_name: 'matched_junk',
|
|
1286
|
-
},
|
|
1287
|
-
},
|
|
1288
|
-
],
|
|
1289
|
-
minimum_should_match: 1,
|
|
1290
|
-
},
|
|
1291
|
-
} );
|
|
1292
|
-
}
|
|
1293
1155
|
|
|
1294
1156
|
|
|
1295
|
-
let getRevCount = {};
|
|
1296
|
-
if ( inputData.revopsType ) {
|
|
1297
|
-
getRevCount = {
|
|
1298
|
-
size: 0,
|
|
1299
|
-
query: {
|
|
1300
|
-
bool: {
|
|
1301
|
-
filter: filter,
|
|
1302
|
-
},
|
|
1303
|
-
},
|
|
1304
|
-
aggs: {
|
|
1305
|
-
totalCount: {
|
|
1306
|
-
sum: {
|
|
1307
|
-
field: inputData.revopsType == 'employee' ? 'employeeCount' : inputData.revopsType == 'houseKeeping' ? 'houseKeepingCount' :inputData.revopsType == 'junk' ? 'junkCount': 'duplicateCount',
|
|
1308
|
-
},
|
|
1309
|
-
},
|
|
1310
|
-
},
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
1157
|
const getCount = {
|
|
1314
1158
|
query: {
|
|
1315
1159
|
bool: {
|
|
@@ -1320,20 +1164,14 @@ export async function getTickets( req, res ) {
|
|
|
1320
1164
|
|
|
1321
1165
|
|
|
1322
1166
|
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 );
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
if ( !geteDataCount ) {
|
|
1170
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
1332
1171
|
}
|
|
1333
1172
|
|
|
1334
1173
|
const getQuery = {
|
|
1335
|
-
size:
|
|
1336
|
-
from: skip,
|
|
1174
|
+
size: 1,
|
|
1337
1175
|
query: {
|
|
1338
1176
|
bool: {
|
|
1339
1177
|
filter: filter,
|
|
@@ -1343,14 +1181,9 @@ export async function getTickets( req, res ) {
|
|
|
1343
1181
|
};
|
|
1344
1182
|
|
|
1345
1183
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
1346
|
-
|
|
1347
1184
|
const response = getData?.body?.hits?.hits;
|
|
1348
1185
|
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 );
|
|
1186
|
+
return res.sendError( `No Pending items in the selected dates for the store`, 400 );
|
|
1354
1187
|
}
|
|
1355
1188
|
let temp = [];
|
|
1356
1189
|
if ( inputData?.action ) {
|
|
@@ -1388,29 +1221,31 @@ export async function getTickets( req, res ) {
|
|
|
1388
1221
|
approverUserName: hit?._source?.approverUserName,
|
|
1389
1222
|
approverEmail: hit?._source?.approverEmail,
|
|
1390
1223
|
approverRole: hit?._source?.approverRole,
|
|
1224
|
+
type: hit?._source?.type,
|
|
1391
1225
|
};
|
|
1392
1226
|
let result;
|
|
1393
1227
|
|
|
1228
|
+
|
|
1394
1229
|
const matched = hit.matched_queries;
|
|
1395
1230
|
// Add only matched data array
|
|
1396
|
-
if ( matched.includes( 'matched_employee' )&& ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
1231
|
+
if ( matched.includes( 'matched_employee' ) && ( !inputData.revopsType || inputData.revopsType == 'employee' ) ) {
|
|
1397
1232
|
result = defaultData;
|
|
1398
1233
|
result.employee = hit?._source?.employee;
|
|
1399
1234
|
// result.type = 'employee';
|
|
1400
1235
|
result.matched = matched;
|
|
1401
1236
|
}
|
|
1402
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1237
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
1403
1238
|
result = defaultData;
|
|
1404
1239
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
1405
1240
|
result.matched = matched;
|
|
1406
1241
|
}
|
|
1407
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1242
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
1408
1243
|
result = defaultData;
|
|
1409
1244
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
1410
1245
|
result.matched = matched;
|
|
1411
1246
|
}
|
|
1412
1247
|
|
|
1413
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1248
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
1414
1249
|
result = defaultData;
|
|
1415
1250
|
result.junk = hit?._source?.junk;
|
|
1416
1251
|
result.matched = matched;
|
|
@@ -1429,15 +1264,242 @@ export async function getTickets( req, res ) {
|
|
|
1429
1264
|
}
|
|
1430
1265
|
} );
|
|
1431
1266
|
}
|
|
1267
|
+
|
|
1432
1268
|
const finalResponse = inputData.action ? temp : response;
|
|
1269
|
+
const getRevopQuery = {
|
|
1270
|
+
size: 10000,
|
|
1271
|
+
query: {
|
|
1272
|
+
bool: {
|
|
1273
|
+
must: [
|
|
1274
|
+
{ term: { 'storeId.keyword': response?.[0]?._source?.storeId } }, // assuming inputData.storeId is an array
|
|
1275
|
+
{ term: { 'dateString': response?.[0]?._source?.dateString } },
|
|
1276
|
+
],
|
|
1277
|
+
},
|
|
1278
|
+
},
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
const revopResp = await getOpenSearchData( openSearch.revop, getRevopQuery );
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
// Map revopResp.body.hits.hits to revopSources
|
|
1286
|
+
const revopSources = Array.isArray( revopResp?.body?.hits?.hits ) ?
|
|
1287
|
+
revopResp?.body?.hits?.hits?.map( ( hit ) => hit._source ) :
|
|
1288
|
+
[];
|
|
1289
|
+
|
|
1290
|
+
// Create a map of revopSources by id for quick lookup
|
|
1291
|
+
const revopSourcesMap = new Map();
|
|
1292
|
+
revopSources.forEach( ( item ) => {
|
|
1293
|
+
if ( item?.id ) {
|
|
1294
|
+
revopSourcesMap.set( String( item.id ), item );
|
|
1295
|
+
}
|
|
1296
|
+
} );
|
|
1297
|
+
|
|
1298
|
+
// Process revopSources to replace duplicateImage entries with full objects from the array
|
|
1299
|
+
const processedRevopSources = revopSources.map( ( item ) => {
|
|
1300
|
+
// Check if this is a duplicate parent item
|
|
1301
|
+
if ( item?.revopsType === 'duplicate' && item?.isParent === true && Array.isArray( item?.duplicateImage ) ) {
|
|
1302
|
+
// Map each duplicateImage entry to the full object from revopSources
|
|
1303
|
+
const updatedDuplicateImage = item.duplicateImage.map( ( duplicateImg ) => {
|
|
1304
|
+
const duplicateId = String( duplicateImg?.id );
|
|
1305
|
+
// Find the full object in revopSources that matches this id
|
|
1306
|
+
const fullObject = revopSourcesMap.get( duplicateId );
|
|
1307
|
+
// Return the full object if found, otherwise return the original duplicateImg
|
|
1308
|
+
return fullObject || duplicateImg;
|
|
1309
|
+
} );
|
|
1310
|
+
|
|
1311
|
+
return {
|
|
1312
|
+
...item,
|
|
1313
|
+
duplicateImage: updatedDuplicateImage,
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
// Return item as-is if it doesn't meet the criteria
|
|
1317
|
+
return item;
|
|
1318
|
+
} );
|
|
1319
|
+
|
|
1433
1320
|
if ( finalResponse?.length == 0 || !finalResponse ) {
|
|
1434
1321
|
if ( inputData.storeId?.length > 0 ) {
|
|
1435
|
-
const getStoreName = await findOneStore( { storeId:
|
|
1322
|
+
const getStoreName = await findOneStore( { storeId: response?.[0]?._source?.storeId }, { storeName: 1 } );
|
|
1436
1323
|
return res.sendError( `No Pending items in the selected dates for the store ${getStoreName?.storeName || ''}`, 400 );
|
|
1437
1324
|
}
|
|
1438
1325
|
return res.sendError( 'No Data found', 204 );
|
|
1439
1326
|
}
|
|
1440
|
-
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
// replace the mappingInfo.revisedDetail with processedRevopSources
|
|
1330
|
+
if ( Array.isArray( finalResponse ) ) {
|
|
1331
|
+
for ( let item of finalResponse ) {
|
|
1332
|
+
if (
|
|
1333
|
+
item &&
|
|
1334
|
+
item._source &&
|
|
1335
|
+
item._source.mappingInfo
|
|
1336
|
+
|
|
1337
|
+
) {
|
|
1338
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1339
|
+
const vmsCommentsLogIndex = openSearch.vmsCommentsLog || 'vms-comments-log-dev';
|
|
1340
|
+
let commentsResponse = [];
|
|
1341
|
+
const commentsFilter = [
|
|
1342
|
+
{ term: { 'storeId.keyword': item?._source?.storeId } },
|
|
1343
|
+
{ term: { dateString: item?._source?.dateString } },
|
|
1344
|
+
|
|
1345
|
+
];
|
|
1346
|
+
|
|
1347
|
+
const commentsQuery = {
|
|
1348
|
+
size: 10000,
|
|
1349
|
+
sort: [
|
|
1350
|
+
{ createdAt: { order: 'desc' } }, // Sort descending by createdAt
|
|
1351
|
+
],
|
|
1352
|
+
query: {
|
|
1353
|
+
bool: {
|
|
1354
|
+
filter: commentsFilter,
|
|
1355
|
+
},
|
|
1356
|
+
},
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const commentsRes = await getOpenSearchData( vmsCommentsLogIndex, commentsQuery );
|
|
1360
|
+
// If mappingInfo is an array, update revisedDetail for each mappingInfo object
|
|
1361
|
+
if ( Array.isArray( item._source.mappingInfo ) ) {
|
|
1362
|
+
item._source.mappingInfo.forEach( ( mappingObj ) => {
|
|
1363
|
+
if ( mappingObj.status == 'In-Progress' ) {
|
|
1364
|
+
commentsResponse = commentsRes?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
1365
|
+
|
|
1366
|
+
// Check if duplicate condition exists in commentsResponse
|
|
1367
|
+
const isDuplicate = Array.isArray( commentsResponse ) &&
|
|
1368
|
+
commentsResponse.some( ( c ) => c.category === 'duplicate' );
|
|
1369
|
+
|
|
1370
|
+
// Structure comments output
|
|
1371
|
+
let commentsDetails = [];
|
|
1372
|
+
if ( isDuplicate ) {
|
|
1373
|
+
// Duplicate case - check from commentsResponse
|
|
1374
|
+
// Collect for each type (tagging, review, approve)
|
|
1375
|
+
const types = [ 'tagging', 'review', 'approve' ];
|
|
1376
|
+
commentsDetails = types.map( ( typeValue ) => {
|
|
1377
|
+
// parent value from original comment structure
|
|
1378
|
+
let parent = null;
|
|
1379
|
+
// Get all comments of this type (no filter by category)
|
|
1380
|
+
let comms = commentsResponse
|
|
1381
|
+
.filter( ( c ) => c.type === typeValue )
|
|
1382
|
+
.map( ( c ) => {
|
|
1383
|
+
if ( typeValue === 'tagging' ) {
|
|
1384
|
+
parent = c.parent;
|
|
1385
|
+
return {
|
|
1386
|
+
createdByEmail: c.createdByEmail,
|
|
1387
|
+
createdByUserName: c.createdByUserName,
|
|
1388
|
+
createdByRole: c.createdByRole,
|
|
1389
|
+
message: c.message,
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
if ( typeValue === 'review' || typeValue === 'approve' ) {
|
|
1393
|
+
return {
|
|
1394
|
+
parent: c.parent,
|
|
1395
|
+
category: c.category,
|
|
1396
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
1397
|
+
c.taggedImages.map( ( img ) => ( {
|
|
1398
|
+
id: img?._source?.id,
|
|
1399
|
+
tempId: img?._source?.tempId,
|
|
1400
|
+
timeRange: img?._source?.timeRange,
|
|
1401
|
+
entryTime: img?._source?.entryTime,
|
|
1402
|
+
exitTime: img?._source?.exitTime,
|
|
1403
|
+
filePath: img?._source?.filePath,
|
|
1404
|
+
isChecked: img?._source?.isChecked,
|
|
1405
|
+
} ) ) :
|
|
1406
|
+
[],
|
|
1407
|
+
createdByEmail: c.createdByEmail,
|
|
1408
|
+
createdByUserName: c.createdByUserName,
|
|
1409
|
+
createdByRole: c.createdByRole,
|
|
1410
|
+
status: c.status,
|
|
1411
|
+
message: c.message,
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
return {};
|
|
1415
|
+
} );
|
|
1416
|
+
return {
|
|
1417
|
+
...( typeValue === 'tagging' ? { category: 'duplicate' } : {} ),
|
|
1418
|
+
type: typeValue,
|
|
1419
|
+
...( typeValue === 'tagging' ? { parent } : {} ),
|
|
1420
|
+
comments: comms,
|
|
1421
|
+
};
|
|
1422
|
+
} );
|
|
1423
|
+
} else {
|
|
1424
|
+
// For non-duplicate categories
|
|
1425
|
+
// Collect by type/tag (tagging/review/approve) and build similar structure
|
|
1426
|
+
const types = [ 'tagging', 'review', 'approve' ];
|
|
1427
|
+
commentsDetails = types.map( ( typeValue ) => {
|
|
1428
|
+
// parent for these non-duplicate is always null
|
|
1429
|
+
let comms = commentsResponse
|
|
1430
|
+
.filter( ( c ) => c.type === typeValue )
|
|
1431
|
+
.map( ( c ) => {
|
|
1432
|
+
if ( typeValue === 'tagging' ) {
|
|
1433
|
+
return {
|
|
1434
|
+
id: c.id,
|
|
1435
|
+
tempId: c.tempId,
|
|
1436
|
+
timeRange: c.timeRange,
|
|
1437
|
+
entryTime: c.entryTime,
|
|
1438
|
+
exitTime: c.exitTime,
|
|
1439
|
+
filePath: c.filePath,
|
|
1440
|
+
isChecked: c.isChecked,
|
|
1441
|
+
createdAt: c.createdAt,
|
|
1442
|
+
message: c.message,
|
|
1443
|
+
createdByEmail: c.createdByEmail,
|
|
1444
|
+
createdByUserName: c.createdByUserName,
|
|
1445
|
+
createdByRole: c.createdByRole,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
if ( typeValue === 'review' || typeValue === 'approve' ) {
|
|
1449
|
+
return {
|
|
1450
|
+
category: c.category,
|
|
1451
|
+
taggedImages: Array.isArray( c.taggedImages ) ?
|
|
1452
|
+
c.taggedImages.map( ( img ) => ( {
|
|
1453
|
+
id: img?._source?.id,
|
|
1454
|
+
tempId: img?._source?.tempId,
|
|
1455
|
+
timeRange: img?._source?.timeRange,
|
|
1456
|
+
entryTime: img?._source?.entryTime,
|
|
1457
|
+
exitTime: img?._source?.exitTime,
|
|
1458
|
+
filePath: img?._source?.filePath,
|
|
1459
|
+
isChecked: img?._source?.isChecked,
|
|
1460
|
+
} ) ) :
|
|
1461
|
+
[],
|
|
1462
|
+
createdByEmail: c.createdByEmail,
|
|
1463
|
+
createdByUserName: c.createdByUserName,
|
|
1464
|
+
createdByRole: c.createdByRole,
|
|
1465
|
+
status: c.status,
|
|
1466
|
+
message: c.message,
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
return {};
|
|
1470
|
+
} );
|
|
1471
|
+
return {
|
|
1472
|
+
...( typeValue === 'tagging' ? { category: 'duplicate' } : {} ),
|
|
1473
|
+
parent: null,
|
|
1474
|
+
type: typeValue,
|
|
1475
|
+
comments: comms,
|
|
1476
|
+
};
|
|
1477
|
+
} );
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
item._source.commentsDetails = commentsDetails;
|
|
1481
|
+
}
|
|
1482
|
+
if (
|
|
1483
|
+
Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) &&
|
|
1484
|
+
mappingObj.type !== 'tangoreview'
|
|
1485
|
+
) {
|
|
1486
|
+
mappingObj.revisedDetail = processedRevopSources;
|
|
1487
|
+
}
|
|
1488
|
+
} );
|
|
1489
|
+
} else {
|
|
1490
|
+
item._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
} else if (
|
|
1495
|
+
finalResponse &&
|
|
1496
|
+
finalResponse._source &&
|
|
1497
|
+
finalResponse._source.mappingInfo
|
|
1498
|
+
) {
|
|
1499
|
+
finalResponse._source.mappingInfo.revisedDetail = processedRevopSources;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
return res.sendSuccess( { result: finalResponse } );
|
|
1441
1503
|
} catch ( error ) {
|
|
1442
1504
|
const err = error.message || 'Internal Server Error';
|
|
1443
1505
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -1509,7 +1571,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1509
1571
|
item.isChecked == true ? tempId.push( { tempId: item.tempId, timeRange: item.timeRange } ) : null;
|
|
1510
1572
|
bulkBody.push(
|
|
1511
1573
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${item.timeRange}_${item.tempId}` } },
|
|
1512
|
-
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true? 'approved':'rejected' } },
|
|
1574
|
+
{ doc: { isChecked: item.isChecked, status: item?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1513
1575
|
);
|
|
1514
1576
|
} );
|
|
1515
1577
|
}
|
|
@@ -1524,11 +1586,11 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1524
1586
|
updateData.employee = updatedEmployee;
|
|
1525
1587
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: updateData } );
|
|
1526
1588
|
for ( let employee of updateData?.employee ) {
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1589
|
+
( employee.isChecked == true ) ? tempId.push( { tempId: employee.tempId, timeRange: employee.timeRange } ) : null;
|
|
1590
|
+
bulkBody.push(
|
|
1591
|
+
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${employee.timeRange}_${employee.tempId}` } },
|
|
1592
|
+
{ doc: { isChecked: employee.isChecked, status: employee?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1593
|
+
);
|
|
1532
1594
|
}
|
|
1533
1595
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { employeeACCount: tempId?.length || 0 } } );
|
|
1534
1596
|
}
|
|
@@ -1547,7 +1609,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1547
1609
|
houseKeeping.isChecked == true ? tempId.push( { tempId: houseKeeping.tempId, timeRange: houseKeeping.timeRange } ) : null;
|
|
1548
1610
|
bulkBody.push(
|
|
1549
1611
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${houseKeeping.timeRange}_${houseKeeping.tempId}` } },
|
|
1550
|
-
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true? 'approved':'rejected' } },
|
|
1612
|
+
{ doc: { isChecked: houseKeeping.isChecked, status: houseKeeping?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1551
1613
|
);
|
|
1552
1614
|
}
|
|
1553
1615
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { houseKeepingACCount: tempId?.length || 0 } } );
|
|
@@ -1568,7 +1630,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1568
1630
|
junk.isChecked == true ? tempId.push( { tempId: junk.tempId, timeRange: junk.timeRange } ) : null;
|
|
1569
1631
|
bulkBody.push(
|
|
1570
1632
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${junk.timeRange}_${junk.tempId}` } },
|
|
1571
|
-
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true? 'approved':'rejected' } },
|
|
1633
|
+
{ doc: { isChecked: junk.isChecked, status: junk?.isChecked == true ? 'approved' : 'rejected' } },
|
|
1572
1634
|
);
|
|
1573
1635
|
}
|
|
1574
1636
|
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { junkACCount: tempId?.length || 0 } } );
|
|
@@ -1593,7 +1655,7 @@ export async function updateTicketStatus( data, openSearch, temp, user ) {
|
|
|
1593
1655
|
let getUpdateExistingOne = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
1594
1656
|
const tempIdList = await extractCheckedTempIds( getUpdateExistingOne?.body );
|
|
1595
1657
|
const isSendMessge = await sendSqsMessage( data, tempIdList, getStoreType, storeId );
|
|
1596
|
-
if ( isSendMessge ==true ) {
|
|
1658
|
+
if ( isSendMessge == true ) {
|
|
1597
1659
|
return true; // res.sendSuccess( 'Ticket has been updated successfully' );
|
|
1598
1660
|
} else {
|
|
1599
1661
|
return false; // res.sendError( 'No SQS message sent', 500 );
|
|
@@ -1656,7 +1718,7 @@ function updateEmployeeCheckFlags( existingEmployees, inputEmployees, status ) {
|
|
|
1656
1718
|
// Step 2: Loop through all existing and update isChecked accordingly
|
|
1657
1719
|
const updatedEmployees = existingEmployees.map( ( emp ) => ( {
|
|
1658
1720
|
...emp,
|
|
1659
|
-
isChecked: status === 'rejected'? !checkedTempIds.has( emp.tempId ):checkedTempIds.has( emp.tempId ),
|
|
1721
|
+
isChecked: status === 'rejected' ? !checkedTempIds.has( emp.tempId ) : checkedTempIds.has( emp.tempId ),
|
|
1660
1722
|
} ) );
|
|
1661
1723
|
|
|
1662
1724
|
return updatedEmployees;
|
|
@@ -1706,7 +1768,7 @@ function mergeDuplicateImagesWithUncheck( existingData, inputData, status ) {
|
|
|
1706
1768
|
export async function sendSqsMessage( inputData, tempId, getStoreType, storeId ) {
|
|
1707
1769
|
const sqs = JSON.parse( process.env.SQS );
|
|
1708
1770
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1709
|
-
const sqsName = getStoreType > 0? sqs.revopTrackTicket: sqs.revopTicket;
|
|
1771
|
+
const sqsName = getStoreType > 0 ? sqs.revopTrackTicket : sqs.revopTicket;
|
|
1710
1772
|
const sqsProduceQueue = getStoreType > 0 ? {
|
|
1711
1773
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
1712
1774
|
MessageBody: JSON.stringify( {
|
|
@@ -1734,12 +1796,11 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1734
1796
|
|
|
1735
1797
|
} ),
|
|
1736
1798
|
};
|
|
1737
|
-
const sqsQueue = getStoreType > 0? await sendMessageToFIFOQueue( sqsProduceQueue ):
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
);
|
|
1799
|
+
const sqsQueue = getStoreType > 0 ? await sendMessageToFIFOQueue( sqsProduceQueue ) :
|
|
1800
|
+
await sendMessageToQueue(
|
|
1801
|
+
sqsProduceQueue.QueueUrl,
|
|
1802
|
+
sqsProduceQueue.MessageBody,
|
|
1803
|
+
);
|
|
1743
1804
|
if ( sqsQueue.statusCode ) {
|
|
1744
1805
|
logger.error( {
|
|
1745
1806
|
error: `${sqsQueue}`,
|
|
@@ -1747,7 +1808,7 @@ export async function sendSqsMessage( inputData, tempId, getStoreType, storeId )
|
|
|
1747
1808
|
} );
|
|
1748
1809
|
return false;
|
|
1749
1810
|
} else {
|
|
1750
|
-
const id
|
|
1811
|
+
const id = `${storeId}_${inputData.dateString.split( '-' ).reverse().join( '-' )}_${getStoreType > 0 ? 'live' : 'reduction'}_${Date.now()}`;
|
|
1751
1812
|
const logs = {
|
|
1752
1813
|
QueueUrl: sqsProduceQueue.QueueUrl,
|
|
1753
1814
|
MessageBody: sqsProduceQueue.MessageBody,
|
|
@@ -1797,13 +1858,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
1797
1858
|
},
|
|
1798
1859
|
},
|
|
1799
1860
|
];
|
|
1800
|
-
|
|
1801
|
-
// filter.push(
|
|
1802
|
-
// {
|
|
1803
|
-
// terms: { 'storeId.keyword': req.stores },
|
|
1804
|
-
// },
|
|
1805
|
-
// );
|
|
1806
|
-
// }
|
|
1861
|
+
|
|
1807
1862
|
filter.push(
|
|
1808
1863
|
{
|
|
1809
1864
|
terms: { 'storeId.keyword': req?.stores || [] },
|
|
@@ -1918,8 +1973,8 @@ export async function downloadTickets( req, res ) {
|
|
|
1918
1973
|
{
|
|
1919
1974
|
terms: {
|
|
1920
1975
|
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
1921
|
-
|
|
1922
|
-
|
|
1976
|
+
inputData.storeId :
|
|
1977
|
+
inputData.storeId,
|
|
1923
1978
|
},
|
|
1924
1979
|
},
|
|
1925
1980
|
);
|
|
@@ -1967,26 +2022,26 @@ export async function downloadTickets( req, res ) {
|
|
|
1967
2022
|
},
|
|
1968
2023
|
|
|
1969
2024
|
} ) :
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2025
|
+
inputData.revopsType === 'junk' ?
|
|
2026
|
+
filter.push( {
|
|
2027
|
+
range: {
|
|
2028
|
+
junkCount: {
|
|
2029
|
+
gt: 0,
|
|
2030
|
+
},
|
|
1975
2031
|
},
|
|
1976
|
-
}
|
|
1977
|
-
} ):
|
|
2032
|
+
} ) :
|
|
1978
2033
|
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2034
|
+
filter.push( {
|
|
2035
|
+
range: {
|
|
2036
|
+
duplicateCount: {
|
|
2037
|
+
gt: 0,
|
|
2038
|
+
},
|
|
1983
2039
|
},
|
|
1984
|
-
}
|
|
1985
|
-
} );
|
|
2040
|
+
} );
|
|
1986
2041
|
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1987
2042
|
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1988
2043
|
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] :
|
|
1989
|
-
|
|
2044
|
+
inputData.revopsType == 'junk' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'junkCount', 'comments', 'junk', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus', 'junkStatus' ] : [];
|
|
1990
2045
|
}
|
|
1991
2046
|
|
|
1992
2047
|
if ( inputData.action ) {
|
|
@@ -2063,7 +2118,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2063
2118
|
let temp = [];
|
|
2064
2119
|
if ( inputData?.action ) {
|
|
2065
2120
|
response.map( ( hit ) => {
|
|
2066
|
-
const defaultData ={
|
|
2121
|
+
const defaultData = {
|
|
2067
2122
|
storeId: hit._source.storeId,
|
|
2068
2123
|
dateString: hit?._source?.dateString,
|
|
2069
2124
|
ticketName: hit?._source?.ticketName,
|
|
@@ -2093,19 +2148,19 @@ export async function downloadTickets( req, res ) {
|
|
|
2093
2148
|
// result.type = 'employee';
|
|
2094
2149
|
result.matched = matched;
|
|
2095
2150
|
}
|
|
2096
|
-
if ( matched.includes( 'matched_housekeeping' )&& ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
2151
|
+
if ( matched.includes( 'matched_housekeeping' ) && ( !inputData.revopsType || inputData.revopsType == 'houseKeeping' ) ) {
|
|
2097
2152
|
logger.info( { revop: inputData.revopsType } );
|
|
2098
2153
|
result = defaultData;
|
|
2099
2154
|
result.houseKeeping = hit?._source?.houseKeeping;
|
|
2100
2155
|
result.matched = matched;
|
|
2101
2156
|
}
|
|
2102
|
-
if ( matched.includes( 'matched_duplicate' )&& ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
2157
|
+
if ( matched.includes( 'matched_duplicate' ) && ( !inputData.revopsType || inputData.revopsType == 'duplicateImages' ) ) {
|
|
2103
2158
|
logger.info( { revop: inputData.revopsType } );
|
|
2104
2159
|
result = defaultData;
|
|
2105
2160
|
result.duplicateImages = hit?._source?.duplicateImages;
|
|
2106
2161
|
result.matched = matched;
|
|
2107
2162
|
}
|
|
2108
|
-
if ( matched.includes( 'matched_junk' )&& ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
2163
|
+
if ( matched.includes( 'matched_junk' ) && ( !inputData.revopsType || inputData.revopsType == 'junk' ) ) {
|
|
2109
2164
|
result = defaultData;
|
|
2110
2165
|
result.junk = hit?._source?.junk;
|
|
2111
2166
|
result.matched = matched;
|
|
@@ -2149,7 +2204,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2149
2204
|
revopsType: inputData?.revopsType,
|
|
2150
2205
|
type: 'get-tickets',
|
|
2151
2206
|
};
|
|
2152
|
-
const record={
|
|
2207
|
+
const record = {
|
|
2153
2208
|
stores: inputData?.storeId,
|
|
2154
2209
|
fromDate: inputData?.fromDate,
|
|
2155
2210
|
toDate: inputData?.toDate,
|
|
@@ -2167,7 +2222,7 @@ export async function downloadTickets( req, res ) {
|
|
|
2167
2222
|
const sqs = JSON.parse( process.env.SQS );
|
|
2168
2223
|
const sqsName = sqs.revopDownload;
|
|
2169
2224
|
|
|
2170
|
-
const sqsProduceQueue ={
|
|
2225
|
+
const sqsProduceQueue = {
|
|
2171
2226
|
QueueUrl: `${sqs.url}${sqsName}`,
|
|
2172
2227
|
MessageBody: JSON.stringify( {
|
|
2173
2228
|
_id: getId?._id,
|
|
@@ -2246,7 +2301,7 @@ export async function reviewerList( req, res ) {
|
|
|
2246
2301
|
featureName: 'FootfallDirectory',
|
|
2247
2302
|
modules: {
|
|
2248
2303
|
$elemMatch: {
|
|
2249
|
-
name: '
|
|
2304
|
+
name: inputData?.type === 'review' ? 'reviewer' : 'approver',
|
|
2250
2305
|
$or: [ { isAdd: true }, { isEdit: true } ],
|
|
2251
2306
|
},
|
|
2252
2307
|
},
|
|
@@ -2255,7 +2310,7 @@ export async function reviewerList( req, res ) {
|
|
|
2255
2310
|
};
|
|
2256
2311
|
|
|
2257
2312
|
const getUserlist = await findUser( reviewerRoleQuery, { userName: 1, email: 1, role: 1 } );
|
|
2258
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
2313
|
+
return res.sendSuccess( getUserlist || [] );
|
|
2259
2314
|
} catch ( error ) {
|
|
2260
2315
|
const err = error.message || 'Internal Server Error';
|
|
2261
2316
|
return res.sendError( err, 500 );
|
|
@@ -2277,6 +2332,16 @@ export async function openTicketList( req, res ) {
|
|
|
2277
2332
|
clientId: Array.isArray( clientId ) ? clientId : [ clientId ],
|
|
2278
2333
|
},
|
|
2279
2334
|
},
|
|
2335
|
+
{
|
|
2336
|
+
term: {
|
|
2337
|
+
'mappingInfo.type': inputData.type,
|
|
2338
|
+
},
|
|
2339
|
+
},
|
|
2340
|
+
{
|
|
2341
|
+
term: {
|
|
2342
|
+
'mappingInfo.status.keyword': 'Open',
|
|
2343
|
+
},
|
|
2344
|
+
},
|
|
2280
2345
|
{
|
|
2281
2346
|
range: {
|
|
2282
2347
|
dateString: {
|
|
@@ -2295,14 +2360,20 @@ export async function openTicketList( req, res ) {
|
|
|
2295
2360
|
filter: filter,
|
|
2296
2361
|
},
|
|
2297
2362
|
},
|
|
2298
|
-
_source: [ 'ticketId', 'storeName', 'revicedFootfall', 'footfallCount', 'revicedPerc' ],
|
|
2363
|
+
_source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
|
|
2299
2364
|
};
|
|
2365
|
+
// INSERT_YOUR_CODE
|
|
2366
|
+
// Add sorting by revicedPerc descending (highest revised accuracy first)
|
|
2367
|
+
openSearchQuery.sort = [
|
|
2368
|
+
{ 'revicedPerc.keyword': { order: inputData?.sortOrder === 1? 'asc':'desc' } },
|
|
2369
|
+
];
|
|
2300
2370
|
|
|
2301
2371
|
|
|
2302
2372
|
// Assuming getOpenSearchData and openSearch.footfallDirectoryTagging are available
|
|
2303
2373
|
const result = await getOpenSearchData( openSearch.footfallDirectory, openSearchQuery );
|
|
2374
|
+
logger.info( { result } );
|
|
2304
2375
|
const getUserlist = result?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
|
|
2305
|
-
return res.sendSuccess( getUserlist|| [] );
|
|
2376
|
+
return res.sendSuccess( getUserlist || [] );
|
|
2306
2377
|
} catch ( error ) {
|
|
2307
2378
|
const err = error.message || 'Internal Server Error';
|
|
2308
2379
|
logger.error( { error: error, function: 'openTicketList' } );
|
|
@@ -2310,82 +2381,479 @@ export async function openTicketList( req, res ) {
|
|
|
2310
2381
|
}
|
|
2311
2382
|
}
|
|
2312
2383
|
|
|
2313
|
-
export async function
|
|
2384
|
+
export async function assignTicket( req, res ) {
|
|
2314
2385
|
try {
|
|
2315
2386
|
const inputData = req.body;
|
|
2316
2387
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2317
2388
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
const
|
|
2389
|
+
const { email, userName, role, actionType, storeId, dateString } = inputData;
|
|
2390
|
+
logger.info( { actionType } );
|
|
2391
|
+
const _id = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2321
2392
|
|
|
2322
|
-
|
|
2393
|
+
const getTicket = await getOpenSearchById( openSearch.footfallDirectory, _id );
|
|
2394
|
+
if ( !getTicket ) {
|
|
2395
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
2396
|
+
}
|
|
2397
|
+
const source = getTicket?.body?._source;
|
|
2398
|
+
const mappingInfo = Array.isArray( source.mappingInfo ) ? source.mappingInfo : [];
|
|
2399
|
+
if ( mappingInfo.length === 0 ) {
|
|
2400
|
+
return res.sendError( 'Ticket is not found', 400 );
|
|
2401
|
+
}
|
|
2402
|
+
const lastIndex = mappingInfo.length - 1;
|
|
2403
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
2404
|
+
if ( String( lastEntry.status ) !== 'In-Progress' ) {
|
|
2405
|
+
return res.sendError( 'Ticket is not in progress', 400 );
|
|
2406
|
+
}
|
|
2323
2407
|
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
role,
|
|
2350
|
-
actionType,
|
|
2351
|
-
},
|
|
2352
|
-
},
|
|
2408
|
+
const currentTime = new Date();
|
|
2409
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
2410
|
+
updatedMappingInfo[lastIndex] = { ...lastEntry, createdByEmail: email, updatedAt: currentTime, createdByUserName: userName, createdByRole: role };
|
|
2411
|
+
const updateResult = await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: { mappingInfo: updatedMappingInfo } } );
|
|
2412
|
+
if ( !updateResult ) {
|
|
2413
|
+
return res.sendError( 'Failed to update ticket', 400 );
|
|
2414
|
+
}
|
|
2415
|
+
return res.sendSuccess( { message: 'Ticket assigned successfully' } );
|
|
2416
|
+
} catch ( error ) {
|
|
2417
|
+
const err = error.message || 'Internal Server Error';
|
|
2418
|
+
logger.error( { error: error, function: 'assignTicket' } );
|
|
2419
|
+
return res.sendError( err, 500 );
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
export async function updateTempStatus( req, res ) {
|
|
2424
|
+
try {
|
|
2425
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2426
|
+
const inputData = req.body;
|
|
2427
|
+
const { id, type, status, comments } = inputData;
|
|
2428
|
+
|
|
2429
|
+
// Use bulk update API via bucketing (batch update) -- fetch docs, then bulk-update
|
|
2430
|
+
// 1. Search for all documents matching the ticket IDs
|
|
2431
|
+
const searchBody = {
|
|
2432
|
+
size: 10000,
|
|
2353
2433
|
query: {
|
|
2354
2434
|
bool: {
|
|
2355
2435
|
must: [
|
|
2356
|
-
{ term: { 'ticketId.keyword': ticketId } },
|
|
2357
2436
|
{
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
query: {
|
|
2361
|
-
bool: {
|
|
2362
|
-
must: [
|
|
2363
|
-
{ match: { 'mappingInfo.type': actionType } },
|
|
2364
|
-
],
|
|
2365
|
-
},
|
|
2366
|
-
},
|
|
2437
|
+
terms: {
|
|
2438
|
+
'id.keyword': id,
|
|
2367
2439
|
},
|
|
2368
2440
|
},
|
|
2369
2441
|
],
|
|
2370
2442
|
},
|
|
2371
2443
|
},
|
|
2444
|
+
_source: [ '_id' ], // Only bring _id for efficiency
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
const searchResp = await getOpenSearchData(
|
|
2448
|
+
openSearch.revop,
|
|
2449
|
+
searchBody,
|
|
2450
|
+
);
|
|
2451
|
+
|
|
2452
|
+
// Extract bulk IDs to update
|
|
2453
|
+
const hits = searchResp?.body?.hits?.hits ?? [];
|
|
2454
|
+
|
|
2455
|
+
if ( !hits.length ) {
|
|
2456
|
+
return res.sendError( 'no data', 204 );
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
|
|
2460
|
+
// 1. Get all IDs from hits
|
|
2461
|
+
const docIdToIndex = {};
|
|
2462
|
+
hits.forEach( ( doc ) => {
|
|
2463
|
+
docIdToIndex[doc._id] = doc._index;
|
|
2464
|
+
} );
|
|
2465
|
+
const docIds = hits.map( ( doc ) => doc._id );
|
|
2466
|
+
// 2. Fetch all docs by ID to get 'actions' (in chunks if large)
|
|
2467
|
+
const getBody = [];
|
|
2468
|
+
for ( const doc of hits ) {
|
|
2469
|
+
getBody.push( { _index: doc._index, _id: doc._id } );
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
let mgetResp;
|
|
2473
|
+
|
|
2474
|
+
mgetResp = await getOpenSearchData(
|
|
2475
|
+
openSearch.revop,
|
|
2476
|
+
{
|
|
2477
|
+
size: 10000,
|
|
2478
|
+
query: {
|
|
2479
|
+
ids: {
|
|
2480
|
+
values: docIds,
|
|
2481
|
+
},
|
|
2482
|
+
},
|
|
2483
|
+
_source: true,
|
|
2484
|
+
},
|
|
2485
|
+
);
|
|
2486
|
+
// (If you have a utility for multi-get, you may want to use that. Else, you might need to fetch each by ID.)
|
|
2487
|
+
// For fallback, fetch all source fields via another search
|
|
2488
|
+
let fullDocs = mgetResp?.body?.hits?.hits || searchResp?.body?.hits?.hits || [];
|
|
2489
|
+
|
|
2490
|
+
// 3. Prepare the new actions array for each doc, and set up bulk update payloads
|
|
2491
|
+
const reviewActions = [ 'approved', 'rejected' ];
|
|
2492
|
+
const docsToUpdate = [];
|
|
2493
|
+
for ( const doc of fullDocs ) {
|
|
2494
|
+
const source = doc._source || doc.fields || {}; // support mget and search hits
|
|
2495
|
+
let actions = Array.isArray( source.actions ) ? [ ...source.actions ] : [];
|
|
2496
|
+
if ( reviewActions.includes( status ) ) {
|
|
2497
|
+
// for review: update or push 'review'
|
|
2498
|
+
let found = false;
|
|
2499
|
+
switch ( type ) {
|
|
2500
|
+
case 'review':
|
|
2501
|
+
actions = actions.map( ( item ) => {
|
|
2502
|
+
if ( item.actionType === 'review' ) {
|
|
2503
|
+
found = true;
|
|
2504
|
+
return { ...item, action: status };
|
|
2505
|
+
}
|
|
2506
|
+
return item;
|
|
2507
|
+
} );
|
|
2508
|
+
if ( !found ) {
|
|
2509
|
+
actions.push( { actionType: 'review', action: status } );
|
|
2510
|
+
}
|
|
2511
|
+
break;
|
|
2512
|
+
case 'approve':
|
|
2513
|
+
actions = actions.map( ( item ) => {
|
|
2514
|
+
if ( item.actionType === 'approve' ) {
|
|
2515
|
+
found = true;
|
|
2516
|
+
return { ...item, action: status };
|
|
2517
|
+
}
|
|
2518
|
+
return item;
|
|
2519
|
+
} );
|
|
2520
|
+
if ( !found ) {
|
|
2521
|
+
actions.push( { actionType: 'approve', action: status } );
|
|
2522
|
+
}
|
|
2523
|
+
break;
|
|
2524
|
+
default:
|
|
2525
|
+
return res.sendError( 'wrong vaue', 400 );
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
let isChecked = true;
|
|
2529
|
+
switch ( type ) {
|
|
2530
|
+
case 'review':
|
|
2531
|
+
isChecked = status === 'approved' ? true : false;
|
|
2532
|
+
break;
|
|
2533
|
+
case 'approve':
|
|
2534
|
+
isChecked = status === 'approved' ? doc?._source?.isChecked : !doc?._source?.isChecked;
|
|
2535
|
+
break;
|
|
2536
|
+
}
|
|
2537
|
+
docsToUpdate.push( {
|
|
2538
|
+
_index: doc._index || docIdToIndex[doc._id],
|
|
2539
|
+
_id: doc._id,
|
|
2540
|
+
actions,
|
|
2541
|
+
isChecked: isChecked,
|
|
2542
|
+
comments: comments || '',
|
|
2543
|
+
} );
|
|
2544
|
+
}
|
|
2545
|
+
const bulkPayload = [];
|
|
2546
|
+
// 4. Build bulk update payload
|
|
2547
|
+
for ( const doc of docsToUpdate ) {
|
|
2548
|
+
bulkPayload.push( {
|
|
2549
|
+
update: { _index: doc._index, _id: doc._id },
|
|
2550
|
+
} );
|
|
2551
|
+
bulkPayload.push( {
|
|
2552
|
+
doc: { actions: doc.actions, isChecked: doc?.isChecked, comments: doc?.comments || '' },
|
|
2553
|
+
} );
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
// 3. Execute bulk update
|
|
2557
|
+
const bulkResp = await bulkUpdate( bulkPayload );
|
|
2558
|
+
|
|
2559
|
+
// Count successes
|
|
2560
|
+
const updatedCount = bulkResp?.body?.items?.filter( ( item ) => item?.update?.result === 'updated' || item?.update?.result === 'noop' ).length ?? 0;
|
|
2561
|
+
|
|
2562
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
2563
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
2564
|
+
const searchBody1 = {
|
|
2565
|
+
size: 10000,
|
|
2566
|
+
query: {
|
|
2567
|
+
bool: {
|
|
2568
|
+
must: [
|
|
2569
|
+
{
|
|
2570
|
+
terms: {
|
|
2571
|
+
'_id': docIds,
|
|
2572
|
+
},
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
term: {
|
|
2576
|
+
isParent: false,
|
|
2577
|
+
},
|
|
2578
|
+
},
|
|
2579
|
+
],
|
|
2580
|
+
},
|
|
2581
|
+
},
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2584
|
+
const getSearchResp = await getOpenSearchData(
|
|
2585
|
+
openSearch.revop,
|
|
2586
|
+
searchBody1,
|
|
2587
|
+
);
|
|
2588
|
+
|
|
2589
|
+
|
|
2590
|
+
const taggedImages = getSearchResp?.body?.hits?.hits?.length > 0? getSearchResp?.body?.hits?.hits : [];
|
|
2591
|
+
const logs = {
|
|
2592
|
+
type: inputData.type,
|
|
2593
|
+
storeId: taggedImages?.[0]?._source?.storeId,
|
|
2594
|
+
dateString: taggedImages?.[0]?._source?.dateString,
|
|
2595
|
+
category: taggedImages?.[0]?._source?.revopsType || '',
|
|
2596
|
+
taggedImages: taggedImages,
|
|
2597
|
+
status: inputData?.status,
|
|
2598
|
+
createdByEmail: req?.user?.email,
|
|
2599
|
+
createdByUserName: req?.user?.userName,
|
|
2600
|
+
createdByRole: req?.user?.role,
|
|
2601
|
+
message: inputData.comments || '',
|
|
2602
|
+
createdAt: new Date(),
|
|
2603
|
+
};
|
|
2604
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
2605
|
+
}
|
|
2606
|
+
return res.sendSuccess( { updated: updatedCount } );
|
|
2607
|
+
} catch ( error ) {
|
|
2608
|
+
const err = error.message;
|
|
2609
|
+
logger.info( { error: err, function: 'updateTempStatus' } );
|
|
2610
|
+
return res.sendError( err, 500 );
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
export async function updateUserTicketStatus( req, res ) {
|
|
2615
|
+
try {
|
|
2616
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2617
|
+
const { storeId, dateString } = req.body || {};
|
|
2618
|
+
|
|
2619
|
+
if ( !storeId || !dateString ) {
|
|
2620
|
+
return res.sendError( 'storeId and dateString are required', 400 );
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2624
|
+
|
|
2625
|
+
// Fetch existing ticket so we can validate mappingInfo state
|
|
2626
|
+
const existingDoc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
2627
|
+
const ticketSource = existingDoc?.body?._source;
|
|
2628
|
+
|
|
2629
|
+
if ( !ticketSource ) {
|
|
2630
|
+
return res.sendError( 'Ticket not found', 404 );
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
const mappingInfo = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [];
|
|
2634
|
+
if ( mappingInfo.length === 0 ) {
|
|
2635
|
+
return res.sendError( 'mappingInfo is missing for this ticket', 400 );
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
const lastIndex = mappingInfo.length - 1;
|
|
2639
|
+
const lastEntry = mappingInfo[lastIndex];
|
|
2640
|
+
|
|
2641
|
+
if ( !lastEntry ) {
|
|
2642
|
+
return res.sendError( 'Unable to determine current ticket status', 400 );
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
if ( String( lastEntry.status ).toLowerCase() !== 'open' ) {
|
|
2646
|
+
return res.sendError( 'Ticket is already picked by another user', 409 );
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
const currentTime = new Date();
|
|
2650
|
+
const updatedMappingInfo = [ ...mappingInfo ];
|
|
2651
|
+
updatedMappingInfo[lastIndex] = {
|
|
2652
|
+
...lastEntry,
|
|
2653
|
+
status: 'In-Progress',
|
|
2654
|
+
updatedAt: currentTime,
|
|
2655
|
+
createdByRole: req?.user?.role || '',
|
|
2656
|
+
createdByEmail: req?.user?.email || '',
|
|
2657
|
+
createdByUserName: req?.user?.userName || '',
|
|
2658
|
+
};
|
|
2659
|
+
|
|
2660
|
+
const updatePayload = {
|
|
2661
|
+
doc: {
|
|
2662
|
+
status: 'In-Progress',
|
|
2663
|
+
mappingInfo: updatedMappingInfo,
|
|
2664
|
+
updatedAt: currentTime,
|
|
2665
|
+
},
|
|
2372
2666
|
};
|
|
2373
2667
|
|
|
2374
|
-
|
|
2375
|
-
const response = await upsertOpenSearchData(
|
|
2668
|
+
const updateResult = await updateOpenSearchData(
|
|
2376
2669
|
openSearch.footfallDirectory,
|
|
2377
|
-
|
|
2378
|
-
|
|
2670
|
+
docId,
|
|
2671
|
+
updatePayload,
|
|
2379
2672
|
);
|
|
2380
2673
|
|
|
2381
2674
|
|
|
2382
|
-
|
|
2675
|
+
if ( !updateResult || !( updateResult.statusCode === 200 || updateResult.statusCode === 201 ) ) {
|
|
2676
|
+
return res.sendError( 'Failed to update ticket status', 500 );
|
|
2677
|
+
}
|
|
2678
|
+
return res.sendSuccess( 'Ticket status updated successfully' );
|
|
2679
|
+
} catch ( error ) {
|
|
2680
|
+
const err = error.message;
|
|
2681
|
+
logger.info( { error: err, function: 'updateUserTicketStatus' } );
|
|
2682
|
+
return res.sendError( err, 500 );
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
export async function multiCloseTicket( req, res ) {
|
|
2687
|
+
try {
|
|
2688
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2689
|
+
const inputData = req.body;
|
|
2690
|
+
|
|
2691
|
+
// inputData structure should include an array of items to close
|
|
2692
|
+
// Accept both array or single ticket update
|
|
2693
|
+
const tickets = Array.isArray( inputData.ticketList ) ? inputData.ticketList : [ inputData.ticketList ];
|
|
2694
|
+
// const mode = inputData.mode || '';
|
|
2695
|
+
|
|
2696
|
+
if ( !tickets.length ) {
|
|
2697
|
+
return res.sendError( 'No tickets provided', 400 );
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
const results = [];
|
|
2701
|
+
for ( const ticket of tickets ) {
|
|
2702
|
+
const { storeId, dateString } = ticket || {};
|
|
2703
|
+
if ( !storeId || !dateString ) {
|
|
2704
|
+
results.push( { storeId, dateString, success: false, error: 'Missing storeId or dateString' } );
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
// 1. Update the ticket document in footfallDirectory index
|
|
2708
|
+
const docId = `${storeId}_${dateString}_footfall-directory-tagging`;
|
|
2709
|
+
|
|
2710
|
+
// Fetch existing doc to update mappingInfo
|
|
2711
|
+
|
|
2712
|
+
const doc = await getOpenSearchById( openSearch.footfallDirectory, docId );
|
|
2713
|
+
|
|
2714
|
+
|
|
2715
|
+
const ticketSource = doc?.body?._source;
|
|
2716
|
+
if ( !ticketSource || !ticketSource.mappingInfo ) {
|
|
2717
|
+
results.push( { storeId, dateString, success: false, error: 'Ticket or mappingInfo missing' } );
|
|
2718
|
+
continue;
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
let mappingInfoArray = Array.isArray( ticketSource.mappingInfo ) ? ticketSource.mappingInfo : [ ticketSource.mappingInfo ];
|
|
2722
|
+
// Find all mappingInfo items matching type 'approve'
|
|
2723
|
+
let updated = false;
|
|
2724
|
+
let newMappingInfoArray = mappingInfoArray.map( ( mi, i ) => {
|
|
2725
|
+
if ( mi?.type === 'approve' && mi?.status === 'Open' ) {
|
|
2726
|
+
updated = true;
|
|
2727
|
+
return {
|
|
2728
|
+
...mi,
|
|
2729
|
+
status: 'Approver-Closed',
|
|
2730
|
+
status: 'Closed', // following the user's instruction to set sttaus property (assumed typo, but explicitly used)
|
|
2731
|
+
updatedAt: new Date(),
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
return mi;
|
|
2735
|
+
} );
|
|
2736
|
+
|
|
2737
|
+
if ( !updated ) {
|
|
2738
|
+
// None found to update
|
|
2739
|
+
results.push( { storeId, dateString, success: false, error: `coudn't approve this store` } );
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
// Write update to footfallDirectory
|
|
2744
|
+
const ticketUpdatePayload = {
|
|
2745
|
+
doc: {
|
|
2746
|
+
mappingInfo: newMappingInfoArray,
|
|
2747
|
+
status: 'Approver-Closed', // status updated at top level as well
|
|
2748
|
+
updatedAt: new Date(),
|
|
2749
|
+
},
|
|
2750
|
+
};
|
|
2751
|
+
let ticketUpdateResult;
|
|
2752
|
+
try {
|
|
2753
|
+
ticketUpdateResult = await updateOpenSearchData( openSearch.footfallDirectory, docId, ticketUpdatePayload );
|
|
2754
|
+
if ( !( ticketUpdateResult && ( ticketUpdateResult.statusCode === 200 || ticketUpdateResult.statusCode === 201 ) ) ) {
|
|
2755
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
2756
|
+
continue;
|
|
2757
|
+
}
|
|
2758
|
+
} catch ( err ) {
|
|
2759
|
+
results.push( { storeId, dateString, success: false, error: 'Failed to update ticket' } );
|
|
2760
|
+
continue;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
|
|
2764
|
+
// For each ticket, update actions array for all matching image docs (storeId & dateString)
|
|
2765
|
+
|
|
2766
|
+
// Query to find all matching docs in revopTagging index with storeId and dateString
|
|
2767
|
+
const revopImageQuery = {
|
|
2768
|
+
size: 10000, // assume there won't be more than 1000 images per ticket
|
|
2769
|
+
query: {
|
|
2770
|
+
bool: {
|
|
2771
|
+
must: [
|
|
2772
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
2773
|
+
{ term: { dateString: dateString } },
|
|
2774
|
+
],
|
|
2775
|
+
},
|
|
2776
|
+
},
|
|
2777
|
+
};
|
|
2778
|
+
|
|
2779
|
+
// Fetch matching docs
|
|
2780
|
+
const searchRes = await getOpenSearchData( openSearch.revop, revopImageQuery );
|
|
2781
|
+
const revopHits = searchRes?.body?.hits?.hits || [];
|
|
2782
|
+
|
|
2783
|
+
// Optimized: batch and parallelize image-doc updates to avoid long sequential waits
|
|
2784
|
+
const BATCH_SIZE = 100;
|
|
2785
|
+
const now = new Date();
|
|
2786
|
+
|
|
2787
|
+
for ( let i = 0; i < revopHits.length; i += BATCH_SIZE ) {
|
|
2788
|
+
const batch = revopHits.slice( i, i + BATCH_SIZE );
|
|
2789
|
+
|
|
2790
|
+
const updatePromises = batch.map( async ( hit ) => {
|
|
2791
|
+
const imageDocId = hit._id;
|
|
2792
|
+
const imageSource = hit._source || {};
|
|
2793
|
+
const imageActionsArray = Array.isArray( imageSource.actions ) ? [ ...imageSource.actions ] : [];
|
|
2794
|
+
|
|
2795
|
+
imageActionsArray.push( {
|
|
2796
|
+
actionType: 'approve',
|
|
2797
|
+
action: 'approved',
|
|
2798
|
+
} );
|
|
2799
|
+
|
|
2800
|
+
const imageUpdatePayload = {
|
|
2801
|
+
doc: {
|
|
2802
|
+
actions: imageActionsArray,
|
|
2803
|
+
updatedAt: now,
|
|
2804
|
+
},
|
|
2805
|
+
};
|
|
2806
|
+
|
|
2807
|
+
return updateOpenSearchData( openSearch.revop, imageDocId, imageUpdatePayload );
|
|
2808
|
+
} );
|
|
2383
2809
|
|
|
2384
|
-
|
|
2810
|
+
// Wait for this batch to finish before starting the next one
|
|
2811
|
+
await Promise.all( updatePromises );
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
if ( results && results?.length > 0 ) {
|
|
2815
|
+
return res.sendError( results, 500 );
|
|
2816
|
+
}
|
|
2817
|
+
// Return batch summary
|
|
2385
2818
|
} catch ( error ) {
|
|
2386
|
-
const err = error.message
|
|
2387
|
-
logger.
|
|
2819
|
+
const err = error.message;
|
|
2820
|
+
logger.info( { error: err, function: 'multiCloseTicket' } );
|
|
2388
2821
|
return res.sendError( err, 500 );
|
|
2389
2822
|
}
|
|
2390
2823
|
}
|
|
2391
2824
|
|
|
2825
|
+
|
|
2826
|
+
export async function checkTicketExists( req, res ) {
|
|
2827
|
+
try {
|
|
2828
|
+
let inputData = req.body;
|
|
2829
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2830
|
+
let findQuery = {
|
|
2831
|
+
size: 10000,
|
|
2832
|
+
query: {
|
|
2833
|
+
bool: {
|
|
2834
|
+
must: [
|
|
2835
|
+
{
|
|
2836
|
+
term: {
|
|
2837
|
+
'storeId.keyword': inputData.storeId,
|
|
2838
|
+
},
|
|
2839
|
+
},
|
|
2840
|
+
{
|
|
2841
|
+
term: {
|
|
2842
|
+
'dateString': inputData.dateString,
|
|
2843
|
+
},
|
|
2844
|
+
},
|
|
2845
|
+
],
|
|
2846
|
+
},
|
|
2847
|
+
},
|
|
2848
|
+
};
|
|
2849
|
+
let findTicket = await getOpenSearchData( openSearch.footfallDirectory, findQuery );
|
|
2850
|
+
let Ticket = findTicket.body?.hits?.hits;
|
|
2851
|
+
|
|
2852
|
+
|
|
2853
|
+
res.sendSuccess( Ticket );
|
|
2854
|
+
} catch ( error ) {
|
|
2855
|
+
const err = error.message;
|
|
2856
|
+
logger.info( { error: err, function: 'checkTicketExists' } );
|
|
2857
|
+
return res.sendError( err, 500 );
|
|
2858
|
+
}
|
|
2859
|
+
}
|