tango-app-api-audit 1.0.34 → 1.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.cjs CHANGED
@@ -36,7 +36,7 @@ module.exports = {
36
36
  'no-unused-vars': 'error',
37
37
  'new-cap': [ 'error', { 'newIsCap': true, 'capIsNew': false } ],
38
38
  'prefer-const': 'off',
39
- // 'no-console': 'error',
39
+ 'no-console': 'error',
40
40
  },
41
41
  };
42
42
 
package/README.md CHANGED
@@ -1,29 +1,29 @@
1
- # README #
2
-
3
- This README would normally document whatever steps are necessary to get your application up and running.
4
-
5
- ### What is this repository for? ###
6
-
7
- * Quick summary
8
- * Version
9
- * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
-
11
- ### How do I get set up? ###
12
-
13
- * Summary of set up
14
- * Configuration
15
- * Dependencies
16
- * Database configuration
17
- * How to run tests
18
- * Deployment instructions
19
-
20
- ### Contribution guidelines ###
21
-
22
- * Writing tests
23
- * Code review
24
- * Other guidelines
25
-
26
- ### Who do I talk to? ###
27
-
28
- * Repo owner or admin
1
+ # README #
2
+
3
+ This README would normally document whatever steps are necessary to get your application up and running.
4
+
5
+ ### What is this repository for? ###
6
+
7
+ * Quick summary
8
+ * Version
9
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
+
11
+ ### How do I get set up? ###
12
+
13
+ * Summary of set up
14
+ * Configuration
15
+ * Dependencies
16
+ * Database configuration
17
+ * How to run tests
18
+ * Deployment instructions
19
+
20
+ ### Contribution guidelines ###
21
+
22
+ * Writing tests
23
+ * Code review
24
+ * Other guidelines
25
+
26
+ ### Who do I talk to? ###
27
+
28
+ * Repo owner or admin
29
29
  * Other community or team contact
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
- {
2
- "name": "tango-app-api-audit",
3
- "version": "1.0.34",
4
- "description": "audit & audit metrics apis",
5
- "main": "index.js",
6
- "type": "module",
7
- "scripts": {
8
- "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
- },
10
- "engines": {
11
- "node": ">=18.10.0"
12
- },
13
- "author": "praveenraj",
14
- "license": "ISC",
15
- "dependencies": {
16
- "adm-zip": "^0.5.15",
17
- "aws-sdk": "^2.1643.0",
18
- "dayjs": "^1.11.11",
19
- "dotenv": "^16.4.5",
20
- "express": "^4.19.2",
21
- "handlebars": "^4.7.8",
22
- "joi-to-swagger": "^6.2.0",
23
- "lodash": "^4.17.21",
24
- "mongodb": "^6.7.0",
25
- "nodemon": "^3.1.3",
26
- "swagger-ui-express": "^5.0.1",
27
- "tango-api-schema": "^2.0.143",
28
- "tango-app-api-middleware": "^3.1.28",
29
- "winston": "^3.13.0",
30
- "winston-daily-rotate-file": "^5.0.0"
31
- },
32
- "devDependencies": {
33
- "eslint": "^8.57.0",
34
- "eslint-config-google": "^0.14.0",
35
- "eslint-config-semistandard": "^17.0.0",
36
- "eslint-config-standard": "^17.1.0",
37
- "eslint-plugin-import": "^2.29.1",
38
- "eslint-plugin-promise": "^6.2.0"
39
- }
40
- }
1
+ {
2
+ "name": "tango-app-api-audit",
3
+ "version": "1.0.36",
4
+ "description": "audit & audit metrics apis",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
+ },
10
+ "engines": {
11
+ "node": ">=18.10.0"
12
+ },
13
+ "author": "praveenraj",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "adm-zip": "^0.5.15",
17
+ "aws-sdk": "^2.1643.0",
18
+ "dayjs": "^1.11.11",
19
+ "dotenv": "^16.4.5",
20
+ "express": "^4.19.2",
21
+ "handlebars": "^4.7.8",
22
+ "joi-to-swagger": "^6.2.0",
23
+ "lodash": "^4.17.21",
24
+ "mongodb": "^6.7.0",
25
+ "nodemon": "^3.1.3",
26
+ "swagger-ui-express": "^5.0.1",
27
+ "tango-api-schema": "^2.0.144",
28
+ "tango-app-api-middleware": "^3.1.28",
29
+ "winston": "^3.13.0",
30
+ "winston-daily-rotate-file": "^5.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "eslint": "^8.57.0",
34
+ "eslint-config-google": "^0.14.0",
35
+ "eslint-config-semistandard": "^17.0.0",
36
+ "eslint-config-standard": "^17.1.0",
37
+ "eslint-plugin-import": "^2.29.1",
38
+ "eslint-plugin-promise": "^6.2.0"
39
+ }
40
+ }
@@ -1,14 +1,20 @@
1
- import { getUTC, insertOpenSearchData, listFileByPath, signedUrl, sqsReceive } from 'tango-app-api-middleware';
1
+ import { getJsonFileData, getUTC, insertOpenSearchData, listFileByPath, sendMessageToQueue, signedUrl, sqsReceive } from 'tango-app-api-middleware';
2
2
  import { aggregateZoneUserAudit, createZoneUserAudit, findOneZoneUserAudit } from '../service/zoneUserAudit.service.js';
3
3
  import { logger } from 'tango-app-api-middleware';
4
4
  import { aggregateAssignZoneAudit } from '../service/assignZoneAudit.service.js';
