tango-app-api-infra 3.9.5-vms.53 → 3.9.5-vms.55

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-infra",
3
- "version": "3.9.5-vms.53",
3
+ "version": "3.9.5-vms.55",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -856,6 +856,7 @@ export async function ticketList( req, res ) {
856
856
  size: limit, // or use parseInt(req.query.limit) for dynamic
857
857
  from: offset, // or use parseInt(req.query.offset) for dynamic
858
858
  sort: [ { 'createdAt': { order: 'desc' } } ],
859
+
859
860
  query: {
860
861
  bool: {
861
862
  must: [
@@ -881,7 +882,24 @@ export async function ticketList( req, res ) {
881
882
  },
882
883
  };
883
884
 
885
+ if ( inputData.sortBy ) {
886
+ let sortOrder = inputData.sortOrder === 1? 'asc': 'desc';
887
+
888
+ // Remove default sort so we don't duplicate/conflict
889
+ // INSERT_YOUR_CODE
890
+ // If sortBy is present, check if the field needs ".keyword" (for string fields like storeName, storeId, ticketId)
891
+ // This avoids OpenSearch errors about sorting on text fields.
892
+ const stringKeywordFields = [ 'storeName', 'storeId', 'ticketId', 'status', 'type', 'clientId' ];
893
+ let sortField = inputData.sortBy == 'footfall'? 'footfallCount' :inputData.sortBy == 'issueDate'?'dateString':inputData.sortBy;
894
+ if ( stringKeywordFields.includes( sortField ) ) {
895
+ sortField = `${sortField}.keyword`;
896
+ }
884
897
 
898
+ // Remove default sort so we don't duplicate/conflict
899
+ searchQuery.sort = [
900
+ { [sortField]: { order: sortOrder } },
901
+ ];
902
+ }
885
903
  // Example: Filtering by storeId if present in the query
886
904
  if ( inputData.storeId ) {
887
905
  inputData.storeId = inputData?.storeId?.split( ',' );
@@ -963,7 +981,7 @@ export async function ticketList( req, res ) {
963
981
  }
964
982
  // You can add more filters as needed
965
983
  const searchResult = await getOpenSearchData( openSearch.footfallDirectory, searchQuery );
966
-
984
+ logger.info( { searchResult } );
967
985
  const count = searchResult?.body?.hits?.total?.value || 0;
968
986
 
969
987
  if ( count === 0 ) {
@@ -1318,10 +1336,153 @@ export async function getTickets( req, res ) {
1318
1336
 
1319
1337
  ) {
1320
1338
  item._source.mappingInfo.revisedDetail = processedRevopSources;
1339
+ const vmsCommentsLogIndex = openSearch.vmsCommentsLog || 'vms-comments-log-dev';
1340
+ let commentsResponse = [];
1341
+ const commentsFilter = [
1342
+ { term: { 'storeId.keyword': item?._source?.storeId } },
1343
+ { term: { dateString: item?._source?.dateString } },
1344
+
1345
+ ];
1346
+
1347
+ const commentsQuery = {
1348
+ size: 10000,
1349
+ sort: [
1350
+ { createdAt: { order: 'desc' } }, // Sort descending by createdAt
1351
+ ],
1352
+ query: {
1353
+ bool: {
1354
+ filter: commentsFilter,
1355
+ },
1356
+ },
1357
+ };
1358
+
1359
+ const commentsRes = await getOpenSearchData( vmsCommentsLogIndex, commentsQuery );
1321
1360
  // If mappingInfo is an array, update revisedDetail for each mappingInfo object
1322
1361
  if ( Array.isArray( item._source.mappingInfo ) ) {
1323
1362
  item._source.mappingInfo.forEach( ( mappingObj ) => {
1324
- if ( Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) && mappingObj.type !== 'tangoreview' ) {
1363
+ if ( mappingObj.status == 'In-Progress' ) {
1364
+ commentsResponse = commentsRes?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
1365
+
1366
+ // Check if duplicate condition exists in commentsResponse
1367
+ const isDuplicate = Array.isArray( commentsResponse ) &&
1368
+ commentsResponse.some( ( c ) => c.category === 'duplicate' );
1369
+
1370
+ // Structure comments output
1371
+ let commentsDetails = [];
1372
+ if ( isDuplicate ) {
1373
+ // Duplicate case - check from commentsResponse
1374
+ // Collect for each type (tagging, review, approve)
1375
+ const types = [ 'tagging', 'review', 'approve' ];
1376
+ commentsDetails = types.map( ( typeValue ) => {
1377
+ // parent value from original comment structure
1378
+ let parent = null;
1379
+ // Get all comments of this type (no filter by category)
1380
+ let comms = commentsResponse
1381
+ .filter( ( c ) => c.type === typeValue )
1382
+ .map( ( c ) => {
1383
+ if ( typeValue === 'tagging' ) {
1384
+ parent = c.parent;
1385
+ return {
1386
+ createdByEmail: c.createdByEmail,
1387
+ createdByUserName: c.createdByUserName,
1388
+ createdByRole: c.createdByRole,
1389
+ message: c.message,
1390
+ };
1391
+ }
1392
+ if ( typeValue === 'review' || typeValue === 'approve' ) {
1393
+ return {
1394
+ parent: c.parent,
1395
+ category: c.category,
1396
+ taggedImages: Array.isArray( c.taggedImages ) ?
1397
+ c.taggedImages.map( ( img ) => ( {
1398
+ id: img?._source?.id,
1399
+ tempId: img?._source?.tempId,
1400
+ timeRange: img?._source?.timeRange,
1401
+ entryTime: img?._source?.entryTime,
1402
+ exitTime: img?._source?.exitTime,
1403
+ filePath: img?._source?.filePath,
1404
+ isChecked: img?._source?.isChecked,
1405
+ } ) ) :
1406
+ [],
1407
+ createdByEmail: c.createdByEmail,
1408
+ createdByUserName: c.createdByUserName,
1409
+ createdByRole: c.createdByRole,
1410
+ status: c.status,
1411
+ message: c.message,
1412
+ };
1413
+ }
1414
+ return {};
1415
+ } );
1416
+ return {
1417
+ ...( typeValue === 'tagging' ? { category: 'duplicate' } : {} ),
1418
+ type: typeValue,
1419
+ ...( typeValue === 'tagging' ? { parent } : {} ),
1420
+ comments: comms,
1421
+ };
1422
+ } );
1423
+ } else {
1424
+ // For non-duplicate categories
1425
+ // Collect by type/tag (tagging/review/approve) and build similar structure
1426
+ const types = [ 'tagging', 'review', 'approve' ];
1427
+ commentsDetails = types.map( ( typeValue ) => {
1428
+ // parent for these non-duplicate is always null
1429
+ let comms = commentsResponse
1430
+ .filter( ( c ) => c.type === typeValue )
1431
+ .map( ( c ) => {
1432
+ if ( typeValue === 'tagging' ) {
1433
+ return {
1434
+ id: c.id,
1435
+ tempId: c.tempId,
1436
+ timeRange: c.timeRange,
1437
+ entryTime: c.entryTime,
1438
+ exitTime: c.exitTime,
1439
+ filePath: c.filePath,
1440
+ isChecked: c.isChecked,
1441
+ createdAt: c.createdAt,
1442
+ message: c.message,
1443
+ createdByEmail: c.createdByEmail,
1444
+ createdByUserName: c.createdByUserName,
1445
+ createdByRole: c.createdByRole,
1446
+ };
1447
+ }
1448
+ if ( typeValue === 'review' || typeValue === 'approve' ) {
1449
+ return {
1450
+ category: c.category,
1451
+ taggedImages: Array.isArray( c.taggedImages ) ?
1452
+ c.taggedImages.map( ( img ) => ( {
1453
+ id: img?._source?.id,
1454
+ tempId: img?._source?.tempId,
1455
+ timeRange: img?._source?.timeRange,
1456
+ entryTime: img?._source?.entryTime,
1457
+ exitTime: img?._source?.exitTime,
1458
+ filePath: img?._source?.filePath,
1459
+ isChecked: img?._source?.isChecked,
1460
+ } ) ) :
1461
+ [],
1462
+ createdByEmail: c.createdByEmail,
1463
+ createdByUserName: c.createdByUserName,
1464
+ createdByRole: c.createdByRole,
1465
+ status: c.status,
1466
+ message: c.message,
1467
+ };
1468
+ }
1469
+ return {};
1470
+ } );
1471
+ return {
1472
+ ...( typeValue === 'tagging' ? { category: 'duplicate' } : {} ),
1473
+ parent: null,
1474
+ type: typeValue,
1475
+ comments: comms,
1476
+ };
1477
+ } );
1478
+ }
1479
+
1480
+ item._source.commentsDetails = commentsDetails;
1481
+ }
1482
+ if (
1483
+ Object.prototype.hasOwnProperty.call( mappingObj, 'revisedDetail' ) &&
1484
+ mappingObj.type !== 'tangoreview'
1485
+ ) {
1325
1486
  mappingObj.revisedDetail = processedRevopSources;
1326
1487
  }
1327
1488
  } );
@@ -2171,6 +2332,16 @@ export async function openTicketList( req, res ) {
2171
2332
  clientId: Array.isArray( clientId ) ? clientId : [ clientId ],
2172
2333
  },
2173
2334
  },
2335
+ {
2336
+ term: {
2337
+ 'mappingInfo.type': inputData.type,
2338
+ },
2339
+ },
2340
+ {
2341
+ term: {
2342
+ 'mappingInfo.status.keyword': 'Open',
2343
+ },
2344
+ },
2174
2345
  {
2175
2346
  range: {
2176
2347
  dateString: {
@@ -2195,6 +2366,7 @@ export async function openTicketList( req, res ) {
2195
2366
 
2196
2367
  // Assuming getOpenSearchData and openSearch.footfallDirectoryTagging are available
2197
2368
  const result = await getOpenSearchData( openSearch.footfallDirectory, openSearchQuery );
2369
+ logger.info( { result } );
2198
2370
  const getUserlist = result?.body?.hits?.hits?.map( ( hit ) => hit._source ) || [];
2199
2371
  return res.sendSuccess( getUserlist || [] );
2200
2372
  } catch ( error ) {
@@ -509,6 +509,7 @@ export const openTicketListSchema = Joi.object().keys( {
509
509
  clientId: Joi.array().items(
510
510
  Joi.string().required(),
511
511
  ).required(),
512
+ type: Joi.string().required().allow( 'review', 'approve' ),
512
513
 
513
514
 
514
515
  } );
@@ -546,7 +546,7 @@ export async function ticketCreation( req, res, next ) {
546
546
  record.mappingInfo.push( tangoReviewMapping );
547
547
  }
548
548
  }
549
- console.log( req.body, getConfig.footfallDirectoryConfigs.revision );
549
+
550
550
  let checkreview = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'reviewer' && data.isChecked === true );
551
551
  let checkapprove = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'approver' && data.isChecked === true );
552
552
 
@@ -566,14 +566,13 @@ export async function ticketCreation( req, res, next ) {
566
566
  let title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
567
567
  let createdOn = dayjs().format( 'DD MMM YYYY' );
568
568
  let description = `Created on ${createdOn}`;
569
- console.log( '🚀 ~ ticketCreation ~ userData.role:', userData.email );
570
569
  let Data = {
571
570
  storeId: getstoreName.storeId,
572
571
  issueDate: inputData.dateString,
573
572
  };
574
573
 
575
574
  const ticketsFeature = userData?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
576
- console.log( ticketsFeature );
575
+
577
576
  if ( ticketsFeature ) {
578
577
  let notifyuser = await getAssinedStore( userData, req.body.storeId, Data );
579
578
  if ( userData && userData.fcmToken && notifyuser ) {
@@ -1143,7 +1142,7 @@ export async function ticketReview( req, res, next ) {
1143
1142
  record.mappingInfo.push( tangoReviewMapping );
1144
1143
  }
1145
1144
  }
1146
- console.log( req.body, getConfig.footfallDirectoryConfigs.revision );
1145
+
1147
1146
  let checkreview = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'reviewer' && data.isChecked === true );
1148
1147
  let checkapprove = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'approver' && data.isChecked === true );
1149
1148
 
@@ -1163,13 +1162,13 @@ export async function ticketReview( req, res, next ) {
1163
1162
  let title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
1164
1163
  let createdOn = dayjs().format( 'DD MMM YYYY' );
1165
1164
  let description = `Created on ${createdOn}`;
1166
- console.log( '🚀 ~ ticketCreation ~ userData.role:', userData.email );
1165
+
1167
1166
  let Data = {
1168
1167
  storeId: getstoreName.storeId,
1169
1168
  issueDate: inputData.dateString,
1170
1169
  };
1171
1170
  const ticketsFeature = userData?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
1172
- console.log( ticketsFeature );
1171
+
1173
1172
  if ( ticketsFeature ) {
1174
1173
  let notifyuser = await getAssinedStore( userData, req.body.storeId, Data );
1175
1174
  if ( userData && userData.fcmToken && notifyuser ) {
@@ -1636,23 +1635,40 @@ export async function ticketApprove( req, res, next ) {
1636
1635
  },
1637
1636
  );
1638
1637
  }
1639
- for ( let userData of finduserList ) {
1640
- let title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
1641
- let createdOn = dayjs().format( 'DD MMM YYYY' );
1642
- let description = `Created on ${createdOn}`;
1643
- console.log( '🚀 ~ ticketCreation ~ userData.role:', userData.email );
1644
- let Data = {
1645
- storeId: getstoreName.storeId,
1646
- issueDate: inputData.dateString,
1647
- };
1648
1638
 
1649
- const ticketsFeature = userData?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
1650
- console.log( ticketsFeature );
1651
- if ( ticketsFeature ) {
1652
- let notifyuser = await getAssinedStore( userData, req.body.storeId, Data );
1653
- if ( userData && userData.fcmToken && notifyuser ) {
1654
- const fcmToken = userData.fcmToken;
1655
- await sendPushNotification( title, description, fcmToken );
1639
+
1640
+ let checkreview = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'reviewer' && data.isChecked === true );
1641
+ let checkapprove = getConfig.footfallDirectoryConfigs.revision.filter( ( data ) => data.actionType === 'approver' && data.isChecked === true );
1642
+
1643
+ if ( checkreview.length > 0 || checkapprove.length > 0 ) {
1644
+ let userQuery = [
1645
+ {
1646
+ $match: {
1647
+ clientId: getstoreName.clientId,
1648
+ role: 'admin',
1649
+ },
1650
+ },
1651
+ ];
1652
+ let finduserList = await aggregateUser( userQuery );
1653
+
1654
+ // return;
1655
+ for ( let userData of finduserList ) {
1656
+ let title = `${getstoreName?.storeName} Have raised a ticket for a Footfall Mismatch`;
1657
+ let createdOn = dayjs().format( 'DD MMM YYYY' );
1658
+ let description = `Created on ${createdOn}`;
1659
+
1660
+ let Data = {
1661
+ storeId: getstoreName.storeId,
1662
+ issueDate: inputData.dateString,
1663
+ };
1664
+ const ticketsFeature = userData?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
1665
+
1666
+ if ( ticketsFeature ) {
1667
+ let notifyuser = await getAssinedStore( userData, req.body.storeId, Data );
1668
+ if ( userData && userData.fcmToken && notifyuser ) {
1669
+ const fcmToken = userData.fcmToken;
1670
+ await sendPushNotification( title, description, fcmToken );
1671
+ }
1656
1672
  }
1657
1673
  }
1658
1674
  }
@@ -1735,7 +1751,6 @@ export async function getAssinedStore( user, storeId ) {
1735
1751
  // Convert Set back to Array if needed
1736
1752
  let assignedStores = Array.from( storeIds );
1737
1753
  if ( assignedStores.includes( storeId ) ) {
1738
- console.log( '🚀 ~ getAssinedStore ~ req.body.assignedStores:', assignedStores );
1739
1754
  return true;
1740
1755
  } else {
1741
1756
  return true;