tango-app-api-infra 3.9.5-vms.77 → 3.9.5-vms.79
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.
|
@@ -385,22 +385,97 @@ export async function tangoReviewTicket( req, res ) {
|
|
|
385
385
|
|
|
386
386
|
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
387
387
|
let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
388
|
-
|
|
389
|
-
if ( inputData.ticketType === 'internal'&&!getExistingOne?.body?._source ) {
|
|
388
|
+
if ( inputData.ticketType === 'internal' &&!getExistingOne?.body?._source ) {
|
|
390
389
|
id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
391
390
|
}
|
|
392
391
|
|
|
393
|
-
|
|
394
392
|
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
395
|
-
console.log( insertResult );
|
|
396
393
|
|
|
397
394
|
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
395
|
+
if ( record.status = 'Closed' && inputData.ticketType !== 'internal' ) {
|
|
396
|
+
const query = {
|
|
397
|
+
storeId: inputData?.storeId,
|
|
398
|
+
isVideoStream: true,
|
|
399
|
+
};
|
|
400
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
401
|
+
const revopInfoQuery = {
|
|
402
|
+
size: 10000,
|
|
403
|
+
query: {
|
|
404
|
+
bool: {
|
|
405
|
+
must: [
|
|
406
|
+
{
|
|
407
|
+
term: {
|
|
408
|
+
'storeId.keyword': inputData.storeId,
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
term: {
|
|
413
|
+
'dateString': inputData.dateString,
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
term: {
|
|
418
|
+
'isParent': false,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
term: {
|
|
423
|
+
isChecked: true,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
_source: [ 'tempId' ],
|
|
430
|
+
|
|
431
|
+
};
|
|
432
|
+
const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
|
|
433
|
+
// Get all tempIds from revopInfo response
|
|
434
|
+
const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
435
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
436
|
+
const managerEyeZoneQuery = {
|
|
437
|
+
size: 1,
|
|
438
|
+
query: {
|
|
439
|
+
bool: {
|
|
440
|
+
must: [
|
|
441
|
+
{
|
|
442
|
+
term: {
|
|
443
|
+
'storeId.keyword': inputData.storeId,
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
term: {
|
|
448
|
+
'storeDate': inputData.dateString,
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
],
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
_source: [ 'originalToTrackerCustomerMapping' ],
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Query the managerEyeZone index for the matching document
|
|
458
|
+
const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
|
|
459
|
+
const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
|
|
460
|
+
// Extract originalToTrackerCustomerMapping if it exists
|
|
461
|
+
const mapping =
|
|
462
|
+
managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
|
|
463
|
+
managerEyeZoneHit.originalToTrackerCustomerMapping :
|
|
464
|
+
{};
|
|
465
|
+
|
|
466
|
+
// Find tempIds that exist in both revopInfo results and manager mapping
|
|
467
|
+
const temp = [];
|
|
468
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
|
|
469
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
470
|
+
if ( isSendMessge == true ) {
|
|
471
|
+
logger.info( '....1' );
|
|
472
|
+
}
|
|
473
|
+
}
|
|
398
474
|
return res.sendSuccess( 'Ticket closed successfully' );
|
|
399
475
|
} else {
|
|
400
476
|
return res.sendError( 'Internal Server Error', 500 );
|
|
401
477
|
}
|
|
402
478
|
} catch ( error ) {
|
|
403
|
-
console.log( '🚀 ~ tangoReviewTicket ~ error:', error );
|
|
404
479
|
const err = error.message || 'Internal Server Error';
|
|
405
480
|
logger.error( { error: error, funtion: 'tangoReviewTicket' } );
|
|
406
481
|
return res.sendError( err, 500 );
|
|
@@ -518,22 +593,6 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
|
|
|
518
593
|
} );
|
|
519
594
|
}
|
|
520
595
|
|
|
521
|
-
|
|
522
|
-
// If no review mapping existed, push a new one
|
|
523
|
-
// if ( record.mappingInfo.length === 0 ) {
|
|
524
|
-
// record.mappingInfo.push( {
|
|
525
|
-
// type: 'tangoreview',
|
|
526
|
-
// mode: inputData.mappingInfo?.mode,
|
|
527
|
-
// revicedFootfall: temp?.mappingInfo?.revicedFootfall,
|
|
528
|
-
// revicedPerc: temp?.mappingInfo?.revicedPerc,
|
|
529
|
-
// count: temp?.mappingInfo?.count,
|
|
530
|
-
// revisedDetail: temp?.mappingInfo?.revisedDetail,
|
|
531
|
-
// status: 'Closed',
|
|
532
|
-
// createdByEmail: req?.user?.email,
|
|
533
|
-
// createdByUserName: req?.user?.userName,
|
|
534
|
-
// createdByRole: req?.user?.role,
|
|
535
|
-
// } );
|
|
536
|
-
// }
|
|
537
596
|
record.mappingInfo.push(
|
|
538
597
|
{
|
|
539
598
|
type: 'finalRevision',
|
|
@@ -559,6 +618,84 @@ export async function tangoReviewAccuracyClosedTicket( req, res ) {
|
|
|
559
618
|
const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
|
|
560
619
|
|
|
561
620
|
if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
|
|
621
|
+
const query = {
|
|
622
|
+
storeId: inputData?.storeId,
|
|
623
|
+
isVideoStream: true,
|
|
624
|
+
};
|
|
625
|
+
const getStoreType = await countDocumnetsCamera( query );
|
|
626
|
+
const revopInfoQuery = {
|
|
627
|
+
size: 10000,
|
|
628
|
+
query: {
|
|
629
|
+
bool: {
|
|
630
|
+
must: [
|
|
631
|
+
{
|
|
632
|
+
term: {
|
|
633
|
+
'storeId.keyword': inputData.storeId,
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
term: {
|
|
638
|
+
'dateString': inputData.dateString,
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
term: {
|
|
643
|
+
'isParent': false,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
term: {
|
|
648
|
+
isChecked: true,
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
_source: [ 'tempId' ],
|
|
655
|
+
|
|
656
|
+
};
|
|
657
|
+
const revopInfo = await getOpenSearchData( openSearch.revop, revopInfoQuery );
|
|
658
|
+
// Get all tempIds from revopInfo response
|
|
659
|
+
const tempIds = revopInfo?.body?.hits?.hits?.map( ( hit ) => hit?._source?.tempId ).filter( Boolean ) || [];
|
|
660
|
+
// Prepare management eyeZone query based on storeId and dateString
|
|
661
|
+
const managerEyeZoneQuery = {
|
|
662
|
+
size: 1,
|
|
663
|
+
query: {
|
|
664
|
+
bool: {
|
|
665
|
+
must: [
|
|
666
|
+
{
|
|
667
|
+
term: {
|
|
668
|
+
'storeId.keyword': inputData.storeId,
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
term: {
|
|
673
|
+
'storeDate': inputData.dateString,
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
],
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
_source: [ 'originalToTrackerCustomerMapping' ],
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
// Query the managerEyeZone index for the matching document
|
|
683
|
+
const managerEyeZoneResp = await getOpenSearchData( openSearch.managerEyeZone, managerEyeZoneQuery );
|
|
684
|
+
const managerEyeZoneHit = managerEyeZoneResp?.body?.hits?.hits?.[0]?._source;
|
|
685
|
+
// Extract originalToTrackerCustomerMapping if it exists
|
|
686
|
+
const mapping =
|
|
687
|
+
managerEyeZoneHit && managerEyeZoneHit.originalToTrackerCustomerMapping ?
|
|
688
|
+
managerEyeZoneHit.originalToTrackerCustomerMapping :
|
|
689
|
+
{};
|
|
690
|
+
|
|
691
|
+
// Find tempIds that exist in both revopInfo results and manager mapping
|
|
692
|
+
const temp = [];
|
|
693
|
+
tempIds.filter( ( tid ) => mapping[tid] !== null ? temp.push( { tempId: mapping[tid] } ) :'' );
|
|
694
|
+
const isSendMessge = await sendSqsMessage( inputData, temp, getStoreType, inputData.storeId );
|
|
695
|
+
if ( isSendMessge == true ) {
|
|
696
|
+
logger.info( '....1' );
|
|
697
|
+
}
|
|
698
|
+
|
|
562
699
|
return res.sendSuccess( 'Ticket closed successfully' );
|
|
563
700
|
} else {
|
|
564
701
|
return res.sendError( 'Internal Server Error', 500 );
|
|
@@ -628,145 +765,1210 @@ async function bulkUpdateStatusToPending( indexName, inputData ) {
|
|
|
628
765
|
}
|
|
629
766
|
}
|
|
630
767
|
|
|
631
|
-
// Final bulk call
|
|
632
|
-
if ( bulkBody.length > 0 ) {
|
|
633
|
-
const res = await bulkUpdate( bulkBody );
|
|
634
|
-
if ( res?.errors ) {
|
|
635
|
-
logger.error( 'Bulk update errors:', res.items );
|
|
636
|
-
return { success: false, errors: res.items };
|
|
637
|
-
} else {
|
|
638
|
-
logger.error( 'Bulk status update successful.' );
|
|
639
|
-
return { success: true };
|
|
640
|
-
}
|
|
641
|
-
} else {
|
|
642
|
-
logger.error( 'No data to update.' );
|
|
643
|
-
return { success: false, message: 'No updates needed' };
|
|
644
|
-
}
|
|
645
|
-
}
|
|
768
|
+
// Final bulk call
|
|
769
|
+
if ( bulkBody.length > 0 ) {
|
|
770
|
+
const res = await bulkUpdate( bulkBody );
|
|
771
|
+
if ( res?.errors ) {
|
|
772
|
+
logger.error( 'Bulk update errors:', res.items );
|
|
773
|
+
return { success: false, errors: res.items };
|
|
774
|
+
} else {
|
|
775
|
+
logger.error( 'Bulk status update successful.' );
|
|
776
|
+
return { success: true };
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
logger.error( 'No data to update.' );
|
|
780
|
+
return { success: false, message: 'No updates needed' };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export async function ticketSummary1( req, res ) {
|
|
785
|
+
try {
|
|
786
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
787
|
+
const inputData = req.query;
|
|
788
|
+
inputData.clientId = inputData.clientId.split( ',' ); // convert strig to array
|
|
789
|
+
const getQuery = {
|
|
790
|
+
size: 0,
|
|
791
|
+
query: {
|
|
792
|
+
bool: {
|
|
793
|
+
filter: [
|
|
794
|
+
{ terms: { 'clientId.keyword': inputData.clientId } },
|
|
795
|
+
{
|
|
796
|
+
range: {
|
|
797
|
+
dateString: {
|
|
798
|
+
gte: inputData.fromDate,
|
|
799
|
+
lte: inputData.toDate,
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
],
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
aggs: {
|
|
807
|
+
totalTicketCount: {
|
|
808
|
+
value_count: { field: '_id' },
|
|
809
|
+
},
|
|
810
|
+
openStatusCount: {
|
|
811
|
+
filter: {
|
|
812
|
+
term: { 'status.keyword': 'open' },
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
closeTicketCount: {
|
|
816
|
+
filter: {
|
|
817
|
+
term: { 'status.keyword': 'closed' },
|
|
818
|
+
},
|
|
819
|
+
},
|
|
820
|
+
duplicateCount: {
|
|
821
|
+
sum: {
|
|
822
|
+
field: 'duplicateCount',
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
employeeCount: {
|
|
826
|
+
sum: {
|
|
827
|
+
field: 'employeeCount',
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
houseKeepingCount: {
|
|
831
|
+
sum: {
|
|
832
|
+
field: 'houseKeepingCount',
|
|
833
|
+
},
|
|
834
|
+
},
|
|
835
|
+
junkCount: {
|
|
836
|
+
sum: {
|
|
837
|
+
field: 'junkCount',
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
844
|
+
const aggs = getData?.body?.aggregations;
|
|
845
|
+
|
|
846
|
+
const result = {
|
|
847
|
+
totalTickets: aggs.totalTicketCount.value,
|
|
848
|
+
openTickets: aggs.openStatusCount.doc_count,
|
|
849
|
+
closedTickets: aggs.closeTicketCount.doc_count,
|
|
850
|
+
duplicateCount: aggs.duplicateCount.value,
|
|
851
|
+
employeeCount: aggs.employeeCount.value,
|
|
852
|
+
houseKeepingCount: aggs.houseKeepingCount.value,
|
|
853
|
+
junkCount: aggs.junkCount.value,
|
|
854
|
+
noShopper: aggs.duplicateCount.value + aggs.employeeCount.value + aggs.houseKeepingCount.value + aggs.junkCount.value,
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
return res.sendSuccess( { result: result } );
|
|
858
|
+
} catch ( error ) {
|
|
859
|
+
const err = error.message || 'Internal Server Error';
|
|
860
|
+
logger.error( { error: error, messgage: req.query } );
|
|
861
|
+
return res.sendSuccess( err, 500 );
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export async function ticketSummary( req, res ) {
|
|
866
|
+
try {
|
|
867
|
+
const inputData = req.query;
|
|
868
|
+
|
|
869
|
+
let result = '';
|
|
870
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
871
|
+
const userInfo = req.user;
|
|
872
|
+
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
873
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
874
|
+
|
|
875
|
+
// const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
876
|
+
if ( req?.user?.userType === 'tango' ) {
|
|
877
|
+
switch ( inputData?.tangoType ) {
|
|
878
|
+
case 'store':
|
|
879
|
+
const storeQuery = {
|
|
880
|
+
size: 0,
|
|
881
|
+
query: {
|
|
882
|
+
bool: {
|
|
883
|
+
must: [
|
|
884
|
+
{
|
|
885
|
+
'range': {
|
|
886
|
+
'dateString': {
|
|
887
|
+
'gte': inputData?.fromDate,
|
|
888
|
+
'lte': inputData?.toDate,
|
|
889
|
+
'format': 'yyyy-MM-dd',
|
|
890
|
+
},
|
|
891
|
+
},
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
nested: {
|
|
895
|
+
path: 'mappingInfo',
|
|
896
|
+
query: {
|
|
897
|
+
bool: {
|
|
898
|
+
must: [
|
|
899
|
+
{
|
|
900
|
+
term: {
|
|
901
|
+
'mappingInfo.type': 'tangoreview',
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
],
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
|
|
910
|
+
],
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
916
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
917
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
918
|
+
// Remove any previous mappingInfo.status term
|
|
919
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
920
|
+
if ( nested ) {
|
|
921
|
+
// filter out all mappingInfo.status
|
|
922
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
923
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
924
|
+
} );
|
|
925
|
+
// add desired status
|
|
926
|
+
nested.nested.query.bool.must.push( {
|
|
927
|
+
term: {
|
|
928
|
+
'mappingInfo.status': statusValue,
|
|
929
|
+
},
|
|
930
|
+
} );
|
|
931
|
+
}
|
|
932
|
+
return q;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
936
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
937
|
+
|
|
938
|
+
// locate nested section
|
|
939
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
940
|
+
|
|
941
|
+
if ( nested ) {
|
|
942
|
+
// remove old status filters
|
|
943
|
+
nested.nested.query.bool.must =
|
|
944
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
945
|
+
|
|
946
|
+
// add new filters
|
|
947
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return {
|
|
951
|
+
...q,
|
|
952
|
+
size: 0,
|
|
953
|
+
aggs: {
|
|
954
|
+
avg_value: {
|
|
955
|
+
avg: {
|
|
956
|
+
script: {
|
|
957
|
+
lang: 'painless',
|
|
958
|
+
source: `
|
|
959
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
960
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
961
|
+
try {
|
|
962
|
+
return Double.parseDouble(v);
|
|
963
|
+
} catch (Exception e) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
`,
|
|
967
|
+
},
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
// Get OpenSearch connection
|
|
976
|
+
|
|
977
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
978
|
+
|
|
979
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
980
|
+
let totalTickets = 0;
|
|
981
|
+
|
|
982
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
983
|
+
|
|
984
|
+
allQuery.size = 0;
|
|
985
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
986
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
987
|
+
|
|
988
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
989
|
+
let openTickets = 0;
|
|
990
|
+
|
|
991
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
992
|
+
otQ.size = 0;
|
|
993
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
994
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
995
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
999
|
+
let openInfraIssues = 0;
|
|
1000
|
+
|
|
1001
|
+
let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
|
|
1002
|
+
oiQ.size = 0;
|
|
1003
|
+
const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
|
|
1004
|
+
openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
|
|
1005
|
+
|
|
1006
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1007
|
+
let inprogress = 0;
|
|
1008
|
+
|
|
1009
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1010
|
+
ipQ.size = 0;
|
|
1011
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1012
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1013
|
+
|
|
1014
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1015
|
+
let closedTickets = 0;
|
|
1016
|
+
|
|
1017
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1018
|
+
clQ.size = 0;
|
|
1019
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1020
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1021
|
+
// Average revisedPerc (for all tangoreview)
|
|
1022
|
+
let averageAccuracyOverAll = 0;
|
|
1023
|
+
|
|
1024
|
+
let avgQ = buildAggStoreQuery( baseStoreQuery );
|
|
1025
|
+
const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
|
|
1026
|
+
averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1027
|
+
|
|
1028
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1029
|
+
let ticketAccuracyAbove = 0;
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1033
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1034
|
+
{
|
|
1035
|
+
script: {
|
|
1036
|
+
script: {
|
|
1037
|
+
lang: 'painless',
|
|
1038
|
+
source: `
|
|
1039
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1040
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1041
|
+
`,
|
|
1042
|
+
params: { num: 85 },
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
] );
|
|
1049
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1050
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1051
|
+
|
|
1052
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1053
|
+
let ticketAccuracyBelow = 0;
|
|
1054
|
+
|
|
1055
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1056
|
+
{
|
|
1057
|
+
script: {
|
|
1058
|
+
script: {
|
|
1059
|
+
lang: 'painless',
|
|
1060
|
+
source: `
|
|
1061
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1062
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1063
|
+
`,
|
|
1064
|
+
params: { num: 85 },
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
},
|
|
1068
|
+
|
|
1069
|
+
] );
|
|
1070
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1071
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1072
|
+
|
|
1073
|
+
// Final result object
|
|
1074
|
+
result = {
|
|
1075
|
+
totalTickets,
|
|
1076
|
+
averageAccuracyOverAll: averageAccuracyOverAll+'%',
|
|
1077
|
+
openTickets,
|
|
1078
|
+
openInfraIssues,
|
|
1079
|
+
inprogress,
|
|
1080
|
+
closedTickets,
|
|
1081
|
+
ticketAccuracyAbove: ticketAccuracyAbove+'%',
|
|
1082
|
+
ticketAccuracyBelow: ticketAccuracyBelow+'%',
|
|
1083
|
+
};
|
|
1084
|
+
break;
|
|
1085
|
+
case 'internal':
|
|
1086
|
+
const internalQuery = {
|
|
1087
|
+
size: 0,
|
|
1088
|
+
query: {
|
|
1089
|
+
bool: {
|
|
1090
|
+
must: [
|
|
1091
|
+
{
|
|
1092
|
+
'range': {
|
|
1093
|
+
'dateString': {
|
|
1094
|
+
'gte': inputData?.fromDate,
|
|
1095
|
+
'lte': inputData?.toDate,
|
|
1096
|
+
'format': 'yyyy-MM-dd',
|
|
1097
|
+
},
|
|
1098
|
+
},
|
|
1099
|
+
},
|
|
1100
|
+
|
|
1101
|
+
{
|
|
1102
|
+
nested: {
|
|
1103
|
+
path: 'mappingInfo',
|
|
1104
|
+
query: {
|
|
1105
|
+
bool: {
|
|
1106
|
+
must: [
|
|
1107
|
+
{
|
|
1108
|
+
terms: {
|
|
1109
|
+
'mappingInfo.type': [ 'tagging', 'review', 'approve', 'tangoreview' ],
|
|
1110
|
+
},
|
|
1111
|
+
},
|
|
1112
|
+
],
|
|
1113
|
+
},
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
|
|
1118
|
+
],
|
|
1119
|
+
},
|
|
1120
|
+
},
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1124
|
+
function buildInternalQueryWithStatus( baseQuery, statusValue ) {
|
|
1125
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1126
|
+
// Remove any previous mappingInfo.status term
|
|
1127
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1128
|
+
if ( nested ) {
|
|
1129
|
+
// filter out all mappingInfo.status
|
|
1130
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1131
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1132
|
+
} );
|
|
1133
|
+
// add desired status
|
|
1134
|
+
nested.nested.query.bool.must.push( {
|
|
1135
|
+
term: {
|
|
1136
|
+
'mappingInfo.status': statusValue,
|
|
1137
|
+
},
|
|
1138
|
+
} );
|
|
1139
|
+
}
|
|
1140
|
+
return q;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const buildAggInternalQuery = ( baseQuery, filters = [] ) => {
|
|
1144
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1145
|
+
|
|
1146
|
+
// locate nested section
|
|
1147
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1148
|
+
|
|
1149
|
+
if ( nested ) {
|
|
1150
|
+
// remove old status filters
|
|
1151
|
+
nested.nested.query.bool.must =
|
|
1152
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1153
|
+
|
|
1154
|
+
// add new filters
|
|
1155
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return {
|
|
1159
|
+
...q,
|
|
1160
|
+
size: 0,
|
|
1161
|
+
aggs: {
|
|
1162
|
+
avg_value: {
|
|
1163
|
+
avg: {
|
|
1164
|
+
script: {
|
|
1165
|
+
lang: 'painless',
|
|
1166
|
+
source: `
|
|
1167
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1168
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1169
|
+
try {
|
|
1170
|
+
return Double.parseDouble(v);
|
|
1171
|
+
} catch (Exception e) {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
`,
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
};
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
// Get OpenSearch connection
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
const baseInternalQuery = JSON.parse( JSON.stringify( internalQuery ) );
|
|
1187
|
+
|
|
1188
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1189
|
+
let totalInternalTickets = 0;
|
|
1190
|
+
|
|
1191
|
+
let allInternalQuery = JSON.parse( JSON.stringify( baseInternalQuery ) );
|
|
1192
|
+
|
|
1193
|
+
allInternalQuery.size = 0;
|
|
1194
|
+
const totalInternalResp = await getOpenSearchData( openSearch.footfallDirectory, allInternalQuery );
|
|
1195
|
+
totalInternalTickets = totalInternalResp?.body?.hits?.total?.value || 0;
|
|
1196
|
+
|
|
1197
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1198
|
+
let openInternalTickets = 0;
|
|
1199
|
+
|
|
1200
|
+
let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
|
|
1201
|
+
otQInternal.size = 0;
|
|
1202
|
+
const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, otQInternal );
|
|
1203
|
+
openInternalTickets = openInternalResp?.body?.hits?.total?.value || 0;
|
|
1204
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1208
|
+
let openInternalInfraIssues = 0;
|
|
1209
|
+
|
|
1210
|
+
let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open Accuracy Issue' );
|
|
1211
|
+
oiQinternal.size = 0;
|
|
1212
|
+
const infraInternalResp = await getOpenSearchData( openSearch.footfallDirectory, oiQinternal );
|
|
1213
|
+
openInternalInfraIssues = infraInternalResp?.body?.hits?.total?.value || 0;
|
|
1214
|
+
|
|
1215
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1216
|
+
let inprogressIntrenal = 0;
|
|
1217
|
+
|
|
1218
|
+
let ipQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'In-Progress' );
|
|
1219
|
+
ipQInternal.size = 0;
|
|
1220
|
+
const ipInternalResp = await getOpenSearchData( openSearch.footfallDirectory, ipQInternal );
|
|
1221
|
+
inprogressIntrenal = ipInternalResp?.body?.hits?.total?.value || 0;
|
|
1222
|
+
|
|
1223
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1224
|
+
let closedInternalTickets = 0;
|
|
1225
|
+
|
|
1226
|
+
let clQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Closed' );
|
|
1227
|
+
clQInternal.size = 0;
|
|
1228
|
+
const clInternalResp = await getOpenSearchData( openSearch.footfallDirectory, clQInternal );
|
|
1229
|
+
closedInternalTickets = clInternalResp?.body?.hits?.total?.value || 0;
|
|
1230
|
+
// Average revisedPerc (for all tangoreview)
|
|
1231
|
+
let internalAverageAccuracyOverAll = 0;
|
|
1232
|
+
|
|
1233
|
+
let avgQInternal = buildAggInternalQuery( baseInternalQuery );
|
|
1234
|
+
const avgInternalResp = await getOpenSearchData( openSearch.footfallDirectory, avgQInternal );
|
|
1235
|
+
internalAverageAccuracyOverAll = avgInternalResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1236
|
+
|
|
1237
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1238
|
+
let internalTicketAccuracyAbove = 0;
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1242
|
+
let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
|
|
1243
|
+
{
|
|
1244
|
+
script: {
|
|
1245
|
+
script: {
|
|
1246
|
+
lang: 'painless',
|
|
1247
|
+
source: `
|
|
1248
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1249
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1250
|
+
`,
|
|
1251
|
+
params: { num: 85 },
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1254
|
+
},
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
] );
|
|
1258
|
+
const aboveRespInternal = await getOpenSearchData( openSearch.footfallDirectory, aboveQinternal );
|
|
1259
|
+
internalTicketAccuracyAbove = aboveRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1260
|
+
|
|
1261
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1262
|
+
let internalTicketAccuracyBelow = 0;
|
|
1263
|
+
|
|
1264
|
+
let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
|
|
1265
|
+
{
|
|
1266
|
+
script: {
|
|
1267
|
+
script: {
|
|
1268
|
+
lang: 'painless',
|
|
1269
|
+
source: `
|
|
1270
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1271
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1272
|
+
`,
|
|
1273
|
+
params: { num: 85 },
|
|
1274
|
+
},
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
|
|
1278
|
+
] );
|
|
1279
|
+
const belowRespInternal = await getOpenSearchData( openSearch.footfallDirectory, belowQIneranl );
|
|
1280
|
+
internalTicketAccuracyBelow = belowRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1281
|
+
|
|
1282
|
+
// Final result object
|
|
1283
|
+
result = {
|
|
1284
|
+
totalTickets: totalInternalTickets,
|
|
1285
|
+
averageAccuracyOverAll: internalAverageAccuracyOverAll+'%',
|
|
1286
|
+
openTickets: openInternalTickets,
|
|
1287
|
+
openInfraIssues: openInternalInfraIssues,
|
|
1288
|
+
inprogress: inprogressIntrenal,
|
|
1289
|
+
closedTickets: closedInternalTickets,
|
|
1290
|
+
ticketAccuracyAbove: internalTicketAccuracyAbove+'%',
|
|
1291
|
+
ticketAccuracyBelow: internalTicketAccuracyBelow+'%',
|
|
1292
|
+
};
|
|
1293
|
+
break;
|
|
1294
|
+
default: '';
|
|
1295
|
+
}
|
|
1296
|
+
} else if ( req?.user?.userType === 'client' ) {
|
|
1297
|
+
if ( ticketsFeature && !ticketsApproveFeature ) {
|
|
1298
|
+
const storeQuery = {
|
|
1299
|
+
size: 0,
|
|
1300
|
+
query: {
|
|
1301
|
+
bool: {
|
|
1302
|
+
must: [
|
|
1303
|
+
{
|
|
1304
|
+
'range': {
|
|
1305
|
+
'dateString': {
|
|
1306
|
+
'gte': inputData?.fromDate,
|
|
1307
|
+
'lte': inputData?.toDate,
|
|
1308
|
+
'format': 'yyyy-MM-dd',
|
|
1309
|
+
},
|
|
1310
|
+
},
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
nested: {
|
|
1314
|
+
path: 'mappingInfo',
|
|
1315
|
+
query: {
|
|
1316
|
+
bool: {
|
|
1317
|
+
must: [
|
|
1318
|
+
{
|
|
1319
|
+
term: {
|
|
1320
|
+
'mappingInfo.type': 'review',
|
|
1321
|
+
},
|
|
1322
|
+
},
|
|
1323
|
+
],
|
|
1324
|
+
},
|
|
1325
|
+
},
|
|
1326
|
+
},
|
|
1327
|
+
},
|
|
1328
|
+
|
|
1329
|
+
],
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1335
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1336
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1337
|
+
// Remove any previous mappingInfo.status term
|
|
1338
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1339
|
+
if ( nested ) {
|
|
1340
|
+
// filter out all mappingInfo.status
|
|
1341
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1342
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1343
|
+
} );
|
|
1344
|
+
// add desired status
|
|
1345
|
+
nested.nested.query.bool.must.push( {
|
|
1346
|
+
term: {
|
|
1347
|
+
'mappingInfo.status': statusValue,
|
|
1348
|
+
},
|
|
1349
|
+
} );
|
|
1350
|
+
}
|
|
1351
|
+
return q;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1355
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1356
|
+
|
|
1357
|
+
// locate nested section
|
|
1358
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1359
|
+
|
|
1360
|
+
if ( nested ) {
|
|
1361
|
+
// remove old status filters
|
|
1362
|
+
nested.nested.query.bool.must =
|
|
1363
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1364
|
+
|
|
1365
|
+
// add new filters
|
|
1366
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
return {
|
|
1370
|
+
...q,
|
|
1371
|
+
size: 0,
|
|
1372
|
+
aggs: {
|
|
1373
|
+
avg_value: {
|
|
1374
|
+
avg: {
|
|
1375
|
+
script: {
|
|
1376
|
+
lang: 'painless',
|
|
1377
|
+
source: `
|
|
1378
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1379
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1380
|
+
try {
|
|
1381
|
+
return Double.parseDouble(v);
|
|
1382
|
+
} catch (Exception e) {
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
`,
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
},
|
|
1389
|
+
},
|
|
1390
|
+
};
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
// Get OpenSearch connection
|
|
1395
|
+
|
|
1396
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1397
|
+
|
|
1398
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1399
|
+
let totalTickets = 0;
|
|
1400
|
+
|
|
1401
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1402
|
+
|
|
1403
|
+
allQuery.size = 0;
|
|
1404
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1405
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1406
|
+
|
|
1407
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1408
|
+
let openTickets = 0;
|
|
1409
|
+
|
|
1410
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1411
|
+
otQ.size = 0;
|
|
1412
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1413
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1414
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1415
|
+
|
|
1416
|
+
|
|
1417
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1418
|
+
let inprogress = 0;
|
|
1419
|
+
|
|
1420
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1421
|
+
ipQ.size = 0;
|
|
1422
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1423
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1424
|
+
|
|
1425
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1426
|
+
let closedTickets = 0;
|
|
1427
|
+
|
|
1428
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1429
|
+
clQ.size = 0;
|
|
1430
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1431
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1432
|
+
|
|
1433
|
+
let dueToday = 0;
|
|
1434
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1435
|
+
|
|
1436
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1437
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1438
|
+
// Locate nested mappingInfo query
|
|
1439
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1440
|
+
if ( nestedDue ) {
|
|
1441
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1442
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1443
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1444
|
+
);
|
|
1445
|
+
// Add new dueDate filter
|
|
1446
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1447
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1448
|
+
} );
|
|
1449
|
+
}
|
|
1450
|
+
dueTodayQuery.size = 0;
|
|
1451
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1452
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1453
|
+
|
|
1454
|
+
// filter expired Tickets
|
|
1455
|
+
let expiredTickets = 0;
|
|
1456
|
+
|
|
1457
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1458
|
+
eQ.size = 0;
|
|
1459
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1460
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1461
|
+
|
|
1462
|
+
let ticketAccuracyAbove = 0;
|
|
1463
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1464
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1465
|
+
{
|
|
1466
|
+
script: {
|
|
1467
|
+
script: {
|
|
1468
|
+
lang: 'painless',
|
|
1469
|
+
source: `
|
|
1470
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1471
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1472
|
+
`,
|
|
1473
|
+
params: { num: 85 },
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
},
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
] );
|
|
1480
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1481
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1482
|
+
|
|
1483
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1484
|
+
let ticketAccuracyBelow = 0;
|
|
1485
|
+
|
|
1486
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1487
|
+
{
|
|
1488
|
+
script: {
|
|
1489
|
+
script: {
|
|
1490
|
+
lang: 'painless',
|
|
1491
|
+
source: `
|
|
1492
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1493
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1494
|
+
`,
|
|
1495
|
+
params: { num: 85 },
|
|
1496
|
+
},
|
|
1497
|
+
},
|
|
1498
|
+
},
|
|
1499
|
+
|
|
1500
|
+
] );
|
|
1501
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1502
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1503
|
+
|
|
1504
|
+
// Final result object
|
|
1505
|
+
result = {
|
|
1506
|
+
totalTickets,
|
|
1507
|
+
openTickets,
|
|
1508
|
+
inprogress,
|
|
1509
|
+
closedTickets,
|
|
1510
|
+
dueToday: dueToday,
|
|
1511
|
+
Expired: expiredTickets,
|
|
1512
|
+
avgTicket: ticketAccuracyAbove+'%',
|
|
1513
|
+
avgAccuracy: ticketAccuracyBelow+'%',
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
} else if ( ticketsFeature && ticketsApproveFeature ) {
|
|
1517
|
+
if ( inputData?.permissionType === 'review' ) {
|
|
1518
|
+
const storeQuery = {
|
|
1519
|
+
size: 0,
|
|
1520
|
+
query: {
|
|
1521
|
+
bool: {
|
|
1522
|
+
must: [
|
|
1523
|
+
{
|
|
1524
|
+
'range': {
|
|
1525
|
+
'dateString': {
|
|
1526
|
+
'gte': inputData?.fromDate,
|
|
1527
|
+
'lte': inputData?.toDate,
|
|
1528
|
+
'format': 'yyyy-MM-dd',
|
|
1529
|
+
},
|
|
1530
|
+
},
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
nested: {
|
|
1534
|
+
path: 'mappingInfo',
|
|
1535
|
+
query: {
|
|
1536
|
+
bool: {
|
|
1537
|
+
must: [
|
|
1538
|
+
{
|
|
1539
|
+
term: {
|
|
1540
|
+
'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1543
|
+
],
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
},
|
|
1548
|
+
|
|
1549
|
+
],
|
|
1550
|
+
},
|
|
1551
|
+
},
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1555
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1556
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1557
|
+
// Remove any previous mappingInfo.status term
|
|
1558
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1559
|
+
if ( nested ) {
|
|
1560
|
+
// filter out all mappingInfo.status
|
|
1561
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1562
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1563
|
+
} );
|
|
1564
|
+
// add desired status
|
|
1565
|
+
nested.nested.query.bool.must.push( {
|
|
1566
|
+
term: {
|
|
1567
|
+
'mappingInfo.status': statusValue,
|
|
1568
|
+
},
|
|
1569
|
+
} );
|
|
1570
|
+
}
|
|
1571
|
+
return q;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1575
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1576
|
+
|
|
1577
|
+
// locate nested section
|
|
1578
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1579
|
+
|
|
1580
|
+
if ( nested ) {
|
|
1581
|
+
// remove old status filters
|
|
1582
|
+
nested.nested.query.bool.must =
|
|
1583
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1584
|
+
|
|
1585
|
+
// add new filters
|
|
1586
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
return {
|
|
1590
|
+
...q,
|
|
1591
|
+
size: 0,
|
|
1592
|
+
aggs: {
|
|
1593
|
+
avg_value: {
|
|
1594
|
+
avg: {
|
|
1595
|
+
script: {
|
|
1596
|
+
lang: 'painless',
|
|
1597
|
+
source: `
|
|
1598
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1599
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1600
|
+
try {
|
|
1601
|
+
return Double.parseDouble(v);
|
|
1602
|
+
} catch (Exception e) {
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1605
|
+
`,
|
|
1606
|
+
},
|
|
1607
|
+
},
|
|
1608
|
+
},
|
|
1609
|
+
},
|
|
1610
|
+
};
|
|
1611
|
+
};
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
// Get OpenSearch connection
|
|
1615
|
+
|
|
1616
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1617
|
+
|
|
1618
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1619
|
+
let totalTickets = 0;
|
|
1620
|
+
|
|
1621
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1622
|
+
|
|
1623
|
+
allQuery.size = 0;
|
|
1624
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1625
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1626
|
+
|
|
1627
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1628
|
+
let openTickets = 0;
|
|
1629
|
+
|
|
1630
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1631
|
+
otQ.size = 0;
|
|
1632
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1633
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1634
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1635
|
+
|
|
1636
|
+
|
|
1637
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1638
|
+
let inprogress = 0;
|
|
1639
|
+
|
|
1640
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1641
|
+
ipQ.size = 0;
|
|
1642
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1643
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1644
|
+
|
|
1645
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1646
|
+
let closedTickets = 0;
|
|
1647
|
+
|
|
1648
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1649
|
+
clQ.size = 0;
|
|
1650
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1651
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1652
|
+
// Average revisedPerc (for all tangoreview)
|
|
1653
|
+
|
|
1654
|
+
// dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
|
|
1655
|
+
let dueToday = 0;
|
|
1656
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1657
|
+
|
|
1658
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1659
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1660
|
+
// Locate nested mappingInfo query
|
|
1661
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1662
|
+
if ( nestedDue ) {
|
|
1663
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1664
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1665
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1666
|
+
);
|
|
1667
|
+
// Add new dueDate filter
|
|
1668
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1669
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1670
|
+
} );
|
|
1671
|
+
}
|
|
1672
|
+
dueTodayQuery.size = 0;
|
|
1673
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1674
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
// filter expired Tickets
|
|
1678
|
+
let expiredTickets = 0;
|
|
1679
|
+
|
|
1680
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1681
|
+
eQ.size = 0;
|
|
1682
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1683
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1684
|
+
|
|
1685
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1686
|
+
let ticketAccuracyAbove = 0;
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1690
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1691
|
+
{
|
|
1692
|
+
script: {
|
|
1693
|
+
script: {
|
|
1694
|
+
lang: 'painless',
|
|
1695
|
+
source: `
|
|
1696
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1697
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1698
|
+
`,
|
|
1699
|
+
params: { num: 85 },
|
|
1700
|
+
},
|
|
1701
|
+
},
|
|
1702
|
+
},
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
] );
|
|
1706
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1707
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1708
|
+
|
|
1709
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1710
|
+
let ticketAccuracyBelow = 0;
|
|
1711
|
+
|
|
1712
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1713
|
+
{
|
|
1714
|
+
script: {
|
|
1715
|
+
script: {
|
|
1716
|
+
lang: 'painless',
|
|
1717
|
+
source: `
|
|
1718
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1719
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1720
|
+
`,
|
|
1721
|
+
params: { num: 85 },
|
|
1722
|
+
},
|
|
1723
|
+
},
|
|
1724
|
+
},
|
|
1725
|
+
|
|
1726
|
+
] );
|
|
1727
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1728
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1729
|
+
|
|
1730
|
+
// Final result object
|
|
1731
|
+
result = {
|
|
1732
|
+
totalTickets,
|
|
1733
|
+
openTickets,
|
|
1734
|
+
inprogress,
|
|
1735
|
+
closedTickets,
|
|
1736
|
+
dueToday: dueToday,
|
|
1737
|
+
Expired: expiredTickets,
|
|
1738
|
+
avgTicket: ticketAccuracyAbove+'%',
|
|
1739
|
+
avgAccuracy: ticketAccuracyBelow+'%',
|
|
1740
|
+
};
|
|
1741
|
+
} else {
|
|
1742
|
+
const storeQuery = {
|
|
1743
|
+
size: 0,
|
|
1744
|
+
query: {
|
|
1745
|
+
bool: {
|
|
1746
|
+
must: [
|
|
1747
|
+
{
|
|
1748
|
+
'range': {
|
|
1749
|
+
'dateString': {
|
|
1750
|
+
'gte': inputData?.fromDate,
|
|
1751
|
+
'lte': inputData?.toDate,
|
|
1752
|
+
'format': 'yyyy-MM-dd',
|
|
1753
|
+
},
|
|
1754
|
+
},
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
nested: {
|
|
1758
|
+
path: 'mappingInfo',
|
|
1759
|
+
query: {
|
|
1760
|
+
bool: {
|
|
1761
|
+
must: [
|
|
1762
|
+
{
|
|
1763
|
+
term: {
|
|
1764
|
+
'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
|
|
1765
|
+
},
|
|
1766
|
+
},
|
|
1767
|
+
],
|
|
1768
|
+
},
|
|
1769
|
+
},
|
|
1770
|
+
},
|
|
1771
|
+
},
|
|
1772
|
+
|
|
1773
|
+
],
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1779
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1780
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1781
|
+
// Remove any previous mappingInfo.status term
|
|
1782
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1783
|
+
if ( nested ) {
|
|
1784
|
+
// filter out all mappingInfo.status
|
|
1785
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1786
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1787
|
+
} );
|
|
1788
|
+
// add desired status
|
|
1789
|
+
nested.nested.query.bool.must.push( {
|
|
1790
|
+
term: {
|
|
1791
|
+
'mappingInfo.status': statusValue,
|
|
1792
|
+
},
|
|
1793
|
+
} );
|
|
1794
|
+
}
|
|
1795
|
+
return q;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1799
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1800
|
+
|
|
1801
|
+
// locate nested section
|
|
1802
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1803
|
+
|
|
1804
|
+
if ( nested ) {
|
|
1805
|
+
// remove old status filters
|
|
1806
|
+
nested.nested.query.bool.must =
|
|
1807
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1808
|
+
|
|
1809
|
+
// add new filters
|
|
1810
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
return {
|
|
1814
|
+
...q,
|
|
1815
|
+
size: 0,
|
|
1816
|
+
aggs: {
|
|
1817
|
+
avg_value: {
|
|
1818
|
+
avg: {
|
|
1819
|
+
script: {
|
|
1820
|
+
lang: 'painless',
|
|
1821
|
+
source: `
|
|
1822
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1823
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1824
|
+
try {
|
|
1825
|
+
return Double.parseDouble(v);
|
|
1826
|
+
} catch (Exception e) {
|
|
1827
|
+
return null;
|
|
1828
|
+
}
|
|
1829
|
+
`,
|
|
1830
|
+
},
|
|
1831
|
+
},
|
|
1832
|
+
},
|
|
1833
|
+
},
|
|
1834
|
+
};
|
|
1835
|
+
};
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
// Get OpenSearch connection
|
|
1839
|
+
|
|
1840
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1841
|
+
|
|
1842
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1843
|
+
let totalTickets = 0;
|
|
1844
|
+
|
|
1845
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1846
|
+
|
|
1847
|
+
allQuery.size = 0;
|
|
1848
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1849
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1850
|
+
|
|
1851
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1852
|
+
let openTickets = 0;
|
|
1853
|
+
|
|
1854
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1855
|
+
otQ.size = 0;
|
|
1856
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1857
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1858
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1862
|
+
let inprogress = 0;
|
|
1863
|
+
|
|
1864
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1865
|
+
ipQ.size = 0;
|
|
1866
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1867
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1868
|
+
|
|
1869
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1870
|
+
let closedTickets = 0;
|
|
1871
|
+
|
|
1872
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1873
|
+
clQ.size = 0;
|
|
1874
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1875
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1876
|
+
// dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
|
|
1877
|
+
let dueToday = 0;
|
|
1878
|
+
let todayDateString = new Date().toISOString().slice( 0, 10 );
|
|
1879
|
+
|
|
1880
|
+
// Build a query for tickets with mappingInfo.dueDate == todayDateString
|
|
1881
|
+
let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1882
|
+
// Locate nested mappingInfo query
|
|
1883
|
+
let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
|
|
1884
|
+
if ( nestedDue ) {
|
|
1885
|
+
// Remove any previous mappingInfo.dueDate term
|
|
1886
|
+
nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
|
|
1887
|
+
( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
|
|
1888
|
+
);
|
|
1889
|
+
// Add new dueDate filter
|
|
1890
|
+
nestedDue.nested.query.bool.must.push( {
|
|
1891
|
+
term: { 'mappingInfo.dueDate': todayDateString },
|
|
1892
|
+
} );
|
|
1893
|
+
}
|
|
1894
|
+
dueTodayQuery.size = 0;
|
|
1895
|
+
const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
|
|
1896
|
+
dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
|
|
1897
|
+
|
|
646
1898
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
1899
|
+
// filter expired Tickets
|
|
1900
|
+
let expiredTickets = 0;
|
|
1901
|
+
|
|
1902
|
+
let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1903
|
+
eQ.size = 0;
|
|
1904
|
+
const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
|
|
1905
|
+
expiredTickets = eResp?.body?.hits?.total?.value || 0;
|
|
1906
|
+
|
|
1907
|
+
// filter under tango review
|
|
1908
|
+
|
|
1909
|
+
let undertangoTickets = 0;
|
|
1910
|
+
|
|
1911
|
+
let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
|
|
1912
|
+
utrQ.size = 0;
|
|
1913
|
+
const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
|
|
1914
|
+
undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
|
|
1915
|
+
|
|
1916
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1917
|
+
let ticketAccuracyAbove = 0;
|
|
1918
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1919
|
+
{
|
|
1920
|
+
script: {
|
|
1921
|
+
script: {
|
|
1922
|
+
lang: 'painless',
|
|
1923
|
+
source: `
|
|
1924
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1925
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1926
|
+
`,
|
|
1927
|
+
params: { num: 85 },
|
|
664
1928
|
},
|
|
665
1929
|
},
|
|
666
|
-
],
|
|
667
|
-
},
|
|
668
|
-
},
|
|
669
|
-
aggs: {
|
|
670
|
-
totalTicketCount: {
|
|
671
|
-
value_count: { field: '_id' },
|
|
672
|
-
},
|
|
673
|
-
openStatusCount: {
|
|
674
|
-
filter: {
|
|
675
|
-
term: { 'status.keyword': 'open' },
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
closeTicketCount: {
|
|
679
|
-
filter: {
|
|
680
|
-
term: { 'status.keyword': 'closed' },
|
|
681
|
-
},
|
|
682
|
-
},
|
|
683
|
-
duplicateCount: {
|
|
684
|
-
sum: {
|
|
685
|
-
field: 'duplicateCount',
|
|
686
|
-
},
|
|
687
|
-
},
|
|
688
|
-
employeeCount: {
|
|
689
|
-
sum: {
|
|
690
|
-
field: 'employeeCount',
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
houseKeepingCount: {
|
|
694
|
-
sum: {
|
|
695
|
-
field: 'houseKeepingCount',
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
junkCount: {
|
|
699
|
-
sum: {
|
|
700
|
-
field: 'junkCount',
|
|
701
1930
|
},
|
|
702
|
-
},
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
1931
|
|
|
706
|
-
const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
|
|
707
|
-
const aggs = getData?.body?.aggregations;
|
|
708
1932
|
|
|
709
|
-
|
|
710
|
-
totalTickets: aggs.totalTicketCount.value,
|
|
711
|
-
openTickets: aggs.openStatusCount.doc_count,
|
|
712
|
-
closedTickets: aggs.closeTicketCount.doc_count,
|
|
713
|
-
duplicateCount: aggs.duplicateCount.value,
|
|
714
|
-
employeeCount: aggs.employeeCount.value,
|
|
715
|
-
houseKeepingCount: aggs.houseKeepingCount.value,
|
|
716
|
-
junkCount: aggs.junkCount.value,
|
|
717
|
-
noShopper: aggs.duplicateCount.value + aggs.employeeCount.value + aggs.houseKeepingCount.value + aggs.junkCount.value,
|
|
718
|
-
};
|
|
1933
|
+
] );
|
|
719
1934
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const err = error.message || 'Internal Server Error';
|
|
723
|
-
logger.error( { error: error, messgage: req.query } );
|
|
724
|
-
return res.sendSuccess( err, 500 );
|
|
725
|
-
}
|
|
726
|
-
}
|
|
1935
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1936
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
727
1937
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
openTickets: 0,
|
|
763
|
-
inprogress: 0,
|
|
764
|
-
closedTickets: 0,
|
|
765
|
-
dueToday: 0,
|
|
766
|
-
Expired: 0,
|
|
767
|
-
avgTicket: '0%',
|
|
768
|
-
avgAccuracy: '0%',
|
|
769
|
-
} : 'NA';
|
|
1938
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1939
|
+
let ticketAccuracyBelow = 0;
|
|
1940
|
+
|
|
1941
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1942
|
+
{
|
|
1943
|
+
script: {
|
|
1944
|
+
script: {
|
|
1945
|
+
lang: 'painless',
|
|
1946
|
+
source: `
|
|
1947
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1948
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1949
|
+
`,
|
|
1950
|
+
params: { num: 85 },
|
|
1951
|
+
},
|
|
1952
|
+
},
|
|
1953
|
+
},
|
|
1954
|
+
|
|
1955
|
+
] );
|
|
1956
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1957
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1958
|
+
|
|
1959
|
+
// Final result object
|
|
1960
|
+
result = {
|
|
1961
|
+
totalTickets,
|
|
1962
|
+
openTickets,
|
|
1963
|
+
inprogress,
|
|
1964
|
+
closedTickets,
|
|
1965
|
+
dueToday: dueToday,
|
|
1966
|
+
Expired: expiredTickets,
|
|
1967
|
+
underTangoReview: undertangoTickets,
|
|
1968
|
+
avgTicket: ticketAccuracyAbove+'%',
|
|
1969
|
+
avgAccuracy: ticketAccuracyBelow+'%',
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
770
1972
|
}
|
|
771
1973
|
|
|
772
1974
|
return res.sendSuccess( { result: result } );
|
|
@@ -781,7 +1983,7 @@ export async function ticketList1( req, res ) {
|
|
|
781
1983
|
try {
|
|
782
1984
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
783
1985
|
const inputData = req.query;
|
|
784
|
-
const limit = inputData.limit || 10;
|
|
1986
|
+
const limit =inputData?.isExport ? 10000: inputData.limit || 10;
|
|
785
1987
|
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
786
1988
|
const order = inputData?.sortOrder || -1;
|
|
787
1989
|
|
|
@@ -1003,7 +2205,7 @@ export async function ticketList( req, res ) {
|
|
|
1003
2205
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1004
2206
|
const inputData = req.query;
|
|
1005
2207
|
const userInfo = req.user;
|
|
1006
|
-
const limit = inputData?.limit || 10;
|
|
2208
|
+
const limit =inputData?.isExport? 10000: inputData?.limit || 10;
|
|
1007
2209
|
const offset = inputData.offset == 0 ? 0 : ( inputData.offset - 1 ) * limit || 0;
|
|
1008
2210
|
inputData.clientId = inputData?.clientId?.split( ',' ); // convert strig to array
|
|
1009
2211
|
|
|
@@ -1326,6 +2528,87 @@ export async function ticketList( req, res ) {
|
|
|
1326
2528
|
}
|
|
1327
2529
|
}
|
|
1328
2530
|
|
|
2531
|
+
if ( inputData?.filterByStore && inputData?.filterByStore !== '' ) {
|
|
2532
|
+
let percQuery = null;
|
|
2533
|
+
const value = inputData.filterByStore;
|
|
2534
|
+
|
|
2535
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2536
|
+
const percValue = ( val ) => {
|
|
2537
|
+
if ( typeof val === 'string' ) {
|
|
2538
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2539
|
+
}
|
|
2540
|
+
return parseFloat( val );
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2544
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2545
|
+
// "<90" or "<=90"
|
|
2546
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2547
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2548
|
+
percQuery = {
|
|
2549
|
+
script: {
|
|
2550
|
+
script: {
|
|
2551
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2552
|
+
params: { num },
|
|
2553
|
+
},
|
|
2554
|
+
},
|
|
2555
|
+
};
|
|
2556
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2557
|
+
// ">=90"
|
|
2558
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2559
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2560
|
+
percQuery = {
|
|
2561
|
+
script: {
|
|
2562
|
+
script: {
|
|
2563
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2564
|
+
params: { num },
|
|
2565
|
+
},
|
|
2566
|
+
},
|
|
2567
|
+
};
|
|
2568
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2569
|
+
// "50 to 90"
|
|
2570
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2571
|
+
const from = percValue( match[1] );
|
|
2572
|
+
const to = percValue( match[2] );
|
|
2573
|
+
percQuery = {
|
|
2574
|
+
script: {
|
|
2575
|
+
script: {
|
|
2576
|
+
source:
|
|
2577
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2578
|
+
params: { from, to },
|
|
2579
|
+
},
|
|
2580
|
+
},
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2584
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2585
|
+
percQuery = {
|
|
2586
|
+
script: {
|
|
2587
|
+
script: {
|
|
2588
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2589
|
+
params: { num: percValue( value ) },
|
|
2590
|
+
},
|
|
2591
|
+
},
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
if ( percQuery ) {
|
|
2596
|
+
searchQuery.query.bool.must.push( {
|
|
2597
|
+
nested: {
|
|
2598
|
+
path: 'mappingInfo',
|
|
2599
|
+
query: {
|
|
2600
|
+
bool: {
|
|
2601
|
+
must: [
|
|
2602
|
+
{ term: { 'mappingInfo.type': 'tagging' } },
|
|
2603
|
+
percQuery,
|
|
2604
|
+
],
|
|
2605
|
+
},
|
|
2606
|
+
},
|
|
2607
|
+
},
|
|
2608
|
+
} );
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
|
|
1329
2612
|
if ( inputData?.filterByReviewer && inputData?.filterByReviewer !== '' ) {
|
|
1330
2613
|
let percQuery = null;
|
|
1331
2614
|
const value = inputData.filterByReviewer;
|
|
@@ -1633,7 +2916,7 @@ export async function ticketList( req, res ) {
|
|
|
1633
2916
|
|
|
1634
2917
|
|
|
1635
2918
|
);
|
|
1636
|
-
} else if ( req?.user?.userType === 'client'
|
|
2919
|
+
} else if ( req?.user?.userType === 'client' ) {
|
|
1637
2920
|
searchQuery.query.bool.must.push(
|
|
1638
2921
|
{
|
|
1639
2922
|
term: {
|
|
@@ -1727,137 +3010,285 @@ export async function ticketList( req, res ) {
|
|
|
1727
3010
|
let temp = [];
|
|
1728
3011
|
if ( req.user.userType === 'tango' ) {
|
|
1729
3012
|
if ( inputData.tangoType === 'store' ) {
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
3013
|
+
if ( inputData?.isExport ) {
|
|
3014
|
+
const exportData = [];
|
|
3015
|
+
for ( let item of ticketListData ) {
|
|
3016
|
+
exportData.push( {
|
|
3017
|
+
|
|
3018
|
+
'Ticket ID': item?.ticketId,
|
|
3019
|
+
'store Name': item?.storeName,
|
|
3020
|
+
'store ID': item?.storeId,
|
|
3021
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
|
|
3022
|
+
'Issue Date': item?.dateString,
|
|
3023
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
|
|
3024
|
+
'Actual FF': item?.footfallCount,
|
|
3025
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3026
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3027
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3028
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3029
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
1745
3030
|
|
|
1746
|
-
|
|
3031
|
+
} );
|
|
3032
|
+
}
|
|
3033
|
+
return await download( exportData, res );
|
|
3034
|
+
} else {
|
|
3035
|
+
for ( let item of ticketListData ) {
|
|
3036
|
+
temp.push( {
|
|
3037
|
+
|
|
3038
|
+
ticketId: item?.ticketId,
|
|
3039
|
+
storeId: item?.storeId,
|
|
3040
|
+
storeName: item?.storeName,
|
|
3041
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.createdAt,
|
|
3042
|
+
issueDate: item?.dateString,
|
|
3043
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.dueDate,
|
|
3044
|
+
footfall: item?.footfallCount,
|
|
3045
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3046
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3047
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3048
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3049
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3050
|
+
|
|
3051
|
+
} );
|
|
3052
|
+
}
|
|
1747
3053
|
}
|
|
1748
3054
|
} else {
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
3055
|
+
if ( inputData?.isExport ) {
|
|
3056
|
+
const exportData = [];
|
|
3057
|
+
for ( let item of ticketListData ) {
|
|
3058
|
+
exportData.push( {
|
|
3059
|
+
|
|
3060
|
+
'Ticket ID': item?.ticketId,
|
|
3061
|
+
'Store Name': item?.storeName,
|
|
3062
|
+
'Store ID': item?.storeId,
|
|
3063
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
3064
|
+
'Issue Date': item?.dateString,
|
|
3065
|
+
'Ticket Type': item?.type,
|
|
3066
|
+
'Actual FF': item?.footfallCount,
|
|
3067
|
+
// Use the dueDate from the last mappingInfo item whose type is NOT 'finalRevisoon'
|
|
3068
|
+
'Due Date': ( () => {
|
|
3069
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3070
|
+
const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
|
|
3071
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3072
|
+
}
|
|
3073
|
+
return '';
|
|
3074
|
+
} )(),
|
|
3075
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3076
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3077
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3078
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3079
|
+
'Ticket Status': item?.status,
|
|
3080
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
1767
3081
|
|
|
1768
|
-
|
|
3082
|
+
} );
|
|
3083
|
+
}
|
|
3084
|
+
return await download( exportData, res );
|
|
3085
|
+
} else {
|
|
3086
|
+
for ( let item of ticketListData ) {
|
|
3087
|
+
temp.push( {
|
|
3088
|
+
|
|
3089
|
+
ticketId: item?.ticketId,
|
|
3090
|
+
storeId: item?.storeId,
|
|
3091
|
+
storeName: item?.storeName,
|
|
3092
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.createdAt,
|
|
3093
|
+
issueDate: item?.dateString,
|
|
3094
|
+
footfall: item?.footfallCount,
|
|
3095
|
+
dueDate: ( () => {
|
|
3096
|
+
if ( Array.isArray( item?.mappingInfo ) ) {
|
|
3097
|
+
const filtered = item.mappingInfo.filter( ( f ) => f.dueDate && f.type !== 'finalRevisoon' );
|
|
3098
|
+
return filtered.length > 0 ? filtered[filtered.length - 1].dueDate : '';
|
|
3099
|
+
}
|
|
3100
|
+
return '';
|
|
3101
|
+
} )(),
|
|
3102
|
+
type: item?.type || 'store',
|
|
3103
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3104
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3105
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3106
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3107
|
+
status: item?.status,
|
|
3108
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.status || '--',
|
|
3109
|
+
|
|
3110
|
+
} );
|
|
3111
|
+
}
|
|
1769
3112
|
}
|
|
1770
3113
|
}
|
|
1771
3114
|
} else {
|
|
1772
3115
|
if ( inputData?.permissionType === 'approve' ) {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3116
|
+
if ( inputData.exportData ) {
|
|
3117
|
+
const exportData = [];
|
|
3118
|
+
for ( let item of ticketListData ) {
|
|
3119
|
+
exportData.push( {
|
|
3120
|
+
|
|
3121
|
+
'Ticket ID': item?.ticketId,
|
|
3122
|
+
'Store Name': item?.storeName,
|
|
3123
|
+
'Store ID': item?.storeId,
|
|
3124
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3125
|
+
'Issue Date': item?.dateString,
|
|
3126
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3127
|
+
'Actual FF': item?.footfallCount,
|
|
3128
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3129
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3130
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3131
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3132
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3133
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3134
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1793
3135
|
|
|
1794
|
-
|
|
3136
|
+
} );
|
|
3137
|
+
}
|
|
3138
|
+
return await download( exportData, res );
|
|
3139
|
+
} else {
|
|
3140
|
+
for ( let item of ticketListData ) {
|
|
3141
|
+
temp.push( {
|
|
3142
|
+
|
|
3143
|
+
ticketId: item?.ticketId,
|
|
3144
|
+
storeId: item?.storeId,
|
|
3145
|
+
storeName: item?.storeName,
|
|
3146
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3147
|
+
issueDate: item?.dateString,
|
|
3148
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3149
|
+
footfall: item?.footfallCount,
|
|
3150
|
+
type: item.type || 'store',
|
|
3151
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3152
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3153
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3154
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3155
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3156
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3157
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3158
|
+
|
|
3159
|
+
} );
|
|
3160
|
+
}
|
|
1795
3161
|
}
|
|
1796
3162
|
} else if ( inputData?.permissionType === 'review' ) {
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
3163
|
+
if ( inputData?.isExport ) {
|
|
3164
|
+
const exportData = [];
|
|
3165
|
+
for ( let item of ticketListData ) {
|
|
3166
|
+
exportData.push( {
|
|
3167
|
+
|
|
3168
|
+
'Ticket ID': item?.ticketId,
|
|
3169
|
+
'Store Name': item?.storeName,
|
|
3170
|
+
'Store ID': item?.storeId,
|
|
3171
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3172
|
+
'Issue Date': item?.dateString,
|
|
3173
|
+
'Actual FF': item?.footfallCount,
|
|
3174
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3175
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3176
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3177
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3178
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1813
3179
|
|
|
1814
|
-
|
|
3180
|
+
} );
|
|
3181
|
+
}
|
|
3182
|
+
return await download( exportData, res );
|
|
3183
|
+
} else {
|
|
3184
|
+
for ( let item of ticketListData ) {
|
|
3185
|
+
temp.push( {
|
|
3186
|
+
|
|
3187
|
+
ticketId: item?.ticketId,
|
|
3188
|
+
storeId: item?.storeId,
|
|
3189
|
+
storeName: item?.storeName,
|
|
3190
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3191
|
+
issueDate: item?.dateString,
|
|
3192
|
+
footfall: item?.footfallCount,
|
|
3193
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3194
|
+
type: item.type || 'store',
|
|
3195
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3196
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3197
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3198
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3199
|
+
|
|
3200
|
+
} );
|
|
3201
|
+
}
|
|
1815
3202
|
}
|
|
1816
3203
|
} else if ( req.user.role === 'user' ) {
|
|
1817
3204
|
temp = [];
|
|
1818
3205
|
} else if ( ticketsFeature ) {
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
3206
|
+
if ( inputData?.isExport ) {
|
|
3207
|
+
const exportData = [];
|
|
3208
|
+
for ( let item of ticketListData ) {
|
|
3209
|
+
exportData.push( {
|
|
3210
|
+
|
|
3211
|
+
'Ticket ID': item?.ticketId,
|
|
3212
|
+
'Store ID': item?.storeId,
|
|
3213
|
+
'Store Name': item?.storeName,
|
|
3214
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3215
|
+
'Issue Date': item?.dateString,
|
|
3216
|
+
'Actual FF': item?.footfallCount,
|
|
3217
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3218
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3219
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3220
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3221
|
+
'Reviewed by': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
1835
3222
|
|
|
1836
|
-
|
|
3223
|
+
} );
|
|
3224
|
+
}
|
|
3225
|
+
return await download( exportData, res );
|
|
3226
|
+
} else {
|
|
3227
|
+
for ( let item of ticketListData ) {
|
|
3228
|
+
temp.push( {
|
|
3229
|
+
|
|
3230
|
+
ticketId: item?.ticketId,
|
|
3231
|
+
storeId: item?.storeId,
|
|
3232
|
+
storeName: item?.storeName,
|
|
3233
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdAt,
|
|
3234
|
+
issueDate: item?.dateString,
|
|
3235
|
+
footfall: item?.footfallCount,
|
|
3236
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.dueDate,
|
|
3237
|
+
type: item.type || 'store',
|
|
3238
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3239
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3240
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.status || '--',
|
|
3241
|
+
ReviewedBy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.createdByEmail || '--',
|
|
3242
|
+
|
|
3243
|
+
} );
|
|
3244
|
+
}
|
|
1837
3245
|
}
|
|
1838
3246
|
} else if ( ticketsApproveFeature ) {
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3247
|
+
if ( inputData.isExport ) {
|
|
3248
|
+
const exportData = [];
|
|
3249
|
+
for ( let item of ticketListData ) {
|
|
3250
|
+
exportData.push( {
|
|
3251
|
+
|
|
3252
|
+
'Ticket ID': item?.ticketId,
|
|
3253
|
+
'Store Name': item?.storeName,
|
|
3254
|
+
'Store ID': item?.storeId,
|
|
3255
|
+
'Ticket Raised': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3256
|
+
'Issue Date': item?.dateString,
|
|
3257
|
+
'Due Date': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3258
|
+
'Actual FF': item?.footfallCount,
|
|
3259
|
+
'Store (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3260
|
+
'Reviewer (%)': item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3261
|
+
'Approver (%)': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3262
|
+
'Tango (%)': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3263
|
+
'Status': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3264
|
+
'Tango Status': item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3265
|
+
'Approved by': item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
1859
3266
|
|
|
1860
|
-
|
|
3267
|
+
} );
|
|
3268
|
+
}
|
|
3269
|
+
return await download( exportData, res );
|
|
3270
|
+
} else {
|
|
3271
|
+
for ( let item of ticketListData ) {
|
|
3272
|
+
temp.push( {
|
|
3273
|
+
|
|
3274
|
+
ticketId: item?.ticketId,
|
|
3275
|
+
storeId: item?.storeId,
|
|
3276
|
+
storeName: item?.storeName,
|
|
3277
|
+
ticketRaised: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdAt,
|
|
3278
|
+
issueDate: item?.dateString,
|
|
3279
|
+
dueDate: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.dueDate,
|
|
3280
|
+
footfall: item?.footfallCount,
|
|
3281
|
+
type: item.type || 'store',
|
|
3282
|
+
storeRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tagging' )?.revicedPerc || '--',
|
|
3283
|
+
reviewerRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'review' )?.revicedPerc || '--',
|
|
3284
|
+
approverRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.revicedPerc || '--',
|
|
3285
|
+
tangoRevisedAccuracy: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3286
|
+
status: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.status || '--',
|
|
3287
|
+
tangoStatus: item?.mappingInfo?.find( ( f ) => f.type === 'tangoreview' )?.revicedPerc || '--',
|
|
3288
|
+
approvedBy: item?.mappingInfo?.find( ( f ) => f.type === 'approve' )?.createdByEmail || '--',
|
|
3289
|
+
|
|
3290
|
+
} );
|
|
3291
|
+
}
|
|
1861
3292
|
}
|
|
1862
3293
|
} else {
|
|
1863
3294
|
temp = [];
|
|
@@ -2666,7 +4097,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
2666
4097
|
+ (doc.containsKey('employeeCount') && !doc['employeeCount'].empty ? doc['employeeCount'].value : 0)
|
|
2667
4098
|
+ (doc.containsKey('junkCount') && !doc['junkCount'].empty ? doc['junkCount'].value : 0);
|
|
2668
4099
|
`,
|
|
2669
|
-
lang: '
|
|
4100
|
+
lang: 'scripting',
|
|
2670
4101
|
},
|
|
2671
4102
|
},
|
|
2672
4103
|
},
|
|
@@ -3076,6 +4507,7 @@ export async function reviewerList( req, res ) {
|
|
|
3076
4507
|
export async function openTicketList( req, res ) {
|
|
3077
4508
|
try {
|
|
3078
4509
|
const inputData = req.body;
|
|
4510
|
+
logger.info( { inputData } );
|
|
3079
4511
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
3080
4512
|
|
|
3081
4513
|
// INSERT_YOUR_CODE
|
|
@@ -3122,6 +4554,7 @@ export async function openTicketList( req, res ) {
|
|
|
3122
4554
|
},
|
|
3123
4555
|
];
|
|
3124
4556
|
|
|
4557
|
+
|
|
3125
4558
|
const openSearchQuery = {
|
|
3126
4559
|
size: 10000,
|
|
3127
4560
|
query: {
|
|
@@ -3131,6 +4564,31 @@ export async function openTicketList( req, res ) {
|
|
|
3131
4564
|
},
|
|
3132
4565
|
_source: [ 'ticketId', 'storeName', 'storeId', 'dateString', 'revicedFootfall', 'footfallCount', 'revicedPerc', 'type' ],
|
|
3133
4566
|
};
|
|
4567
|
+
|
|
4568
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
4569
|
+
openSearchQuery.query.bool['should'] = [];
|
|
4570
|
+
openSearchQuery.query.bool.should = [
|
|
4571
|
+
|
|
4572
|
+
{
|
|
4573
|
+
'wildcard': {
|
|
4574
|
+
'storeName.keyword': {
|
|
4575
|
+
'value': `*${inputData.searchValue}*`,
|
|
4576
|
+
},
|
|
4577
|
+
},
|
|
4578
|
+
},
|
|
4579
|
+
{
|
|
4580
|
+
'wildcard': {
|
|
4581
|
+
'ticketId.keyword': {
|
|
4582
|
+
'value': `*${inputData.searchValue}*`,
|
|
4583
|
+
},
|
|
4584
|
+
},
|
|
4585
|
+
},
|
|
4586
|
+
|
|
4587
|
+
|
|
4588
|
+
];
|
|
4589
|
+
openSearchQuery.query.bool['minimum_should_match'] = 1;
|
|
4590
|
+
}
|
|
4591
|
+
|
|
3134
4592
|
// INSERT_YOUR_CODE
|
|
3135
4593
|
// Add sorting by revicedPerc descending (highest revised accuracy first)
|
|
3136
4594
|
openSearchQuery.sort = [
|