tango-app-api-infra 3.3.3-beta.2 → 3.3.3-beta.3

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,1564 +1,1564 @@
1
-
2
- import { logger, getOpenSearchData } from 'tango-app-api-middleware';
3
- import dayjs from 'dayjs';
4
- import utc from 'dayjs/plugin/utc.js';
5
- import timezone from 'dayjs/plugin/timezone.js';
6
- import 'dayjs/locale/en.js';
7
- import { readFileSync } from 'fs';
8
- import { join } from 'path';
9
- import handlebars from 'handlebars';
10
- dayjs.extend( utc );
11
- dayjs.extend( timezone );
12
- import { sendEmailWithSES, signedUrl } from 'tango-app-api-middleware';
13
- import { createClient, findClient, aggregateClient, findOneClient } from '../services/client.service.js';
14
- import { createStore, findStore, updateOneStore, findOneStore } from '../services/store.service.js';
15
- import { findTangoTicket, findOneTangoTicket, countDocumentsTangoTicket, updateOneTangoTicketunset, aggregateTangoTicket, updateOneTangoTicket } from '../services/tangoTicket.service.js';
16
- import { findOneGroup } from '../services/group.service.js';
17
- import { findinfraReason } from '../services/infraReason.service.js';
18
- import { findOneUser } from '../services/user.service.js';
19
- import xl from 'excel4node';
20
- import { findOneinfraReason } from '../services/infraReason.service.js';
21
- import userAssignedStoreModel from 'tango-api-schema/schema/userAssignedStore.model.js';
22
- import { findmailonlyuser } from '../services/mailonlyusers.service.js';
23
- import { findOneCluster } from '../services/cluster.service.js';
24
- import { findcluster } from '../services/cluster.service.js';
25
- import { findteams } from '../services/teams.service.js';
26
-
27
-
28
- export async function migrateClient() {
29
- try {
30
- let oldclient = [];
31
- for ( let i = 0; i < oldclient.length; i++ ) {
32
- let framedClient = {
33
- clientName: oldclient[i].brandName,
34
- clientId: oldclient[i].clientId,
35
- tangoId: oldclient[i].brandIndex,
36
- status: 'active',
37
- ticketConfigs: {
38
- installationReAssign: 1,
39
- downTimeType: 1,
40
- infraDownTime: 1,
41
- minFilesCount: 0,
42
- rcaTicketAssign: 2,
43
- refreshAlert: 2,
44
- statusCheckAlert: 2,
45
- accuracyPercentage: 0,
46
- reTrain: 1,
47
- },
48
- };
49
- await createClient( framedClient );
50
- };
51
- } catch ( error ) {
52
- logger.error( { error: error, function: 'migrateClient' } );
53
- res.sendError( error, 500 );
54
- }
55
- }
56
-
57
-
58
- export async function migrateStores() {
59
- try {
60
- let oldstores = [];
61
- for ( let i = 0; i < oldstores.length; i++ ) {
62
- let framedStores = {
63
- storeId: oldstores[i].id,
64
- storeName: oldstores[i].name,
65
- appId: oldstores[i].appId,
66
- clientId: oldstores[i].client_id,
67
- status: 'active',
68
- storeProfile: {
69
- open: oldstores[i].configuration.storeOpenTime,
70
- close: oldstores[i].configuration.storeCloseTime,
71
- timeZone: oldstores[i].timezone,
72
- },
73
- };
74
-
75
- await createStore( framedStores );
76
- };
77
- } catch ( error ) {
78
- logger.error( { error: error, function: 'migrateStores' } );
79
- res.sendError( error, 500 );
80
- }
81
- }
82
- export async function clientList( req, res ) {
83
- try {
84
- let clientList = await findClient( { status: { $in: [ 'active', 'hold' ] } },
85
- { 'clientName': 1, 'clientId': 1, 'ticketConfigs': 1 } );
86
- logger.info( { 'clientList': clientList, 'message': 'clientList' } );
87
- res.sendSuccess( clientList );
88
- } catch ( error ) {
89
- logger.error( { error: error, function: 'basicList' } );
90
- res.sendError( error, 500 );
91
- }
92
- }
93
-
94
- export async function basicList( req, res ) {
95
- try {
96
- let clientList = await findClient( { status: { $in: [ 'active', 'hold' ] } }, { 'clientId': 1 } );
97
- let clients = [];
98
- for ( let client of clientList ) {
99
- clients.push( client.clientId );
100
- }
101
- let storeList = await findStore( { 'clientId': { $in: clients }, 'status': 'active', 'edge.firstFile': true, 'storeProfile.open': { $exists: true }, 'storeProfile.close': { $exists: true }, 'storeProfile.timeZone': { $exists: true } },
102
- { 'storeName': 1, 'storeId': 1, 'ticketConfigs': 1, 'clientId': 1, 'storeProfile.open': 1, 'storeProfile.close': 1, 'storeProfile.timeZone': 1 } );
103
- logger.info( { 'storeList': storeList, 'message': 'storeList' } );
104
-
105
- res.sendSuccess( storeList );
106
- } catch ( error ) {
107
- logger.error( { error: error, function: 'basicList' } );
108
- res.sendError( error, 500 );
109
- }
110
- }
111
-
112
-
113
- export async function setTicketTime( req, res ) {
114
- try {
115
- let input = req.body;
116
- logger.info( { 'input': input, 'message': 'setTicketTime' } );
117
- for ( let i = 0; i < input.stores.length; i++ ) {
118
- await updateOneStore( { storeId: input.stores[i].storeId }, { 'ticketConfigs.nextTicektGenerationTime': new Date( input.stores[i].nextTicektGenerationTime ) } );
119
- };
120
- res.sendSuccess( 'Updated Suceessfully' );
121
- } catch ( error ) {
122
- logger.error( { error: error, function: 'setTicketTime' } );
123
- res.sendError( error, 500 );
124
- }
125
- }
126
-
127
- export async function downStoresList( req, res ) {
128
- try {
129
- let storesList = await findStore( { 'ticketConfigs.hibernation': { $lt: new Date() }, 'ticketConfigs.nextTicektGenerationTime': new Date( req.body.getTime ) }, { storeId: 1, storeName: 1, storeProfile: 1, ticketConfigs: 1 } );
130
- logger.info( { 'storesList': storesList, 'message': 'downStoresList' } );
131
- if ( storesList.length > 0 ) {
132
- res.sendSuccess( storesList );
133
- } else {
134
- res.sendError( 'No data found', 204 );
135
- }
136
- } catch ( error ) {
137
- logger.error( { error: error, function: 'downStoresList' } );
138
- res.sendError( error, 500 );
139
- }
140
- }
141
- export async function openTicketList( req, res ) {
142
- try {
143
- let openTicketList = await findTangoTicket( { 'issueType': 'infra', 'status': { $ne: 'closed' } }, { issueType: 1, ticketId: 1, basicDetails: 1, createdAt: 1, updateAt: 1, ticketDetails: 1 } );
144
- if ( openTicketList.length ) {
145
- res.sendSuccess( {
146
- count: openTicketList.length,
147
- data: openTicketList,
148
- } );
149
- } else {
150
- res.sendError( 'No data found', 204 );
151
- }
152
- } catch ( error ) {
153
- logger.error( { error: error, function: 'openTicketList' } );
154
- res.sendError( error, 500 );
155
- }
156
- }
157
-
158
- export async function assigntoUser( req, res ) {
159
- try {
160
- await updateOneTangoTicket( { ticketId: req.body.ticketId }, { 'ticketDetails.assigntoUser': true } );
161
- res.sendSuccess( 'assigntoUser Successfully' );
162
- } catch ( error ) {
163
- logger.error( { error: error, function: 'assigntoUser' } );
164
- res.sendError( error, 500 );
165
- }
166
- }
167
- export async function updateRefreshTicket( req, res ) {
168
- try {
169
- for ( let ticket of req.body.TicketList ) {
170
- let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
171
- let findStore = await findOneStore( { storeId: getTicket.basicDetails.storeId, status: 'active' } );
172
- if ( findStore ) {
173
- await updateOneTangoTicket( { ticketId: ticket.ticketId }, { 'ticketDetails.ticketType': 'refreshticket', 'ticketDetails.refreshTicketStatus': 'notidentified' } );
174
- await updateOneTangoTicketunset( { ticketId: ticket.ticketId }, { 'ticketDetails.addressingUser': 1 } );
175
- if ( getTicket && getTicket.basicDetails ) {
176
- let getclient = await findOneClient( { clientId: getTicket.basicDetails.clientId } );
177
- if ( getclient && getclient.ticketConfigs && getclient.ticketConfigs.emailAlert ) {
178
- let downTimeQuery = {
179
- 'size': 1,
180
- 'query': {
181
- 'bool': {
182
- 'must': [
183
- {
184
- 'term': {
185
- 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
186
- },
187
- },
188
- {
189
- 'term': {
190
- 'doc.store_id.keyword': getTicket.basicDetails.storeId,
191
- },
192
- },
193
-
194
- ],
195
-
196
- },
197
- },
198
- };
199
- let downtimetotal;
200
- const downtime = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).downTimeHourly, downTimeQuery );
201
- let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
202
- if ( streamwiseDowntime.length > 0 ) {
203
- const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
204
- return accumulator + currentValue.down_time;
205
- }, 0 );
206
- const average = sum / streamwiseDowntime.length;
207
- downtimetotal = Math.round( average );
208
- } else {
209
- downtimetotal = 0;
210
- }
211
- let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
212
- const attachments = null;
213
- const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
214
- const fileContent = readFileSync( join() + '/src/hbs/refreshTicket.hbs', 'utf8' );
215
- const htmlContent = handlebars.compile( fileContent );
216
- let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
217
- if ( store.spocDetails && store.spocDetails.length > 0 ) {
218
- let spocEmail = store.spocDetails[0].email;
219
- let spocName = store.spocDetails[0].name;
220
- let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
221
- let primaryIssue = 'Not Identified';
222
- if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
223
- primaryIssue = Issue[0].reasons[0].primaryIssue;
224
- }
225
- let Uidomain = `${JSON.parse( process.env.URL ).domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
226
-
227
- const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primary: primaryIssue, storeName: getTicket.basicDetails.storeName, hibernation: '', spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: JSON.parse( process.env.URL ).apiDomain } );
228
- if ( spocEmail && isValidEmail( spocEmail ) ) {
229
- await sendEmailWithSES( spocEmail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
230
- }
231
- }
232
- }
233
- }
234
- }
235
- }
236
-
237
- res.sendSuccess( 'updated Successfully' );
238
- } catch ( error ) {
239
- logger.error( { error: error, function: 'updateRefreshTicket' } );
240
- res.sendError( error, 500 );
241
- }
242
- }
243
- export async function closeTicket( req, res ) {
244
- try {
245
- for ( let ticket of req.body.TicketList ) {
246
- let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
247
- if ( getTicket ) {
248
- if ( ticket.status == 'closed' ) {
249
- getTicket.ticketActivity.push( {
250
- actionType: 'dataRecived',
251
- timeStamp: new Date(),
252
- actionBy: 'Tango',
253
- } );
254
- }
255
- await updateOneTangoTicket( { ticketId: ticket.ticketId },
256
- {
257
- status: ticket.status,
258
- ticketActivity: getTicket.ticketActivity,
259
- issueClosedDate: new Date(),
260
- },
261
- );
262
- let downTimeQuery = {
263
- 'size': 1,
264
- 'query': {
265
- 'bool': {
266
- 'must': [
267
- {
268
- 'term': {
269
- 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
270
- },
271
- },
272
- {
273
- 'term': {
274
- 'doc.store_id.keyword': getTicket.basicDetails.storeId,
275
- },
276
- },
277
-
278
- ],
279
-
280
- },
281
- },
282
- };
283
- let downtimetotal;
284
- const downtime = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).downTimeHourly, downTimeQuery );
285
- let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
286
- if ( streamwiseDowntime.length > 0 ) {
287
- const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
288
- return accumulator + currentValue.down_time;
289
- }, 0 );
290
- const average = sum / streamwiseDowntime.length;
291
- downtimetotal = Math.round( average );
292
- } else {
293
- downtimetotal = 0;
294
- }
295
-
296
- let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
297
- let primaryIssue = '';
298
-
299
- if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
300
- primaryIssue = Issue[0].reasons[0].primaryIssue;
301
- }
302
- let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
303
- const attachments = null;
304
- const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
305
- const fileContent = readFileSync( join() + '/src/hbs/closeTicekt.hbs', 'utf8' );
306
- const htmlContent = handlebars.compile( fileContent );
307
- let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
308
- if ( store.spocDetails && store.spocDetails.length > 0 ) {
309
- let spocEmail = store.spocDetails[0].email;
310
- let spocName = store.spocDetails[0].name;
311
- let Uidomain = `${JSON.parse( process.env.URL ).domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
312
- let getclient = await findOneClient( { clientId: getTicket.basicDetails.clientId } );
313
- const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primaryIssue: primaryIssue, storeName: getTicket.basicDetails.storeName, spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD HH:mm' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: JSON.parse( process.env.URL ).apiDomain } );
314
- if ( getclient.ticketConfigs.emailAlert && spocEmail && isValidEmail( spocEmail ) ) {
315
- await sendEmailWithSES( spocEmail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
316
- }
317
- }
318
- }
319
- }
320
-
321
- res.sendSuccess( 'updated Successfully' );
322
- } catch ( error ) {
323
- logger.error( { error: error, function: 'closeTicket' } );
324
- res.sendError( error, 500 );
325
- }
326
- }
327
- function isValidEmail( email ) {
328
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
329
- return emailRegex.test( email );
330
- }
331
- export async function emailUserList( req, res ) {
332
- try {
333
- let clientList = await aggregateClient( [
334
- {
335
- $match: {
336
- 'clientId': req.query.clientId,
337
- 'status': 'active',
338
- 'ticketConfigs.infraReport.type': { $exists: true },
339
- },
340
- },
341
- {
342
- $project: {
343
- 'clientId': 1,
344
- 'ticketConfigs.infraReport': 1,
345
- },
346
- },
347
- {
348
- '$lookup': {
349
- 'from': 'users',
350
- 'let': { 'clientId': '$clientId' },
351
- 'pipeline': [
352
- {
353
- '$match': {
354
- '$expr': {
355
- $and: [
356
- { '$eq': [ '$clientId', '$$clientId' ] },
357
- { '$eq': [ '$emailAlert.infra', true ] },
358
- ],
359
- },
360
- },
361
- },
362
- {
363
- $project: {
364
- userName: 1,
365
- email: 1,
366
- role: 1,
367
- },
368
- },
369
- ],
370
- 'as': 'user',
371
- },
372
- },
373
- {
374
- $unwind: {
375
- path: '$user',
376
- preserveNullAndEmptyArrays: true,
377
- },
378
- },
379
- {
380
- '$lookup': {
381
- 'from': 'userAssignedStore',
382
- 'let': { 'email': '$user.email' },
383
- 'pipeline': [
384
- {
385
- '$match': {
386
- '$expr': {
387
- $and: [
388
- { '$eq': [ '$userEmail', '$$email' ] },
389
-
390
- ],
391
- },
392
- },
393
- },
394
- {
395
- $project: {
396
- assignedValue: 1,
397
- assignedType: 1,
398
- },
399
- },
400
- ],
401
- 'as': 'assignedstore',
402
- },
403
- },
404
- {
405
- $unwind: {
406
- path: '$assignedstore',
407
- preserveNullAndEmptyArrays: true,
408
- },
409
- },
410
- {
411
- $project: {
412
- 'clientId': 1,
413
- 'ticketConfigs': 1,
414
- 'email': '$user.email',
415
- 'userName': '$user.userName',
416
- 'role': '$user.role',
417
- 'assignedValue': '$assignedstore.assignedValue',
418
- 'assignedType': '$assignedstore.assignedType',
419
- },
420
- },
421
- {
422
- $group: {
423
- '_id': '$email',
424
- 'clientId': { $first: '$clientId' },
425
- 'ticketConfigs': { $first: '$ticketConfigs' },
426
- 'email': { $first: '$email' },
427
- 'userName': { $first: '$userName' },
428
- 'role': { $first: '$role' },
429
- 'assignedValue': { $push: '$assignedValue' },
430
- 'assignedType': { $first: '$assignedType' },
431
- },
432
- },
433
- ] );
434
-
435
- let getconfigs = await findOneClient( { clientId: req.query.clientId }, { 'ticketConfigs.infraReport': 1 } );
436
- if ( getconfigs && getconfigs.ticketConfigs && getconfigs.ticketConfigs.infraReport ) {
437
- if ( getconfigs.ticketConfigs.infraReport.allowCsm ) {
438
- let findCsm = {
439
- };
440
- let result = await userAssignedStoreModel.findOne( {
441
- 'clientId': req.query.clientId, 'userType': 'tango',
442
- 'tangoUserType': 'csm',
443
- } );
444
- if ( findCsm ) {
445
- findCsm.clientId = req.query.clientId,
446
- findCsm.ticketConfigs = getconfigs.ticketConfigs;
447
- findCsm.email = result.userEmail;
448
- findCsm.userName = result.userEmail ? result.userEmail.split( '@' )[0] : '';
449
- let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
450
- findCsm.storeList = storelist;
451
- clientList = [ ...clientList, ...[ findCsm ] ];
452
- }
453
- }
454
- if ( getconfigs.ticketConfigs.infraReport.allowSpoc ) {
455
- let spocList=[];
456
- let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1, 'spocDetails': 1 } );
457
- for ( let store of storelist ) {
458
- if ( store.spocDetails&&store.spocDetails.length>0 ) {
459
- if ( store.spocDetails[0].email!='' ) {
460
- spocList.push( {
461
- clientId: req.query.clientId,
462
- ticketConfigs: getconfigs.ticketConfigs,
463
- email: store.spocDetails[0].email,
464
- userName: store.spocDetails[0].name,
465
- storeList: [ store ],
466
- } );
467
- }
468
- }
469
- }
470
- clientList = [ ...clientList, ...spocList ];
471
- }
472
-
473
- let findOutsideUserlist = await findmailonlyuser( { 'clientId': req.query.clientId, 'emailAlert.infra': true } );
474
- let MailOnlyUsers = [];
475
- if ( findOutsideUserlist.length > 0 ) {
476
- for ( let user of findOutsideUserlist ) {
477
- let obj = {
478
- clientId: req.query.clientId,
479
- ticketConfigs: getconfigs.ticketConfigs,
480
- email: user.email,
481
- userName: user.name,
482
- storeList: [],
483
- };
484
- if ( user.stores&&user.stores.length>0 ) {
485
- let storelist = await findStore( { 'storeId': { $in: user.stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
486
- obj.storeList = storelist;
487
- }
488
- for ( let group of user.groups ) {
489
- let groupdata = await findOneGroup( { 'groupName': group, 'clientId': req.query.clientId } );
490
- let storelist = await findStore( { 'storeId': { $in: groupdata.storeList }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
491
- obj.storeList = [ ...obj.storeList, ...storelist ];
492
- }
493
- MailOnlyUsers.push( obj );
494
- }
495
- clientList = [ ...clientList, ...MailOnlyUsers ];
496
- }
497
- }
498
-
499
- let response = [];
500
- for ( let user of clientList ) {
501
- if ( user.role == 'superadmin' ) {
502
- let storelist = await findStore( { 'clientId': user.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
503
- user.storeList = storelist;
504
- } else if ( user.role != 'superadmin' ) {
505
- if ( user.assignedType == 'store' ) {
506
- let storelist = await findStore( { 'storeId': { $in: user.assignedValue }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
507
- user.storeList = storelist;
508
- }
509
- if ( user.assignedType == 'group' ) {
510
- user.storeList = [];
511
- for ( let group of user.assignedValue ) {
512
- let groupdata = await findOneGroup( { '_id': group, 'clientId': user.clientId } );
513
- if ( groupdata&&groupdata.storeList ) {
514
- let storelist = await findStore( { 'storeId': { $in: groupdata.storeList }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
515
- user.storeList = [ ...user.storeList, ...storelist ];
516
- }
517
- }
518
- }
519
- }
520
-
521
- if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
522
- let finalStoreList = [];
523
- if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'customize' ) {
524
- // console.log( user.ticketConfigs.infraReport.times, user.storeList.length );
525
- for ( let store of user.storeList ) {
526
- const result = await checkStoreTimezoneMatchesInterval( user.ticketConfigs.infraReport.times, store.storeProfile.timeZone );
527
- if ( result ) {
528
- finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
529
- }
530
- }
531
- } else if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'defined' ) {
532
- let times =await generateTimeIntervals( user.ticketConfigs.infraReport.startTime, user.ticketConfigs.infraReport.endTime, user.ticketConfigs.infraReport.interval );
533
- for ( let store of user.storeList ) {
534
- const result = await checkStoreTimezoneMatchesInterval( times, store.storeProfile.timeZone );
535
- if ( result ) {
536
- finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
537
- }
538
- }
539
- }
540
- if ( finalStoreList.length > 0 ) {
541
- response.push( {
542
- email: user.email,
543
- userName: user.userName,
544
- clientId: user.clientId,
545
- role: user.role,
546
- storeList: finalStoreList,
547
- ticketConfigs: user.ticketConfigs.infraReport,
548
- } );
549
- }
550
- }
551
- }
552
- res.sendSuccess( { count: response.length, response: response } );
553
- } catch ( error ) {
554
- logger.error( { error: error, function: 'emailUserList' } );
555
- res.sendError( error, 500 );
556
- }
557
- }
558
- export async function emailUserListv2( req, res ) {
559
- try {
560
- let clientList = await aggregateClient( [
561
- {
562
- $match: {
563
- 'clientId': req.query.clientId,
564
- 'status': 'active',
565
- 'ticketConfigs.infraReport.type': { $exists: true },
566
- },
567
- },
568
- {
569
- $project: {
570
- 'clientId': 1,
571
- 'ticketConfigs.infraReport': 1,
572
- },
573
- },
574
- {
575
- '$lookup': {
576
- 'from': 'users',
577
- 'let': { 'clientId': '$clientId' },
578
- 'pipeline': [
579
- {
580
- '$match': {
581
- '$expr': {
582
- $and: [
583
- { '$eq': [ '$clientId', '$$clientId' ] },
584
- { '$eq': [ '$emailAlert.infra', true ] },
585
- ],
586
- },
587
- },
588
- },
589
- {
590
- $project: {
591
- userName: 1,
592
- email: 1,
593
- role: 1,
594
- userType: 1,
595
- assignedStores: 1,
596
- },
597
- },
598
- ],
599
- 'as': 'user',
600
- },
601
- },
602
- {
603
- $unwind: {
604
- path: '$user',
605
- preserveNullAndEmptyArrays: true,
606
- },
607
- },
608
-
609
- {
610
- $project: {
611
- 'clientId': 1,
612
- 'ticketConfigs': 1,
613
- 'email': '$user.email',
614
- 'userName': '$user.userName',
615
- 'role': '$user.role',
616
- 'userType': '$user.userType',
617
- 'assignedStores': '$user.assignedStores',
618
- },
619
- },
620
- {
621
- $group: {
622
- '_id': '$email',
623
- 'clientId': { $first: '$clientId' },
624
- 'ticketConfigs': { $first: '$ticketConfigs' },
625
- 'email': { $first: '$email' },
626
- 'userName': { $first: '$userName' },
627
- 'role': { $first: '$role' },
628
- 'userType': { $first: '$userType' },
629
- 'assignedStores': { $first: '$assignedStores' },
630
-
631
-
632
- },
633
- },
634
- ] );
635
-
636
- let getconfigs = await findOneClient( { clientId: req.query.clientId }, { 'ticketConfigs.infraReport': 1 } );
637
- if ( getconfigs && getconfigs.ticketConfigs && getconfigs.ticketConfigs.infraReport ) {
638
- if ( getconfigs.ticketConfigs.infraReport.allowCsm ) {
639
- let findCsm = {
640
- };
641
- let result = await userAssignedStoreModel.findOne( {
642
- 'clientId': req.query.clientId, 'userType': 'tango',
643
- 'tangoUserType': 'csm',
644
- } );
645
- if ( findCsm ) {
646
- findCsm.clientId = req.query.clientId,
647
- findCsm.ticketConfigs = getconfigs.ticketConfigs;
648
- findCsm.email = result.userEmail;
649
- findCsm.userName = result.userEmail ? result.userEmail.split( '@' )[0] : '';
650
- let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
651
- findCsm.storeList = storelist;
652
- clientList = [ ...clientList, ...[ findCsm ] ];
653
- }
654
- }
655
- if ( getconfigs.ticketConfigs.infraReport.allowSpoc ) {
656
- let spocList=[];
657
- let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1, 'spocDetails': 1 } );
658
- for ( let store of storelist ) {
659
- if ( store.spocDetails&&store.spocDetails.length>0 ) {
660
- if ( store.spocDetails[0].email!='' ) {
661
- spocList.push( {
662
- clientId: req.query.clientId,
663
- ticketConfigs: getconfigs.ticketConfigs,
664
- email: store.spocDetails[0].email,
665
- userName: store.spocDetails[0].name,
666
- storeList: [ store ],
667
- } );
668
- }
669
- }
670
- }
671
- clientList = [ ...clientList, ...spocList ];
672
- }
673
-
674
- let findOutsideUserlist = await findmailonlyuser( { 'clientId': req.query.clientId, 'emailAlert.infra': true } );
675
- let MailOnlyUsers = [];
676
- if ( findOutsideUserlist.length > 0 ) {
677
- for ( let user of findOutsideUserlist ) {
678
- let obj = {
679
- clientId: req.query.clientId,
680
- ticketConfigs: getconfigs.ticketConfigs,
681
- email: user.email,
682
- userName: user.name,
683
- storeList: [],
684
- };
685
- if ( user.stores&&user.stores.length>0 ) {
686
- let storelist = await findStore( { 'storeId': { $in: user.stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
687
- obj.storeList = storelist;
688
- }
689
- for ( let cluster of user.clusters ) {
690
- let clusterdata = await findOneCluster( { '_id': cluster, 'clientId': req.query.clientId } );
691
-
692
- if ( clusterdata&&clusterdata.stores ) {
693
- let stores = clusterdata.stores.map( ( data ) => data.storeId );
694
- let storelist = await findStore( { 'storeId': { $in: stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
695
-
696
- obj.storeList = [ ...obj.storeList, ...storelist ];
697
- }
698
- }
699
- MailOnlyUsers.push( obj );
700
- }
701
- clientList = [ ...clientList, ...MailOnlyUsers ];
702
- }
703
- }
704
-
705
- let response = [];
706
- console.log( clientList );
707
- for ( let user of clientList ) {
708
- if ( user.userType == 'client' ) {
709
- if ( user.role !== 'superadmin' ) {
710
- let storeIds =new Set();
711
- if ( user.assignedStores ) {
712
- storeIds = new Set( user.assignedStores.map( ( store ) => store.storeId ) );
713
- }
714
-
715
- // Fetch clusters and teams in parallel
716
- const [ clustersList, teamsList ] = await Promise.all( [
717
- findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
718
- findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
719
- ] );
720
-
721
- // Process clusters
722
- if ( clustersList.length > 0 ) {
723
- for ( let cluster of clustersList ) {
724
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
725
- }
726
- }
727
-
728
- // Process teams
729
- if ( teamsList.length > 0 ) {
730
- for ( let team of teamsList ) {
731
- for ( let user of team.users ) {
732
- let findUser = await findOneUser( { _id: user.userId } );
733
- if ( findUser && findUser.assignedStores?.length > 0 ) {
734
- findUser.assignedStores.forEach( ( store ) => storeIds.add( store.storeId ) );
735
- }
736
-
737
- // Fetch clusters for the user
738
- let userClustersList = await findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: findUser.email } } } );
739
- if ( userClustersList.length > 0 ) {
740
- for ( let cluster of userClustersList ) {
741
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
742
- }
743
- }
744
- }
745
- }
746
- }
747
- let TeamMember = await findteams( { clientId: user.clientId, users: { $elemMatch: { email: user.email } } } );
748
- if ( TeamMember&&TeamMember.length>0 ) {
749
- for ( let team of TeamMember ) {
750
- let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
751
- if ( clusterList.length > 0 ) {
752
- for ( let cluster of clusterList ) {
753
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
754
- }
755
- }
756
- }
757
- }
758
- let TeamLeader = await findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } );
759
- if ( TeamLeader&&TeamLeader.length>0 ) {
760
- for ( let team of TeamLeader ) {
761
- let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
762
- if ( clusterList.length > 0 ) {
763
- for ( let cluster of clusterList ) {
764
- cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
765
- }
766
- }
767
- }
768
- }
769
-
770
- user.assigned = Array.from( storeIds );
771
- let storeList = await findStore( { clientId: user.clientId, storeId: { $in: user.assigned }, status: 'active' }, { storeId: 1, storeName: 1, storeProfile: 1 } );
772
- user.assigned = storeList.length;
773
- user.storeList = storeList;
774
- } else {
775
- let storeList = await findStore( { clientId: user.clientId, status: 'active' }, { storeId: 1, storeName: 1, storeProfile: 1 } );
776
- user.assigned = storeList.length;
777
- user.storeList = storeList;
778
- }
779
- }
780
-
781
-
782
- if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
783
- let finalStoreList = [];
784
- if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'customize' ) {
785
- // console.log( user.ticketConfigs.infraReport.times, user.storeList.length );
786
- for ( let store of user.storeList ) {
787
- const result = await checkStoreTimezoneMatchesInterval( user.ticketConfigs.infraReport.times, store.storeProfile.timeZone );
788
- if ( result ) {
789
- finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
790
- }
791
- }
792
- } else if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'defined' ) {
793
- let times =await generateTimeIntervals( user.ticketConfigs.infraReport.startTime, user.ticketConfigs.infraReport.endTime, user.ticketConfigs.infraReport.interval );
794
- for ( let store of user.storeList ) {
795
- const result = await checkStoreTimezoneMatchesInterval( times, store.storeProfile.timeZone );
796
- if ( result ) {
797
- finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
798
- }
799
- }
800
- }
801
- if ( finalStoreList.length > 0 ) {
802
- response.push( {
803
- email: user.email,
804
- userName: user.userName,
805
- clientId: user.clientId,
806
- role: user.role,
807
- storeList: finalStoreList,
808
- ticketConfigs: user.ticketConfigs.infraReport,
809
- } );
810
- }
811
- }
812
- }
813
- res.sendSuccess( { count: response.length, response: response } );
814
- } catch ( error ) {
815
- logger.error( { error: error, function: 'emailUserListv2' } );
816
- res.sendError( error, 500 );
817
- }
818
- }
819
-
820
- export async function checkStoreTimezoneMatchesInterval( times, storeTimezone ) {
821
- const currentTimeInStore = dayjs().tz( storeTimezone ).format( 'HH:mm' );
822
- // console.log( storeTimezone, currentTimeInStore );
823
- return times.some( ( timeObj ) => {
824
- const interval = timeObj.interval;
825
- console.log( `Checking interval ${interval} against current time ${currentTimeInStore}` );
826
- const currentStoreTime = dayjs().tz( storeTimezone );
827
- const intervalTime = dayjs.tz( `${currentStoreTime.format( 'YYYY-MM-DD' )} ${interval}`, 'YYYY-MM-DD HH:mm', storeTimezone );
828
- const diffInMinutes = intervalTime.diff( currentStoreTime, 'minute' );
829
- // console.log( diffInMinutes );
830
- return diffInMinutes >= 0 && diffInMinutes <= 15;
831
- } );
832
- };
833
- export async function generateTimeIntervals( startTime, endTime, interval ) {
834
- function timeToMinutes( time ) {
835
- const [ hours, minutes ] = time.split( ':' ).map( Number );
836
- return hours * 60 + minutes;
837
- }
838
-
839
- function minutesToTime( minutes ) {
840
- const hours = Math.floor( minutes / 60 );
841
- const mins = minutes % 60;
842
- return `${String( hours ).padStart( 2, '0' )}:${String( mins ).padStart( 2, '0' )}`;
843
- }
844
- const startMinutes = timeToMinutes( startTime );
845
- const endMinutes = timeToMinutes( endTime );
846
- const intervalMinutes = parseInt( interval, 10 );
847
- const intervals = [];
848
- for ( let currentTime = startMinutes; currentTime <= endMinutes; currentTime += intervalMinutes ) {
849
- intervals.push( { interval: minutesToTime( currentTime ) } );
850
- }
851
- return intervals;
852
- }
853
-
854
-
855
- export async function infraReportSent( req, res ) {
856
- try {
857
- let date = dayjs().format( 'YYYY-MM-DD' );
858
- let query = [ {
859
- $match: {
860
- $and: [
861
- { issueDate: { $lte: new Date( date ) } },
862
- { 'basicDetails.storeId': { $in: req.body.storeList } },
863
- { issueType: 'infra' },
864
- { status: { $ne: 'closed' } },
865
- ],
866
- },
867
- },
868
- {
869
- $project: {
870
- ticketId: 1,
871
- basicDetails: 1,
872
- createdAt: 1,
873
- status: 1,
874
- ticketDetails: 1,
875
- issueClosedDate: 1,
876
- otherscomment: {
877
- $filter: {
878
- input: '$ticketActivity',
879
- as: 'item',
880
- cond: { $eq: [ '$$item.actionType', 'comment' ] },
881
- },
882
- },
883
- primaryIssue: {
884
- $filter: {
885
- input: '$ticketActivity',
886
- as: 'item',
887
- cond: { $eq: [ '$$item.actionType', 'issueUpdate' ] },
888
- },
889
- },
890
- },
891
- },
892
- {
893
- $unwind: {
894
- path: '$otherscomment', preserveNullAndEmptyArrays: true,
895
- },
896
- },
897
- {
898
- $unwind: {
899
- path: '$primaryIssue', preserveNullAndEmptyArrays: true,
900
- },
901
- },
902
- {
903
- $unwind: {
904
- path: '$primaryIssue.reasons', preserveNullAndEmptyArrays: true,
905
- },
906
- },
907
- {
908
- $unwind: {
909
- path: '$primaryIssue.reasons.secondaryIssue', preserveNullAndEmptyArrays: true,
910
- },
911
- },
912
- {
913
- $project: {
914
- ticketId: 1,
915
- basicDetails: 1,
916
- createdAt: 1,
917
- status: 1,
918
- ticketDetails: 1,
919
- issueClosedDate: 1,
920
- ommentText: { $ifNull: [ '$primaryIssue.comment', '-' ] },
921
- otherscomment: { $ifNull: [ '$otherscomment.comment', '-' ] },
922
- primaryIssue: { $ifNull: [ '$primaryIssue.reasons.primaryIssue', '-' ] },
923
- secondaryIssue: { $ifNull: [ '$primaryIssue.reasons.secondaryIssue.name', '-' ] },
924
- },
925
- },
926
- {
927
- $group: {
928
- _id: '$ticketId',
929
- ticketDetails: { $first: '$ticketDetails' },
930
- issueClosedDate: { $first: '$issueClosedDate' },
931
- basicDetails: { $first: '$basicDetails' },
932
- commentText: { $last: '$commentText' },
933
- primaryIssue: { $last: '$primaryIssue' },
934
- secondaryIssue: { $last: '$secondaryIssue' },
935
- otherscomment: { $last: '$otherscomment' },
936
- createdAt: { $first: '$createdAt' },
937
- status: { $last: '$status' },
938
- },
939
- },
940
- ];
941
-
942
- let ticketList = await aggregateTangoTicket( query );
943
- const exportdata = [];
944
- if ( ticketList&&ticketList.length>0 ) {
945
- for ( let element of ticketList ) {
946
- let clientuser = await findOneUser( { _id: element.ticketDetails.addressingClient } );
947
- let tangouser = await findOneUser( { _id: element.ticketDetails.addressingUser } );
948
- let findstore = await findOneStore( { storeId: element.basicDetails.storeId }, { storeProfile: 1 } );
949
- console.log( findstore.storeProfile.storeCode );
950
-
951
- exportdata.push( {
952
- 'BrandID': element.basicDetails.clientId,
953
- 'BrandName': element.basicDetails.clientName,
954
- 'TicketCreatedDate & Time': dayjs( element.createdAt ).tz( 'Asia/Kolkata' ).format( 'YYYY-MM-DD HH:mm A' ),
955
- 'StoreID': element.basicDetails.storeId,
956
- 'StoreCode': findstore.storeProfile&&findstore.storeProfile.storeCode?findstore.storeProfile.storeCode:'-',
957
- 'StoreName': element.basicDetails.storeName,
958
- 'PrimaryIssue ': element.primaryIssue,
959
- 'SecondaryIssue': element.secondaryIssue,
960
- 'Status ': element.status,
961
- 'RespondedBy': clientuser && clientuser.userName ? clientuser.userName : '-',
962
- 'ResolvedBy': tangouser && tangouser.userName ? tangouser.userName : '-',
963
- 'TicketClosedDate & Time': element.issueClosedDate ? dayjs( element.issueClosedDate ).tz( 'Asia/Kolkata' ).format( 'YYYY-MM-DD HH:mm A' ) : '-',
964
- 'LatestComment': element.otherscomment ? element.otherscomment : element.commentText,
965
- 'ActivityLog': {
966
- label: 'Link',
967
- url: `${JSON.parse( process.env.URL ).domain + '/manage/stores/infra-ticket?storeId=' + element.basicDetails.storeId}`,
968
- },
969
- } );
970
- }
971
-
972
- let issueCount = await countDocumentsTangoTicket( {
973
- $and: [
974
- { issueDate: { $lte: new Date( date ) } },
975
- { 'basicDetails.storeId': { $in: req.body.storeList } },
976
- { issueType: 'infra' },
977
- { status: { $ne: 'closed' } },
978
- ],
979
- } );
980
- let client = await findOneClient( { clientId: req.body.clientId } );
981
- let avgDownTime = client.ticketConfigs.infraDownTime * 60;
982
- let issueList = await findinfraReason( { parentId: { '$exists': false } } );
983
- const categoryCounts = {};
984
- let response;
985
- if ( ticketList.length > 0 ) {
986
- ticketList.forEach( ( item ) => {
987
- const categoryName = item.primaryIssue;
988
- if ( categoryCounts[categoryName] ) {
989
- categoryCounts[categoryName]++;
990
- } else {
991
- categoryCounts[categoryName] = 1;
992
- }
993
- } );
994
- response = issueList.map( ( category ) => ( {
995
- name: category.name,
996
- count: categoryCounts[category.name] || 0,
997
- } ) );
998
- } else {
999
- response = issueList.map( ( category ) => ( {
1000
- name: category.name,
1001
- count: 0,
1002
- } ) );
1003
- }
1004
- let obj = {};
1005
- for ( let issue of response ) {
1006
- if ( issue.name === 'System Issues' ) {
1007
- obj.systemIssue = issue.count;
1008
- } else if ( issue.name === 'Store Operation Issues' ) {
1009
- obj.storeoperationIssue = issue.count;
1010
- } else if ( issue.name === 'Application Issues' ) {
1011
- obj.applicationIssue = issue.count;
1012
- } else if ( issue.name === 'Camera Issues' ) {
1013
- obj.cameraIssue = issue.count;
1014
- } else if ( issue.name === 'Internet Issues' ) {
1015
- obj.internetIssue = issue.count;
1016
- }
1017
- }
1018
- let csmEmail = await userAssignedStoreModel.findOne( { clientId: req.body.clientId, userType: 'tango', tangoUserType: 'csm' } );
1019
- if ( csmEmail ) {
1020
- csmEmail = csmEmail && csmEmail.userEmail ? csmEmail.userEmail : 'csm@tangotech.co.in';
1021
- }
1022
-
1023
- let attachments = null;
1024
- let buffer = await download( exportdata );
1025
- let reportDate = dayjs().format( 'DD-MM-YYYY' );
1026
- attachments = {
1027
- filename: `dailyInfraReport- ${reportDate}.xlsx`,
1028
- content: buffer,
1029
- contentType: 'application/xlsx', // e.g., 'application/pdf'
1030
- };
1031
-
1032
- const subject = `${client.clientName} Daily Digest - Infra Downtime Report - ${reportDate}`;
1033
- const fileContent = readFileSync( join() + '/src/hbs/dailyInfraReport.hbs', 'utf8' );
1034
- const htmlContent = handlebars.compile( fileContent );
1035
- let Uidomain = `${JSON.parse( process.env.URL ).domain}`;
1036
- const html = htmlContent( { ...req.body, Uidomain: Uidomain, issueCount: issueCount, avgDownTime: avgDownTime, reportdate: date, content: obj, date: date, csmEmail: csmEmail, domain: JSON.parse( process.env.URL ).apiDomain } );
1037
- if ( isValidEmail( req.body.email ) ) {
1038
- const result = await sendEmailWithSES( req.body.email, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
1039
- res.sendSuccess( result );
1040
- } else {
1041
- return res.sendError( 'Invalid email', 204 );
1042
- }
1043
- } else {
1044
- return res.sendError( 'No data', 204 );
1045
- }
1046
- } catch ( error ) {
1047
- logger.error( { error: error, function: 'infraReportSent' } );
1048
- res.sendError( error, 500 );
1049
- }
1050
- }
1051
- export async function spocmailchange() {
1052
- let storelist = await findStore( {} );
1053
- for ( let store of storelist ) {
1054
- if ( store && store.spocDetails ) {
1055
- let spoclist = [];
1056
- for ( let spoc of store.spocDetails ) {
1057
- spoc.email = spoc.email.replace( '@', '1@' ),
1058
- spoclist.push( spoc );
1059
- }
1060
- await updateOneStore( { storeId: store.storeId }, { spocDetails: spoclist } );
1061
- }
1062
- }
1063
- }
1064
-
1065
-
1066
- export async function camAngleChangeReport( req, res ) {
1067
- try {
1068
- const currentDate = dayjs();
1069
-
1070
-
1071
- const previousDay = currentDate.subtract( 1, 'day' );
1072
-
1073
-
1074
- const formattedPreviousDay = previousDay.format( 'DD-MM-YYYY' );
1075
- const angleChange = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).cameraAngleChange,
1076
- {
1077
- 'query': {
1078
- 'bool': {
1079
- 'must': [
1080
- {
1081
- 'term': {
1082
- 'date.keyword': formattedPreviousDay,
1083
- },
1084
- },
1085
- {
1086
- 'term': {
1087
- 'client_id.keyword': req.body.clientId,
1088
- },
1089
- },
1090
- ],
1091
- },
1092
- },
1093
- } );
1094
- if ( angleChange.body.hits.hits.length > 0 ) {
1095
- const exportdata = [];
1096
-
1097
- for ( let camera of angleChange.body.hits.hits ) {
1098
- let result = camera._source;
1099
-
1100
- if ( result && result.cameraAngleChangeStatus && result.camera_info.length > 0 ) {
1101
- for ( let stream of result.camera_info ) {
1102
- let params = {
1103
- Bucket: JSON.parse( process.env.BUCKET ).cameraAngle,
1104
- file_path: stream.path,
1105
- };
1106
- let Image = await signedUrl( params );
1107
-
1108
-
1109
- let findStore = await findOneStore( { storeId: result.store_id } );
1110
-
1111
-
1112
- exportdata.push( {
1113
- 'Store Name': findStore.storeName,
1114
- 'Store Code': findStore.storeId,
1115
- 'Date Changed': formattedPreviousDay,
1116
- 'ImageURL': {
1117
- url: Image,
1118
- label: 'Angle changed Image',
1119
- },
1120
- } );
1121
- }
1122
- }
1123
- }
1124
- let buffer = await download( exportdata );
1125
-
1126
- let attachments = {
1127
- filename: `Camera angle change- ${formattedPreviousDay}.xlsx`,
1128
- content: buffer,
1129
- contentType: 'application/xlsx', // e.g., 'application/pdf'
1130
- };
1131
- let subject = `Camera Angle Modified - ${formattedPreviousDay}`;
1132
- let html = `<div>We wanted to inform you that the camera angle in your stores has been adjusted recently.</div>`;
1133
- let result = await sendEmailWithSES( req.body.toMail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
1134
- if ( result ) {
1135
- res.sendSuccess( 'Email send successfully' );
1136
- }
1137
- } else {
1138
- res.sendSuccess( 'No changes in camera Angle' );
1139
- }
1140
- } catch ( error ) {
1141
- logger.error( { error: error, function: 'camAngleChangeList' } );
1142
- res.sendError( error, 500 );
1143
- }
1144
- }
1145
-
1146
-
1147
- export async function download( data ) {
1148
- const wb = new xl.Workbook();
1149
- const ws = wb.addWorksheet( 'Worksheet Name' );
1150
- const headers = Object.keys( data[0] );
1151
-
1152
- for ( let i = 0; i < headers.length; i++ ) {
1153
- ws.cell( 1, i + 1 ).string( headers[i] );
1154
- };
1155
- for ( let i = 0; i < data.length; i++ ) {
1156
- const dataRow = data[i];
1157
- for ( let j = 0; j < headers.length; j++ ) {
1158
- const header = headers[j];
1159
- const value = dataRow[header];
1160
- if ( value && typeof value === 'object' && value.hasOwnProperty( 'label' ) && value.hasOwnProperty( 'url' ) ) {
1161
- ws.cell( i + 2, j + 1 ).link( value.url );
1162
- ws.cell( i + 2, j + 1 ).string( value.label );
1163
- } else {
1164
- ws.cell( i + 2, j + 1 ).string( value?.toString() );
1165
- }
1166
- }
1167
- }
1168
- return await wb.writeToBuffer();
1169
- }
1170
- export async function edgeApplogsCheck( req, res ) {
1171
- try {
1172
- let finalresult = [];
1173
- let ticketList = await findTangoTicket( { 'issueDate': new Date( req.body.issueDate ), 'status': { $ne: 'closed' }, 'ticketDetails.issueStatus': 'notidentified', 'issueType': 'infra' } );
1174
- for ( let ticket of ticketList ) {
1175
- req.body.date = dayjs( ticket.createdAt ).format( 'YYYY-MM-DD' );
1176
- let errorLog = {
1177
- 'size': 5000,
1178
- 'query': {
1179
- 'bool': {
1180
- 'must': [
1181
- {
1182
- 'range': {
1183
- 'log_code': {
1184
- 'gte': 1000,
1185
- },
1186
- },
1187
- },
1188
- {
1189
- 'term': {
1190
- 'store_date.keyword': dayjs( req.body.date ).format( 'DD-MM-YYYY' ),
1191
- },
1192
- },
1193
- {
1194
- 'term': {
1195
- 'storeId.keyword': ticket.basicDetails.storeId,
1196
- },
1197
- },
1198
-
1199
- ],
1200
-
1201
- },
1202
- },
1203
- 'sort': [
1204
- { 'timestamp': { 'order': 'desc' } },
1205
- ],
1206
- };
1207
-
1208
- const errorLogList = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).edgeAppSystemLogs, errorLog );
1209
- let internetSlowcount = [];
1210
- let result = [];
1211
- let apploginCount=[];
1212
- function createLogCheck( error ) {
1213
- return {
1214
- code: error._source.log_code,
1215
- edgelog: error._source.data,
1216
- };
1217
- }
1218
- for ( let error of errorLogList.body.hits.hits ) {
1219
- const logCode = error._source.log_code;
1220
- const relevantCodes = [ 1003, 1011, 1022, 1024, 1025, 1026, 1000 ];
1221
- if ( relevantCodes.includes( Number( logCode ) ) ) {
1222
- result.push( createLogCheck( error ) );
1223
- }
1224
-
1225
-
1226
- if ( Number( logCode ) === 1004 ) {
1227
- apploginCount.push( createLogCheck( error ) );
1228
- }
1229
- if ( logCode === '1005' ) {
1230
- const bytes = error._source.data.upload_Speed.split( '.' )[0];
1231
- const megabytes = bytesToMB( bytes );
1232
- if ( megabytes < 2 ) {
1233
- internetSlowcount.push( error );
1234
- result.push( createLogCheck( error ) );
1235
- }
1236
- }
1237
-
1238
- if ( Number( logCode ) > 2000 && Number( logCode ) !== 2007 && Number( logCode ) !== 2014 ) {
1239
- result.push( createLogCheck( error ) );
1240
- }
1241
- }
1242
-
1243
- let newraray = apploginCount.reverse();
1244
-
1245
- let findissueEdgeApp = {};
1246
- if ( newraray.length>0 ) {
1247
- result = [ ...result, ...[ newraray[0] ] ];
1248
- }
1249
- if ( result.length > 0 ) {
1250
- let logincount = [];
1251
- let appcrashcount = [];
1252
-
1253
-
1254
- for ( let findissue of result ) {
1255
- const istTimestamp = dayjs.utc( ticket.createdAt ).tz( 'Asia/Kolkata' ).format( 'HH:mm:ss' );
1256
- if ( findissue.code == '1003' ) {
1257
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1258
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1259
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1260
- const existsInArray = finalresult.some( ( item ) =>
1261
- item.ticketId === ticket.ticketId,
1262
- );
1263
- if ( isOccurringTimeEarlier && !existsInArray ) {
1264
- findissueEdgeApp = {
1265
- ticketId: ticket.ticketId,
1266
- edgelog: findissue,
1267
- storeId: ticket.basicDetails.storeId,
1268
- primary: 'System Issues',
1269
- secondary: [ 'Antivirus blockages' ],
1270
- };
1271
- updateIssue( findissueEdgeApp );
1272
- finalresult.push( findissueEdgeApp );
1273
- }
1274
- } if ( findissue.code == '2004' ) {
1275
- let store = await findOneStore( { storeId: ticket.basicDetails.storeId } );
1276
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1277
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1278
- const stroreopenTime = dayjs( `${req.body.date} ${store.storeProfile.open}` );
1279
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1280
- const afterStoreOpen = occurringTimeParsed.isAfter( stroreopenTime );
1281
- const existsInArray = finalresult.some( ( item ) =>
1282
- item.ticketId === ticket.ticketId,
1283
- );
1284
- if ( isOccurringTimeEarlier && afterStoreOpen && !existsInArray ) {
1285
- logincount.push( findissue.edgelog.occuringTime );
1286
- if ( logincount.length > 3 ) {
1287
- findissueEdgeApp = {
1288
- ticketId: ticket.ticketId,
1289
- edgelog: findissue,
1290
- storeId: ticket.basicDetails.storeId,
1291
- primary: 'Internet Issues',
1292
- secondary: [ 'Internet not working' ],
1293
- };
1294
- updateIssue( findissueEdgeApp );
1295
- finalresult.push( findissueEdgeApp );
1296
- }
1297
- }
1298
- } else if ( findissue.code == '2024' ) {
1299
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1300
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1301
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1302
- const existsInArray = finalresult.some( ( item ) =>
1303
- item.ticketId === ticket.ticketId,
1304
- );
1305
- if ( isOccurringTimeEarlier && !existsInArray ) {
1306
- findissueEdgeApp = {
1307
- ticketId: ticket.ticketId,
1308
- storeId: ticket.basicDetails.storeId,
1309
- edgelog: findissue.edgelog,
1310
- primary: 'Camera Issues',
1311
- secondary: [ 'Internet and camera not working' ],
1312
- };
1313
- updateIssue( findissueEdgeApp );
1314
- finalresult.push( findissueEdgeApp );
1315
- }
1316
- } else if ( findissue.code == '1024' ) {
1317
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.data.occuringTime}` );
1318
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1319
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1320
-
1321
- const existsInArray = finalresult.some( ( item ) =>
1322
- item.ticketId === ticket.ticketId,
1323
- );
1324
- if ( isOccurringTimeEarlier && !existsInArray ) {
1325
- const pingOutput = findissue.edgelog.data.message;
1326
- const regex = /Sent = (\d+), Received = (\d+), Lost = (\d+)/;
1327
- const match = pingOutput.match( regex );
1328
-
1329
- if ( match ) {
1330
- // const sent = match[1];
1331
- const received = match[2];
1332
- // const lost = match[3];
1333
-
1334
- if ( Number( received ) < 2 ) {
1335
- findissueEdgeApp = {
1336
- ticketId: ticket.ticketId,
1337
- storeId: ticket.basicDetails.storeId,
1338
- edgelog: findissue.edgelog.data,
1339
- primary: 'Camera Issues',
1340
- secondary: [ 'IP not pinging / Camera not working' ],
1341
- };
1342
- } else if ( Number( received ) > 2 ) {
1343
- findissueEdgeApp = {
1344
- ticketId: ticket.ticketId,
1345
- storeId: ticket.basicDetails.storeId,
1346
- edgelog: findissue.edgelog.data,
1347
- primary: 'Camera Issues',
1348
- secondary: [ 'Rtsp/url not supported/Camera credential changed' ],
1349
- };
1350
- }
1351
- updateIssue( findissueEdgeApp );
1352
- finalresult.push( findissueEdgeApp );
1353
- }
1354
- }
1355
- } else if ( findissue.code == '1025' ) {
1356
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1357
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1358
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1359
-
1360
- const existsInArray = finalresult.some( ( item ) =>
1361
- item.ticketId === ticket.ticketId,
1362
- );
1363
- if ( isOccurringTimeEarlier && !existsInArray ) {
1364
- findissueEdgeApp = {
1365
- ticketId: ticket.ticketId,
1366
- storeId: ticket.basicDetails.storeId,
1367
- edgelog: findissue.edgelog,
1368
- primary: 'Application Issues',
1369
- secondary: [ 'App without stream' ],
1370
- };
1371
- updateIssue( findissueEdgeApp );
1372
- finalresult.push( findissueEdgeApp );
1373
- }
1374
- } else if ( findissue.code == '1011' ) {
1375
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1376
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1377
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1378
- const existsInArray = finalresult.some( ( item ) =>
1379
- item.ticketId === ticket.ticketId,
1380
- );
1381
- if ( isOccurringTimeEarlier && !existsInArray ) {
1382
- findissueEdgeApp = {
1383
- ticketId: ticket.ticketId,
1384
- edgelog: findissue,
1385
- storeId: ticket.basicDetails.storeId,
1386
- primary: 'System Issues',
1387
- secondary: [ 'System is in Sleep Mode' ],
1388
- };
1389
- updateIssue( findissueEdgeApp );
1390
- finalresult.push( findissueEdgeApp );
1391
- }
1392
- } else if ( findissue.code == '1026' ) {
1393
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1394
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1395
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1396
- const existsInArray = finalresult.some( ( item ) =>
1397
- item.ticketId === ticket.ticketId,
1398
- );
1399
- if ( isOccurringTimeEarlier && !existsInArray ) {
1400
- if ( ( Number( findissue.edgelog.files_generated ) - Number( findissue.edgelog.files_pushed ) ) > 3 && internetSlowcount.length == 1 ) {
1401
- findissueEdgeApp = {
1402
- ticketId: ticket.ticketId,
1403
- edgelog: findissue,
1404
- storeId: ticket.basicDetails.storeId,
1405
- primary: 'Internet Issues',
1406
- secondary: [ 'Internet slow' ],
1407
- };
1408
- }
1409
- updateIssue( findissueEdgeApp );
1410
- finalresult.push( findissueEdgeApp );
1411
- }
1412
- } else if ( findissue.code == '1016' ) {
1413
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1414
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1415
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1416
- const existsInArray = finalresult.some( ( item ) =>
1417
- item.ticketId === ticket.ticketId,
1418
- );
1419
- if ( isOccurringTimeEarlier && !existsInArray ) {
1420
- findissueEdgeApp = {
1421
- ticketId: ticket.ticketId,
1422
- edgelog: findissue,
1423
- storeId: ticket.basicDetails.storeId,
1424
- primary: 'Application Issues',
1425
- secondary: [ 'App login issue' ],
1426
- };
1427
- updateIssue( findissueEdgeApp );
1428
- finalresult.push( findissueEdgeApp );
1429
- }
1430
- } else if ( findissue.code == '1004' ) {
1431
- let store = await findOneStore( { storeId: ticket.basicDetails.storeId } );
1432
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1433
- const storeOpenTime = dayjs( `${req.body.date} ${store.storeProfile.open}` );
1434
- const diffInMinutes = occurringTimeParsed.diff( storeOpenTime, 'minute' );
1435
- const existsInArray = finalresult.some( ( item ) =>
1436
- item.ticketId === ticket.ticketId,
1437
- );
1438
- if ( !existsInArray ) {
1439
- if ( diffInMinutes>60 ) {
1440
- findissueEdgeApp = {
1441
- ticketId: ticket.ticketId,
1442
- edgelog: findissue,
1443
- storeId: ticket.basicDetails.storeId,
1444
- primary: 'Store Operation Issues',
1445
- secondary: [ 'Store opened Lately/System turned on lately' ],
1446
- };
1447
- updateIssue( findissueEdgeApp );
1448
- finalresult.push( findissueEdgeApp );
1449
- }
1450
- }
1451
- } else if ( findissue.code == '1000' ) {
1452
- const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1453
- const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1454
- const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1455
- const existsInArray = finalresult.some( ( item ) =>
1456
- item.ticketId === ticket.ticketId,
1457
- );
1458
- if ( isOccurringTimeEarlier && !existsInArray ) {
1459
- appcrashcount.push( findissue );
1460
- if ( appcrashcount.length > 3 ) {
1461
- findissueEdgeApp = {
1462
- ticketId: ticket.ticketId,
1463
- edgelog: findissue,
1464
- storeId: ticket.basicDetails.storeId,
1465
- primary: 'System Issues',
1466
- secondary: [ 'System utilization high' ],
1467
- };
1468
- updateIssue( findissueEdgeApp );
1469
- finalresult.push( findissueEdgeApp );
1470
- }
1471
- }
1472
- }
1473
- }
1474
- }
1475
- }
1476
- res.sendSuccess( { count: finalresult.length, result: finalresult } );
1477
- } catch ( error ) {
1478
- logger.error( { error: error, function: 'camAngleChangeList' } );
1479
- res.sendError( error, 500 );
1480
- }
1481
- }
1482
- function bytesToMB( bytes ) {
1483
- return bytes / ( 1024 * 1024 );
1484
- }
1485
-
1486
- export async function updateIssue( data ) {
1487
- try {
1488
- let Ticket = await findOneTangoTicket(
1489
- {
1490
- ticketId: data.ticketId,
1491
- },
1492
- );
1493
- if ( Ticket ) {
1494
- data.issueType = Ticket.issueType;
1495
- data.basicDetails = Ticket.basicDetails;
1496
- data.ticketDetails = Ticket.ticketDetails;
1497
- data.ticketActivity = Ticket.ticketActivity;
1498
- if ( data.primary && data.secondary && data.secondary.length ) {
1499
- let primaryReason = await findOneinfraReason( { name: data.primary } );
1500
- if ( !primaryReason ) {
1501
- return res.sendError( 'Primary Reason Not exists in database', 500 );
1502
- }
1503
- const secondary = [];
1504
- const steptoReslove = [];
1505
- for ( let i = 0; i < data.secondary.length; i++ ) {
1506
- let secondaryReason = await findOneinfraReason( { name: data.secondary[i] } );
1507
- if ( !secondaryReason ) {
1508
- return res.sendError( `secondary Reason - ${data.secondary[i]} Not exists in database`, 500 );
1509
- }
1510
- secondary.push( {
1511
- name: secondaryReason.name,
1512
- } );
1513
- let resolveSteps = [];
1514
- for ( let i = 0; i < secondaryReason.stepstoResolve.length; i++ ) {
1515
- resolveSteps.push( {
1516
- name: secondaryReason.stepstoResolve[i].name,
1517
- } );
1518
- }
1519
- steptoReslove.push( {
1520
- primaryIssue: secondaryReason.name,
1521
- secondaryIsssue: [ ...resolveSteps ],
1522
- } );
1523
- }
1524
-
1525
- data.ticketActivity.push( {
1526
- actionType: 'issueUpdate',
1527
- actionBy: 'automated',
1528
- IdentifiedBy: 'Tango',
1529
- timeStamp: new Date(),
1530
- reasons: [ {
1531
- primaryIssue: primaryReason.name,
1532
- secondaryIssue: secondary,
1533
- } ],
1534
- },
1535
- );
1536
- }
1537
- if ( data.issueType == 'infra' ) {
1538
- let client = await findOneClient( { clientId: data.basicDetails.clientId }, { ticketConfigs: 1 } );
1539
- let statusCheckAlertTime = dayjs().add( client.ticketConfigs.statusCheckAlert, 'hours' ).format( 'YYYY-MM-DD hh:mm' );
1540
- data.ticketActivity.push( {
1541
- actionType: 'statusCheck',
1542
- timeStamp: statusCheckAlertTime,
1543
- actionBy: 'Tango',
1544
- IdentifiedBy: 'Tango',
1545
- statusCheckAlertTime: statusCheckAlertTime,
1546
- } );
1547
- }
1548
- let client = await findOneClient( { clientId: data.basicDetails.clientId } );
1549
- let refreshdate = dayjs().add( client.ticketConfigs.refreshAlert, 'days' );
1550
-
1551
- let query = {
1552
- 'ticketActivity': data.ticketActivity,
1553
- 'ticketDetails.issueIdentifiedDate': new Date(),
1554
- 'ticketDetails.issueStatus': 'identified',
1555
- 'status': 'inprogress',
1556
- 'ticketDetails.ticketRefreshTime': new Date( dayjs( refreshdate ).format( 'YYYY-MM-DD' ) ),
1557
- };
1558
- await updateOneTangoTicket( { ticketId: data.ticketId }, query );
1559
- }
1560
- } catch ( error ) {
1561
- logger.error( { error: error, function: 'updateAutomaticIssue' } );
1562
- }
1563
- }
1564
-
1
+
2
+ import { logger, getOpenSearchData } from 'tango-app-api-middleware';
3
+ import dayjs from 'dayjs';
4
+ import utc from 'dayjs/plugin/utc.js';
5
+ import timezone from 'dayjs/plugin/timezone.js';
6
+ import 'dayjs/locale/en.js';
7
+ import { readFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import handlebars from 'handlebars';
10
+ dayjs.extend( utc );
11
+ dayjs.extend( timezone );
12
+ import { sendEmailWithSES, signedUrl } from 'tango-app-api-middleware';
13
+ import { createClient, findClient, aggregateClient, findOneClient } from '../services/client.service.js';
14
+ import { createStore, findStore, updateOneStore, findOneStore } from '../services/store.service.js';
15
+ import { findTangoTicket, findOneTangoTicket, countDocumentsTangoTicket, updateOneTangoTicketunset, aggregateTangoTicket, updateOneTangoTicket } from '../services/tangoTicket.service.js';
16
+ import { findOneGroup } from '../services/group.service.js';
17
+ import { findinfraReason } from '../services/infraReason.service.js';
18
+ import { findOneUser } from '../services/user.service.js';
19
+ import xl from 'excel4node';
20
+ import { findOneinfraReason } from '../services/infraReason.service.js';
21
+ import userAssignedStoreModel from 'tango-api-schema/schema/userAssignedStore.model.js';
22
+ import { findmailonlyuser } from '../services/mailonlyusers.service.js';
23
+ import { findOneCluster } from '../services/cluster.service.js';
24
+ import { findcluster } from '../services/cluster.service.js';
25
+ import { findteams } from '../services/teams.service.js';
26
+
27
+
28
+ export async function migrateClient() {
29
+ try {
30
+ let oldclient = [];
31
+ for ( let i = 0; i < oldclient.length; i++ ) {
32
+ let framedClient = {
33
+ clientName: oldclient[i].brandName,
34
+ clientId: oldclient[i].clientId,
35
+ tangoId: oldclient[i].brandIndex,
36
+ status: 'active',
37
+ ticketConfigs: {
38
+ installationReAssign: 1,
39
+ downTimeType: 1,
40
+ infraDownTime: 1,
41
+ minFilesCount: 0,
42
+ rcaTicketAssign: 2,
43
+ refreshAlert: 2,
44
+ statusCheckAlert: 2,
45
+ accuracyPercentage: 0,
46
+ reTrain: 1,
47
+ },
48
+ };
49
+ await createClient( framedClient );
50
+ };
51
+ } catch ( error ) {
52
+ logger.error( { error: error, function: 'migrateClient' } );
53
+ res.sendError( error, 500 );
54
+ }
55
+ }
56
+
57
+
58
+ export async function migrateStores() {
59
+ try {
60
+ let oldstores = [];
61
+ for ( let i = 0; i < oldstores.length; i++ ) {
62
+ let framedStores = {
63
+ storeId: oldstores[i].id,
64
+ storeName: oldstores[i].name,
65
+ appId: oldstores[i].appId,
66
+ clientId: oldstores[i].client_id,
67
+ status: 'active',
68
+ storeProfile: {
69
+ open: oldstores[i].configuration.storeOpenTime,
70
+ close: oldstores[i].configuration.storeCloseTime,
71
+ timeZone: oldstores[i].timezone,
72
+ },
73
+ };
74
+
75
+ await createStore( framedStores );
76
+ };
77
+ } catch ( error ) {
78
+ logger.error( { error: error, function: 'migrateStores' } );
79
+ res.sendError( error, 500 );
80
+ }
81
+ }
82
+ export async function clientList( req, res ) {
83
+ try {
84
+ let clientList = await findClient( { status: { $in: [ 'active', 'hold' ] } },
85
+ { 'clientName': 1, 'clientId': 1, 'ticketConfigs': 1 } );
86
+ logger.info( { 'clientList': clientList, 'message': 'clientList' } );
87
+ res.sendSuccess( clientList );
88
+ } catch ( error ) {
89
+ logger.error( { error: error, function: 'basicList' } );
90
+ res.sendError( error, 500 );
91
+ }
92
+ }
93
+
94
+ export async function basicList( req, res ) {
95
+ try {
96
+ let clientList = await findClient( { status: { $in: [ 'active', 'hold' ] } }, { 'clientId': 1 } );
97
+ let clients = [];
98
+ for ( let client of clientList ) {
99
+ clients.push( client.clientId );
100
+ }
101
+ let storeList = await findStore( { 'clientId': { $in: clients }, 'status': 'active', 'edge.firstFile': true, 'storeProfile.open': { $exists: true }, 'storeProfile.close': { $exists: true }, 'storeProfile.timeZone': { $exists: true } },
102
+ { 'storeName': 1, 'storeId': 1, 'ticketConfigs': 1, 'clientId': 1, 'storeProfile.open': 1, 'storeProfile.close': 1, 'storeProfile.timeZone': 1 } );
103
+ logger.info( { 'storeList': storeList, 'message': 'storeList' } );
104
+
105
+ res.sendSuccess( storeList );
106
+ } catch ( error ) {
107
+ logger.error( { error: error, function: 'basicList' } );
108
+ res.sendError( error, 500 );
109
+ }
110
+ }
111
+
112
+
113
+ export async function setTicketTime( req, res ) {
114
+ try {
115
+ let input = req.body;
116
+ logger.info( { 'input': input, 'message': 'setTicketTime' } );
117
+ for ( let i = 0; i < input.stores.length; i++ ) {
118
+ await updateOneStore( { storeId: input.stores[i].storeId }, { 'ticketConfigs.nextTicektGenerationTime': new Date( input.stores[i].nextTicektGenerationTime ) } );
119
+ };
120
+ res.sendSuccess( 'Updated Suceessfully' );
121
+ } catch ( error ) {
122
+ logger.error( { error: error, function: 'setTicketTime' } );
123
+ res.sendError( error, 500 );
124
+ }
125
+ }
126
+
127
+ export async function downStoresList( req, res ) {
128
+ try {
129
+ let storesList = await findStore( { 'ticketConfigs.hibernation': { $lt: new Date() }, 'ticketConfigs.nextTicektGenerationTime': new Date( req.body.getTime ) }, { storeId: 1, storeName: 1, storeProfile: 1, ticketConfigs: 1 } );
130
+ logger.info( { 'storesList': storesList, 'message': 'downStoresList' } );
131
+ if ( storesList.length > 0 ) {
132
+ res.sendSuccess( storesList );
133
+ } else {
134
+ res.sendError( 'No data found', 204 );
135
+ }
136
+ } catch ( error ) {
137
+ logger.error( { error: error, function: 'downStoresList' } );
138
+ res.sendError( error, 500 );
139
+ }
140
+ }
141
+ export async function openTicketList( req, res ) {
142
+ try {
143
+ let openTicketList = await findTangoTicket( { 'issueType': 'infra', 'status': { $ne: 'closed' } }, { issueType: 1, ticketId: 1, basicDetails: 1, createdAt: 1, updateAt: 1, ticketDetails: 1 } );
144
+ if ( openTicketList.length ) {
145
+ res.sendSuccess( {
146
+ count: openTicketList.length,
147
+ data: openTicketList,
148
+ } );
149
+ } else {
150
+ res.sendError( 'No data found', 204 );
151
+ }
152
+ } catch ( error ) {
153
+ logger.error( { error: error, function: 'openTicketList' } );
154
+ res.sendError( error, 500 );
155
+ }
156
+ }
157
+
158
+ export async function assigntoUser( req, res ) {
159
+ try {
160
+ await updateOneTangoTicket( { ticketId: req.body.ticketId }, { 'ticketDetails.assigntoUser': true } );
161
+ res.sendSuccess( 'assigntoUser Successfully' );
162
+ } catch ( error ) {
163
+ logger.error( { error: error, function: 'assigntoUser' } );
164
+ res.sendError( error, 500 );
165
+ }
166
+ }
167
+ export async function updateRefreshTicket( req, res ) {
168
+ try {
169
+ for ( let ticket of req.body.TicketList ) {
170
+ let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
171
+ let findStore = await findOneStore( { storeId: getTicket.basicDetails.storeId, status: 'active' } );
172
+ if ( findStore ) {
173
+ await updateOneTangoTicket( { ticketId: ticket.ticketId }, { 'ticketDetails.ticketType': 'refreshticket', 'ticketDetails.refreshTicketStatus': 'notidentified' } );
174
+ await updateOneTangoTicketunset( { ticketId: ticket.ticketId }, { 'ticketDetails.addressingUser': 1 } );
175
+ if ( getTicket && getTicket.basicDetails ) {
176
+ let getclient = await findOneClient( { clientId: getTicket.basicDetails.clientId } );
177
+ if ( getclient && getclient.ticketConfigs && getclient.ticketConfigs.emailAlert ) {
178
+ let downTimeQuery = {
179
+ 'size': 1,
180
+ 'query': {
181
+ 'bool': {
182
+ 'must': [
183
+ {
184
+ 'term': {
185
+ 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
186
+ },
187
+ },
188
+ {
189
+ 'term': {
190
+ 'doc.store_id.keyword': getTicket.basicDetails.storeId,
191
+ },
192
+ },
193
+
194
+ ],
195
+
196
+ },
197
+ },
198
+ };
199
+ let downtimetotal;
200
+ const downtime = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).downTimeHourly, downTimeQuery );
201
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
202
+ if ( streamwiseDowntime.length > 0 ) {
203
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
204
+ return accumulator + currentValue.down_time;
205
+ }, 0 );
206
+ const average = sum / streamwiseDowntime.length;
207
+ downtimetotal = Math.round( average );
208
+ } else {
209
+ downtimetotal = 0;
210
+ }
211
+ let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
212
+ const attachments = null;
213
+ const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
214
+ const fileContent = readFileSync( join() + '/src/hbs/refreshTicket.hbs', 'utf8' );
215
+ const htmlContent = handlebars.compile( fileContent );
216
+ let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
217
+ if ( store.spocDetails && store.spocDetails.length > 0 ) {
218
+ let spocEmail = store.spocDetails[0].email;
219
+ let spocName = store.spocDetails[0].name;
220
+ let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
221
+ let primaryIssue = 'Not Identified';
222
+ if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
223
+ primaryIssue = Issue[0].reasons[0].primaryIssue;
224
+ }
225
+ let Uidomain = `${JSON.parse( process.env.URL ).domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
226
+
227
+ const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primary: primaryIssue, storeName: getTicket.basicDetails.storeName, hibernation: '', spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: JSON.parse( process.env.URL ).apiDomain } );
228
+ if ( spocEmail && isValidEmail( spocEmail ) ) {
229
+ await sendEmailWithSES( spocEmail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ res.sendSuccess( 'updated Successfully' );
238
+ } catch ( error ) {
239
+ logger.error( { error: error, function: 'updateRefreshTicket' } );
240
+ res.sendError( error, 500 );
241
+ }
242
+ }
243
+ export async function closeTicket( req, res ) {
244
+ try {
245
+ for ( let ticket of req.body.TicketList ) {
246
+ let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
247
+ if ( getTicket ) {
248
+ if ( ticket.status == 'closed' ) {
249
+ getTicket.ticketActivity.push( {
250
+ actionType: 'dataRecived',
251
+ timeStamp: new Date(),
252
+ actionBy: 'Tango',
253
+ } );
254
+ }
255
+ await updateOneTangoTicket( { ticketId: ticket.ticketId },
256
+ {
257
+ status: ticket.status,
258
+ ticketActivity: getTicket.ticketActivity,
259
+ issueClosedDate: new Date(),
260
+ },
261
+ );
262
+ let downTimeQuery = {
263
+ 'size': 1,
264
+ 'query': {
265
+ 'bool': {
266
+ 'must': [
267
+ {
268
+ 'term': {
269
+ 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
270
+ },
271
+ },
272
+ {
273
+ 'term': {
274
+ 'doc.store_id.keyword': getTicket.basicDetails.storeId,
275
+ },
276
+ },
277
+
278
+ ],
279
+
280
+ },
281
+ },
282
+ };
283
+ let downtimetotal;
284
+ const downtime = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).downTimeHourly, downTimeQuery );
285
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
286
+ if ( streamwiseDowntime.length > 0 ) {
287
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
288
+ return accumulator + currentValue.down_time;
289
+ }, 0 );
290
+ const average = sum / streamwiseDowntime.length;
291
+ downtimetotal = Math.round( average );
292
+ } else {
293
+ downtimetotal = 0;
294
+ }
295
+
296
+ let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
297
+ let primaryIssue = '';
298
+
299
+ if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
300
+ primaryIssue = Issue[0].reasons[0].primaryIssue;
301
+ }
302
+ let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
303
+ const attachments = null;
304
+ const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
305
+ const fileContent = readFileSync( join() + '/src/hbs/closeTicekt.hbs', 'utf8' );
306
+ const htmlContent = handlebars.compile( fileContent );
307
+ let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
308
+ if ( store.spocDetails && store.spocDetails.length > 0 ) {
309
+ let spocEmail = store.spocDetails[0].email;
310
+ let spocName = store.spocDetails[0].name;
311
+ let Uidomain = `${JSON.parse( process.env.URL ).domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
312
+ let getclient = await findOneClient( { clientId: getTicket.basicDetails.clientId } );
313
+ const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primaryIssue: primaryIssue, storeName: getTicket.basicDetails.storeName, spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD HH:mm' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: JSON.parse( process.env.URL ).apiDomain } );
314
+ if ( getclient.ticketConfigs.emailAlert && spocEmail && isValidEmail( spocEmail ) ) {
315
+ await sendEmailWithSES( spocEmail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ res.sendSuccess( 'updated Successfully' );
322
+ } catch ( error ) {
323
+ logger.error( { error: error, function: 'closeTicket' } );
324
+ res.sendError( error, 500 );
325
+ }
326
+ }
327
+ function isValidEmail( email ) {
328
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
329
+ return emailRegex.test( email );
330
+ }
331
+ export async function emailUserList( req, res ) {
332
+ try {
333
+ let clientList = await aggregateClient( [
334
+ {
335
+ $match: {
336
+ 'clientId': req.query.clientId,
337
+ 'status': 'active',
338
+ 'ticketConfigs.infraReport.type': { $exists: true },
339
+ },
340
+ },
341
+ {
342
+ $project: {
343
+ 'clientId': 1,
344
+ 'ticketConfigs.infraReport': 1,
345
+ },
346
+ },
347
+ {
348
+ '$lookup': {
349
+ 'from': 'users',
350
+ 'let': { 'clientId': '$clientId' },
351
+ 'pipeline': [
352
+ {
353
+ '$match': {
354
+ '$expr': {
355
+ $and: [
356
+ { '$eq': [ '$clientId', '$$clientId' ] },
357
+ { '$eq': [ '$emailAlert.infra', true ] },
358
+ ],
359
+ },
360
+ },
361
+ },
362
+ {
363
+ $project: {
364
+ userName: 1,
365
+ email: 1,
366
+ role: 1,
367
+ },
368
+ },
369
+ ],
370
+ 'as': 'user',
371
+ },
372
+ },
373
+ {
374
+ $unwind: {
375
+ path: '$user',
376
+ preserveNullAndEmptyArrays: true,
377
+ },
378
+ },
379
+ {
380
+ '$lookup': {
381
+ 'from': 'userAssignedStore',
382
+ 'let': { 'email': '$user.email' },
383
+ 'pipeline': [
384
+ {
385
+ '$match': {
386
+ '$expr': {
387
+ $and: [
388
+ { '$eq': [ '$userEmail', '$$email' ] },
389
+
390
+ ],
391
+ },
392
+ },
393
+ },
394
+ {
395
+ $project: {
396
+ assignedValue: 1,
397
+ assignedType: 1,
398
+ },
399
+ },
400
+ ],
401
+ 'as': 'assignedstore',
402
+ },
403
+ },
404
+ {
405
+ $unwind: {
406
+ path: '$assignedstore',
407
+ preserveNullAndEmptyArrays: true,
408
+ },
409
+ },
410
+ {
411
+ $project: {
412
+ 'clientId': 1,
413
+ 'ticketConfigs': 1,
414
+ 'email': '$user.email',
415
+ 'userName': '$user.userName',
416
+ 'role': '$user.role',
417
+ 'assignedValue': '$assignedstore.assignedValue',
418
+ 'assignedType': '$assignedstore.assignedType',
419
+ },
420
+ },
421
+ {
422
+ $group: {
423
+ '_id': '$email',
424
+ 'clientId': { $first: '$clientId' },
425
+ 'ticketConfigs': { $first: '$ticketConfigs' },
426
+ 'email': { $first: '$email' },
427
+ 'userName': { $first: '$userName' },
428
+ 'role': { $first: '$role' },
429
+ 'assignedValue': { $push: '$assignedValue' },
430
+ 'assignedType': { $first: '$assignedType' },
431
+ },
432
+ },
433
+ ] );
434
+
435
+ let getconfigs = await findOneClient( { clientId: req.query.clientId }, { 'ticketConfigs.infraReport': 1 } );
436
+ if ( getconfigs && getconfigs.ticketConfigs && getconfigs.ticketConfigs.infraReport ) {
437
+ if ( getconfigs.ticketConfigs.infraReport.allowCsm ) {
438
+ let findCsm = {
439
+ };
440
+ let result = await userAssignedStoreModel.findOne( {
441
+ 'clientId': req.query.clientId, 'userType': 'tango',
442
+ 'tangoUserType': 'csm',
443
+ } );
444
+ if ( findCsm ) {
445
+ findCsm.clientId = req.query.clientId,
446
+ findCsm.ticketConfigs = getconfigs.ticketConfigs;
447
+ findCsm.email = result.userEmail;
448
+ findCsm.userName = result.userEmail ? result.userEmail.split( '@' )[0] : '';
449
+ let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
450
+ findCsm.storeList = storelist;
451
+ clientList = [ ...clientList, ...[ findCsm ] ];
452
+ }
453
+ }
454
+ if ( getconfigs.ticketConfigs.infraReport.allowSpoc ) {
455
+ let spocList=[];
456
+ let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1, 'spocDetails': 1 } );
457
+ for ( let store of storelist ) {
458
+ if ( store.spocDetails&&store.spocDetails.length>0 ) {
459
+ if ( store.spocDetails[0].email!='' ) {
460
+ spocList.push( {
461
+ clientId: req.query.clientId,
462
+ ticketConfigs: getconfigs.ticketConfigs,
463
+ email: store.spocDetails[0].email,
464
+ userName: store.spocDetails[0].name,
465
+ storeList: [ store ],
466
+ } );
467
+ }
468
+ }
469
+ }
470
+ clientList = [ ...clientList, ...spocList ];
471
+ }
472
+
473
+ let findOutsideUserlist = await findmailonlyuser( { 'clientId': req.query.clientId, 'emailAlert.infra': true } );
474
+ let MailOnlyUsers = [];
475
+ if ( findOutsideUserlist.length > 0 ) {
476
+ for ( let user of findOutsideUserlist ) {
477
+ let obj = {
478
+ clientId: req.query.clientId,
479
+ ticketConfigs: getconfigs.ticketConfigs,
480
+ email: user.email,
481
+ userName: user.name,
482
+ storeList: [],
483
+ };
484
+ if ( user.stores&&user.stores.length>0 ) {
485
+ let storelist = await findStore( { 'storeId': { $in: user.stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
486
+ obj.storeList = storelist;
487
+ }
488
+ for ( let group of user.groups ) {
489
+ let groupdata = await findOneGroup( { 'groupName': group, 'clientId': req.query.clientId } );
490
+ let storelist = await findStore( { 'storeId': { $in: groupdata.storeList }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
491
+ obj.storeList = [ ...obj.storeList, ...storelist ];
492
+ }
493
+ MailOnlyUsers.push( obj );
494
+ }
495
+ clientList = [ ...clientList, ...MailOnlyUsers ];
496
+ }
497
+ }
498
+
499
+ let response = [];
500
+ for ( let user of clientList ) {
501
+ if ( user.role == 'superadmin' ) {
502
+ let storelist = await findStore( { 'clientId': user.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
503
+ user.storeList = storelist;
504
+ } else if ( user.role != 'superadmin' ) {
505
+ if ( user.assignedType == 'store' ) {
506
+ let storelist = await findStore( { 'storeId': { $in: user.assignedValue }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
507
+ user.storeList = storelist;
508
+ }
509
+ if ( user.assignedType == 'group' ) {
510
+ user.storeList = [];
511
+ for ( let group of user.assignedValue ) {
512
+ let groupdata = await findOneGroup( { '_id': group, 'clientId': user.clientId } );
513
+ if ( groupdata&&groupdata.storeList ) {
514
+ let storelist = await findStore( { 'storeId': { $in: groupdata.storeList }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
515
+ user.storeList = [ ...user.storeList, ...storelist ];
516
+ }
517
+ }
518
+ }
519
+ }
520
+
521
+ if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
522
+ let finalStoreList = [];
523
+ if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'customize' ) {
524
+ // console.log( user.ticketConfigs.infraReport.times, user.storeList.length );
525
+ for ( let store of user.storeList ) {
526
+ const result = await checkStoreTimezoneMatchesInterval( user.ticketConfigs.infraReport.times, store.storeProfile.timeZone );
527
+ if ( result ) {
528
+ finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
529
+ }
530
+ }
531
+ } else if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'defined' ) {
532
+ let times =await generateTimeIntervals( user.ticketConfigs.infraReport.startTime, user.ticketConfigs.infraReport.endTime, user.ticketConfigs.infraReport.interval );
533
+ for ( let store of user.storeList ) {
534
+ const result = await checkStoreTimezoneMatchesInterval( times, store.storeProfile.timeZone );
535
+ if ( result ) {
536
+ finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
537
+ }
538
+ }
539
+ }
540
+ if ( finalStoreList.length > 0 ) {
541
+ response.push( {
542
+ email: user.email,
543
+ userName: user.userName,
544
+ clientId: user.clientId,
545
+ role: user.role,
546
+ storeList: finalStoreList,
547
+ ticketConfigs: user.ticketConfigs.infraReport,
548
+ } );
549
+ }
550
+ }
551
+ }
552
+ res.sendSuccess( { count: response.length, response: response } );
553
+ } catch ( error ) {
554
+ logger.error( { error: error, function: 'emailUserList' } );
555
+ res.sendError( error, 500 );
556
+ }
557
+ }
558
+ export async function emailUserListv2( req, res ) {
559
+ try {
560
+ let clientList = await aggregateClient( [
561
+ {
562
+ $match: {
563
+ 'clientId': req.query.clientId,
564
+ 'status': 'active',
565
+ 'ticketConfigs.infraReport.type': { $exists: true },
566
+ },
567
+ },
568
+ {
569
+ $project: {
570
+ 'clientId': 1,
571
+ 'ticketConfigs.infraReport': 1,
572
+ },
573
+ },
574
+ {
575
+ '$lookup': {
576
+ 'from': 'users',
577
+ 'let': { 'clientId': '$clientId' },
578
+ 'pipeline': [
579
+ {
580
+ '$match': {
581
+ '$expr': {
582
+ $and: [
583
+ { '$eq': [ '$clientId', '$$clientId' ] },
584
+ { '$eq': [ '$emailAlert.infra', true ] },
585
+ ],
586
+ },
587
+ },
588
+ },
589
+ {
590
+ $project: {
591
+ userName: 1,
592
+ email: 1,
593
+ role: 1,
594
+ userType: 1,
595
+ assignedStores: 1,
596
+ },
597
+ },
598
+ ],
599
+ 'as': 'user',
600
+ },
601
+ },
602
+ {
603
+ $unwind: {
604
+ path: '$user',
605
+ preserveNullAndEmptyArrays: true,
606
+ },
607
+ },
608
+
609
+ {
610
+ $project: {
611
+ 'clientId': 1,
612
+ 'ticketConfigs': 1,
613
+ 'email': '$user.email',
614
+ 'userName': '$user.userName',
615
+ 'role': '$user.role',
616
+ 'userType': '$user.userType',
617
+ 'assignedStores': '$user.assignedStores',
618
+ },
619
+ },
620
+ {
621
+ $group: {
622
+ '_id': '$email',
623
+ 'clientId': { $first: '$clientId' },
624
+ 'ticketConfigs': { $first: '$ticketConfigs' },
625
+ 'email': { $first: '$email' },
626
+ 'userName': { $first: '$userName' },
627
+ 'role': { $first: '$role' },
628
+ 'userType': { $first: '$userType' },
629
+ 'assignedStores': { $first: '$assignedStores' },
630
+
631
+
632
+ },
633
+ },
634
+ ] );
635
+
636
+ let getconfigs = await findOneClient( { clientId: req.query.clientId }, { 'ticketConfigs.infraReport': 1 } );
637
+ if ( getconfigs && getconfigs.ticketConfigs && getconfigs.ticketConfigs.infraReport ) {
638
+ if ( getconfigs.ticketConfigs.infraReport.allowCsm ) {
639
+ let findCsm = {
640
+ };
641
+ let result = await userAssignedStoreModel.findOne( {
642
+ 'clientId': req.query.clientId, 'userType': 'tango',
643
+ 'tangoUserType': 'csm',
644
+ } );
645
+ if ( findCsm ) {
646
+ findCsm.clientId = req.query.clientId,
647
+ findCsm.ticketConfigs = getconfigs.ticketConfigs;
648
+ findCsm.email = result.userEmail;
649
+ findCsm.userName = result.userEmail ? result.userEmail.split( '@' )[0] : '';
650
+ let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
651
+ findCsm.storeList = storelist;
652
+ clientList = [ ...clientList, ...[ findCsm ] ];
653
+ }
654
+ }
655
+ if ( getconfigs.ticketConfigs.infraReport.allowSpoc ) {
656
+ let spocList=[];
657
+ let storelist = await findStore( { 'clientId': req.query.clientId, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1, 'spocDetails': 1 } );
658
+ for ( let store of storelist ) {
659
+ if ( store.spocDetails&&store.spocDetails.length>0 ) {
660
+ if ( store.spocDetails[0].email!='' ) {
661
+ spocList.push( {
662
+ clientId: req.query.clientId,
663
+ ticketConfigs: getconfigs.ticketConfigs,
664
+ email: store.spocDetails[0].email,
665
+ userName: store.spocDetails[0].name,
666
+ storeList: [ store ],
667
+ } );
668
+ }
669
+ }
670
+ }
671
+ clientList = [ ...clientList, ...spocList ];
672
+ }
673
+
674
+ let findOutsideUserlist = await findmailonlyuser( { 'clientId': req.query.clientId, 'emailAlert.infra': true } );
675
+ let MailOnlyUsers = [];
676
+ if ( findOutsideUserlist.length > 0 ) {
677
+ for ( let user of findOutsideUserlist ) {
678
+ let obj = {
679
+ clientId: req.query.clientId,
680
+ ticketConfigs: getconfigs.ticketConfigs,
681
+ email: user.email,
682
+ userName: user.name,
683
+ storeList: [],
684
+ };
685
+ if ( user.stores&&user.stores.length>0 ) {
686
+ let storelist = await findStore( { 'storeId': { $in: user.stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
687
+ obj.storeList = storelist;
688
+ }
689
+ for ( let cluster of user.clusters ) {
690
+ let clusterdata = await findOneCluster( { '_id': cluster, 'clientId': req.query.clientId } );
691
+
692
+ if ( clusterdata&&clusterdata.stores ) {
693
+ let stores = clusterdata.stores.map( ( data ) => data.storeId );
694
+ let storelist = await findStore( { 'storeId': { $in: stores }, 'status': 'active', 'edge.firstFile': true }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
695
+
696
+ obj.storeList = [ ...obj.storeList, ...storelist ];
697
+ }
698
+ }
699
+ MailOnlyUsers.push( obj );
700
+ }
701
+ clientList = [ ...clientList, ...MailOnlyUsers ];
702
+ }
703
+ }
704
+
705
+ let response = [];
706
+ console.log( clientList );
707
+ for ( let user of clientList ) {
708
+ if ( user.userType == 'client' ) {
709
+ if ( user.role !== 'superadmin' ) {
710
+ let storeIds =new Set();
711
+ if ( user.assignedStores ) {
712
+ storeIds = new Set( user.assignedStores.map( ( store ) => store.storeId ) );
713
+ }
714
+
715
+ // Fetch clusters and teams in parallel
716
+ const [ clustersList, teamsList ] = await Promise.all( [
717
+ findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
718
+ findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } ),
719
+ ] );
720
+
721
+ // Process clusters
722
+ if ( clustersList.length > 0 ) {
723
+ for ( let cluster of clustersList ) {
724
+ cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
725
+ }
726
+ }
727
+
728
+ // Process teams
729
+ if ( teamsList.length > 0 ) {
730
+ for ( let team of teamsList ) {
731
+ for ( let user of team.users ) {
732
+ let findUser = await findOneUser( { _id: user.userId } );
733
+ if ( findUser && findUser.assignedStores?.length > 0 ) {
734
+ findUser.assignedStores.forEach( ( store ) => storeIds.add( store.storeId ) );
735
+ }
736
+
737
+ // Fetch clusters for the user
738
+ let userClustersList = await findcluster( { clientId: user.clientId, Teamlead: { $elemMatch: { email: findUser.email } } } );
739
+ if ( userClustersList.length > 0 ) {
740
+ for ( let cluster of userClustersList ) {
741
+ cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
742
+ }
743
+ }
744
+ }
745
+ }
746
+ }
747
+ let TeamMember = await findteams( { clientId: user.clientId, users: { $elemMatch: { email: user.email } } } );
748
+ if ( TeamMember&&TeamMember.length>0 ) {
749
+ for ( let team of TeamMember ) {
750
+ let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
751
+ if ( clusterList.length > 0 ) {
752
+ for ( let cluster of clusterList ) {
753
+ cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
754
+ }
755
+ }
756
+ }
757
+ }
758
+ let TeamLeader = await findteams( { clientId: user.clientId, Teamlead: { $elemMatch: { email: user.email } } } );
759
+ if ( TeamLeader&&TeamLeader.length>0 ) {
760
+ for ( let team of TeamLeader ) {
761
+ let clusterList = await findcluster( { clientId: user.clientId, teams: { $elemMatch: { name: team.teamName } } } );
762
+ if ( clusterList.length > 0 ) {
763
+ for ( let cluster of clusterList ) {
764
+ cluster.stores.forEach( ( store ) => storeIds.add( store.storeId ) );
765
+ }
766
+ }
767
+ }
768
+ }
769
+
770
+ user.assigned = Array.from( storeIds );
771
+ let storeList = await findStore( { clientId: user.clientId, storeId: { $in: user.assigned }, status: 'active' }, { storeId: 1, storeName: 1, storeProfile: 1 } );
772
+ user.assigned = storeList.length;
773
+ user.storeList = storeList;
774
+ } else {
775
+ let storeList = await findStore( { clientId: user.clientId, status: 'active' }, { storeId: 1, storeName: 1, storeProfile: 1 } );
776
+ user.assigned = storeList.length;
777
+ user.storeList = storeList;
778
+ }
779
+ }
780
+
781
+
782
+ if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
783
+ let finalStoreList = [];
784
+ if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'customize' ) {
785
+ // console.log( user.ticketConfigs.infraReport.times, user.storeList.length );
786
+ for ( let store of user.storeList ) {
787
+ const result = await checkStoreTimezoneMatchesInterval( user.ticketConfigs.infraReport.times, store.storeProfile.timeZone );
788
+ if ( result ) {
789
+ finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
790
+ }
791
+ }
792
+ } else if ( user.ticketConfigs.infraReport.type && user.ticketConfigs.infraReport.type === 'defined' ) {
793
+ let times =await generateTimeIntervals( user.ticketConfigs.infraReport.startTime, user.ticketConfigs.infraReport.endTime, user.ticketConfigs.infraReport.interval );
794
+ for ( let store of user.storeList ) {
795
+ const result = await checkStoreTimezoneMatchesInterval( times, store.storeProfile.timeZone );
796
+ if ( result ) {
797
+ finalStoreList.push( { storeId: store.storeId, timeZone: store.storeProfile.timeZone } );
798
+ }
799
+ }
800
+ }
801
+ if ( finalStoreList.length > 0 ) {
802
+ response.push( {
803
+ email: user.email,
804
+ userName: user.userName,
805
+ clientId: user.clientId,
806
+ role: user.role,
807
+ storeList: finalStoreList,
808
+ ticketConfigs: user.ticketConfigs.infraReport,
809
+ } );
810
+ }
811
+ }
812
+ }
813
+ res.sendSuccess( { count: response.length, response: response } );
814
+ } catch ( error ) {
815
+ logger.error( { error: error, function: 'emailUserListv2' } );
816
+ res.sendError( error, 500 );
817
+ }
818
+ }
819
+
820
+ export async function checkStoreTimezoneMatchesInterval( times, storeTimezone ) {
821
+ const currentTimeInStore = dayjs().tz( storeTimezone ).format( 'HH:mm' );
822
+ // console.log( storeTimezone, currentTimeInStore );
823
+ return times.some( ( timeObj ) => {
824
+ const interval = timeObj.interval;
825
+ console.log( `Checking interval ${interval} against current time ${currentTimeInStore}` );
826
+ const currentStoreTime = dayjs().tz( storeTimezone );
827
+ const intervalTime = dayjs.tz( `${currentStoreTime.format( 'YYYY-MM-DD' )} ${interval}`, 'YYYY-MM-DD HH:mm', storeTimezone );
828
+ const diffInMinutes = intervalTime.diff( currentStoreTime, 'minute' );
829
+ // console.log( diffInMinutes );
830
+ return diffInMinutes >= 0 && diffInMinutes <= 15;
831
+ } );
832
+ };
833
+ export async function generateTimeIntervals( startTime, endTime, interval ) {
834
+ function timeToMinutes( time ) {
835
+ const [ hours, minutes ] = time.split( ':' ).map( Number );
836
+ return hours * 60 + minutes;
837
+ }
838
+
839
+ function minutesToTime( minutes ) {
840
+ const hours = Math.floor( minutes / 60 );
841
+ const mins = minutes % 60;
842
+ return `${String( hours ).padStart( 2, '0' )}:${String( mins ).padStart( 2, '0' )}`;
843
+ }
844
+ const startMinutes = timeToMinutes( startTime );
845
+ const endMinutes = timeToMinutes( endTime );
846
+ const intervalMinutes = parseInt( interval, 10 );
847
+ const intervals = [];
848
+ for ( let currentTime = startMinutes; currentTime <= endMinutes; currentTime += intervalMinutes ) {
849
+ intervals.push( { interval: minutesToTime( currentTime ) } );
850
+ }
851
+ return intervals;
852
+ }
853
+
854
+
855
+ export async function infraReportSent( req, res ) {
856
+ try {
857
+ let date = dayjs().format( 'YYYY-MM-DD' );
858
+ let query = [ {
859
+ $match: {
860
+ $and: [
861
+ { issueDate: { $lte: new Date( date ) } },
862
+ { 'basicDetails.storeId': { $in: req.body.storeList } },
863
+ { issueType: 'infra' },
864
+ { status: { $ne: 'closed' } },
865
+ ],
866
+ },
867
+ },
868
+ {
869
+ $project: {
870
+ ticketId: 1,
871
+ basicDetails: 1,
872
+ createdAt: 1,
873
+ status: 1,
874
+ ticketDetails: 1,
875
+ issueClosedDate: 1,
876
+ otherscomment: {
877
+ $filter: {
878
+ input: '$ticketActivity',
879
+ as: 'item',
880
+ cond: { $eq: [ '$$item.actionType', 'comment' ] },
881
+ },
882
+ },
883
+ primaryIssue: {
884
+ $filter: {
885
+ input: '$ticketActivity',
886
+ as: 'item',
887
+ cond: { $eq: [ '$$item.actionType', 'issueUpdate' ] },
888
+ },
889
+ },
890
+ },
891
+ },
892
+ {
893
+ $unwind: {
894
+ path: '$otherscomment', preserveNullAndEmptyArrays: true,
895
+ },
896
+ },
897
+ {
898
+ $unwind: {
899
+ path: '$primaryIssue', preserveNullAndEmptyArrays: true,
900
+ },
901
+ },
902
+ {
903
+ $unwind: {
904
+ path: '$primaryIssue.reasons', preserveNullAndEmptyArrays: true,
905
+ },
906
+ },
907
+ {
908
+ $unwind: {
909
+ path: '$primaryIssue.reasons.secondaryIssue', preserveNullAndEmptyArrays: true,
910
+ },
911
+ },
912
+ {
913
+ $project: {
914
+ ticketId: 1,
915
+ basicDetails: 1,
916
+ createdAt: 1,
917
+ status: 1,
918
+ ticketDetails: 1,
919
+ issueClosedDate: 1,
920
+ ommentText: { $ifNull: [ '$primaryIssue.comment', '-' ] },
921
+ otherscomment: { $ifNull: [ '$otherscomment.comment', '-' ] },
922
+ primaryIssue: { $ifNull: [ '$primaryIssue.reasons.primaryIssue', '-' ] },
923
+ secondaryIssue: { $ifNull: [ '$primaryIssue.reasons.secondaryIssue.name', '-' ] },
924
+ },
925
+ },
926
+ {
927
+ $group: {
928
+ _id: '$ticketId',
929
+ ticketDetails: { $first: '$ticketDetails' },
930
+ issueClosedDate: { $first: '$issueClosedDate' },
931
+ basicDetails: { $first: '$basicDetails' },
932
+ commentText: { $last: '$commentText' },
933
+ primaryIssue: { $last: '$primaryIssue' },
934
+ secondaryIssue: { $last: '$secondaryIssue' },
935
+ otherscomment: { $last: '$otherscomment' },
936
+ createdAt: { $first: '$createdAt' },
937
+ status: { $last: '$status' },
938
+ },
939
+ },
940
+ ];
941
+
942
+ let ticketList = await aggregateTangoTicket( query );
943
+ const exportdata = [];
944
+ if ( ticketList&&ticketList.length>0 ) {
945
+ for ( let element of ticketList ) {
946
+ let clientuser = await findOneUser( { _id: element.ticketDetails.addressingClient } );
947
+ let tangouser = await findOneUser( { _id: element.ticketDetails.addressingUser } );
948
+ let findstore = await findOneStore( { storeId: element.basicDetails.storeId }, { storeProfile: 1 } );
949
+ console.log( findstore.storeProfile.storeCode );
950
+
951
+ exportdata.push( {
952
+ 'BrandID': element.basicDetails.clientId,
953
+ 'BrandName': element.basicDetails.clientName,
954
+ 'TicketCreatedDate & Time': dayjs( element.createdAt ).tz( 'Asia/Kolkata' ).format( 'YYYY-MM-DD HH:mm A' ),
955
+ 'StoreID': element.basicDetails.storeId,
956
+ 'StoreCode': findstore.storeProfile&&findstore.storeProfile.storeCode?findstore.storeProfile.storeCode:'-',
957
+ 'StoreName': element.basicDetails.storeName,
958
+ 'PrimaryIssue ': element.primaryIssue,
959
+ 'SecondaryIssue': element.secondaryIssue,
960
+ 'Status ': element.status,
961
+ 'RespondedBy': clientuser && clientuser.userName ? clientuser.userName : '-',
962
+ 'ResolvedBy': tangouser && tangouser.userName ? tangouser.userName : '-',
963
+ 'TicketClosedDate & Time': element.issueClosedDate ? dayjs( element.issueClosedDate ).tz( 'Asia/Kolkata' ).format( 'YYYY-MM-DD HH:mm A' ) : '-',
964
+ 'LatestComment': element.otherscomment ? element.otherscomment : element.commentText,
965
+ 'ActivityLog': {
966
+ label: 'Link',
967
+ url: `${JSON.parse( process.env.URL ).domain + '/manage/stores/infra-ticket?storeId=' + element.basicDetails.storeId}`,
968
+ },
969
+ } );
970
+ }
971
+
972
+ let issueCount = await countDocumentsTangoTicket( {
973
+ $and: [
974
+ { issueDate: { $lte: new Date( date ) } },
975
+ { 'basicDetails.storeId': { $in: req.body.storeList } },
976
+ { issueType: 'infra' },
977
+ { status: { $ne: 'closed' } },
978
+ ],
979
+ } );
980
+ let client = await findOneClient( { clientId: req.body.clientId } );
981
+ let avgDownTime = client.ticketConfigs.infraDownTime * 60;
982
+ let issueList = await findinfraReason( { parentId: { '$exists': false } } );
983
+ const categoryCounts = {};
984
+ let response;
985
+ if ( ticketList.length > 0 ) {
986
+ ticketList.forEach( ( item ) => {
987
+ const categoryName = item.primaryIssue;
988
+ if ( categoryCounts[categoryName] ) {
989
+ categoryCounts[categoryName]++;
990
+ } else {
991
+ categoryCounts[categoryName] = 1;
992
+ }
993
+ } );
994
+ response = issueList.map( ( category ) => ( {
995
+ name: category.name,
996
+ count: categoryCounts[category.name] || 0,
997
+ } ) );
998
+ } else {
999
+ response = issueList.map( ( category ) => ( {
1000
+ name: category.name,
1001
+ count: 0,
1002
+ } ) );
1003
+ }
1004
+ let obj = {};
1005
+ for ( let issue of response ) {
1006
+ if ( issue.name === 'System Issues' ) {
1007
+ obj.systemIssue = issue.count;
1008
+ } else if ( issue.name === 'Store Operation Issues' ) {
1009
+ obj.storeoperationIssue = issue.count;
1010
+ } else if ( issue.name === 'Application Issues' ) {
1011
+ obj.applicationIssue = issue.count;
1012
+ } else if ( issue.name === 'Camera Issues' ) {
1013
+ obj.cameraIssue = issue.count;
1014
+ } else if ( issue.name === 'Internet Issues' ) {
1015
+ obj.internetIssue = issue.count;
1016
+ }
1017
+ }
1018
+ let csmEmail = await userAssignedStoreModel.findOne( { clientId: req.body.clientId, userType: 'tango', tangoUserType: 'csm' } );
1019
+ if ( csmEmail ) {
1020
+ csmEmail = csmEmail && csmEmail.userEmail ? csmEmail.userEmail : 'csm@tangotech.co.in';
1021
+ }
1022
+
1023
+ let attachments = null;
1024
+ let buffer = await download( exportdata );
1025
+ let reportDate = dayjs().format( 'DD-MM-YYYY' );
1026
+ attachments = {
1027
+ filename: `dailyInfraReport- ${reportDate}.xlsx`,
1028
+ content: buffer,
1029
+ contentType: 'application/xlsx', // e.g., 'application/pdf'
1030
+ };
1031
+
1032
+ const subject = `${client.clientName} Daily Digest - Infra Downtime Report - ${reportDate}`;
1033
+ const fileContent = readFileSync( join() + '/src/hbs/dailyInfraReport.hbs', 'utf8' );
1034
+ const htmlContent = handlebars.compile( fileContent );
1035
+ let Uidomain = `${JSON.parse( process.env.URL ).domain}`;
1036
+ const html = htmlContent( { ...req.body, Uidomain: Uidomain, issueCount: issueCount, avgDownTime: avgDownTime, reportdate: date, content: obj, date: date, csmEmail: csmEmail, domain: JSON.parse( process.env.URL ).apiDomain } );
1037
+ if ( isValidEmail( req.body.email ) ) {
1038
+ const result = await sendEmailWithSES( req.body.email, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
1039
+ res.sendSuccess( result );
1040
+ } else {
1041
+ return res.sendError( 'Invalid email', 204 );
1042
+ }
1043
+ } else {
1044
+ return res.sendError( 'No data', 204 );
1045
+ }
1046
+ } catch ( error ) {
1047
+ logger.error( { error: error, function: 'infraReportSent' } );
1048
+ res.sendError( error, 500 );
1049
+ }
1050
+ }
1051
+ export async function spocmailchange() {
1052
+ let storelist = await findStore( {} );
1053
+ for ( let store of storelist ) {
1054
+ if ( store && store.spocDetails ) {
1055
+ let spoclist = [];
1056
+ for ( let spoc of store.spocDetails ) {
1057
+ spoc.email = spoc.email.replace( '@', '1@' ),
1058
+ spoclist.push( spoc );
1059
+ }
1060
+ await updateOneStore( { storeId: store.storeId }, { spocDetails: spoclist } );
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+
1066
+ export async function camAngleChangeReport( req, res ) {
1067
+ try {
1068
+ const currentDate = dayjs();
1069
+
1070
+
1071
+ const previousDay = currentDate.subtract( 1, 'day' );
1072
+
1073
+
1074
+ const formattedPreviousDay = previousDay.format( 'DD-MM-YYYY' );
1075
+ const angleChange = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).cameraAngleChange,
1076
+ {
1077
+ 'query': {
1078
+ 'bool': {
1079
+ 'must': [
1080
+ {
1081
+ 'term': {
1082
+ 'date.keyword': formattedPreviousDay,
1083
+ },
1084
+ },
1085
+ {
1086
+ 'term': {
1087
+ 'client_id.keyword': req.body.clientId,
1088
+ },
1089
+ },
1090
+ ],
1091
+ },
1092
+ },
1093
+ } );
1094
+ if ( angleChange.body.hits.hits.length > 0 ) {
1095
+ const exportdata = [];
1096
+
1097
+ for ( let camera of angleChange.body.hits.hits ) {
1098
+ let result = camera._source;
1099
+
1100
+ if ( result && result.cameraAngleChangeStatus && result.camera_info.length > 0 ) {
1101
+ for ( let stream of result.camera_info ) {
1102
+ let params = {
1103
+ Bucket: JSON.parse( process.env.BUCKET ).cameraAngle,
1104
+ file_path: stream.path,
1105
+ };
1106
+ let Image = await signedUrl( params );
1107
+
1108
+
1109
+ let findStore = await findOneStore( { storeId: result.store_id } );
1110
+
1111
+
1112
+ exportdata.push( {
1113
+ 'Store Name': findStore.storeName,
1114
+ 'Store Code': findStore.storeId,
1115
+ 'Date Changed': formattedPreviousDay,
1116
+ 'ImageURL': {
1117
+ url: Image,
1118
+ label: 'Angle changed Image',
1119
+ },
1120
+ } );
1121
+ }
1122
+ }
1123
+ }
1124
+ let buffer = await download( exportdata );
1125
+
1126
+ let attachments = {
1127
+ filename: `Camera angle change- ${formattedPreviousDay}.xlsx`,
1128
+ content: buffer,
1129
+ contentType: 'application/xlsx', // e.g., 'application/pdf'
1130
+ };
1131
+ let subject = `Camera Angle Modified - ${formattedPreviousDay}`;
1132
+ let html = `<div>We wanted to inform you that the camera angle in your stores has been adjusted recently.</div>`;
1133
+ let result = await sendEmailWithSES( req.body.toMail, subject, html, attachments, JSON.parse( process.env.SES ).adminEmail );
1134
+ if ( result ) {
1135
+ res.sendSuccess( 'Email send successfully' );
1136
+ }
1137
+ } else {
1138
+ res.sendSuccess( 'No changes in camera Angle' );
1139
+ }
1140
+ } catch ( error ) {
1141
+ logger.error( { error: error, function: 'camAngleChangeList' } );
1142
+ res.sendError( error, 500 );
1143
+ }
1144
+ }
1145
+
1146
+
1147
+ export async function download( data ) {
1148
+ const wb = new xl.Workbook();
1149
+ const ws = wb.addWorksheet( 'Worksheet Name' );
1150
+ const headers = Object.keys( data[0] );
1151
+
1152
+ for ( let i = 0; i < headers.length; i++ ) {
1153
+ ws.cell( 1, i + 1 ).string( headers[i] );
1154
+ };
1155
+ for ( let i = 0; i < data.length; i++ ) {
1156
+ const dataRow = data[i];
1157
+ for ( let j = 0; j < headers.length; j++ ) {
1158
+ const header = headers[j];
1159
+ const value = dataRow[header];
1160
+ if ( value && typeof value === 'object' && value.hasOwnProperty( 'label' ) && value.hasOwnProperty( 'url' ) ) {
1161
+ ws.cell( i + 2, j + 1 ).link( value.url );
1162
+ ws.cell( i + 2, j + 1 ).string( value.label );
1163
+ } else {
1164
+ ws.cell( i + 2, j + 1 ).string( value?.toString() );
1165
+ }
1166
+ }
1167
+ }
1168
+ return await wb.writeToBuffer();
1169
+ }
1170
+ export async function edgeApplogsCheck( req, res ) {
1171
+ try {
1172
+ let finalresult = [];
1173
+ let ticketList = await findTangoTicket( { 'issueDate': new Date( req.body.issueDate ), 'status': { $ne: 'closed' }, 'ticketDetails.issueStatus': 'notidentified', 'issueType': 'infra' } );
1174
+ for ( let ticket of ticketList ) {
1175
+ req.body.date = dayjs( ticket.createdAt ).format( 'YYYY-MM-DD' );
1176
+ let errorLog = {
1177
+ 'size': 5000,
1178
+ 'query': {
1179
+ 'bool': {
1180
+ 'must': [
1181
+ {
1182
+ 'range': {
1183
+ 'log_code': {
1184
+ 'gte': 1000,
1185
+ },
1186
+ },
1187
+ },
1188
+ {
1189
+ 'term': {
1190
+ 'store_date.keyword': dayjs( req.body.date ).format( 'DD-MM-YYYY' ),
1191
+ },
1192
+ },
1193
+ {
1194
+ 'term': {
1195
+ 'storeId.keyword': ticket.basicDetails.storeId,
1196
+ },
1197
+ },
1198
+
1199
+ ],
1200
+
1201
+ },
1202
+ },
1203
+ 'sort': [
1204
+ { 'timestamp': { 'order': 'desc' } },
1205
+ ],
1206
+ };
1207
+
1208
+ const errorLogList = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).edgeAppSystemLogs, errorLog );
1209
+ let internetSlowcount = [];
1210
+ let result = [];
1211
+ let apploginCount=[];
1212
+ function createLogCheck( error ) {
1213
+ return {
1214
+ code: error._source.log_code,
1215
+ edgelog: error._source.data,
1216
+ };
1217
+ }
1218
+ for ( let error of errorLogList.body.hits.hits ) {
1219
+ const logCode = error._source.log_code;
1220
+ const relevantCodes = [ 1003, 1011, 1022, 1024, 1025, 1026, 1000 ];
1221
+ if ( relevantCodes.includes( Number( logCode ) ) ) {
1222
+ result.push( createLogCheck( error ) );
1223
+ }
1224
+
1225
+
1226
+ if ( Number( logCode ) === 1004 ) {
1227
+ apploginCount.push( createLogCheck( error ) );
1228
+ }
1229
+ if ( logCode === '1005' ) {
1230
+ const bytes = error._source.data.upload_Speed.split( '.' )[0];
1231
+ const megabytes = bytesToMB( bytes );
1232
+ if ( megabytes < 2 ) {
1233
+ internetSlowcount.push( error );
1234
+ result.push( createLogCheck( error ) );
1235
+ }
1236
+ }
1237
+
1238
+ if ( Number( logCode ) > 2000 && Number( logCode ) !== 2007 && Number( logCode ) !== 2014 ) {
1239
+ result.push( createLogCheck( error ) );
1240
+ }
1241
+ }
1242
+
1243
+ let newraray = apploginCount.reverse();
1244
+
1245
+ let findissueEdgeApp = {};
1246
+ if ( newraray.length>0 ) {
1247
+ result = [ ...result, ...[ newraray[0] ] ];
1248
+ }
1249
+ if ( result.length > 0 ) {
1250
+ let logincount = [];
1251
+ let appcrashcount = [];
1252
+
1253
+
1254
+ for ( let findissue of result ) {
1255
+ const istTimestamp = dayjs.utc( ticket.createdAt ).tz( 'Asia/Kolkata' ).format( 'HH:mm:ss' );
1256
+ if ( findissue.code == '1003' ) {
1257
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1258
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1259
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1260
+ const existsInArray = finalresult.some( ( item ) =>
1261
+ item.ticketId === ticket.ticketId,
1262
+ );
1263
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1264
+ findissueEdgeApp = {
1265
+ ticketId: ticket.ticketId,
1266
+ edgelog: findissue,
1267
+ storeId: ticket.basicDetails.storeId,
1268
+ primary: 'System Issues',
1269
+ secondary: [ 'Antivirus blockages' ],
1270
+ };
1271
+ updateIssue( findissueEdgeApp );
1272
+ finalresult.push( findissueEdgeApp );
1273
+ }
1274
+ } if ( findissue.code == '2004' ) {
1275
+ let store = await findOneStore( { storeId: ticket.basicDetails.storeId } );
1276
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1277
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1278
+ const stroreopenTime = dayjs( `${req.body.date} ${store.storeProfile.open}` );
1279
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1280
+ const afterStoreOpen = occurringTimeParsed.isAfter( stroreopenTime );
1281
+ const existsInArray = finalresult.some( ( item ) =>
1282
+ item.ticketId === ticket.ticketId,
1283
+ );
1284
+ if ( isOccurringTimeEarlier && afterStoreOpen && !existsInArray ) {
1285
+ logincount.push( findissue.edgelog.occuringTime );
1286
+ if ( logincount.length > 3 ) {
1287
+ findissueEdgeApp = {
1288
+ ticketId: ticket.ticketId,
1289
+ edgelog: findissue,
1290
+ storeId: ticket.basicDetails.storeId,
1291
+ primary: 'Internet Issues',
1292
+ secondary: [ 'Internet not working' ],
1293
+ };
1294
+ updateIssue( findissueEdgeApp );
1295
+ finalresult.push( findissueEdgeApp );
1296
+ }
1297
+ }
1298
+ } else if ( findissue.code == '2024' ) {
1299
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1300
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1301
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1302
+ const existsInArray = finalresult.some( ( item ) =>
1303
+ item.ticketId === ticket.ticketId,
1304
+ );
1305
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1306
+ findissueEdgeApp = {
1307
+ ticketId: ticket.ticketId,
1308
+ storeId: ticket.basicDetails.storeId,
1309
+ edgelog: findissue.edgelog,
1310
+ primary: 'Camera Issues',
1311
+ secondary: [ 'Internet and camera not working' ],
1312
+ };
1313
+ updateIssue( findissueEdgeApp );
1314
+ finalresult.push( findissueEdgeApp );
1315
+ }
1316
+ } else if ( findissue.code == '1024' ) {
1317
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.data.occuringTime}` );
1318
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1319
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1320
+
1321
+ const existsInArray = finalresult.some( ( item ) =>
1322
+ item.ticketId === ticket.ticketId,
1323
+ );
1324
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1325
+ const pingOutput = findissue.edgelog.data.message;
1326
+ const regex = /Sent = (\d+), Received = (\d+), Lost = (\d+)/;
1327
+ const match = pingOutput.match( regex );
1328
+
1329
+ if ( match ) {
1330
+ // const sent = match[1];
1331
+ const received = match[2];
1332
+ // const lost = match[3];
1333
+
1334
+ if ( Number( received ) < 2 ) {
1335
+ findissueEdgeApp = {
1336
+ ticketId: ticket.ticketId,
1337
+ storeId: ticket.basicDetails.storeId,
1338
+ edgelog: findissue.edgelog.data,
1339
+ primary: 'Camera Issues',
1340
+ secondary: [ 'IP not pinging / Camera not working' ],
1341
+ };
1342
+ } else if ( Number( received ) > 2 ) {
1343
+ findissueEdgeApp = {
1344
+ ticketId: ticket.ticketId,
1345
+ storeId: ticket.basicDetails.storeId,
1346
+ edgelog: findissue.edgelog.data,
1347
+ primary: 'Camera Issues',
1348
+ secondary: [ 'Rtsp/url not supported/Camera credential changed' ],
1349
+ };
1350
+ }
1351
+ updateIssue( findissueEdgeApp );
1352
+ finalresult.push( findissueEdgeApp );
1353
+ }
1354
+ }
1355
+ } else if ( findissue.code == '1025' ) {
1356
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1357
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1358
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1359
+
1360
+ const existsInArray = finalresult.some( ( item ) =>
1361
+ item.ticketId === ticket.ticketId,
1362
+ );
1363
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1364
+ findissueEdgeApp = {
1365
+ ticketId: ticket.ticketId,
1366
+ storeId: ticket.basicDetails.storeId,
1367
+ edgelog: findissue.edgelog,
1368
+ primary: 'Application Issues',
1369
+ secondary: [ 'App without stream' ],
1370
+ };
1371
+ updateIssue( findissueEdgeApp );
1372
+ finalresult.push( findissueEdgeApp );
1373
+ }
1374
+ } else if ( findissue.code == '1011' ) {
1375
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1376
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1377
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1378
+ const existsInArray = finalresult.some( ( item ) =>
1379
+ item.ticketId === ticket.ticketId,
1380
+ );
1381
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1382
+ findissueEdgeApp = {
1383
+ ticketId: ticket.ticketId,
1384
+ edgelog: findissue,
1385
+ storeId: ticket.basicDetails.storeId,
1386
+ primary: 'System Issues',
1387
+ secondary: [ 'System is in Sleep Mode' ],
1388
+ };
1389
+ updateIssue( findissueEdgeApp );
1390
+ finalresult.push( findissueEdgeApp );
1391
+ }
1392
+ } else if ( findissue.code == '1026' ) {
1393
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1394
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1395
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1396
+ const existsInArray = finalresult.some( ( item ) =>
1397
+ item.ticketId === ticket.ticketId,
1398
+ );
1399
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1400
+ if ( ( Number( findissue.edgelog.files_generated ) - Number( findissue.edgelog.files_pushed ) ) > 3 && internetSlowcount.length == 1 ) {
1401
+ findissueEdgeApp = {
1402
+ ticketId: ticket.ticketId,
1403
+ edgelog: findissue,
1404
+ storeId: ticket.basicDetails.storeId,
1405
+ primary: 'Internet Issues',
1406
+ secondary: [ 'Internet slow' ],
1407
+ };
1408
+ }
1409
+ updateIssue( findissueEdgeApp );
1410
+ finalresult.push( findissueEdgeApp );
1411
+ }
1412
+ } else if ( findissue.code == '1016' ) {
1413
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1414
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1415
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1416
+ const existsInArray = finalresult.some( ( item ) =>
1417
+ item.ticketId === ticket.ticketId,
1418
+ );
1419
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1420
+ findissueEdgeApp = {
1421
+ ticketId: ticket.ticketId,
1422
+ edgelog: findissue,
1423
+ storeId: ticket.basicDetails.storeId,
1424
+ primary: 'Application Issues',
1425
+ secondary: [ 'App login issue' ],
1426
+ };
1427
+ updateIssue( findissueEdgeApp );
1428
+ finalresult.push( findissueEdgeApp );
1429
+ }
1430
+ } else if ( findissue.code == '1004' ) {
1431
+ let store = await findOneStore( { storeId: ticket.basicDetails.storeId } );
1432
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1433
+ const storeOpenTime = dayjs( `${req.body.date} ${store.storeProfile.open}` );
1434
+ const diffInMinutes = occurringTimeParsed.diff( storeOpenTime, 'minute' );
1435
+ const existsInArray = finalresult.some( ( item ) =>
1436
+ item.ticketId === ticket.ticketId,
1437
+ );
1438
+ if ( !existsInArray ) {
1439
+ if ( diffInMinutes>60 ) {
1440
+ findissueEdgeApp = {
1441
+ ticketId: ticket.ticketId,
1442
+ edgelog: findissue,
1443
+ storeId: ticket.basicDetails.storeId,
1444
+ primary: 'Store Operation Issues',
1445
+ secondary: [ 'Store opened Lately/System turned on lately' ],
1446
+ };
1447
+ updateIssue( findissueEdgeApp );
1448
+ finalresult.push( findissueEdgeApp );
1449
+ }
1450
+ }
1451
+ } else if ( findissue.code == '1000' ) {
1452
+ const occurringTimeParsed = dayjs( `${req.body.date} ${findissue.edgelog.occuringTime}` );
1453
+ const ticketCreatedParsed = dayjs( `${req.body.date} ${istTimestamp}` );
1454
+ const isOccurringTimeEarlier = occurringTimeParsed.isBefore( ticketCreatedParsed );
1455
+ const existsInArray = finalresult.some( ( item ) =>
1456
+ item.ticketId === ticket.ticketId,
1457
+ );
1458
+ if ( isOccurringTimeEarlier && !existsInArray ) {
1459
+ appcrashcount.push( findissue );
1460
+ if ( appcrashcount.length > 3 ) {
1461
+ findissueEdgeApp = {
1462
+ ticketId: ticket.ticketId,
1463
+ edgelog: findissue,
1464
+ storeId: ticket.basicDetails.storeId,
1465
+ primary: 'System Issues',
1466
+ secondary: [ 'System utilization high' ],
1467
+ };
1468
+ updateIssue( findissueEdgeApp );
1469
+ finalresult.push( findissueEdgeApp );
1470
+ }
1471
+ }
1472
+ }
1473
+ }
1474
+ }
1475
+ }
1476
+ res.sendSuccess( { count: finalresult.length, result: finalresult } );
1477
+ } catch ( error ) {
1478
+ logger.error( { error: error, function: 'camAngleChangeList' } );
1479
+ res.sendError( error, 500 );
1480
+ }
1481
+ }
1482
+ function bytesToMB( bytes ) {
1483
+ return bytes / ( 1024 * 1024 );
1484
+ }
1485
+
1486
+ export async function updateIssue( data ) {
1487
+ try {
1488
+ let Ticket = await findOneTangoTicket(
1489
+ {
1490
+ ticketId: data.ticketId,
1491
+ },
1492
+ );
1493
+ if ( Ticket ) {
1494
+ data.issueType = Ticket.issueType;
1495
+ data.basicDetails = Ticket.basicDetails;
1496
+ data.ticketDetails = Ticket.ticketDetails;
1497
+ data.ticketActivity = Ticket.ticketActivity;
1498
+ if ( data.primary && data.secondary && data.secondary.length ) {
1499
+ let primaryReason = await findOneinfraReason( { name: data.primary } );
1500
+ if ( !primaryReason ) {
1501
+ return res.sendError( 'Primary Reason Not exists in database', 500 );
1502
+ }
1503
+ const secondary = [];
1504
+ const steptoReslove = [];
1505
+ for ( let i = 0; i < data.secondary.length; i++ ) {
1506
+ let secondaryReason = await findOneinfraReason( { name: data.secondary[i] } );
1507
+ if ( !secondaryReason ) {
1508
+ return res.sendError( `secondary Reason - ${data.secondary[i]} Not exists in database`, 500 );
1509
+ }
1510
+ secondary.push( {
1511
+ name: secondaryReason.name,
1512
+ } );
1513
+ let resolveSteps = [];
1514
+ for ( let i = 0; i < secondaryReason.stepstoResolve.length; i++ ) {
1515
+ resolveSteps.push( {
1516
+ name: secondaryReason.stepstoResolve[i].name,
1517
+ } );
1518
+ }
1519
+ steptoReslove.push( {
1520
+ primaryIssue: secondaryReason.name,
1521
+ secondaryIsssue: [ ...resolveSteps ],
1522
+ } );
1523
+ }
1524
+
1525
+ data.ticketActivity.push( {
1526
+ actionType: 'issueUpdate',
1527
+ actionBy: 'automated',
1528
+ IdentifiedBy: 'Tango',
1529
+ timeStamp: new Date(),
1530
+ reasons: [ {
1531
+ primaryIssue: primaryReason.name,
1532
+ secondaryIssue: secondary,
1533
+ } ],
1534
+ },
1535
+ );
1536
+ }
1537
+ if ( data.issueType == 'infra' ) {
1538
+ let client = await findOneClient( { clientId: data.basicDetails.clientId }, { ticketConfigs: 1 } );
1539
+ let statusCheckAlertTime = dayjs().add( client.ticketConfigs.statusCheckAlert, 'hours' ).format( 'YYYY-MM-DD hh:mm' );
1540
+ data.ticketActivity.push( {
1541
+ actionType: 'statusCheck',
1542
+ timeStamp: statusCheckAlertTime,
1543
+ actionBy: 'Tango',
1544
+ IdentifiedBy: 'Tango',
1545
+ statusCheckAlertTime: statusCheckAlertTime,
1546
+ } );
1547
+ }
1548
+ let client = await findOneClient( { clientId: data.basicDetails.clientId } );
1549
+ let refreshdate = dayjs().add( client.ticketConfigs.refreshAlert, 'days' );
1550
+
1551
+ let query = {
1552
+ 'ticketActivity': data.ticketActivity,
1553
+ 'ticketDetails.issueIdentifiedDate': new Date(),
1554
+ 'ticketDetails.issueStatus': 'identified',
1555
+ 'status': 'inprogress',
1556
+ 'ticketDetails.ticketRefreshTime': new Date( dayjs( refreshdate ).format( 'YYYY-MM-DD' ) ),
1557
+ };
1558
+ await updateOneTangoTicket( { ticketId: data.ticketId }, query );
1559
+ }
1560
+ } catch ( error ) {
1561
+ logger.error( { error: error, function: 'updateAutomaticIssue' } );
1562
+ }
1563
+ }
1564
+