5
- import { createStoreZoneAudit } from '../service/storeZoneAudit.service.js';
5
+ import { aggregateStoreZoneAudit, createStoreZoneAudit, findOneStoreZoneAudit, updateOneStoreZoneAudit } from '../service/storeZoneAudit.service.js';
6
6
  import { findOneUser } from '../service/user.service.js';
7
7
  import { findOneStore } from '../service/store.service.js';
8
+ import dayjs from 'dayjs';
9
+ import { aggregateUserAudit, findOneUserAudit, updateOneUserAudit } from '../service/userAudit.service.js';
10
+ import { aggregateAssignAudit } from '../service/assignAudit.service.js';
11
+ import { createAuditLog, findOneAuditLog } from '../service/auditLog.service.js';
12
+ import { mapCustomer, mapEmployee, mapJunk, splitB20000, splitE0, splitG20000 } from '../validation/audit.validation.js';
8
13
 
9
14
  export async function getZoneAuditFile( req, res ) {
10
15
  try {
11
16
  const bucket = JSON.parse( process.env.BUCKET );
17
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
12
18
  const inputData = req.query;
13
19
  const data = await getUTC( new Date(), new Date() );
14
20
  const start = data.start;
@@ -37,7 +43,6 @@ export async function getZoneAuditFile( req, res ) {
37
43
  },
38
44
  ];
39
45
  const userDetails = await aggregateZoneUserAudit( userQuery );
40
- console.log( userDetails, 'zoneAuditStatus==>' );
41
46
  const zoneAuditStatus =
42
47
  userDetails && userDetails.length > 0 ? userDetails[0].auditStatus : null;
43
48
 
@@ -62,13 +67,11 @@ export async function getZoneAuditFile( req, res ) {
62
67
  ];
63
68
 
64
69
  const userAssign = await aggregateAssignZoneAudit( query );
65
- console.log( userAssign, 'userAssign' );
66
70
  if ( userAssign.length > 0 ) {
67
71
  logger.info( 'Hit in user assign', { inputData } );
68
72
  } else {
69
73
  logger.info( 'Hit in new file', { inputData } );
70
74
  const consumer = await sqsReceive( inputData.queueName );
71
- console.log( consumer, 'consumer' );
72
75
  if ( !consumer ) {
73
76
  logger.error( {
74
77
  error: consumer,
@@ -192,13 +195,10 @@ export async function getZoneAuditFile( req, res ) {
192
195
  zoneName: msg.zone_id,
193
196
 
194
197
  };
195
- console.log( record );
196
- console.log( storeRecord );
197
198
  insertData = await createZoneUserAudit( record );
198
199
  await createStoreZoneAudit( storeRecord );
199
200
  } else {
200
201
  insertData = userDetails[0];
201
- console.log( insertData );
202
202
  }
203
203
  } else {
204
204
  logger.error( {
@@ -271,7 +271,7 @@ export async function getZoneAuditFile( req, res ) {
271
271
 
272
272
  export async function saveDraft( req, res ) {
273
273
  try {
274
- // const openSearch = JSON.parse( process.env.OPENSEARCH );
274
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
275
275
  const inputData = req.body;
276
276
  inputData.userId = req.user._id;
277
277
  const getUserAuditData = await findOneZoneUserAudit( { _id: inputData.auditId } );
@@ -282,17 +282,17 @@ export async function saveDraft( req, res ) {
282
282
  return res.sendError( 'File Assigned to Someone else', 203 );
283
283
  }
284
284
 
285
- // const userQuery = {
286
- // _id: inputData.auditId,
287
- // };
288
- // let userRecord = {
289
- // isDraft: true,
290
- // auditStatus: 'drafted',
291
- // };
292
- // const storeQuery = {
293
- // storeId: inputData.storeId,
294
- // fileDate: inputData.fileDate,
295
- // };
285
+ const userQuery = {
286
+ _id: inputData.auditId,
287
+ };
288
+ let userRecord = {
289
+ isDraft: true,
290
+ auditStatus: 'drafted',
291
+ };
292
+ const storeQuery = {
293
+ storeId: inputData.storeId,
294
+ fileDate: inputData.fileDate,
295
+ };
296
296
 
297
297
  let storeRecord = {
298
298
  status: 'drafted',
@@ -309,33 +309,588 @@ export async function saveDraft( req, res ) {
309
309
  storeRecord.timeSpent = timeDifferenceInMinutes;
310
310
  }
311
311
 
312
- // if ( inputData.userCommands ) {
313
- // userRecord.userCommands = inputData.userCommands;
314
- // const logData = {
315
- // userId: req.user._id,
316
- // userName: req.user.userName,
317
- // logType: 'audit',
318
- // logSubType: 'auditDraft',
319
- // logData: {
320
- // auditType: inputData.auditType,
321
- // storeId: inputData.storeId,
322
- // clientName: inputData.queueName,
323
- // beforeCount: inputData.totalCount,
324
- // afterCount: inputData.customerCount,
325
- // auditId: inputData.auditId,
326
- // },
327
- // };
328
- // await insertOpenSearchData( openSearch.activityLog, logData );
329
- // }
330
- // await createAuditLog( inputData );
331
- // await updateOneUserAudit( userQuery, userRecord );
332
- // await updateOneStoreAudit( storeQuery, storeRecord );
333
- // return res.sendSuccess( {
334
- // result: 'The file has been drafted successfully',
335
- // } );
312
+ if ( inputData.userCommands ) {
313
+ userRecord.userCommands = inputData.userCommands;
314
+ const logData = {
315
+ userId: req.user._id,
316
+ userName: req.user.userName,
317
+ logType: 'zoneAudit',
318
+ logSubType: 'auditDraft',
319
+ logData: {
320
+ auditType: inputData.auditType,
321
+ storeId: inputData.storeId,
322
+ clientName: inputData.queueName,
323
+ beforeCount: inputData.totalCount,
324
+ afterCount: inputData.customerCount,
325
+ auditId: inputData.auditId,
326
+ },
327
+ };
328
+ await insertOpenSearchData( openSearch.activityLog, logData );
329
+ }
330
+ await createAuditLog( inputData );
331
+ await updateOneUserAudit( userQuery, userRecord );
332
+ await updateOneStoreZoneAudit( storeQuery, storeRecord );
333
+ return res.sendSuccess( {
334
+ result: 'The file has been drafted successfully',
335
+ } );
336
336
  } catch ( error ) {
337
337
  const err = error.message || 'Internal Server Error';
338
338
  logger.error( { error: error, message: req.body, function: 'saveDraft' } );
339
339
  return res.sendError( err, 500 );
340
340
  }
341
341
  }
342
+
343
+ export async function getDraftedData( req, res ) {
344
+ try {
345
+ const inputData = req.query;
346
+ const userId = inputData.userId || req.user._id;
347
+ const query = {
348
+ fileDate: inputData.fileDate,
349
+ storeId: inputData.storeId,
350
+ zoneName: inputData.zoneName,
351
+ moduleType: 'zone',
352
+ userId: userId,
353
+ };
354
+ const result = await findOneAuditLog( query, {}, { createdAt: -1 }, 1 );
355
+
356
+ if ( result.length == 0 ) {
357
+ return res.sendError( 'No Data Found', 500 );
358
+ }
359
+ return res.sendSuccess( { result: result } );
360
+ } catch ( error ) {
361
+ const err = error.message || 'Internal Server Error';
362
+ logger.error( {
363
+ error: error,
364
+ message: req.query,
365
+ function: 'getDraftedData',
366
+ } );
367
+ return res.sendError( err, 500 );
368
+ }
369
+ }
370
+
371
+ export async function save( req, res ) {
372
+ try {
373
+ const bucket = JSON.parse( process.env.BUCKET );
374
+ const sqs = JSON.parse( process.env.SQS );
375
+
376
+ const inputData = req.body;
377
+ const params = {
378
+ Key: `${inputData.storeId}/${inputData.zoneName}/${inputData.fileDate}/${bucket.masterJsonFile}`,
379
+ };
380
+
381
+ if ( inputData.auditType === 'ReAudit' ) {
382
+ params.Bucket = bucket.auditUploadBucket;
383
+ } else {
384
+ params.Bucket = bucket.masterJson;
385
+ }
386
+ const fileData = await getJsonFileData( params );
387
+
388
+ if ( fileData?.statusCode ) {
389
+ logger.error( {
390
+ error: `ERROR IN UPLOAD PATH: ${inputData.storeId}/${inputData.zoneName}/${inputData.fileDate}/${bucket.masterJsonFile}`,
391
+ type: 'UPLOAD_ERROR',
392
+ } );
393
+ return res.sendError( fileData, 500 );
394
+ }
395
+
396
+ const masterJsonData = JSON.parse( fileData.toString( 'utf-8' ) );
397
+
398
+ const [ G20000, B20000, E0 ] = await Promise.all( [
399
+ splitG20000( masterJsonData.person_data ),
400
+ splitB20000( masterJsonData.person_data ),
401
+ splitE0( masterJsonData.person_data ),
402
+ ] );
403
+
404
+ const [ mappingData ] = await Promise.all( [
405
+ mapEmployee( B20000, inputData.employee ),
406
+ mapJunk( B20000, inputData.junk ),
407
+ mapCustomer( B20000, inputData.customer ),
408
+ ] );
409
+
410
+ const tempData = _.sortBy( [ ...mappingData, ...G20000, ...E0 ], 'index' );
411
+
412
+ const tempObject = { person_data: tempData };
413
+
414
+ const uploadDataParams = {
415
+ Bucket: bucket.auditUploadBucket,
416
+ Key: `${inputData.storeId}/${inputData.zoneName}/${inputData.fileDate}/`,
417
+ ContentType: 'application/json',
418
+ fileName: `${bucket.masterJsonFile}`,
419
+ body: JSON.stringify( tempObject ),
420
+ };
421
+ const mappingUpload = await fileUpload( uploadDataParams );
422
+
423
+ if ( mappingUpload?.statusCode ) {
424
+ logger.error( {
425
+ error: `ERROR IN UPLOAD PATH: ${inputData.storeId}/${inputData.fileDate}/${bucket.masterJsonFile}`,
426
+ type: 'UPLOAD_ERROR',
427
+ } );
428
+ return res.sendError( mappingUpload, 500 );
429
+ }
430
+
431
+ let getUserAuditData = await findOneUserAudit( {
432
+ _id: new mongoose.Types.ObjectId( inputData.auditId ),
433
+ } );
434
+ const isoDate = getUserAuditData.startTime;
435
+ const currentTime = dayjs();
436
+ const isoDateTime = dayjs( isoDate );
437
+ const timeDifferenceInMinutes = currentTime.diff( isoDateTime, 'seconds' );
438
+
439
+ await updateOneUserAudit(
440
+ { _id: new mongoose.Types.ObjectId( inputData.auditId ) },
441
+ {
442
+ auditStatus: 'completed',
443
+ endTime: Date.now(),
444
+ isDraft: false,
445
+ afterCount: inputData.customerCount,
446
+ timeSpent: timeDifferenceInMinutes,
447
+ },
448
+ );
449
+
450
+ /** *
451
+ *
452
+ * logs need to be added
453
+ *
454
+ *
455
+ */
456
+
457
+ let storeConfig = await findOneStore(
458
+ {
459
+ storeId: getUserAuditData.storeId,
460
+ },
461
+ {
462
+ auditConfigs: 1,
463
+ clientId: 1,
464
+ },
465
+ );
466
+ let storeAuditData = await findOneStoreZoneAudit( {
467
+ storeId: inputData.storeId,
468
+ zoneName: inputData.zoneName,
469
+ fileDate: inputData.fileDate,
470
+ } );
471
+
472
+ let clientData = await findClient( { clientId: storeConfig.clientId } );
473
+
474
+ if ( (
475
+ ( storeAuditData.beforeCount - inputData.customerCount ) /
476
+ storeAuditData.beforeCount <
477
+ storeConfig.auditConfigs.ratio
478
+ ) && inputData.auditType == 'Audit' ) {
479
+ logger.info(
480
+ `Hit in Reaudit pushing queue Store Id=${inputData.storeId},File Date=${inputData.fileDate}, /n \nQueue Name = ${clientData[0].auditConfigs.queueName}`,
481
+ 'Reaudit',
482
+ );
483
+
484
+ const sqsProduceQueue = {
485
+ QueueUrl: `${sqs.url}${clientData[0].auditConfigs.queueName}`,
486
+ MessageBody: JSON.stringify( {
487
+ store_id: inputData.storeId,
488
+ zone_id: inputData.zoneName,
489
+ audit_type: 'ReAudit',
490
+ curr_date: inputData.fileDate,
491
+ total_count: inputData.customerCount,
492
+ before_count: storeAuditData.beforeCount,
493
+ } ),
494
+ };
495
+ const sqsQueue = await sendMessageToQueue( sqsProduceQueue.QueueUrl, sqsProduceQueue.MessageBody );
496
+ logger.info( { sqsQueue: sqsQueue } );
497
+
498
+ if ( sqsQueue.statusCode ) {
499
+ logger.error( {
500
+ error: `ERROR IN UPLOAD PATH: ${inputData.storeId}/${inputData.fileDate}/${bucket.masterJsonFile}`,
501
+ type: 'UPLOAD_ERROR',
502
+ } );
503
+ return res.sendError( mappingUpload, 500 );
504
+ }
505
+
506
+ await updateOneStoreZoneAudit( { storeId: inputData.storeId, zoneName: inputData.zoneName },
507
+ {
508
+ status: 'not_assign',
509
+ afterCount: inputData.customerCount,
510
+ auditType: 'ReAudit',
511
+ timeSpent: timeDifferenceInMinutes,
512
+ } );
513
+ } else {
514
+ logger.info( 'Hit in Features', { data: ( _.omit( inputData, [ 'junk', 'employee', 'customer' ] ) ) } );
515
+ await updateOneStoreZoneAudit( { storeId: inputData.storeId, zoneName: inputData.zoneName },
516
+ {
517
+ status: 'completed',
518
+ afterCount: inputData.customerCount,
519
+ timeSpent: timeDifferenceInMinutes,
520
+ } );
521
+
522
+ const sqsProduceQueue = {
523
+ QueueUrl: `${sqs.url}${sqs.feature}`,
524
+
525
+ MessageBody: JSON.stringify( {
526
+ store_id: inputData.storeId,
527
+ zone_id: inputData.zoneName,
528
+ store_date: inputData.fileDate,
529
+ bucket_name: `${bucket.auditUploadBucket}`,
530
+ process_type: 'audit',
531
+ } ),
532
+ };
533
+ logger.info( { sqsProduceQueue: sqsProduceQueue } );
534
+ const sqsQueue = await sendMessageToQueue( sqsProduceQueue.QueueUrl, sqsProduceQueue.MessageBody );
535
+ logger.info( { sqsQueue: sqsQueue } );
536
+ }
537
+
538
+ return res.sendSuccess( { result: 'Updated Successfully' } );
539
+ } catch ( error ) {
540
+ const err = error.message || 'Internal Server Error';
541
+ logger.error( { error: error, message: req.body, function: 'save' } );
542
+ return res.sendError( err );
543
+ }
544
+ }
545
+
546
+ export async function workSpace( req, res ) {
547
+ try {
548
+ const inputData = req.query;
549
+ const limit = inputData.limit || 10;
550
+ const offset = inputData.offset ?
551
+ ( inputData.offset - 1 ) * inputData.limit : 0;
552
+ const dateRange = await getUTC( new Date(), new Date() );
553
+
554
+ const temp = [];
555
+ const clientQuery = [
556
+ {
557
+ $match: {
558
+ $and: [
559
+ { status: { $in: [ 'active', 'hold' ] } },
560
+ { 'auditConfigs.audit': { $eq: true } }, // NOTE: modified the code need to check for enable zone
561
+ ],
562
+ },
563
+ },
564
+ {
565
+ $project: {
566
+ clientName: '$clientName',
567
+ clientId: '$clientId',
568
+ },
569
+ },
570
+ ];
571
+
572
+ if ( inputData.searchValue && inputData.searchValue !== '' ) {
573
+ clientQuery.push( {
574
+ $match: {
575
+ $or: [
576
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
577
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
578
+ ],
579
+ },
580
+ } );
581
+ }
582
+ const count = await aggregateClient( clientQuery );
583
+ logger.info( { count: count } );
584
+ if ( count?.length == 0 ) {
585
+ return res.sendError( 'No Data Found', 204 );
586
+ }
587
+
588
+ if ( inputData.limit && !inputData.isExport ) {
589
+ clientQuery.push( { $skip: offset }, { $limit: limit } );
590
+ }
591
+
592
+ const clientDetails = await aggregateClient( clientQuery );
593
+ const clientList = clientDetails.map( ( i ) => i.clientId );
594
+ const query = [
595
+ {
596
+ $match: {
597
+ $and: [
598
+ {
599
+ clientId: { $in: clientList },
600
+ },
601
+ {
602
+ moduleType: 'zone',
603
+ },
604
+ ],
605
+
606
+ },
607
+ },
608
+ {
609
+ $group: {
610
+ _id: '$clientId',
611
+ clientId: { $last: '$clientId' },
612
+ totalFilesCount: { $last: '$totalFilesCount' },
613
+ clientName: { $last: '$clientName' },
614
+ queueName: { $last: '$queueName' },
615
+ },
616
+ },
617
+ {
618
+ $project: {
619
+ clientId: 1,
620
+ totalFilesCount: 1,
621
+ clientName: 1,
622
+ queueName: 1,
623
+ },
624
+ },
625
+ ];
626
+
627
+ const fileDate = dayjs( dateRange.start ).subtract( 1, 'days' );
628
+
629
+ const completedFiles = [
630
+ {
631
+ $match: {
632
+ $and: [
633
+ { clientId: { $in: clientList } },
634
+ { fileDateISO: { $eq: new Date( fileDate ) } },
635
+ ],
636
+ },
637
+ },
638
+ {
639
+ $project: {
640
+ clientId: 1,
641
+ completed: {
642
+ $cond: [ { $eq: [ '$status', 'completed' ] }, 1, 0 ],
643
+ },
644
+ },
645
+ },
646
+ {
647
+ $group: {
648
+ _id: { clientId: '$clientId', storeId: '$storeId' },
649
+ clientId: { $first: '$clientId' },
650
+ completedCount: { $sum: '$completed' },
651
+ },
652
+ },
653
+ {
654
+ $group: {
655
+ _id: '$clientId',
656
+ clientId: { $first: '$clientId' },
657
+ completedCount: { $first: '$completedCount' },
658
+ },
659
+ },
660
+ ];
661
+
662
+ const auditFiles = [
663
+ {
664
+ $match: {
665
+ $and: [
666
+ { clientId: { $in: clientList } },
667
+ { moduleType: { $eq: 'zone' } },
668
+ { createdAt: { $gte: dateRange.start } },
669
+ { createdAt: { $lte: dateRange.end } },
670
+ ],
671
+ },
672
+ },
673
+ {
674
+ $project: {
675
+ clientId: 1,
676
+ pending: {
677
+ $cond: [
678
+ { $or: [ { $in: [ '$auditStatus', [ 'inprogress', 'drafted' ] ] } ] },
679
+ 1,
680
+ 0,
681
+ ],
682
+ },
683
+ },
684
+ },
685
+ {
686
+ $group: {
687
+ _id: { clientId: '$clientId', storeId: '$storeId' },
688
+ clientId: { $first: '$clientId' },
689
+ pendingCount: { $sum: '$pending' },
690
+ totalCount: { $sum: 1 },
691
+ },
692
+ },
693
+ {
694
+ $group: {
695
+ _id: '$clientId',
696
+ clientId: { $first: '$clientId' },
697
+ pendingCount: { $first: { $ifNull: [ '$pendingCount', 0 ] } },
698
+ totalCount: { $first: '$totalCount' },
699
+ },
700
+ },
701
+ ];
702
+
703
+ const draftedFiles = [
704
+ {
705
+ $match: {
706
+ $and: [
707
+ { clientId: { $in: clientList } },
708
+ { moduleType: { $eq: 'zone' } },
709
+ { userId: { $eq: inputData.userId } },
710
+ { createdAt: { $gte: dateRange.start } },
711
+ { createdAt: { $lte: dateRange.end } },
712
+ ],
713
+ },
714
+ },
715
+ {
716
+ $sort: {
717
+ updatedAt: -1,
718
+ },
719
+ },
720
+ {
721
+ $project: {
722
+ clientId: 1,
723
+ isDraft: 1,
724
+ draft: {
725
+ $cond: [ { $eq: [ '$auditStatus', 'drafted' ] }, 1, 0 ],
726
+ },
727
+ inprogress: {
728
+ $cond: [ { $eq: [ '$auditStatus', 'inprogress' ] }, 1, 0 ],
729
+ },
730
+ },
731
+ },
732
+ {
733
+ $group: {
734
+ _id: '$clientId',
735
+ clientId: { $first: '$clientId' },
736
+ isDrafted: { $first: '$isDraft' },
737
+ draftCount: { $sum: '$draft' },
738
+ inprogressCount: { $sum: '$inprogress' },
739
+ },
740
+ },
741
+ ];
742
+
743
+ const userAsign = [
744
+ {
745
+ $match: {
746
+ $and: [
747
+ { userId: { $eq: req.user._id } },
748
+ { isCompleted: false },
749
+ { clientId: { $in: clientList } },
750
+ { moduleType: { $eq: 'zone' } } ],
751
+ },
752
+ },
753
+ {
754
+ $group: {
755
+ _id: '$clientId',
756
+ clientId: { $first: '$clientId' },
757
+ asignedCount: { $sum: 1 },
758
+ },
759
+ },
760
+ {
761
+ $project: {
762
+ _id: 0,
763
+ clientId: 1,
764
+ asignedCount: 1,
765
+ },
766
+ },
767
+ ];
768
+
769
+ const clientAssign = [
770
+ {
771
+ $match: {
772
+ $and: [
773
+ { isCompleted: false },
774
+ { moduleType: { $eq: 'zone' } },
775
+ { clientId: { $in: clientList } },
776
+ ],
777
+ },
778
+ },
779
+ {
780
+ $group: {
781
+ _id: '$clientId',
782
+ clientId: { $first: '$clientId' },
783
+ ClientAsignedCount: { $sum: 1 },
784
+ },
785
+ },
786
+ {
787
+ $project: {
788
+ _id: 0,
789
+ clientId: 1,
790
+ ClientAsignedCount: { $ifNull: [ '$ClientAsignedCount', 0 ] },
791
+ },
792
+ },
793
+ ];
794
+
795
+ const auditDetails = await aggregateAuditClientData( query );
796
+ const auditCount = await aggregateUserAudit( auditFiles );
797
+ const draftedCount = await aggregateUserAudit( draftedFiles );
798
+ const CompletedCount = await aggregateStoreZoneAudit( completedFiles );
799
+
800
+ const clientAssignresult = await aggregateAssignAudit( clientAssign );
801
+ const userAsignCount = await aggregateAssignAudit( userAsign );
802
+ const mergeAll = _.merge(
803
+ _.keyBy( clientDetails, 'clientId' ),
804
+ _.keyBy( auditCount, 'clientId' ),
805
+ _.keyBy( draftedCount, 'clientId' ),
806
+ _.keyBy( clientAssignresult, 'clientId' ),
807
+ );
808
+ const finalResult = _.values( mergeAll );
809
+ const merge = _.values(
810
+ _.merge(
811
+ _.keyBy( finalResult, 'clientId' ),
812
+ _.keyBy( auditDetails, 'clientId' ),
813
+ _.keyBy( userAsignCount, 'clientId' ),
814
+ _.keyBy( CompletedCount, 'clientId' ),
815
+ ),
816
+ );
817
+ logger.info( { merge: merge } );
818
+ let totalStores = 0;
819
+ for ( let i = 0; i < merge.length; i++ ) {
820
+ totalStores = totalStores + merge[i].totalFilesCount;
821
+
822
+ const pending = await listQueue( merge[i].queueName );
823
+
824
+ if ( !pending.statusCode ) {
825
+ temp.push( {
826
+ clientId: merge[i].clientId,
827
+ clientName: merge[i].clientName,
828
+ completedCount: merge[i].completedCount ?
829
+ Number( merge[i].completedCount ) :
830
+ 0,
831
+ pendingByQueue: Number( pending ),
832
+ pendingByUser:
833
+ merge[i].pendingCount >= 0 ?
834
+ Number( merge[i].pendingCount ) +
835
+ ( merge[i].ClientAsignedCount ?
836
+ Number( merge[i].ClientAsignedCount ) :
837
+ 0 ) :
838
+ 0,
839
+ totalCount: merge[i].totalFilesCount,
840
+ queueName: merge[i].queueName,
841
+ isDraft: merge[i].isDrafted ? merge[i].isDrafted : false,
842
+ isEnable:
843
+ Number( pending ) > 0 ||
844
+ merge[i].isDrafted ||
845
+ ( merge[i].asignedCount && merge[i].asignedCount > 0 ) ||
846
+ ( merge[i].inprogressCount && merge[i].inprogressCount > 0 ) ?
847
+ true :
848
+ false,
849
+ completedRatio: merge[i].totalFilesCount ?
850
+ merge[i].completedCount ?
851
+ Math.round(
852
+ ( Number( merge[i].completedCount ) /
853
+ Number( merge[i].totalFilesCount ) ) *
854
+ 100,
855
+ ) :
856
+ 0 :
857
+ 0,
858
+ isAssigned:
859
+ merge[i].asignedCount && merge[i].asignedCount > 0 ? true : false,
860
+ Assignedcount:
861
+ merge[i].asignedCount && merge[i].asignedCount > 0 ?
862
+ merge[i].asignedCount :
863
+ 0,
864
+ } );
865
+ }
866
+ }
867
+
868
+ if ( inputData.isExport ) {
869
+ const exportdata = [];
870
+ temp.forEach( ( element ) => {
871
+ exportdata.push( {
872
+ 'Client Name': element.clientName,
873
+ 'Client Id': element.clientId,
874
+ 'Completed Percentage': element.completedRatio + ' %',
875
+ 'Pending Queue': element.pendingByQueue,
876
+ 'Pending User': element.pendingByUser,
877
+ 'Completed': element.completedCount,
878
+ 'Draft': element.isDraft ? 1 : 0,
879
+ 'Assigned': element.Assignedcount,
880
+ } );
881
+ } );
882
+ await download( exportdata, res );
883
+ return;
884
+ } else {
885
+ return res.sendSuccess( {
886
+ result: temp,
887
+ count: count.length,
888
+ totalStores: totalStores,
889
+ } );
890
+ }
891
+ } catch ( error ) {
892
+ const err = error.message || 'Internal Server Error';
893
+ logger.info( { error: error, message: req.query, function: 'workSpace' } );
894
+ return res.sendError( err, 500 );
895
+ }
896
+ }
@@ -1,10 +1,10 @@
1
1
  import j2s from 'joi-to-swagger';
2
- import { getFileSchema, saveDraftSchema } from '../dtos/zoneAudit.dtos.js';
2
+ import { getDraftedDataSchema, getFileSchema, saveDraftSchema, saveSchema, workSpaceSchema } from '../dtos/zoneAudit.dtos.js';
3
3
 
4
4
  export const zoneAuditDocs = {
5
- '/v3/zone/get-file': {
5
+ '/v3/zone-audit/get-file': {
6
6
  get: {
7
- tags: [ 'Zone' ],
7
+ tags: [ 'Zone Audit' ],
8
8
  description: 'Get a file via queue message',
9
9
  operationId: 'get-file',
10
10
  parameters: [
@@ -36,9 +36,9 @@ export const zoneAuditDocs = {
36
36
  },
37
37
  },
38
38
  },
39
- '/v3/audit/save-draft': {
39
+ '/v3/zone-audit/save-draft': {
40
40
  post: {
41
- tags: [ 'Zone' ],
41
+ tags: [ 'Zone Audit' ],
42
42
  description: `saved zone audited file. which is save in the mongo db`,
43
43
  operationId: 'save-draft',
44
44
  parameters: {},
@@ -58,95 +58,95 @@ export const zoneAuditDocs = {
58
58
  },
59
59
  },
60
60
  },
61
- // '/v3/audit/get-drafted-data': {
62
- // get: {
63
- // tags: [ 'Audit' ],
64
- // description: 'Get a saved file via DB',
65
- // operationId: 'get-drafted-data',
66
- // parameters: [
67
- // {
68
- // in: 'query',
69
- // name: 'fileDate',
70
- // scema: j2s( getDraftedDataSchema ).swagger,
71
- // require: true,
72
- // },
73
- // {
74
- // in: 'query',
75
- // name: 'storeId',
76
- // scema: j2s( getDraftedDataSchema ).swagger,
77
- // require: false,
78
- // },
79
- // ],
80
- // responses: {
81
- // 200: { description: 'Successful' },
82
- // 401: { description: 'Unauthorized User' },
83
- // 422: { description: 'Field Error' },
84
- // 500: { description: 'Server Error' },
85
- // 204: { description: 'Not Found' },
86
- // },
87
- // },
88
- // },
89
- // '/v3/audit/save': {
90
- // post: {
91
- // tags: [ 'Audit' ],
92
- // description: `submit the audit file. which is store in the bucket in the json format`,
93
- // operationId: 'save',
94
- // parameters: {},
95
- // requestBody: {
96
- // content: {
97
- // 'application/json': {
98
- // schema: j2s( saveSchema ).swagger,
99
- // },
100
- // },
101
- // },
102
- // responses: {
103
- // 200: { description: 'The Audited file has been submited successfully ' },
104
- // 401: { description: 'Unauthorized User' },
105
- // 422: { description: 'Field Error' },
106
- // 500: { description: 'Server Error' },
107
- // 204: { description: 'Not Found' },
108
- // },
109
- // },
110
- // },
111
- // '/v3/audit/work-space': {
112
- // get: {
113
- // tags: [ 'Audit' ],
114
- // description: 'queue wise info of audit files ',
115
- // operationId: 'work-space',
116
- // parameters: [
117
- // {
118
- // in: 'query',
119
- // name: 'searchValue',
120
- // scema: j2s( workSpaceSchema ).swagger,
121
- // require: false,
122
- // },
123
- // {
124
- // in: 'query',
125
- // name: 'offset',
126
- // scema: j2s( workSpaceSchema ).swagger,
127
- // require: false,
128
- // },
129
- // {
130
- // in: 'query',
131
- // name: 'limit',
132
- // scema: j2s( workSpaceSchema ).swagger,
133
- // require: false,
134
- // },
135
- // {
136
- // in: 'query',
137
- // name: 'isExport',
138
- // scema: j2s( workSpaceSchema ).swagger,
139
- // require: false,
140
- // },
141
- // ],
142
- // responses: {
143
- // 200: { description: 'Successful' },
144
- // 401: { description: 'Unauthorized User' },
145
- // 422: { description: 'Field Error' },
146
- // 500: { description: 'Server Error' },
147
- // 204: { description: 'Not Found' },
148
- // },
149
- // },
150
- // },
61
+ '/v3/zone-audit/get-drafted-data': {
62
+ get: {
63
+ tags: [ 'Zone Audit' ],
64
+ description: 'Get a saved file via DB',
65
+ operationId: 'get-drafted-data',
66
+ parameters: [
67
+ {
68
+ in: 'query',
69
+ name: 'fileDate',
70
+ scema: j2s( getDraftedDataSchema ).swagger,
71
+ require: true,
72
+ },
73
+ {
74
+ in: 'query',
75
+ name: 'storeId',
76
+ scema: j2s( getDraftedDataSchema ).swagger,
77
+ require: false,
78
+ },
79
+ ],
80
+ responses: {
81
+ 200: { description: 'Successful' },
82
+ 401: { description: 'Unauthorized User' },
83
+ 422: { description: 'Field Error' },
84
+ 500: { description: 'Server Error' },
85
+ 204: { description: 'Not Found' },
86
+ },
87
+ },
88
+ },
89
+ '/v3/zone-audit/save': {
90
+ post: {
91
+ tags: [ 'Zone Audit' ],
92
+ description: `submit the audit file. which is store in the bucket in the json format`,
93
+ operationId: 'save',
94
+ parameters: {},
95
+ requestBody: {
96
+ content: {
97
+ 'application/json': {
98
+ schema: j2s( saveSchema ).swagger,
99
+ },
100
+ },
101
+ },
102
+ responses: {
103
+ 200: { description: 'The Audited file has been submited successfully ' },
104
+ 401: { description: 'Unauthorized User' },
105
+ 422: { description: 'Field Error' },
106
+ 500: { description: 'Server Error' },
107
+ 204: { description: 'Not Found' },
108
+ },
109
+ },
110
+ },
111
+ '/v3/zone-audit/work-space': {
112
+ get: {
113
+ tags: [ 'Zone Audit' ],
114
+ description: 'queue wise info of audit files ',
115
+ operationId: 'work-space',
116
+ parameters: [
117
+ {
118
+ in: 'query',
119
+ name: 'searchValue',
120
+ scema: j2s( workSpaceSchema ).swagger,
121
+ require: false,
122
+ },
123
+ {
124
+ in: 'query',
125
+ name: 'offset',
126
+ scema: j2s( workSpaceSchema ).swagger,
127
+ require: false,
128
+ },
129
+ {
130
+ in: 'query',
131
+ name: 'limit',
132
+ scema: j2s( workSpaceSchema ).swagger,
133
+ require: false,
134
+ },
135
+ {
136
+ in: 'query',
137
+ name: 'isExport',
138
+ scema: j2s( workSpaceSchema ).swagger,
139
+ require: false,
140
+ },
141
+ ],
142
+ responses: {
143
+ 200: { description: 'Successful' },
144
+ 401: { description: 'Unauthorized User' },
145
+ 422: { description: 'Field Error' },
146
+ 500: { description: 'Server Error' },
147
+ 204: { description: 'Not Found' },
148
+ },
149
+ },
150
+ },
151
151
 
152
152
  };
@@ -38,3 +38,51 @@ export const saveDraftSchema = joi.object(
38
38
  export const saveDraftValid = {
39
39
  body: saveDraftSchema,
40
40
  };
41
+
42
+
43
+ export const saveSchema = joi.object(
44
+ {
45
+ auditId: joi.string().required(),
46
+ storeId: joi.string().required(),
47
+ zoneName: joi.string().required(),
48
+ fileDate: joi.string().required(),
49
+ auditType: joi.string().required(),
50
+ beforeCount: joi.number().required(),
51
+ junkCount: joi.number().required(),
52
+ junk: joi.array().required(),
53
+ employeeCount: joi.number().required(),
54
+ employee: joi.array().required(),
55
+ customerCount: joi.number().required(),
56
+ customer: joi.array().required(),
57
+ },
58
+ );
59
+
60
+ export const saveValid = {
61
+ body: saveSchema,
62
+ };
63
+
64
+ export const getDraftedDataSchema = joi.object(
65
+ {
66
+ storeId: joi.string().required(),
67
+ zoneName: joi.string().required(),
68
+ fileDate: joi.string().required(),
69
+ userId: joi.string().optional(),
70
+ },
71
+ );
72
+
73
+ export const getDraftedDataValid = {
74
+ query: getDraftedDataSchema,
75
+ };
76
+
77
+ export const workSpaceSchema = joi.object(
78
+ {
79
+ searchValue: joi.string().optional().allow( '' ),
80
+ offset: joi.string().optional(),
81
+ limit: joi.number().optional(),
82
+ isExport: joi.boolean().optional(),
83
+ },
84
+ );
85
+
86
+ export const workSpaceValid = {
87
+ query: workSpaceSchema,
88
+ };
@@ -1,11 +1,15 @@
1
1
  import { Router } from 'express';
2
- import { getFileValid, saveDraftValid } from '../dtos/zoneAudit.dtos.js';
3
- import { isExistsQueue } from '../validation/audit.validation.js';
2
+ import { getDraftedDataValid, getFileValid, saveDraftValid, saveValid, workSpaceValid } from '../dtos/zoneAudit.dtos.js';
3
+ import { isExistsQueue, validateUserAudit } from '../validation/audit.validation.js';
4
4
  import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
5
- import { getZoneAuditFile, saveDraft } from '../controllers/zoneAudit.controller.js';
5
+ import { getDraftedData, getZoneAuditFile, save, saveDraft, workSpace } from '../controllers/zoneAudit.controller.js';
6
6
 
7
7
  export const zoneAuditRouter=Router();
8
8
 
9
9
 
10
10
  zoneAuditRouter.get( '/get-file', isAllowedSessionHandler, validate( getFileValid ), isExistsQueue, getZoneAuditFile );
11
11
  zoneAuditRouter.post( '/save-draft', isAllowedSessionHandler, validate( saveDraftValid ), saveDraft );
12
+ zoneAuditRouter.get( '/get-drafted-data', isAllowedSessionHandler, validate( getDraftedDataValid ), getDraftedData );
13
+ zoneAuditRouter.post( '/save', isAllowedSessionHandler, validate( saveValid ), validateUserAudit, save );
14
+ zoneAuditRouter.get( '/work-space', isAllowedSessionHandler, validate( workSpaceValid ), workSpace );
15
+