tango-app-api-infra 3.8.1-beta.0 → 3.8.1-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { download, logger } from 'tango-app-api-middleware';
|
|
1
|
+
import { download, logger, sendMessageToQueue } from 'tango-app-api-middleware';
|
|
2
2
|
import { bulkUpdate, 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 dayjs from 'dayjs';
|
|
@@ -10,11 +10,12 @@ export async function createTicket( req, res ) {
|
|
|
10
10
|
const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1, _id: 0 } );
|
|
11
11
|
inputData.ticketId = 'TE_FDT_' + new Date().valueOf();
|
|
12
12
|
inputData.clientId = inputData?.storeId?.split( '-' )[0];
|
|
13
|
-
inputData.storeName =getStoreName?.storeName;
|
|
13
|
+
inputData.storeName = getStoreName?.storeName;
|
|
14
14
|
inputData.createdAt = new Date();
|
|
15
15
|
inputData.updatedAt = new Date();
|
|
16
|
-
inputData.userName = req
|
|
17
|
-
inputData.
|
|
16
|
+
inputData.userName = req?.user?.userName;
|
|
17
|
+
inputData.email = req?.user?.email;
|
|
18
|
+
inputData.role = req?.user?.role;
|
|
18
19
|
inputData.status = 'open';
|
|
19
20
|
if ( inputData.houseKeepingCount > 0 ) {
|
|
20
21
|
inputData.houseKeepingStatus = 'pending';
|
|
@@ -67,12 +68,19 @@ async function bulkUpdateStatusToPending( indexName, inputData ) {
|
|
|
67
68
|
|
|
68
69
|
// 3. Duplicate Images > data[]
|
|
69
70
|
if ( inputData.duplicateCount > 0 ) {
|
|
71
|
+
let updatedDuplicateImages = [];
|
|
70
72
|
for ( const dup of inputData.duplicateImages || [] ) {
|
|
71
73
|
const id = `${inputData.storeId}_${inputData.dateString}_${dup.timeRange}_${dup.tempId}`;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
dup?.data?.map( ( item ) => {
|
|
75
|
+
updatedDuplicateImages.push( {
|
|
76
|
+
...item,
|
|
77
|
+
status: 'pending',
|
|
78
|
+
} );
|
|
79
|
+
bulkBody.push(
|
|
80
|
+
{ update: { _index: indexName, _id: `${inputData.storeId}_${inputData.dateString}_${item.timeRange}_${item.tempId}` } },
|
|
81
|
+
{ doc: { status: 'pending' } },
|
|
82
|
+
);
|
|
83
|
+
} );
|
|
76
84
|
bulkBody.push(
|
|
77
85
|
{ update: { _index: indexName, _id: id } },
|
|
78
86
|
{ doc: { status: 'pending', duplicateImage: updatedDuplicateImages } },
|
|
@@ -174,120 +182,201 @@ export async function ticketSummary( req, res ) {
|
|
|
174
182
|
|
|
175
183
|
export async function ticketList( req, res ) {
|
|
176
184
|
try {
|
|
177
|
-
// let count = 0;
|
|
178
|
-
// const array =[
|
|
179
|
-
// { A: '3c88e8eb6d499a26d37ca075c390bd2d', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/101' },
|
|
180
|
-
// { A: 'c3dd981f485ccd25221c7da704f35d24', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/101' },
|
|
181
|
-
// { A: 'ee887e5546ab0e96ca0adbf8c2453c8d', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/1001' },
|
|
182
|
-
// { A: 'a59ddeb30d158602d6c672994af2f79a', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/1001' },
|
|
183
|
-
// { A: '1f3318280025f5456262b0322b806940', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/201' },
|
|
184
|
-
// { A: '925a70751a0988793057148b20158186', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/201',
|
|
185
|
-
// }, { A: 'd6d4ab301c322212fb612a7d9661b6be', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/301',
|
|
186
|
-
// }, { A: 'cb09aeedfa5ade043922634eb1c35e46', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/301',
|
|
187
|
-
// }, { A: '58f81e33f66ef7af15424c1dc44e7de1', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/401',
|
|
188
|
-
// }, { A: '7ac8f73f02347d29f6f2bff3c52275f5', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/401',
|
|
189
|
-
// }, { A: '9599acd19a48eb28957ad18e40a0a5b1', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/401',
|
|
190
|
-
// }, { A: '36584a3ce215a5fbd1c0e6535201020c', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/501',
|
|
191
|
-
// }, { A: 'cb7e91870e0a72d4eb691e2618aa5979', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/501',
|
|
192
|
-
// }, { A: 'cd072627e6924df745cba004616ec3c9', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/601',
|
|
193
|
-
// }, { A: 'a5891519a50f68a9d73a774539fe8496', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/601',
|
|
194
|
-
// }, { A: '2008517125b03b1b9a66348e6d017523', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/701',
|
|
195
|
-
// }, { A: '79fc93551b59f9ed07eab48d190ba7f4', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/701',
|
|
196
|
-
// }, { A: '89365abcfc8dc663b26471d81a7f68eb', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/801',
|
|
197
|
-
// }, { A: 'e9fb3b989a245994bd48b97287aad096', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/801',
|
|
198
|
-
// }, { A: '4493fb6dc07611bab03630b154ca2483', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/801',
|
|
199
|
-
// }, { A: 'e31caf56bb86f6e12377db006ac61756', B: 'rtsp://admin:Bli$$%40145@192.168.29.86/streaming/channels/901',
|
|
200
|
-
// }, { A: 'fe66c637bc8da296485494e371f67b99', B: 'rtsp://admin:Bli$$%40145@192.168.29.58/streaming/channels/901' },
|
|
201
|
-
// ];
|
|
202
|
-
|
|
203
|
-
// for ( const item of array ) {
|
|
204
|
-
// const a = await updateOneCamera( { streamName: item.A, clientId: '370' }, { RTSP: item.B } );
|
|
205
|
-
// count++;
|
|
206
|
-
// logger.info( { aaa: a, count: count } );
|
|
207
|
-
// }
|
|
208
|
-
|
|
209
|
-
// process.exit();
|
|
210
185
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
211
186
|
const inputData = req.query;
|
|
212
187
|
const limit = inputData.limit || 10;
|
|
213
|
-
const
|
|
188
|
+
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
189
|
+
const order = inputData?.sortOrder || -1;
|
|
190
|
+
|
|
214
191
|
inputData.clientId = inputData.clientId.split( ',' ); // convert strig to array
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
dateString: {
|
|
226
|
-
gte: inputData.fromDate,
|
|
227
|
-
lte: inputData.toDate,
|
|
228
|
-
format: 'yyyy-MM-dd',
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
],
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
let filter = [
|
|
195
|
+
{
|
|
196
|
+
'range': {
|
|
197
|
+
'dateString': {
|
|
198
|
+
'gte': inputData.fromDate,
|
|
199
|
+
'lte': inputData.toDate,
|
|
200
|
+
'format': 'yyyy-MM-dd',
|
|
201
|
+
},
|
|
233
202
|
},
|
|
234
203
|
},
|
|
235
|
-
|
|
204
|
+
{
|
|
205
|
+
terms: {
|
|
206
|
+
'clientId.keyword': Array.isArray( inputData.clientId ) ?
|
|
207
|
+
inputData.clientId :
|
|
208
|
+
[ inputData.clientId ],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
];
|
|
236
212
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
213
|
+
if ( inputData?.storeId ) {
|
|
214
|
+
filter.push(
|
|
215
|
+
{
|
|
216
|
+
terms: {
|
|
217
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
218
|
+
inputData.storeId :
|
|
219
|
+
[ inputData.storeId ],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
);
|
|
240
223
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
lte: inputData.toDate,
|
|
255
|
-
format: 'yyyy-MM-dd',
|
|
256
|
-
},
|
|
224
|
+
|
|
225
|
+
let search = {
|
|
226
|
+
'must': filter,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
230
|
+
search = {
|
|
231
|
+
'must': filter,
|
|
232
|
+
'should': [
|
|
233
|
+
{
|
|
234
|
+
'wildcard': {
|
|
235
|
+
'storeName.keyword': {
|
|
236
|
+
'value': `*${inputData.searchValue}*`,
|
|
257
237
|
},
|
|
258
238
|
},
|
|
259
|
-
|
|
260
|
-
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
'wildcard': {
|
|
242
|
+
'storeId.keyword': {
|
|
243
|
+
'value': `*${inputData.searchValue}*`,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
'wildcard': {
|
|
249
|
+
'ticketId.keyword': {
|
|
250
|
+
'value': `*${inputData.searchValue}*`,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
'wildcard': {
|
|
256
|
+
'status.keyword': {
|
|
257
|
+
'value': `*${inputData.searchValue}*`,
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
],
|
|
263
|
+
'minimum_should_match': 1,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let searchQuery = {
|
|
268
|
+
'_source': [
|
|
269
|
+
'storeName',
|
|
270
|
+
'storeId',
|
|
271
|
+
'ticketId',
|
|
272
|
+
'createdAt',
|
|
273
|
+
'updatedAt',
|
|
274
|
+
'footfallCount',
|
|
275
|
+
'duplicateCount',
|
|
276
|
+
'employeeCount',
|
|
277
|
+
'houseKeepingCount',
|
|
278
|
+
'status',
|
|
279
|
+
'dateString',
|
|
280
|
+
],
|
|
281
|
+
'from': offset,
|
|
282
|
+
'size': limit,
|
|
283
|
+
'query': {
|
|
284
|
+
'bool': search,
|
|
261
285
|
},
|
|
262
|
-
|
|
286
|
+
'sort': [
|
|
287
|
+
{ dateString: { order: 'desc' } },
|
|
288
|
+
],
|
|
263
289
|
};
|
|
264
290
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
logger.info( { response: response, body: getData?.body, getData: getData, geteDataCount: geteDataCount } );
|
|
291
|
+
if ( inputData.sortBy && inputData.sortBy !== '' ) {
|
|
292
|
+
let sortByValue = '';
|
|
268
293
|
|
|
294
|
+
if ( [ 'storeName', 'storeId', 'ticketId', 'status' ].includes( inputData?.sortBy ) ) {
|
|
295
|
+
sortByValue = `${inputData.sortBy}.keyword`;
|
|
296
|
+
} else {
|
|
297
|
+
sortByValue = inputData.sortBy;
|
|
298
|
+
}
|
|
269
299
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
'
|
|
275
|
-
'
|
|
276
|
-
'
|
|
277
|
-
'
|
|
278
|
-
'
|
|
279
|
-
'
|
|
280
|
-
'
|
|
281
|
-
'
|
|
282
|
-
'
|
|
283
|
-
'
|
|
300
|
+
|
|
301
|
+
searchQuery = {
|
|
302
|
+
'_source': [
|
|
303
|
+
'storeName',
|
|
304
|
+
'storeId',
|
|
305
|
+
'ticketId',
|
|
306
|
+
'createdAt',
|
|
307
|
+
'updatedAt',
|
|
308
|
+
'footfallCount',
|
|
309
|
+
'duplicateCount',
|
|
310
|
+
'employeeCount',
|
|
311
|
+
'houseKeepingCount',
|
|
312
|
+
'status',
|
|
313
|
+
'dateString',
|
|
314
|
+
],
|
|
315
|
+
'from': offset,
|
|
316
|
+
'size': limit,
|
|
317
|
+
'query': {
|
|
318
|
+
'bool': search,
|
|
319
|
+
},
|
|
320
|
+
'sort': [
|
|
321
|
+
{ [sortByValue]: { order: order === -1 ? 'desc' : 'asc' } },
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if ( inputData.isExport == true ) {
|
|
327
|
+
searchQuery = {
|
|
328
|
+
'_source': [
|
|
329
|
+
'storeName',
|
|
330
|
+
'storeId',
|
|
331
|
+
'ticketId',
|
|
332
|
+
'createdAt',
|
|
333
|
+
'updatedAt',
|
|
334
|
+
'footfallCount',
|
|
335
|
+
'duplicateCount',
|
|
336
|
+
'employeeCount',
|
|
337
|
+
'houseKeepingCount',
|
|
338
|
+
'status',
|
|
339
|
+
'dateString',
|
|
340
|
+
],
|
|
341
|
+
'from': 0,
|
|
342
|
+
'size': 10000,
|
|
343
|
+
'query': {
|
|
344
|
+
'bool': search,
|
|
345
|
+
},
|
|
346
|
+
'sort': [
|
|
347
|
+
{ 'storeName.keyword': { order: 'desc' } },
|
|
348
|
+
],
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
const getData = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
|
|
352
|
+
const count = getData?.body?.hits?.total?.value;
|
|
353
|
+
if ( !count || count == 0 ) {
|
|
354
|
+
return res.sendError( 'No data found', 204 );
|
|
355
|
+
}
|
|
356
|
+
const searchValue = getData?.body?.hits?.hits;
|
|
357
|
+
if ( !searchValue || searchValue?.length == 0 ) {
|
|
358
|
+
return res.sendError( 'No data found', 204 );
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if ( inputData.isExport == true ) {
|
|
362
|
+
const exportData = [];
|
|
363
|
+
for ( const item of searchValue ) {
|
|
364
|
+
exportData.push( {
|
|
365
|
+
'Store Name': item._source.storeName || '--',
|
|
366
|
+
'Store ID': item._source.storeId,
|
|
367
|
+
'Ticket ID': item._source.ticketId,
|
|
368
|
+
'Ticket raised on': dayjs( item._source.createdAt ).format( 'DD MMM, YYYY' ),
|
|
369
|
+
'Total Footfalls': item._source.footfallCount,
|
|
370
|
+
'Duplicates': item._source.duplicateCount,
|
|
371
|
+
'Employee/Staff': item._source.employeeCount,
|
|
372
|
+
'HouseKeeping': item._source.houseKeepingCount,
|
|
373
|
+
'Revised Footfalls': item._source.footfallCount - ( item._source.duplicateCount + item._source.employeeCount + item._source.houseKeepingCount ),
|
|
374
|
+
'Status': item._source.status,
|
|
284
375
|
} );
|
|
285
376
|
}
|
|
286
|
-
await download(
|
|
287
|
-
return;
|
|
377
|
+
return await download( exportData, res );
|
|
288
378
|
}
|
|
289
|
-
|
|
290
|
-
return res.sendSuccess( { result: response, count: geteDataCount?.body?.count } );
|
|
379
|
+
return res.sendSuccess( { result: searchValue, count: count } );
|
|
291
380
|
} catch ( error ) {
|
|
292
381
|
const err = error.message || 'Internal Server Error';
|
|
293
382
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -299,65 +388,161 @@ export async function getTickets( req, res ) {
|
|
|
299
388
|
try {
|
|
300
389
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
301
390
|
const inputData = req.query;
|
|
302
|
-
const limit = inputData.limit
|
|
303
|
-
const skip= inputData.offset == 0? 0:( inputData.offset - 1 ) *limit || 0;
|
|
391
|
+
const limit = inputData.limit;
|
|
392
|
+
const skip = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
304
393
|
inputData.storeId = inputData.storeId.split( ',' ); // convert strig to array
|
|
305
394
|
logger.info( { inputData: inputData, limit: limit, skip: skip } );
|
|
306
|
-
|
|
307
|
-
|
|
395
|
+
let source = [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'duplicateCount', 'comments', 'employee', 'houseKeeping', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus' ];
|
|
396
|
+
let filter = [
|
|
397
|
+
{
|
|
398
|
+
terms: {
|
|
399
|
+
'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
400
|
+
inputData.storeId :
|
|
401
|
+
inputData.storeId,
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
range: {
|
|
406
|
+
dateString: {
|
|
407
|
+
gte: inputData.fromDate,
|
|
408
|
+
lte: inputData.toDate,
|
|
409
|
+
format: 'yyyy-MM-dd',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
];
|
|
414
|
+
if ( inputData.status ) {
|
|
415
|
+
filter.push(
|
|
416
|
+
{
|
|
417
|
+
term: {
|
|
418
|
+
'status.keyword': inputData.status,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (
|
|
424
|
+
inputData.revopsType
|
|
425
|
+
) {
|
|
426
|
+
filter.push( {
|
|
427
|
+
exists: {
|
|
428
|
+
field: inputData.revopsType,
|
|
429
|
+
},
|
|
430
|
+
} );
|
|
431
|
+
inputData.revopsType === 'employee' ?
|
|
432
|
+
filter.push( {
|
|
433
|
+
range: {
|
|
434
|
+
employeeCount: {
|
|
435
|
+
gt: 0,
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
} ) :
|
|
439
|
+
inputData.revopsType === 'housekeeping' ?
|
|
440
|
+
filter.push( {
|
|
441
|
+
range: {
|
|
442
|
+
housekeepingCount: {
|
|
443
|
+
gt: 0,
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
} ) :
|
|
447
|
+
filter.push( {
|
|
448
|
+
range: {
|
|
449
|
+
duplicateCount: {
|
|
450
|
+
gt: 0,
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
} );
|
|
454
|
+
source = inputData.revopsType == 'employee' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'employeeCount', 'comments', 'employee', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus' ] :
|
|
455
|
+
inputData.revopsType == 'houseKeeping' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'houseKeepingCount', 'comments', 'houseKeeping', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus' ] :
|
|
456
|
+
inputData.revopsType == 'duplicateImages' ? [ 'storeId', 'dateString', 'ticketName', 'footfallCount', 'duplicateCount', 'comments', 'duplicateImages', 'ticketId', 'clientId', 'storeName', 'createdAt', 'updatedAt', 'userName', 'role', 'status', 'employeeStatus', 'houseKeepingStatus', 'duplicateStatus' ] : [];
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if ( inputData.action ) {
|
|
460
|
+
filter.push( {
|
|
308
461
|
bool: {
|
|
309
|
-
|
|
310
|
-
{ terms: { 'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
311
|
-
inputData.storeId :
|
|
312
|
-
inputData.storeId } },
|
|
462
|
+
should: [
|
|
313
463
|
{
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
464
|
+
term: {
|
|
465
|
+
'houseKeepingStatus.keyword': inputData.action,
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
term: {
|
|
470
|
+
'employeeStatus.keyword': inputData.action,
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
term: {
|
|
475
|
+
'duplicateStatus.keyword': inputData.action,
|
|
320
476
|
},
|
|
321
477
|
},
|
|
322
478
|
],
|
|
479
|
+
minimum_should_match: 1, // Ensures at least one should condition must match
|
|
480
|
+
},
|
|
481
|
+
} );
|
|
482
|
+
}
|
|
483
|
+
let getRevCount = {};
|
|
484
|
+
if ( inputData.revopsType ) {
|
|
485
|
+
getRevCount = {
|
|
486
|
+
size: 0,
|
|
487
|
+
query: {
|
|
488
|
+
bool: {
|
|
489
|
+
filter: filter,
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
aggs: {
|
|
493
|
+
totalCount: {
|
|
494
|
+
sum: {
|
|
495
|
+
field: inputData.revopsType == 'employee' ? 'employeeCount' : inputData.revopsType == 'houseKeeping' ? 'houseKeepingCount' : 'duplicateCount',
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const getCount = {
|
|
502
|
+
query: {
|
|
503
|
+
bool: {
|
|
504
|
+
filter: filter,
|
|
323
505
|
},
|
|
324
506
|
},
|
|
325
507
|
};
|
|
326
508
|
|
|
509
|
+
|
|
327
510
|
const geteDataCount = await getOpenSearchCount( openSearch.footfallDirectory, getCount );
|
|
328
|
-
|
|
511
|
+
const geteRevDataCount = inputData?.revopsType ? await getOpenSearchData( openSearch.footfallDirectory, getRevCount ) : null;
|
|
512
|
+
const revCount = inputData?.revopsType ? geteRevDataCount?.body?.aggregations?.totalCount?.value : 0;
|
|
513
|
+
const count = geteDataCount?.body?.count;
|
|
514
|
+
if ( !geteDataCount || count == 0 ) {
|
|
329
515
|
return res.sendError( 'No data found', 204 );
|
|
330
516
|
}
|
|
517
|
+
|
|
331
518
|
const getQuery = {
|
|
332
519
|
size: limit,
|
|
333
520
|
from: skip,
|
|
334
521
|
query: {
|
|
335
522
|
bool: {
|
|
336
|
-
filter:
|
|
337
|
-
{ terms: { 'storeId.keyword': Array.isArray( inputData.storeId ) ?
|
|
338
|
-
inputData.storeId :
|
|
339
|
-
inputData.storeId } },
|
|
340
|
-
{
|
|
341
|
-
range: {
|
|
342
|
-
dateString: {
|
|
343
|
-
gte: inputData.fromDate,
|
|
344
|
-
lte: inputData.toDate,
|
|
345
|
-
format: 'yyyy-MM-dd',
|
|
346
|
-
},
|
|
347
|
-
},
|
|
348
|
-
},
|
|
349
|
-
],
|
|
523
|
+
filter: filter,
|
|
350
524
|
},
|
|
351
525
|
},
|
|
352
|
-
|
|
526
|
+
_source: source,
|
|
353
527
|
};
|
|
354
528
|
|
|
355
529
|
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
356
530
|
const response = getData?.body?.hits?.hits;
|
|
531
|
+
// let temp=[ response[0] ];
|
|
532
|
+
// logger.info( { temp: temp, response: response } );
|
|
533
|
+
// if ( inputData.revopsType ) {
|
|
534
|
+
// const key =inputData.revopsType == 'employee'? 'employeeCount':inputData.revopsType == 'houseKeeping'?'houseKeepingCount': 'duplicateCount';
|
|
535
|
+
|
|
536
|
+
// const type = inputData.revopsType;
|
|
537
|
+
// temp[0]._source[key] = revCount;
|
|
538
|
+
// for ( const doc of response.slice( 1 ) ) {
|
|
539
|
+
// inputData.revopsType == 'duplicateImages'? temp[0]._source[type].push( ...doc._source[type] ):temp[0]._source[type].push( doc._source[type] );
|
|
540
|
+
// }
|
|
541
|
+
// }
|
|
357
542
|
logger.info( { response: response, body: getData?.body, getData: getData, geteDataCount: geteDataCount } );
|
|
358
543
|
|
|
359
544
|
|
|
360
|
-
if ( inputData.isExport=== true ) {
|
|
545
|
+
if ( inputData.isExport === true ) {
|
|
361
546
|
const temp = [];
|
|
362
547
|
for ( const item of response ) {
|
|
363
548
|
temp.push( {
|
|
@@ -369,7 +554,7 @@ export async function getTickets( req, res ) {
|
|
|
369
554
|
'Duplicates': item.duplicateCount,
|
|
370
555
|
'Employee/Staff': item.employeeCount,
|
|
371
556
|
'HouseKeeping': item.houseKeepingCount,
|
|
372
|
-
'Revised Footfalls': item.footfallCount-( item.duplicateCount+item.employeeCount+item.houseKeepingCount ),
|
|
557
|
+
'Revised Footfalls': item.footfallCount - ( item.duplicateCount + item.employeeCount + item.houseKeepingCount ),
|
|
373
558
|
'Status': item.status,
|
|
374
559
|
} );
|
|
375
560
|
}
|
|
@@ -377,7 +562,7 @@ export async function getTickets( req, res ) {
|
|
|
377
562
|
return;
|
|
378
563
|
}
|
|
379
564
|
|
|
380
|
-
return res.sendSuccess( { result: response, count:
|
|
565
|
+
return res.sendSuccess( { result: inputData.revopsType ? response : response, count: count, revopCount: revCount } );
|
|
381
566
|
} catch ( error ) {
|
|
382
567
|
const err = error.message || 'Internal Server Error';
|
|
383
568
|
logger.error( { error: error, messgage: req.query } );
|
|
@@ -387,45 +572,187 @@ export async function getTickets( req, res ) {
|
|
|
387
572
|
|
|
388
573
|
export async function updateStatus( req, res ) {
|
|
389
574
|
try {
|
|
575
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
390
576
|
const inputData = req.body;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const dateString = splitId[1];
|
|
395
|
-
if ( inputData.duplicateStatus === 'approved' && inputData.employeeStatus === 'approved' && inputData.houseKeepingStatus === 'approved' ) {
|
|
396
|
-
inputData.status = 'closed';
|
|
577
|
+
let temp = [];
|
|
578
|
+
for ( let data of inputData?.data ) {
|
|
579
|
+
await updateTicketStatus( data, openSearch, temp );
|
|
397
580
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
581
|
+
|
|
582
|
+
if ( temp.length == inputData?.data?.length ) {
|
|
583
|
+
return res.sendSuccess( 'Ticket has been updated successfully' );
|
|
584
|
+
}
|
|
585
|
+
} catch ( error ) {
|
|
586
|
+
const err = error.messgae || 'Internal Server Error';
|
|
587
|
+
logger.error( { error: error, data: req.body, function: 'infra-footfallDirectory-updateStatus' } );
|
|
588
|
+
return res.sendError( err, 500 );
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
export async function updateTicketStatus( data, openSearch, temp ) {
|
|
592
|
+
const id = data._id;
|
|
593
|
+
const splitId = id.split( '_' );
|
|
594
|
+
const storeId = splitId[0];
|
|
595
|
+
const dateString = splitId[1];
|
|
596
|
+
if ( data.duplicateStatus !== 'pending' && data.employeeStatus !== 'pending' && data.houseKeepingStatus !== 'pending' ) {
|
|
597
|
+
data.status = 'closed';
|
|
598
|
+
}
|
|
599
|
+
let tempId = [];
|
|
600
|
+
const { _id, ...updateData } = data;
|
|
601
|
+
await updateOpenSearchData( openSearch.footfallDirectory, _id, { doc: updateData } );
|
|
602
|
+
let bulkBody = [];
|
|
603
|
+
for ( let duplicate of data.duplicateImages ) {
|
|
604
|
+
bulkBody.push(
|
|
605
|
+
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${duplicate.timeRange}_${duplicate.tempId}` } },
|
|
606
|
+
{ doc: { duplicateImage: duplicate.duplicateImage } },
|
|
607
|
+
);
|
|
608
|
+
duplicate?.data?.map( ( item ) => {
|
|
609
|
+
item.isChecked == true ? tempId.push( item.tempId ) : null;
|
|
406
610
|
bulkBody.push(
|
|
407
611
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${item.timeRange}_${item.tempId}` } },
|
|
408
612
|
{ doc: { isChecked: item.isChecked } },
|
|
409
613
|
);
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
614
|
+
} );
|
|
615
|
+
}
|
|
616
|
+
for ( let employee of data.employee ) {
|
|
617
|
+
employee.isChecked == true ? tempId.push( employee.tempId ) : null;
|
|
413
618
|
bulkBody.push(
|
|
414
619
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${employee.timeRange}_${employee.tempId}` } },
|
|
415
620
|
{ doc: { isChecked: employee.isChecked } },
|
|
416
621
|
);
|
|
417
|
-
|
|
418
|
-
|
|
622
|
+
}
|
|
623
|
+
for ( let houseKeeping of data.houseKeeping ) {
|
|
624
|
+
houseKeeping.isChecked == true ? tempId.push( houseKeeping.tempId ) : null;
|
|
419
625
|
bulkBody.push(
|
|
420
626
|
{ update: { _index: openSearch.revop, _id: `${storeId}_${dateString}_${houseKeeping.timeRange}_${houseKeeping.tempId}` } },
|
|
421
627
|
{ doc: { isChecked: houseKeeping.isChecked } },
|
|
422
628
|
);
|
|
629
|
+
}
|
|
630
|
+
temp.push( storeId );
|
|
631
|
+
// if ( inputData.status == 'closed' ) {
|
|
632
|
+
// const isSendMessge = await sendSqsMessage( inputData, tempId );
|
|
633
|
+
// if ( isSendMessge ==true ) {
|
|
634
|
+
// return res.sendSuccess( 'Ticket has been updated successfully' );
|
|
635
|
+
// } else {
|
|
636
|
+
// return res.sendError( 'No SQS message sent', 500 );
|
|
637
|
+
// }
|
|
638
|
+
// }
|
|
639
|
+
// if ( data.status == 'closed' ) {
|
|
640
|
+
// await sendSqsMessage( data, tempId );
|
|
641
|
+
// }
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export async function sendSqsMessage( inputData, tempId ) {
|
|
645
|
+
const sqs = JSON.parse( process.env.sqs );
|
|
646
|
+
const sqsProduceQueue = {
|
|
647
|
+
QueueUrl: `${sqs.url}${sqs.revopticket}`,
|
|
648
|
+
MessageBody: JSON.stringify( {
|
|
649
|
+
store_id: inputData.storeId,
|
|
650
|
+
store_date: inputData.dateString.split( '-' ).reverse().join( '-' ),
|
|
651
|
+
bucket_name: '',
|
|
652
|
+
zone_id: 'traffic_zone',
|
|
653
|
+
process_type: 'reduction',
|
|
654
|
+
revop_type: 'footfall',
|
|
655
|
+
temp_id: tempId,
|
|
656
|
+
} ),
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const sqsQueue = await sendMessageToQueue(
|
|
660
|
+
sqsProduceQueue.QueueUrl,
|
|
661
|
+
sqsProduceQueue.MessageBody,
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
if ( sqsQueue.statusCode ) {
|
|
665
|
+
logger.error( {
|
|
666
|
+
error: `${sqsQueue}`,
|
|
667
|
+
type: 'UPLOAD_ERROR',
|
|
668
|
+
} );
|
|
669
|
+
return res.sendError( 'SQS not sent', 500 );
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export async function getTaggedStores( req, res ) {
|
|
674
|
+
try {
|
|
675
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
676
|
+
const inputData = req.query;
|
|
677
|
+
logger.info( { inputData: inputData } );
|
|
678
|
+
let filter = [
|
|
679
|
+
{
|
|
680
|
+
term: { 'clientId.keyword': inputData.clientId },
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
range: {
|
|
684
|
+
dateString: {
|
|
685
|
+
gte: inputData.fromDate,
|
|
686
|
+
lte: inputData.toDate,
|
|
687
|
+
format: 'yyyy-MM-dd',
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
|
|
692
|
+
{
|
|
693
|
+
exists: {
|
|
694
|
+
field: 'storeId',
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
exists: {
|
|
699
|
+
field: 'storeName',
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
];
|
|
703
|
+
|
|
704
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
705
|
+
filter.push( {
|
|
706
|
+
regexp: {
|
|
707
|
+
'storeName': {
|
|
708
|
+
value: `.*${inputData.searchValue.toLowerCase()}.*`,
|
|
709
|
+
flags: 'ALL',
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
} );
|
|
423
713
|
}
|
|
424
|
-
|
|
714
|
+
const getCount = {
|
|
715
|
+
size: 0,
|
|
716
|
+
query: {
|
|
717
|
+
bool: {
|
|
718
|
+
filter: filter,
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
aggs: {
|
|
722
|
+
unique_stores: {
|
|
723
|
+
composite: {
|
|
724
|
+
size: 1000,
|
|
725
|
+
sources: [
|
|
726
|
+
{
|
|
727
|
+
storeId: {
|
|
728
|
+
terms: { field: 'storeId.keyword' },
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
storeName: {
|
|
733
|
+
terms: { field: 'storeName.keyword' },
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
],
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
let temp = [];
|
|
742
|
+
|
|
743
|
+
const geteDataCount = await getOpenSearchData( openSearch.footfallDirectory, getCount );
|
|
744
|
+
if ( !geteDataCount && geteDataCount?.body?.aggregations?.unique_stores?.buckets?.length > 0 ) {
|
|
745
|
+
return res.sendError( 'No data found', 204 );
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const response = geteDataCount?.body?.aggregations?.unique_stores?.buckets;
|
|
749
|
+
logger.info( { response: response } );
|
|
750
|
+
response?.map( ( item ) => temp.push( { storeId: item.key.storeId, storeName: item.key.storeName } ) );
|
|
751
|
+
return res.sendSuccess( { result: temp, count: response?.length } );
|
|
425
752
|
} catch ( error ) {
|
|
426
|
-
const err = error.
|
|
427
|
-
logger.error( { error: error,
|
|
428
|
-
return res.
|
|
753
|
+
const err = error.message || 'Internal Server Error';
|
|
754
|
+
logger.error( { error: error, messgage: req.query } );
|
|
755
|
+
return res.sendSuccess( err, 500 );
|
|
429
756
|
}
|
|
430
757
|
}
|
|
431
758
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import j2s from 'joi-to-swagger';
|
|
2
|
-
import { createTicketSchema, getTicketsSchema, ticketListSchema, ticketSummarySchema,
|
|
2
|
+
import { createTicketSchema, getTaggedStoresSchema, getTicketsSchema, ticketListSchema, ticketSummarySchema, updateStatusSchemea } from '../dtos/footfallDirectory.dtos.js';
|
|
3
3
|
|
|
4
4
|
export const footfallDirectoryDocs = {
|
|
5
5
|
|
|
@@ -59,7 +59,6 @@ export const footfallDirectoryDocs = {
|
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
61
|
},
|
|
62
|
-
|
|
63
62
|
'/v3/footfall-directory-tagging/ticket-list': {
|
|
64
63
|
get: {
|
|
65
64
|
tags: [ 'Footfall Directory Ticket' ],
|
|
@@ -72,6 +71,12 @@ export const footfallDirectoryDocs = {
|
|
|
72
71
|
scema: j2s( ticketListSchema ).swagger,
|
|
73
72
|
required: true,
|
|
74
73
|
},
|
|
74
|
+
{
|
|
75
|
+
in: 'query',
|
|
76
|
+
name: 'storeId',
|
|
77
|
+
scema: j2s( ticketListSchema ).swagger,
|
|
78
|
+
required: false,
|
|
79
|
+
},
|
|
75
80
|
{
|
|
76
81
|
in: 'query',
|
|
77
82
|
name: 'fromDate',
|
|
@@ -86,7 +91,7 @@ export const footfallDirectoryDocs = {
|
|
|
86
91
|
},
|
|
87
92
|
{
|
|
88
93
|
in: 'query',
|
|
89
|
-
name: '
|
|
94
|
+
name: 'searchValue',
|
|
90
95
|
scema: j2s( ticketListSchema ).swagger,
|
|
91
96
|
required: false,
|
|
92
97
|
},
|
|
@@ -108,6 +113,12 @@ export const footfallDirectoryDocs = {
|
|
|
108
113
|
scema: j2s( ticketListSchema ).swagger,
|
|
109
114
|
required: false,
|
|
110
115
|
},
|
|
116
|
+
{
|
|
117
|
+
in: 'query',
|
|
118
|
+
name: 'sortBy',
|
|
119
|
+
scema: j2s( ticketListSchema ).swagger,
|
|
120
|
+
required: false,
|
|
121
|
+
},
|
|
111
122
|
],
|
|
112
123
|
responses: {
|
|
113
124
|
200: { description: 'Successful' },
|
|
@@ -143,6 +154,36 @@ export const footfallDirectoryDocs = {
|
|
|
143
154
|
scema: j2s( getTicketsSchema ).swagger,
|
|
144
155
|
required: true,
|
|
145
156
|
},
|
|
157
|
+
{
|
|
158
|
+
in: 'query',
|
|
159
|
+
name: 'revopsType',
|
|
160
|
+
scema: j2s( getTicketsSchema ).swagger,
|
|
161
|
+
required: false,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
in: 'query',
|
|
165
|
+
name: 'status',
|
|
166
|
+
scema: j2s( getTicketsSchema ).swagger,
|
|
167
|
+
required: false,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
in: 'query',
|
|
171
|
+
name: 'action',
|
|
172
|
+
scema: j2s( getTicketsSchema ).swagger,
|
|
173
|
+
required: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
in: 'query',
|
|
177
|
+
name: 'limit',
|
|
178
|
+
scema: j2s( getTicketsSchema ).swagger,
|
|
179
|
+
required: true,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
in: 'query',
|
|
183
|
+
name: 'offset',
|
|
184
|
+
scema: j2s( getTicketsSchema ).swagger,
|
|
185
|
+
required: false,
|
|
186
|
+
},
|
|
146
187
|
],
|
|
147
188
|
responses: {
|
|
148
189
|
200: { description: 'Successful' },
|
|
@@ -159,14 +200,7 @@ export const footfallDirectoryDocs = {
|
|
|
159
200
|
tags: [ 'Footfall Directory Ticket' ],
|
|
160
201
|
description: 'approve or reject the ticket status',
|
|
161
202
|
operationId: 'update-status',
|
|
162
|
-
parameters: [
|
|
163
|
-
{
|
|
164
|
-
in: 'query',
|
|
165
|
-
name: 'storeId',
|
|
166
|
-
scema: j2s( updateStatusQuerySchema ).swagger,
|
|
167
|
-
required: true,
|
|
168
|
-
},
|
|
169
|
-
],
|
|
203
|
+
parameters: [],
|
|
170
204
|
requestBody: {
|
|
171
205
|
content: {
|
|
172
206
|
'application/json': {
|
|
@@ -183,4 +217,46 @@ export const footfallDirectoryDocs = {
|
|
|
183
217
|
},
|
|
184
218
|
},
|
|
185
219
|
},
|
|
220
|
+
|
|
221
|
+
'/v3/footfall-directory-tagging/get-tagged-stores': {
|
|
222
|
+
get: {
|
|
223
|
+
tags: [ 'Footfall Directory Ticket' ],
|
|
224
|
+
description: 'To get list of tagged stores',
|
|
225
|
+
operationId: 'get-tagged-stores',
|
|
226
|
+
parameters: [
|
|
227
|
+
{
|
|
228
|
+
in: 'query',
|
|
229
|
+
name: 'clientId',
|
|
230
|
+
scema: j2s( getTaggedStoresSchema ).swagger,
|
|
231
|
+
required: true,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
in: 'query',
|
|
235
|
+
name: 'fromDate',
|
|
236
|
+
scema: j2s( getTaggedStoresSchema ).swagger,
|
|
237
|
+
required: true,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
in: 'query',
|
|
241
|
+
name: 'toDate',
|
|
242
|
+
scema: j2s( getTaggedStoresSchema ).swagger,
|
|
243
|
+
required: true,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
in: 'query',
|
|
247
|
+
name: 'searchValue',
|
|
248
|
+
scema: j2s( getTaggedStoresSchema ).swagger,
|
|
249
|
+
required: false,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
responses: {
|
|
253
|
+
200: { description: 'Successful' },
|
|
254
|
+
401: { description: 'Unauthorized User' },
|
|
255
|
+
422: { description: 'Field Error' },
|
|
256
|
+
500: { description: 'Server Error' },
|
|
257
|
+
204: { description: 'Not Found' },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
|
|
186
262
|
};
|
|
@@ -121,10 +121,12 @@ export const ticketSummaryValid = {
|
|
|
121
121
|
|
|
122
122
|
export const ticketListSchema = Joi.object().keys( {
|
|
123
123
|
clientId: Joi.string().required(),
|
|
124
|
+
storeId: Joi.string().optional(),
|
|
124
125
|
searchValue: Joi.string().optional().allow( '' ),
|
|
125
126
|
limit: Joi.number().optional(),
|
|
126
127
|
offset: Joi.number().optional(),
|
|
127
128
|
isExport: Joi.boolean().optional(),
|
|
129
|
+
sortBy: Joi.string().optional().allow( '' ),
|
|
128
130
|
fromDate: Joi.string()
|
|
129
131
|
.pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
|
|
130
132
|
.required()
|
|
@@ -185,7 +187,6 @@ export const ticketListValid = {
|
|
|
185
187
|
|
|
186
188
|
export const getTicketsSchema = Joi.object().keys( {
|
|
187
189
|
storeId: Joi.string().required(),
|
|
188
|
-
|
|
189
190
|
fromDate: Joi.string()
|
|
190
191
|
.pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
|
|
191
192
|
.required()
|
|
@@ -221,6 +222,12 @@ export const getTicketsSchema = Joi.object().keys( {
|
|
|
221
222
|
|
|
222
223
|
return value;
|
|
223
224
|
} ),
|
|
225
|
+
status: Joi.string().optional(),
|
|
226
|
+
action: Joi.string().optional(),
|
|
227
|
+
revopsType: Joi.string().optional(),
|
|
228
|
+
limit: Joi.number().required(),
|
|
229
|
+
offset: Joi.number().optional(),
|
|
230
|
+
|
|
224
231
|
} ).custom( ( value, helpers ) => {
|
|
225
232
|
const from = dayjs( value.fromDate );
|
|
226
233
|
const to = dayjs( value.toDate );
|
|
@@ -244,26 +251,31 @@ export const getTicketsValid = {
|
|
|
244
251
|
query: getTicketsSchema,
|
|
245
252
|
};
|
|
246
253
|
|
|
247
|
-
export const updateStatusQuerySchema =Joi.object().keys( {
|
|
248
|
-
|
|
249
|
-
_id: Joi.string().required(),
|
|
250
|
-
} );
|
|
251
|
-
|
|
252
254
|
export const updateStatusSchemea =Joi.object().keys( {
|
|
253
|
-
|
|
254
|
-
duplicateACCount: Joi.number().required(),
|
|
255
|
-
employeeACCount: Joi.number().required(),
|
|
256
|
-
houseKeepingACCount: Joi.number().required(),
|
|
257
|
-
comments: Joi.string().optional().allow( '' ),
|
|
258
|
-
duplicateImages: Joi.array().items(
|
|
255
|
+
data: Joi.array().items(
|
|
259
256
|
Joi.object( {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
257
|
+
_id: Joi.string().required(),
|
|
258
|
+
dateString: Joi.string().required().custom( ( value, helpers ) => {
|
|
259
|
+
const inputDate = dayjs( value, 'YYYY-MM-DD', true );
|
|
260
|
+
const today = dayjs();
|
|
261
|
+
|
|
262
|
+
if ( !inputDate.isValid() ) {
|
|
263
|
+
return helpers.error( 'any.invalid' );
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const diff = today.diff( inputDate, 'day' );
|
|
267
|
+
|
|
268
|
+
if ( diff > 10 ) {
|
|
269
|
+
return helpers.message( 'Approval is not allowed for a period exceeding 10 days' );
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return value;
|
|
273
|
+
} ),
|
|
274
|
+
duplicateACCount: Joi.number().optional(),
|
|
275
|
+
employeeACCount: Joi.number().optional(),
|
|
276
|
+
houseKeepingACCount: Joi.number().optional(),
|
|
277
|
+
comments: Joi.string().optional().allow( '' ),
|
|
278
|
+
duplicateImages: Joi.array().items(
|
|
267
279
|
Joi.object( {
|
|
268
280
|
tempId: Joi.number().required(),
|
|
269
281
|
filePath: Joi.string().required(),
|
|
@@ -271,33 +283,104 @@ export const updateStatusSchemea =Joi.object().keys( {
|
|
|
271
283
|
exitTime: Joi.string().required(),
|
|
272
284
|
timeRange: Joi.string().required(),
|
|
273
285
|
isChecked: Joi.boolean().required(),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
286
|
+
data: Joi.array().items(
|
|
287
|
+
Joi.object( {
|
|
288
|
+
tempId: Joi.number().required(),
|
|
289
|
+
filePath: Joi.string().required(),
|
|
290
|
+
entryTime: Joi.string().required(),
|
|
291
|
+
exitTime: Joi.string().required(),
|
|
292
|
+
timeRange: Joi.string().required(),
|
|
293
|
+
isChecked: Joi.boolean().required(),
|
|
294
|
+
} ),
|
|
295
|
+
).optional(),
|
|
296
|
+
} ) ).optional(),
|
|
297
|
+
houseKeeping: Joi.array().items( Joi.object( {
|
|
298
|
+
tempId: Joi.number().required(),
|
|
299
|
+
filePath: Joi.string().required(),
|
|
300
|
+
entryTime: Joi.string().required(),
|
|
301
|
+
exitTime: Joi.string().required(),
|
|
302
|
+
timeRange: Joi.string().required(),
|
|
303
|
+
isChecked: Joi.boolean().required(),
|
|
304
|
+
|
|
305
|
+
} ) ).optional(),
|
|
306
|
+
employee: Joi.array().items( Joi.object( {
|
|
307
|
+
tempId: Joi.number().required(),
|
|
308
|
+
filePath: Joi.string().required(),
|
|
309
|
+
entryTime: Joi.string().required(),
|
|
310
|
+
exitTime: Joi.string().required(),
|
|
311
|
+
timeRange: Joi.string().required(),
|
|
312
|
+
isChecked: Joi.boolean().required(),
|
|
313
|
+
|
|
314
|
+
} ) ).optional(),
|
|
315
|
+
duplicateStatus: Joi.string().required(),
|
|
316
|
+
employeeStatus: Joi.string().required(),
|
|
317
|
+
houseKeepingStatus: Joi.string().required(),
|
|
318
|
+
} ) ).required(),
|
|
293
319
|
|
|
294
|
-
} ) ).optional(),
|
|
295
|
-
duplicateStatus: Joi.string().required(),
|
|
296
|
-
employeeStatus: Joi.string().required(),
|
|
297
|
-
houseKeepingStatus: Joi.string().required(),
|
|
298
320
|
} );
|
|
299
321
|
|
|
300
322
|
export const updateStatusValid = {
|
|
301
|
-
query: updateStatusQuerySchema,
|
|
302
323
|
body: updateStatusSchemea,
|
|
303
324
|
};
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
export const getTaggedStoresSchema = Joi.object().keys( {
|
|
328
|
+
clientId: Joi.string().required(),
|
|
329
|
+
searchValue: Joi.string().optional().allow( '' ),
|
|
330
|
+
fromDate: Joi.string()
|
|
331
|
+
.pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
|
|
332
|
+
.required()
|
|
333
|
+
.messages( {
|
|
334
|
+
'string.pattern.name': `'fromDate' must be in the format YYYY-MM-DD (e.g., 2025-07-19).`,
|
|
335
|
+
'string.empty': `'fromDate' is required.`,
|
|
336
|
+
} )
|
|
337
|
+
.custom( ( value, helpers ) => {
|
|
338
|
+
const from = dayjs( value );
|
|
339
|
+
if ( !from.isValid() ) {
|
|
340
|
+
return helpers.error( 'any.invalid', { message: 'Invalid fromDate' } );
|
|
341
|
+
}
|
|
342
|
+
return value;
|
|
343
|
+
} ),
|
|
344
|
+
|
|
345
|
+
toDate: Joi.string()
|
|
346
|
+
.pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
|
|
347
|
+
.required()
|
|
348
|
+
.messages( {
|
|
349
|
+
'string.pattern.name': `'toDate' must be in the format YYYY-MM-DD (e.g., 2025-07-19).`,
|
|
350
|
+
'string.empty': `'toDate' is required.`,
|
|
351
|
+
} )
|
|
352
|
+
.custom( ( value, helpers ) => {
|
|
353
|
+
const to = dayjs( value );
|
|
354
|
+
const today = dayjs();
|
|
355
|
+
|
|
356
|
+
if ( !to.isValid() ) {
|
|
357
|
+
return helpers.error( 'any.invalid', { message: 'Invalid toDate' } );
|
|
358
|
+
}
|
|
359
|
+
if ( to.isAfter( today, 'day' ) ) {
|
|
360
|
+
return helpers.error( 'any.invalid', { message: 'toDate cannot be in the future' } );
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return value;
|
|
364
|
+
} ),
|
|
365
|
+
} ).custom( ( value, helpers ) => {
|
|
366
|
+
const from = dayjs( value.fromDate );
|
|
367
|
+
const to = dayjs( value.toDate );
|
|
368
|
+
|
|
369
|
+
if ( !from.isValid() || !to.isValid() ) {
|
|
370
|
+
return helpers.error( 'any.invalid', { message: 'Invalid dates' } );
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if ( from.isAfter( to ) ) {
|
|
374
|
+
return helpers.error( 'any.invalid', { message: 'fromDate cannot be after toDate' } );
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if ( to.diff( from, 'day' ) > 90 ) {
|
|
378
|
+
return helpers.error( 'any.invalid', { message: 'Date range cannot exceed 90 days' } );
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return value;
|
|
382
|
+
} );
|
|
383
|
+
|
|
384
|
+
export const getTaggedStoresValid = {
|
|
385
|
+
query: getTaggedStoresSchema,
|
|
386
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { isExist } from '../validations/footfallDirectory.validation.js';
|
|
3
|
-
import { createTicket, getTickets, ticketList, ticketSummary, updateStatus } from '../controllers/footfallDirectory.controllers.js';
|
|
4
|
-
import { createTicketValid, getTicketsValid, ticketListValid, ticketSummaryValid, updateStatusValid } from '../dtos/footfallDirectory.dtos.js';
|
|
3
|
+
import { createTicket, getTaggedStores, getTickets, ticketList, ticketSummary, updateStatus } from '../controllers/footfallDirectory.controllers.js';
|
|
4
|
+
import { createTicketValid, getTaggedStoresValid, getTicketsValid, ticketListValid, ticketSummaryValid, updateStatusValid } from '../dtos/footfallDirectory.dtos.js';
|
|
5
5
|
import { bulkValidate, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
6
6
|
|
|
7
7
|
export const footfallDirectoryRouter = express.Router();
|
|
@@ -11,6 +11,7 @@ footfallDirectoryRouter.get( '/ticket-summary', isAllowedSessionHandler, bulkVal
|
|
|
11
11
|
|
|
12
12
|
footfallDirectoryRouter.get( '/ticket-list', isAllowedSessionHandler, bulkValidate( ticketListValid ), ticketList );
|
|
13
13
|
footfallDirectoryRouter.get( '/get-tickets', isAllowedSessionHandler, bulkValidate( getTicketsValid ), getTickets );
|
|
14
|
+
footfallDirectoryRouter.get( '/get-tagged-stores', bulkValidate( getTaggedStoresValid ), getTaggedStores );
|
|
14
15
|
|
|
15
16
|
footfallDirectoryRouter.put( '/update-status', bulkValidate( updateStatusValid ), updateStatus );
|
|
16
17
|
|