tango-app-api-infra 3.9.5-vms.75 → 3.9.5-vms.77

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,11 +1,11 @@
1
1
  {
2
2
  "name": "tango-app-api-infra",
3
- "version": "3.9.5-vms.75",
3
+ "version": "3.9.5-vms.77",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "start": "nodemon --exec \"eslint --fix . && node app.js\""
8
+ "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
9
  },
10
10
  "engines": {
11
11
  "node": ">=18.10.0"
@@ -86,6 +86,10 @@ export async function createinternalTicket( req, res ) {
86
86
  mappingInfo: [],
87
87
  };
88
88
  const id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
89
+ let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
90
+ if ( getExistingOne?.body?._source ) {
91
+ return res.sendError( 'Ticket Already Exists', 500 );
92
+ }
89
93
  const insertResult = await insertWithId( openSearch.footfallDirectory, id, record );
90
94
  if ( insertResult && insertResult.statusCode === 201 ) {
91
95
  return res.sendSuccess( 'Ticket raised successfully' );
@@ -380,11 +384,15 @@ export async function tangoReviewTicket( req, res ) {
380
384
  // return;
381
385
 
382
386
  let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
383
- if ( inputData.ticketType === 'internal' ) {
387
+ let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
388
+ console.log( '🚀 ~ tangoReviewTicket ~ getExistingOne:', getExistingOne );
389
+ if ( inputData.ticketType === 'internal'&&!getExistingOne?.body?._source ) {
384
390
  id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
385
391
  }
386
392
 
393
+
387
394
  const insertResult = await updateOpenSearchData( openSearch.footfallDirectory, id, { doc: record } );
395
+ console.log( insertResult );
388
396
 
389
397
  if ( insertResult && ( insertResult.statusCode === 201 || insertResult.statusCode === 200 ) ) {
390
398
  return res.sendSuccess( 'Ticket closed successfully' );
@@ -392,6 +400,7 @@ export async function tangoReviewTicket( req, res ) {
392
400
  return res.sendError( 'Internal Server Error', 500 );
393
401
  }
394
402
  } catch ( error ) {
403
+ console.log( '🚀 ~ tangoReviewTicket ~ error:', error );
395
404
  const err = error.message || 'Internal Server Error';
396
405
  logger.error( { error: error, funtion: 'tangoReviewTicket' } );
397
406
  return res.sendError( err, 500 );
@@ -1065,7 +1074,7 @@ export async function ticketList( req, res ) {
1065
1074
  if ( req.user.userType === 'tango' ) {
1066
1075
  searchQuery.query.bool.must.push( {
1067
1076
  terms: {
1068
- 'status': Array.isArray( inputData?.status ) ?
1077
+ 'status.keyword': Array.isArray( inputData?.status ) ?
1069
1078
  inputData?.status :
1070
1079
  [ inputData?.status ],
1071
1080
  },
@@ -1162,7 +1171,9 @@ export async function ticketList( req, res ) {
1162
1171
  }
1163
1172
 
1164
1173
  if ( inputData.filterByStatus && inputData.filterByStatus!=='' ) {
1165
- if ( req?.user?.userType == 'tango' ) {
1174
+ inputData.filterByStatus = inputData?.filterByStatus?.split( ',' );
1175
+
1176
+ if ( req?.user?.userType === 'tango' ) {
1166
1177
  {
1167
1178
  switch ( inputData?.tangoType ) {
1168
1179
  case 'store':
@@ -1191,7 +1202,7 @@ export async function ticketList( req, res ) {
1191
1202
  case 'internal':
1192
1203
  searchQuery.query.bool.must.push( {
1193
1204
  'terms': {
1194
- 'status': Array.isArray( inputData?.filterByStatus ) ?
1205
+ 'status.keyword': Array.isArray( inputData?.filterByStatus ) ?
1195
1206
  inputData?.filterByStatus :
1196
1207
  [ inputData?.filterByStatus ],
1197
1208
  },
@@ -1315,6 +1326,285 @@ export async function ticketList( req, res ) {
1315
1326
  }
1316
1327
  }
1317
1328
 
1329
+ if ( inputData?.filterByReviewer && inputData?.filterByReviewer !== '' ) {
1330
+ let percQuery = null;
1331
+ const value = inputData.filterByReviewer;
1332
+
1333
+ // Helper function: remove trailing '%' and convert to number
1334
+ const percValue = ( val ) => {
1335
+ if ( typeof val === 'string' ) {
1336
+ return parseFloat( val.replace( '%', '' ).trim() );
1337
+ }
1338
+ return parseFloat( val );
1339
+ };
1340
+
1341
+ // Example filter values: "<90", "<=90", ">=90", "50 to 90"
1342
+ if ( /^<=?\d+$/.test( value ) ) {
1343
+ // "<90" or "<=90"
1344
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1345
+ const op = value.includes( '=' ) ? 'lte' : 'lt';
1346
+ percQuery = {
1347
+ script: {
1348
+ script: {
1349
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
1350
+ params: { num },
1351
+ },
1352
+ },
1353
+ };
1354
+ } else if ( /^>=?\d+$/.test( value ) ) {
1355
+ // ">=90"
1356
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1357
+ const op = value.includes( '=' ) ? 'gte' : 'gt';
1358
+ percQuery = {
1359
+ script: {
1360
+ script: {
1361
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
1362
+ params: { num },
1363
+ },
1364
+ },
1365
+ };
1366
+ } else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
1367
+ // "50 to 90"
1368
+ const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
1369
+ const from = percValue( match[1] );
1370
+ const to = percValue( match[2] );
1371
+ percQuery = {
1372
+ script: {
1373
+ script: {
1374
+ source:
1375
+ `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`,
1376
+ params: { from, to },
1377
+ },
1378
+ },
1379
+ };
1380
+ }
1381
+ // fallback: treat as exact match (e.g., "90")
1382
+ if ( !percQuery && /^\d+$/.test( value ) ) {
1383
+ percQuery = {
1384
+ script: {
1385
+ script: {
1386
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
1387
+ params: { num: percValue( value ) },
1388
+ },
1389
+ },
1390
+ };
1391
+ }
1392
+
1393
+ if ( percQuery ) {
1394
+ searchQuery.query.bool.must.push( {
1395
+ nested: {
1396
+ path: 'mappingInfo',
1397
+ query: {
1398
+ bool: {
1399
+ must: [
1400
+ { term: { 'mappingInfo.type': 'review' } },
1401
+ percQuery,
1402
+ ],
1403
+ },
1404
+ },
1405
+ },
1406
+ } );
1407
+ }
1408
+ }
1409
+
1410
+ if ( inputData?.filterByApprover && inputData?.filterByApprover !== '' ) {
1411
+ let percQuery = null;
1412
+ const value = inputData.filterByApprover;
1413
+
1414
+ // Helper function: remove trailing '%' and convert to number
1415
+ const percValue = ( val ) => {
1416
+ if ( typeof val === 'string' ) {
1417
+ return parseFloat( val.replace( '%', '' ).trim() );
1418
+ }
1419
+ return parseFloat( val );
1420
+ };
1421
+
1422
+ // Example filter values: "<90", "<=90", ">=90", "50 to 90"
1423
+ if ( /^<=?\d+$/.test( value ) ) {
1424
+ // "<90" or "<=90"
1425
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1426
+ const op = value.includes( '=' ) ? 'lte' : 'lt';
1427
+ percQuery = {
1428
+ script: {
1429
+ script: {
1430
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
1431
+ params: { num },
1432
+ },
1433
+ },
1434
+ };
1435
+ } else if ( /^>=?\d+$/.test( value ) ) {
1436
+ // ">=90"
1437
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1438
+ const op = value.includes( '=' ) ? 'gte' : 'gt';
1439
+ percQuery = {
1440
+ script: {
1441
+ script: {
1442
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
1443
+ params: { num },
1444
+ },
1445
+ },
1446
+ };
1447
+ } else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
1448
+ // "50 to 90"
1449
+ const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
1450
+ const from = percValue( match[1] );
1451
+ const to = percValue( match[2] );
1452
+ percQuery = {
1453
+ script: {
1454
+ script: {
1455
+ source:
1456
+ `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`,
1457
+ params: { from, to },
1458
+ },
1459
+ },
1460
+ };
1461
+ }
1462
+ // fallback: treat as exact match (e.g., "90")
1463
+ if ( !percQuery && /^\d+$/.test( value ) ) {
1464
+ percQuery = {
1465
+ script: {
1466
+ script: {
1467
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
1468
+ params: { num: percValue( value ) },
1469
+ },
1470
+ },
1471
+ };
1472
+ }
1473
+
1474
+ if ( percQuery ) {
1475
+ searchQuery.query.bool.must.push( {
1476
+ nested: {
1477
+ path: 'mappingInfo',
1478
+ query: {
1479
+ bool: {
1480
+ must: [
1481
+ { term: { 'mappingInfo.type': 'approve' } },
1482
+ percQuery,
1483
+ ],
1484
+ },
1485
+ },
1486
+ },
1487
+ } );
1488
+ }
1489
+ }
1490
+
1491
+ if ( inputData?.filterByTango && inputData?.filterByTango !== '' ) {
1492
+ let percQuery = null;
1493
+ const value = inputData.filterByTango;
1494
+
1495
+ // Helper function: remove trailing '%' and convert to number
1496
+ const percValue = ( val ) => {
1497
+ if ( typeof val === 'string' ) {
1498
+ return parseFloat( val.replace( '%', '' ).trim() );
1499
+ }
1500
+ return parseFloat( val );
1501
+ };
1502
+
1503
+ // Example filter values: "<90", "<=90", ">=90", "50 to 90"
1504
+ if ( /^<=?\d+$/.test( value ) ) {
1505
+ // "<90" or "<=90"
1506
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1507
+ const op = value.includes( '=' ) ? 'lte' : 'lt';
1508
+ percQuery = {
1509
+ script: {
1510
+ script: {
1511
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
1512
+ params: { num },
1513
+ },
1514
+ },
1515
+ };
1516
+ } else if ( /^>=?\d+$/.test( value ) ) {
1517
+ // ">=90"
1518
+ const num = percValue( value.replace( /[^\d]/g, '' ) );
1519
+ const op = value.includes( '=' ) ? 'gte' : 'gt';
1520
+ percQuery = {
1521
+ script: {
1522
+ script: {
1523
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
1524
+ params: { num },
1525
+ },
1526
+ },
1527
+ };
1528
+ } else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
1529
+ // "50 to 90"
1530
+ const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
1531
+ const from = percValue( match[1] );
1532
+ const to = percValue( match[2] );
1533
+ percQuery = {
1534
+ script: {
1535
+ script: {
1536
+ source:
1537
+ `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`,
1538
+ params: { from, to },
1539
+ },
1540
+ },
1541
+ };
1542
+ }
1543
+ // fallback: treat as exact match (e.g., "90")
1544
+ if ( !percQuery && /^\d+$/.test( value ) ) {
1545
+ percQuery = {
1546
+ script: {
1547
+ script: {
1548
+ source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
1549
+ params: { num: percValue( value ) },
1550
+ },
1551
+ },
1552
+ };
1553
+ }
1554
+
1555
+ if ( percQuery ) {
1556
+ searchQuery.query.bool.must.push( {
1557
+ nested: {
1558
+ path: 'mappingInfo',
1559
+ query: {
1560
+ bool: {
1561
+ must: [
1562
+ { term: { 'mappingInfo.type': 'tangoreview' } },
1563
+ percQuery,
1564
+ ],
1565
+ },
1566
+ },
1567
+ },
1568
+ } );
1569
+ }
1570
+ }
1571
+
1572
+ if ( inputData?.filterByReviewedBy && inputData?.filterByReviewedBy !== '' ) {
1573
+ inputData.filterByReviewedBy = inputData?.filterByReviewedBy?.split( ',' );
1574
+ searchQuery.query.bool.must.push( {
1575
+ nested: {
1576
+ path: 'mappingInfo',
1577
+ query: {
1578
+ bool: {
1579
+ must: [
1580
+ { term: { 'mappingInfo.type': 'review' } },
1581
+ { terms: { 'mappingInfo.createdByEmail': inputData?.filterByReviewedBy } },
1582
+
1583
+ ],
1584
+ },
1585
+ },
1586
+ },
1587
+ } );
1588
+ }
1589
+
1590
+ if ( inputData?.fileterByApprovedBy && inputData?.fileterByApprovedBy !== '' ) {
1591
+ inputData.fileterByApprovedBy = inputData?.fileterByApprovedBy?.split( ',' );
1592
+ searchQuery.query.bool.must.push( {
1593
+ nested: {
1594
+ path: 'mappingInfo',
1595
+ query: {
1596
+ bool: {
1597
+ must: [
1598
+ { term: { 'mappingInfo.type': 'approve' } },
1599
+ { terms: { 'mappingInfo.createdByEmail': inputData?.fileterByApprovedBy } },
1600
+
1601
+ ],
1602
+ },
1603
+ },
1604
+ },
1605
+ } );
1606
+ }
1607
+
1318
1608
  if ( req?.user?.userType === 'tango' && inputData.tangoType !== 'internal' ) {
1319
1609
  searchQuery.query.bool.must.push(
1320
1610
  {
@@ -1428,7 +1718,7 @@ export async function ticketList( req, res ) {
1428
1718
  // You can add more filters as needed
1429
1719
  const searchResult = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
1430
1720
  const count = searchResult?.body?.hits?.total?.value || 0;
1431
-
1721
+ logger.info( { searchResult } );
1432
1722
  if ( count === 0 ) {
1433
1723
  return res.sendError( 'no data found', 204 );
1434
1724
  }
@@ -158,12 +158,12 @@ export const ticketListSchema = Joi.object().keys( {
158
158
  sortOrder: Joi.number().valid( -1, 1 ).optional(),
159
159
  tangoType: Joi.string().valid( 'store', 'internal', '' ).optional(),
160
160
  permissionType: Joi.string().valid( 'review', 'approve' ).optional(),
161
- filterByStatus: Joi.array().items( Joi.string().required() ).optional(),
162
- filterByReviewer: Joi.string().optional(),
163
- filterByApprover: Joi.string().optional(),
164
- filterByTango: Joi.string().optional(),
165
- filterByReviewedBy: Joi.array().items( Joi.string().required() ).optional(),
166
- fileterByApprovedBy: Joi.array().items( Joi.string().required() ).optional(),
161
+ filterByStatus: Joi.string().optional().allow( '' ),
162
+ filterByReviewer: Joi.string().optional().allow( '' ),
163
+ filterByApprover: Joi.string().optional().allow( '' ),
164
+ filterByTango: Joi.string().optional().allow( '' ),
165
+ filterByReviewedBy: Joi.string().optional().allow( '' ),
166
+ fileterByApprovedBy: Joi.string().optional().allow( '' ),
167
167
  fromDate: Joi.string()
168
168
  .pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
169
169
  .required()