tango-app-api-audit 3.6.21 → 3.6.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-audit",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.22",
|
|
4
4
|
"description": "audit & audit metrics apis",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"author": "praveenraj",
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@aws-sdk/client-ses": "^3.918.0",
|
|
16
17
|
"adm-zip": "^0.5.15",
|
|
17
18
|
"aws-sdk": "^2.1643.0",
|
|
18
19
|
"axios": "^1.7.9",
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
"joi-to-swagger": "^6.2.0",
|
|
25
26
|
"lodash": "^4.17.21",
|
|
26
27
|
"mongodb": "^6.7.0",
|
|
28
|
+
"nodemailer": "^7.0.10",
|
|
27
29
|
"nodemon": "^3.1.3",
|
|
28
30
|
"swagger-ui-express": "^5.0.1",
|
|
29
31
|
"tango-api-schema": "^2.3.28",
|
|
@@ -811,7 +811,6 @@ export async function save( req, res ) {
|
|
|
811
811
|
},
|
|
812
812
|
};
|
|
813
813
|
const getOutput = await getOpenSearchData( openSearch.EyeTestOutput, searchQuery );
|
|
814
|
-
logger.info( { getOutput: getOutput } );
|
|
815
814
|
const output = getOutput?.body?.hits?.hits?.length > 0 ? getOutput?.body?.hits?.hits[0] : null;
|
|
816
815
|
if ( !output ) {
|
|
817
816
|
return res.sendError( 'This file is currently being audited by another user. Any actions you perform will not be saved.', 400 );
|
|
@@ -845,19 +844,27 @@ export async function save( req, res ) {
|
|
|
845
844
|
|
|
846
845
|
const getEyetestConfig = await findOneEyeTestConfig( { clientId: req?.user.userType == 'tango'? inputData?.clientId : req.user.clientId, configureType: 'email' }, { complianceThreshold: 1 } );
|
|
847
846
|
|
|
848
|
-
if ( getEyetestConfig && inputData.auditStatus == 'Audited' && inputUpdatedData ) {
|
|
847
|
+
if ( getEyetestConfig &&( inputData.auditStatus == 'Audited' ||inputData.auditStatus == 'ReAudited' ) && inputUpdatedData && inputData.type === 'physical' && inputData?.isReSend !== false ) {
|
|
849
848
|
const trustScore = Math.round( 100-( ( inputData?.overRides / inputData?.steps?.length ) * 100 ) );
|
|
850
849
|
const value = Number( getEyetestConfig?.complianceThreshold?.value?.split( '%' )[0] );
|
|
851
850
|
const condition = getEyetestConfig?.complianceThreshold?.condition;
|
|
852
|
-
const id=
|
|
851
|
+
const id= inputData?.isReSend === true ?`${inputData.id}_${inputUpdatedData?._source?.optumId}_${inputData?.fileId}_${Date.now()}` : `${inputData.id}_${inputUpdatedData?._source?.optumId}_${inputData?.fileId}`;
|
|
852
|
+
const humanScore = Math.round( ( inputUpdatedData?._source?.auditedSteps /inputUpdatedData?._source?.totalSteps ) * 100 );
|
|
853
|
+
const userName = await findOneUser( { _id: new mongoose.Types.ObjectId( inputUpdatedData?._source?.userId ) }, { userName: 1 } );
|
|
854
|
+
|
|
855
|
+
|
|
853
856
|
switch ( condition ) {
|
|
854
857
|
case '>': if ( trustScore > value ) {
|
|
855
858
|
const record1 = {
|
|
856
859
|
RMEmail: inputData.RMEmail,
|
|
860
|
+
RMName: inputData.RMEmail,
|
|
857
861
|
cluster: inputData.cluster,
|
|
858
862
|
optumId: inputUpdatedData?._source?.optumId,
|
|
863
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
859
864
|
storeName: inputUpdatedData?._source?.storeName,
|
|
860
865
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
866
|
+
humanScore: humanScore,
|
|
867
|
+
auditedBy: userName?.userName || '',
|
|
861
868
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
862
869
|
auditedId: inputUpdatedData?._id,
|
|
863
870
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -871,10 +878,14 @@ export async function save( req, res ) {
|
|
|
871
878
|
case '<': if ( trustScore < value ) {
|
|
872
879
|
const record2 = {
|
|
873
880
|
RMEmail: inputData.RMEmail,
|
|
881
|
+
RMName: inputData.RMEmail,
|
|
874
882
|
cluster: inputData.cluster,
|
|
875
883
|
optumId: inputUpdatedData?._source?.optumId,
|
|
884
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
876
885
|
storeName: inputUpdatedData?._source?.storeName,
|
|
877
886
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
887
|
+
humanScore: humanScore,
|
|
888
|
+
auditedBy: userName?.userName || '',
|
|
878
889
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
879
890
|
auditedId: inputUpdatedData?._id,
|
|
880
891
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -888,10 +899,14 @@ export async function save( req, res ) {
|
|
|
888
899
|
case '>=': if ( trustScore >= value ) {
|
|
889
900
|
const record3 = {
|
|
890
901
|
RMEmail: inputData.RMEmail,
|
|
902
|
+
RMName: inputData.RMEmail,
|
|
891
903
|
cluster: inputData.cluster,
|
|
892
904
|
optumId: inputUpdatedData?._source?.optumId,
|
|
905
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
893
906
|
storeName: inputUpdatedData?._source?.storeName,
|
|
894
907
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
908
|
+
humanScore: humanScore,
|
|
909
|
+
auditedBy: userName?.userName || '',
|
|
895
910
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
896
911
|
auditedId: inputUpdatedData?._id,
|
|
897
912
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -905,10 +920,14 @@ export async function save( req, res ) {
|
|
|
905
920
|
case '<=': if ( trustScore <= value ) {
|
|
906
921
|
const record4 = {
|
|
907
922
|
RMEmail: inputData.RMEmail,
|
|
923
|
+
RMName: inputData.RMEmail,
|
|
908
924
|
cluster: inputData.cluster,
|
|
909
925
|
optumId: inputUpdatedData?._source?.optumId,
|
|
926
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
910
927
|
storeName: inputUpdatedData?._source?.storeName,
|
|
911
928
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
929
|
+
humanScore: humanScore,
|
|
930
|
+
auditedBy: userName?.userName || '',
|
|
912
931
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
913
932
|
auditedId: inputUpdatedData?._id,
|
|
914
933
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -923,10 +942,14 @@ export async function save( req, res ) {
|
|
|
923
942
|
case '==': if ( trustScore == value ) {
|
|
924
943
|
const record5 = {
|
|
925
944
|
RMEmail: inputData.RMEmail,
|
|
945
|
+
RMName: inputData.RMEmail,
|
|
926
946
|
cluster: inputData.cluster,
|
|
927
947
|
optumId: inputUpdatedData?._source?.optumId,
|
|
948
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
928
949
|
storeName: inputUpdatedData?._source?.storeName,
|
|
929
950
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
951
|
+
humanScore: humanScore,
|
|
952
|
+
auditedBy: userName?.userName || '',
|
|
930
953
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
931
954
|
auditedId: inputUpdatedData?._id,
|
|
932
955
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -940,10 +963,14 @@ export async function save( req, res ) {
|
|
|
940
963
|
case '===': if ( trustScore === value ) {
|
|
941
964
|
const record6 = {
|
|
942
965
|
RMEmail: inputData.RMEmail,
|
|
966
|
+
RMName: inputData.RMEmail,
|
|
943
967
|
cluster: inputData.cluster,
|
|
944
968
|
optumId: inputUpdatedData?._source?.optumId,
|
|
969
|
+
optomEmailId: inputUpdatedData?._source?.salesmanEmailId,
|
|
945
970
|
storeName: inputUpdatedData?._source?.storeName,
|
|
946
971
|
complianceScore: inputUpdatedData?._source?.ComplianceScore,
|
|
972
|
+
humanScore: humanScore,
|
|
973
|
+
auditedBy: userName?.userName || '',
|
|
947
974
|
trustScore: inputUpdatedData?._source?.trustScore,
|
|
948
975
|
auditedId: inputUpdatedData?._id,
|
|
949
976
|
queueId: inputUpdatedData?._source?.queueId,
|
|
@@ -956,7 +983,7 @@ export async function save( req, res ) {
|
|
|
956
983
|
default: throw new Error( `Unknown condition: ${condition}` );
|
|
957
984
|
}
|
|
958
985
|
}
|
|
959
|
-
return res.sendSuccess( 'The file has been
|
|
986
|
+
return res.sendSuccess( 'The file has been submitted successfully' );
|
|
960
987
|
} catch ( error ) {
|
|
961
988
|
const err = error.message || 'Internal Server Error';
|
|
962
989
|
logger.error( { message: error, data: req.params, function: 'eyetestAudit-controller-save' } );
|
|
@@ -1198,18 +1225,25 @@ export async function userAuditedData( req, res ) {
|
|
|
1198
1225
|
|
|
1199
1226
|
export async function summaryList( req, res ) {
|
|
1200
1227
|
try {
|
|
1228
|
+
// declare inputData and get request body
|
|
1201
1229
|
const inputData = req.body;
|
|
1230
|
+
|
|
1231
|
+
// throw 204 if client id is not 11
|
|
1202
1232
|
if ( inputData.clientId !== '11' ) {
|
|
1203
1233
|
return res.sendError( 'No data found', 204 );
|
|
1204
1234
|
}
|
|
1205
1235
|
|
|
1236
|
+
// declare openSearch and get data form the .env/secret manager
|
|
1206
1237
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1238
|
+
|
|
1239
|
+
// set limit, offset, sortBy, sortOrder value if not set default value
|
|
1207
1240
|
const limit = inputData.limit || 10;
|
|
1208
1241
|
const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
|
|
1209
1242
|
|
|
1210
1243
|
const sortBy = inputData?.sortBy ? [ 'coveredStepsAI', 'trustScore', 'date', 'ComplianceScore', 'auditedSteps' ].includes( inputData.sortBy ) ? inputData.sortBy :`${inputData.sortBy}.keyword` : 'storeName.keyword';
|
|
1211
1244
|
const order = inputData?.sortOrder || -1;
|
|
1212
1245
|
|
|
1246
|
+
// declare filter and assign range values from inputData
|
|
1213
1247
|
let filter= [
|
|
1214
1248
|
{
|
|
1215
1249
|
'range': {
|
|
@@ -1222,6 +1256,7 @@ export async function summaryList( req, res ) {
|
|
|
1222
1256
|
|
|
1223
1257
|
];
|
|
1224
1258
|
|
|
1259
|
+
// filter by Audit Status if key has minimum one element
|
|
1225
1260
|
if ( inputData?.auditStatus && inputData?.auditStatus?.length > 0 ) {
|
|
1226
1261
|
filter.push( {
|
|
1227
1262
|
'terms': {
|
|
@@ -1229,6 +1264,8 @@ export async function summaryList( req, res ) {
|
|
|
1229
1264
|
},
|
|
1230
1265
|
} );
|
|
1231
1266
|
}
|
|
1267
|
+
|
|
1268
|
+
// filter by Visitors Type if key has minimum one element
|
|
1232
1269
|
if ( inputData?.demographics && inputData?.demographics?.length > 0 ) {
|
|
1233
1270
|
filter.push( {
|
|
1234
1271
|
'terms': {
|
|
@@ -1236,6 +1273,8 @@ export async function summaryList( req, res ) {
|
|
|
1236
1273
|
},
|
|
1237
1274
|
} );
|
|
1238
1275
|
}
|
|
1276
|
+
|
|
1277
|
+
// filter by Eye test Type if key has string value one element
|
|
1239
1278
|
if ( inputData.type && inputData.type!='' ) {
|
|
1240
1279
|
filter.push( {
|
|
1241
1280
|
'term': {
|
|
@@ -1245,6 +1284,17 @@ export async function summaryList( req, res ) {
|
|
|
1245
1284
|
} );
|
|
1246
1285
|
}
|
|
1247
1286
|
|
|
1287
|
+
// filter by near addition if value != "" and eye test must be a physical
|
|
1288
|
+
if ( inputData.nearAddition && inputData.nearAddition!='' && inputData.type ==='physical' ) {
|
|
1289
|
+
filter.push( {
|
|
1290
|
+
'term': {
|
|
1291
|
+
'nearAddition.keyword': inputData.nearAddition,
|
|
1292
|
+
},
|
|
1293
|
+
|
|
1294
|
+
} );
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// filter by stores if key has minimum one element else throw an error
|
|
1248
1298
|
if ( inputData.storeId && inputData.storeId.length > 0 ) {
|
|
1249
1299
|
filter.push( {
|
|
1250
1300
|
|
|
@@ -1257,11 +1307,14 @@ export async function summaryList( req, res ) {
|
|
|
1257
1307
|
return res.sendError( 'No data found', 204 );
|
|
1258
1308
|
}
|
|
1259
1309
|
|
|
1310
|
+
// Declare mustQuery as an array and include all elements from the filter array
|
|
1260
1311
|
let mustQuery = [
|
|
1261
1312
|
...filter,
|
|
1262
1313
|
];
|
|
1263
1314
|
|
|
1264
|
-
|
|
1315
|
+
// filter by complianceScore/tango score if key has minimum one element
|
|
1316
|
+
if ( inputData.complianceScore && inputData.complianceScore?.length > 0 ) {
|
|
1317
|
+
// If the key type is not 'store', split the compliance score ranges
|
|
1265
1318
|
if ( inputData.keyType !== 'store' ) {
|
|
1266
1319
|
const rangeShould = inputData?.complianceScore?.map( ( r ) => {
|
|
1267
1320
|
const [ gte, lte ] = r.split( '-' ).map( Number );
|
|
@@ -1271,7 +1324,7 @@ export async function summaryList( req, res ) {
|
|
|
1271
1324
|
},
|
|
1272
1325
|
};
|
|
1273
1326
|
} );
|
|
1274
|
-
|
|
1327
|
+
// Add the range conditions to mustQuery (any one of the ranges should match)
|
|
1275
1328
|
mustQuery.push(
|
|
1276
1329
|
{
|
|
1277
1330
|
bool: {
|
|
@@ -1280,7 +1333,7 @@ export async function summaryList( req, res ) {
|
|
|
1280
1333
|
},
|
|
1281
1334
|
},
|
|
1282
1335
|
);
|
|
1283
|
-
} else {
|
|
1336
|
+
} else { // If-else condition: use 'terms' filter to match records by Compliance Score / Tango Score
|
|
1284
1337
|
const values = Array.isArray( inputData.complianceScore ) ?
|
|
1285
1338
|
inputData.complianceScore.map( Number ) :
|
|
1286
1339
|
[ Number( inputData.complianceScore ) ];
|
|
@@ -1293,6 +1346,7 @@ export async function summaryList( req, res ) {
|
|
|
1293
1346
|
}
|
|
1294
1347
|
}
|
|
1295
1348
|
|
|
1349
|
+
// filter by category if key exist allow "fake, genuine" else throw valid category
|
|
1296
1350
|
if ( inputData?.category ) {
|
|
1297
1351
|
switch ( inputData?.category ) {
|
|
1298
1352
|
case 'fake':
|
|
@@ -2118,6 +2172,15 @@ export async function summaryCard( req, res ) {
|
|
|
2118
2172
|
},
|
|
2119
2173
|
} );
|
|
2120
2174
|
}
|
|
2175
|
+
|
|
2176
|
+
if ( inputData.nearAddition && inputData.nearAddition!='' && inputData.type ==='physical' ) {
|
|
2177
|
+
filter.push( {
|
|
2178
|
+
'term': {
|
|
2179
|
+
'nearAddition.keyword': inputData.nearAddition,
|
|
2180
|
+
},
|
|
2181
|
+
|
|
2182
|
+
} );
|
|
2183
|
+
}
|
|
2121
2184
|
let mustQuery = [
|
|
2122
2185
|
...filter,
|
|
2123
2186
|
];
|
|
@@ -2386,7 +2449,7 @@ export async function emailAlertLog( req, res ) {
|
|
|
2386
2449
|
inputData.clientId = req.user.userType == 'tango'? inputData?.clientId : req?.user?.clientId;
|
|
2387
2450
|
// get openserach name form the env file
|
|
2388
2451
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
2389
|
-
|
|
2452
|
+
const sortBy = inputData?.sortBy ? [ 'complianceScore', 'humanScore', 'trustScore', 'updatedAt', 'createdAt' ].includes( inputData.sortBy ) ? inputData.sortBy :`${inputData.sortBy}.keyword` : 'RMName.keyword';
|
|
2390
2453
|
|
|
2391
2454
|
let filter =[
|
|
2392
2455
|
{
|
|
@@ -2399,7 +2462,7 @@ export async function emailAlertLog( req, res ) {
|
|
|
2399
2462
|
},
|
|
2400
2463
|
];
|
|
2401
2464
|
let sort =[
|
|
2402
|
-
{ [
|
|
2465
|
+
{ [sortBy]: { order: inputData.sortOrder === 1 ? 'asc' : 'desc' } },
|
|
2403
2466
|
];
|
|
2404
2467
|
if ( inputData.RmList && inputData.RmList?.lenght > 0 ) {
|
|
2405
2468
|
filter.push( {
|
|
@@ -2416,19 +2479,86 @@ export async function emailAlertLog( req, res ) {
|
|
|
2416
2479
|
},
|
|
2417
2480
|
} );
|
|
2418
2481
|
}
|
|
2482
|
+
let search = {
|
|
2483
|
+
must: filter,
|
|
2484
|
+
};
|
|
2485
|
+
if ( inputData.searchValue && inputData.searchValue !== '' ) {
|
|
2486
|
+
search ={
|
|
2487
|
+
'must': filter,
|
|
2488
|
+
'should': [
|
|
2489
|
+
{
|
|
2490
|
+
'wildcard': {
|
|
2491
|
+
'RMEmail.keyword': {
|
|
2492
|
+
'value': `*${inputData.searchValue}*`,
|
|
2493
|
+
},
|
|
2494
|
+
},
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
'wildcard': {
|
|
2498
|
+
'RMName.keyword': {
|
|
2499
|
+
'value': `*${inputData.searchValue}*`,
|
|
2500
|
+
},
|
|
2501
|
+
},
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
'wildcard': {
|
|
2505
|
+
'cluster.keyword': {
|
|
2506
|
+
'value': `*${inputData.searchValue}*`,
|
|
2507
|
+
},
|
|
2508
|
+
},
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
'wildcard': {
|
|
2512
|
+
'optumId.keyword': {
|
|
2513
|
+
'value': `*${inputData.searchValue}*`,
|
|
2514
|
+
},
|
|
2515
|
+
},
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
'wildcard': {
|
|
2519
|
+
'queueId.keyword': {
|
|
2520
|
+
'value': `*${inputData.searchValue}*`,
|
|
2521
|
+
},
|
|
2522
|
+
},
|
|
2523
|
+
},
|
|
2524
|
+
{
|
|
2525
|
+
'wildcard': {
|
|
2526
|
+
'storeName.keyword': {
|
|
2527
|
+
'value': `*${inputData.searchValue}*`,
|
|
2528
|
+
},
|
|
2529
|
+
},
|
|
2530
|
+
},
|
|
2531
|
+
{
|
|
2532
|
+
'wildcard': {
|
|
2533
|
+
'auditedBy.keyword': {
|
|
2534
|
+
'value': `*${inputData.searchValue}*`,
|
|
2535
|
+
},
|
|
2536
|
+
},
|
|
2537
|
+
},
|
|
2538
|
+
{
|
|
2539
|
+
'wildcard': {
|
|
2540
|
+
'optomEmailId.keyword': {
|
|
2541
|
+
'value': `*${inputData.searchValue}*`,
|
|
2542
|
+
},
|
|
2543
|
+
},
|
|
2544
|
+
},
|
|
2545
|
+
],
|
|
2546
|
+
'minimum_should_match': 1,
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2419
2549
|
const getQuery = {
|
|
2420
2550
|
'size': inputData.isExport == true? 50000 : inputData?.limit || 10,
|
|
2421
2551
|
'from': offset || 0,
|
|
2422
2552
|
'query': {
|
|
2423
|
-
'bool':
|
|
2424
|
-
'must': filter,
|
|
2425
|
-
},
|
|
2553
|
+
'bool': search,
|
|
2426
2554
|
},
|
|
2427
2555
|
'sort': sort,
|
|
2428
2556
|
};
|
|
2429
2557
|
|
|
2558
|
+
|
|
2430
2559
|
// get query result from the open saerch
|
|
2431
2560
|
const getData= await getOpenSearchData( openSearch.eyeTestEmailAlert, getQuery );
|
|
2561
|
+
const count = getData?.body?.hits?.total?.value;
|
|
2432
2562
|
// throw error if got null or empty array
|
|
2433
2563
|
if ( !getData || getData?.body?.hits?.hits?.length == 0 ) {
|
|
2434
2564
|
return res.sendError( 'No records found', 204 );
|
|
@@ -2436,14 +2566,18 @@ export async function emailAlertLog( req, res ) {
|
|
|
2436
2566
|
let response =[];
|
|
2437
2567
|
for ( let item of getData?.body?.hits?.hits ) {
|
|
2438
2568
|
response.push( {
|
|
2439
|
-
RMEmail: item?._source?.RMEmail,
|
|
2569
|
+
RMEmail: item?._source?.RMEmail || '',
|
|
2570
|
+
RMName: item?._source?.RMName || '',
|
|
2440
2571
|
cluster: item?._source?.cluster,
|
|
2441
2572
|
optumId: item?._source?.optumId,
|
|
2573
|
+
optomEmailId: item?._source?.optomEmailId,
|
|
2442
2574
|
storeName: item?._source?.storeName,
|
|
2443
2575
|
complianceScore: item?._source?.complianceScore,
|
|
2576
|
+
humanScore: item?._source?.humanScore,
|
|
2444
2577
|
trustScore: item?._source?.trustScore,
|
|
2445
2578
|
auditedId: item?._source?.auditedId,
|
|
2446
2579
|
queueId: item?._source?.queueId,
|
|
2580
|
+
auditedBy: item?._source?.auditedBy,
|
|
2447
2581
|
|
|
2448
2582
|
} );
|
|
2449
2583
|
}
|
|
@@ -2477,7 +2611,7 @@ export async function emailAlertLog( req, res ) {
|
|
|
2477
2611
|
|
|
2478
2612
|
|
|
2479
2613
|
logger.info( { inputData: inputData, getData: response } );
|
|
2480
|
-
return res.sendSuccess( { result: response, count:
|
|
2614
|
+
return res.sendSuccess( { result: response, count: count } );
|
|
2481
2615
|
} catch ( error ) {
|
|
2482
2616
|
const err = error.message || 'Internal Server Error';
|
|
2483
2617
|
logger.error( { message: error, data: req.body, function: 'eyetestAudit-controller-emailAlertLog' } );
|
|
@@ -2637,10 +2771,6 @@ export async function getEmailConfig( req, res ) {
|
|
|
2637
2771
|
configureType: 'email',
|
|
2638
2772
|
};
|
|
2639
2773
|
const getOne = await findOneEyeTestConfig( query, { complianceThreshold: 1, _id: 1 } );
|
|
2640
|
-
logger.info( { getOne: getOne, message: 'getEmailConfig' } );
|
|
2641
|
-
// if ( !getOne?.complianceThreshold ) {
|
|
2642
|
-
// return res.sendError( 'No data found', 204 );
|
|
2643
|
-
// }
|
|
2644
2774
|
if ( !getOne ) {
|
|
2645
2775
|
return res.sendError( ' No data found', 204 );
|
|
2646
2776
|
}
|
|
@@ -64,6 +64,7 @@ export const saveSchema = joi.object( {
|
|
|
64
64
|
spokenLanguage: joi.string().optional(),
|
|
65
65
|
cluster: joi.string().required().allow( '' ),
|
|
66
66
|
RMEmail: joi.string().required().allow( '' ),
|
|
67
|
+
isReSend: joi.boolean().optional(),
|
|
67
68
|
} );
|
|
68
69
|
|
|
69
70
|
export const saveValid = {
|
|
@@ -156,7 +157,7 @@ export const summaryListSchema = joi.object( {
|
|
|
156
157
|
).optional(),
|
|
157
158
|
storeId: joi.array().items( joi.string().required() ).required(),
|
|
158
159
|
complianceScore: joi.array().items( joi.string().required() ).optional(),
|
|
159
|
-
nearAddition: joi.string().optional().allow( '' ),
|
|
160
|
+
nearAddition: joi.string().optional().allow( '', 'true', 'false' ),
|
|
160
161
|
isExport: joi.boolean().optional(),
|
|
161
162
|
searchValue: joi.string().optional().allow( '' ),
|
|
162
163
|
sortBy: joi.string().optional(),
|
|
@@ -234,7 +235,7 @@ export const summaryCardSchema = joi.object( {
|
|
|
234
235
|
).optional(),
|
|
235
236
|
storeId: joi.array().items( joi.string().required() ).required(),
|
|
236
237
|
complianceScore: joi.array().items( joi.string().required() ).optional(),
|
|
237
|
-
nearAddition: joi.string().optional().allow( '' ),
|
|
238
|
+
nearAddition: joi.string().optional().allow( '', 'true', 'false' ),
|
|
238
239
|
category: joi.string().optional() .valid( 'fake', 'genuine' ),
|
|
239
240
|
} );
|
|
240
241
|
|
|
@@ -279,12 +280,8 @@ export const emailAlertLogSchema = joi.object( {
|
|
|
279
280
|
|
|
280
281
|
return value;
|
|
281
282
|
} ),
|
|
282
|
-
type: joi.string().required().messages( {
|
|
283
|
-
'any.required': 'Type is required',
|
|
284
|
-
'string.empty': 'Type cannot be empty',
|
|
285
|
-
} ),
|
|
286
283
|
RMList: joi.array().items(
|
|
287
|
-
joi.string().
|
|
284
|
+
joi.string().required(),
|
|
288
285
|
).optional(),
|
|
289
286
|
clientId: joi.string().when( '$userType', {
|
|
290
287
|
is: 'tango',
|
|
@@ -294,7 +291,7 @@ export const emailAlertLogSchema = joi.object( {
|
|
|
294
291
|
} ),
|
|
295
292
|
otherwise: joi.optional(),
|
|
296
293
|
} ),
|
|
297
|
-
cluster: joi.array().items( joi.string().required() ).
|
|
294
|
+
cluster: joi.array().items( joi.string().required() ).optional(),
|
|
298
295
|
isExport: joi.boolean().optional(),
|
|
299
296
|
searchValue: joi.string().optional().allow( '' ),
|
|
300
297
|
sortBy: joi.string().optional(),
|
|
@@ -413,6 +410,7 @@ export const getEmailConfigSchema = joi.object( {
|
|
|
413
410
|
} ),
|
|
414
411
|
otherwise: joi.optional(),
|
|
415
412
|
} ),
|
|
413
|
+
|
|
416
414
|
} );
|
|
417
415
|
|
|
418
416
|
export const getEmailConfigValid = {
|
|
@@ -3,14 +3,14 @@ import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
|
3
3
|
import { accessVerification } from 'tango-app-api-middleware';
|
|
4
4
|
import { cancelValid, clusterListValid, emailAlertLogValid, getEmailConfigValid, getFileHistoryValid, getFileValid, getListValid, getUserConfigValid, rmListValid, saveValid, summaryCardValid, summaryListValid, updateEmailConfigValid, updateUserConfigValid, userAuditedDataValid, viewFileValid } from '../dtos/eyeTestAudit.dtos.js';
|
|
5
5
|
import { addUserConfig, cancel, clusterList, emailAlertLog, getEmailConfig, getFile, getFileHistory, getList, getUserConfig, rmList, save, summaryCard, summaryList, updateEmailConfig, updateEmailConfig1, updateUserConfig, userAuditedData, viewFile } from '../controllers/eyeTestAudit.controllers.js';
|
|
6
|
-
import { isSuperAdmin, roleValidation } from '../validation/eyeTest.validation.js';
|
|
6
|
+
import { isSuperAdmin, roleValidation, sendAlert } from '../validation/eyeTest.validation.js';
|
|
7
7
|
|
|
8
8
|
export const eyeTestAuditRouter = Router();
|
|
9
9
|
|
|
10
10
|
eyeTestAuditRouter.post( '/get-list', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getListValid ), getList );
|
|
11
11
|
eyeTestAuditRouter.get( '/view-file/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( viewFileValid ), viewFile );
|
|
12
12
|
eyeTestAuditRouter.get( '/get-file/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getFileValid ), getFile );
|
|
13
|
-
eyeTestAuditRouter.post( '/save', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( saveValid ), save );
|
|
13
|
+
eyeTestAuditRouter.post( '/save', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( saveValid ), sendAlert, save );
|
|
14
14
|
eyeTestAuditRouter.post( '/cancel', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( cancelValid ), cancel );
|
|
15
15
|
eyeTestAuditRouter.get( '/get-file-history/:id/:type', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getFileHistoryValid ), getFileHistory );
|
|
16
16
|
eyeTestAuditRouter.get( '/user-audited-data/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( userAuditedDataValid ), userAuditedData );
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { logger } from 'tango-app-api-middleware';
|
|
1
|
+
import { getOpenSearchById, logger } from 'tango-app-api-middleware';
|
|
2
|
+
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
|
|
2
3
|
|
|
3
4
|
export async function roleValidation( req, res, next ) {
|
|
4
5
|
try {
|
|
@@ -39,3 +40,118 @@ export async function isSuperAdmin( req, res, next ) {
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
export async function sendAlert( req, res, next ) {
|
|
44
|
+
try {
|
|
45
|
+
const inputData = req.body;
|
|
46
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
47
|
+
if ( inputData.auditStatus == 'ReAudited' && inputData.type == 'physical' && inputData?.isReSend == undefined ) {
|
|
48
|
+
const getOne = await getOpenSearchById( openSearch.EyeTestInput, inputData.id );
|
|
49
|
+
const id = `${inputData.id}_${getOne?.body?._source?.optumId}_${inputData.fileId}`;
|
|
50
|
+
const getId = await getOpenSearchById( openSearch.eyeTestEmailAlert, id );
|
|
51
|
+
const inputUpdatedData = getId?.body;
|
|
52
|
+
if ( inputUpdatedData ) {
|
|
53
|
+
return res.sendError( 'Do you want resend mail?', 202 );
|
|
54
|
+
} else {
|
|
55
|
+
return next();
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
return next();
|
|
59
|
+
}
|
|
60
|
+
} catch ( error ) {
|
|
61
|
+
const err = error.message;
|
|
62
|
+
return res.sendError( err, 500 );
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Send HTML Email using AWS SES
|
|
68
|
+
* @param {Object} params
|
|
69
|
+
* @param {string} params.toEmail - Recipient email address
|
|
70
|
+
* @param {string} params.userName - Recipient name
|
|
71
|
+
* @param {number} params.compliancePercentage - Compliance score
|
|
72
|
+
*/
|
|
73
|
+
export async function sendComplianceEmail( {
|
|
74
|
+
toEmail,
|
|
75
|
+
fromEmail, // sender email added here
|
|
76
|
+
userName,
|
|
77
|
+
compliancePercentage,
|
|
78
|
+
} ) {
|
|
79
|
+
// Initialize AWS SES client
|
|
80
|
+
const sesClient = new SESClient( {
|
|
81
|
+
region: 'ap-south-1', // Change to your AWS region
|
|
82
|
+
credentials: {
|
|
83
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
84
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
85
|
+
},
|
|
86
|
+
} );
|
|
87
|
+
const htmlBody = `
|
|
88
|
+
<!DOCTYPE html>
|
|
89
|
+
<html>
|
|
90
|
+
<head>
|
|
91
|
+
<meta charset="UTF-8" />
|
|
92
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
93
|
+
<title>Non Compliance Report</title>
|
|
94
|
+
<style>
|
|
95
|
+
body { font-family: 'Inter', sans-serif; margin: 0; padding: 24px; background: #f9fafb; color: #121a26; }
|
|
96
|
+
.container { max-width: 700px; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
97
|
+
.title { font-size: 24px; font-weight: 700; color: #121a26; }
|
|
98
|
+
.subtitle { font-size: 16px; color: #384860; line-height: 1.5; margin-top: 8px; }
|
|
99
|
+
.table { width: 100%; border-collapse: collapse; margin-top: 16px; }
|
|
100
|
+
.table th, .table td { text-align: left; padding: 12px 16px; border-bottom: 1px solid #eaeef0; color: #667085; }
|
|
101
|
+
.footer { margin-top: 32px; font-size: 14px; color: #384860; }
|
|
102
|
+
.compliance { font-size: 18px; font-weight: 700; color: #f04438; margin-top: 16px; }
|
|
103
|
+
</style>
|
|
104
|
+
</head>
|
|
105
|
+
<body>
|
|
106
|
+
<div class="container">
|
|
107
|
+
<p class="title">Non Compliance on Eye Test Procedures</p>
|
|
108
|
+
<p class="subtitle">
|
|
109
|
+
Dear ${userName},<br><br>
|
|
110
|
+
Please ensure that all conducted eye tests meet the required compliance standards.<br>
|
|
111
|
+
Your prompt attention to this matter will help us maintain the highest standards and avoid any discrepancies.<br>
|
|
112
|
+
Please take a look at the below findings.
|
|
113
|
+
</p>
|
|
114
|
+
|
|
115
|
+
<table class="table">
|
|
116
|
+
<thead>
|
|
117
|
+
<tr>
|
|
118
|
+
<th>RM Name</th>
|
|
119
|
+
<th>Comments</th>
|
|
120
|
+
</tr>
|
|
121
|
+
</thead>
|
|
122
|
+
<tbody>
|
|
123
|
+
<tr><td>JCC (Jackson Cross Cylinder)</td><td>Lorem ipsum dolor sit amet consectetur.</td></tr>
|
|
124
|
+
<tr><td>DuoChrome Test</td><td>Lorem ipsum dolor sit amet consectetur.</td></tr>
|
|
125
|
+
<tr><td>Prescription Verification</td><td>Lorem ipsum dolor sit amet consectetur.</td></tr>
|
|
126
|
+
<tr><td>Distance-VA-Check</td><td>Lorem ipsum dolor sit amet consectetur.</td></tr>
|
|
127
|
+
</tbody>
|
|
128
|
+
</table>
|
|
129
|
+
|
|
130
|
+
<p class="compliance">Compliance Percentage: ${compliancePercentage}%</p>
|
|
131
|
+
<p class="footer">Regards,<br>Team Tango</p>
|
|
132
|
+
</div>
|
|
133
|
+
</body>
|
|
134
|
+
</html>`;
|
|
135
|
+
|
|
136
|
+
const params = {
|
|
137
|
+
Source: fromEmail, // sender email (must be verified in SES)
|
|
138
|
+
Destination: {
|
|
139
|
+
ToAddresses: [ toEmail ],
|
|
140
|
+
},
|
|
141
|
+
Message: {
|
|
142
|
+
Subject: { Data: 'Non Compliance on Eye Test Procedures' },
|
|
143
|
+
Body: { Html: { Data: htmlBody } },
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const response = await sesClient.send( new SendEmailCommand( params ) );
|
|
149
|
+
// console.log( 'Email sent successfully:', response.MessageId );
|
|
150
|
+
return response;
|
|
151
|
+
} catch ( error ) {
|
|
152
|
+
// console.error( 'Error sending email:', error );
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|