tango-app-api-client 3.3.3-beta.9 → 3.4.0-beta.1

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.
@@ -142,7 +142,23 @@ export async function create( req, res ) {
142
142
  'isDefaults': true,
143
143
  };
144
144
 
145
- await createclusterModel( defaultcluster );
145
+ const cluster = await createclusterModel( defaultcluster );
146
+
147
+ const logClusterObj = {
148
+ clientId: insertedClientRecord.clientId,
149
+ userName: req.user?.userName,
150
+ userId: req?.user?._id,
151
+ email: req.user?.email,
152
+ clusterId: cluster._id,
153
+ date: new Date(),
154
+ logType: 'cluster',
155
+ logSubType: 'clusterCreated',
156
+ changes: [ 'All stores' ],
157
+ eventType: 'create',
158
+ showTo: [ 'client', 'tango' ],
159
+ };
160
+
161
+ await insertOpenSearchData( openSearch.activityLog, logClusterObj );
146
162
 
147
163
 
148
164
  const defaultGroup = {
@@ -284,6 +300,7 @@ export async function create( req, res ) {
284
300
  const logObj = {
285
301
  clientId: insertedClientRecord.clientId,
286
302
  userName: req.user?.userName,
303
+ userId: req?.user?._id,
287
304
  email: req.user?.email,
288
305
  date: new Date(),
289
306
  logType: 'brandDetails',
@@ -691,7 +708,7 @@ export async function updateBrandInfo( req, res ) {
691
708
  ContentType: req.files.logo.mimetype,
692
709
  body: req.files.logo.data,
693
710
  };
694
- updateKeys.push( 'Logo' );
711
+ updateKeys.push( 'Brand Logo' );
695
712
  await fileUpload( uploadDataParams );
696
713
  }
697
714
 
@@ -701,32 +718,93 @@ export async function updateBrandInfo( req, res ) {
701
718
  } );
702
719
  }
703
720
 
704
- const user = await getUserNameEmailById( req.userId );
721
+ // Get updated client information before the update operation (fetch selected fields only)
722
+ const getPreCientInfo = await findOneClient(
723
+ { clientId: req.params.id }, // Filter by clientId
724
+ {
725
+ '_id': 0,
726
+ 'profileDetails.registeredCompanyName': 1,
727
+ 'profileDetails.industry': 1,
728
+ 'profileDetails.clientType': 1,
729
+ 'profileDetails.registeredAddress': 1,
730
+ 'profileDetails.headQuarters': 1,
731
+ 'profileDetails.website': 1,
732
+ 'status': 1,
733
+ 'averageTransactionValue': 1,
734
+ },
735
+ );
736
+
705
737
 
738
+ const updateAck = await brandInfoUpdate( {
739
+ clientId: req.params?.id, registeredCompanyName: req.body?.registeredCompanyName, industry: req.body?.industry,
740
+ clientType: req.body?.clientType, registeredAddress: req.body?.registeredAddress, headQuarters: req.body?.headQuarters,
741
+ website: req.body?.website, status: req.body?.status, logo: req.files?.logo ? `brandLogo.${req.files.logo.name.split( '.' )[1]}` : undefined, averageTransactionValue: req.body?.averageTransactionValue,
742
+ } );
706
743
 
744
+ // Get updated client information after the update operation (fetch selected fields only)
745
+ const getPosCientInfo = await findOneClient(
746
+ { clientId: req.params.id }, // Filter by clientId
747
+ {
748
+ '_id': 0,
749
+ 'profileDetails.registeredCompanyName': 1,
750
+ 'profileDetails.industry': 1,
751
+ 'profileDetails.clientType': 1,
752
+ 'profileDetails.registeredAddress': 1,
753
+ 'profileDetails.headQuarters': 1,
754
+ 'profileDetails.website': 1,
755
+ 'status': 1,
756
+ 'averageTransactionValue': 1,
757
+ },
758
+ );
759
+ // Map and rename keys from previous client info for UI display and logging
760
+ const oldData = {
761
+ RegisteredCompanyName: getPreCientInfo?.profileDetails?.registeredCompanyName,
762
+ IndustryType: getPreCientInfo?.profileDetails?.industry,
763
+ FirmType: getPreCientInfo?.profileDetails?.clientType,
764
+ RegisteredAddress: getPreCientInfo?.profileDetails?.registeredAddress,
765
+ HeadQuarters: getPreCientInfo?.profileDetails?.headQuarters,
766
+ CompanyWebsite: getPreCientInfo?.profileDetails?.website,
767
+ ProcessingStatus: getPreCientInfo?.status,
768
+ AverageTransactionValue: getPreCientInfo?.averageTransactionValue,
769
+ };
770
+
771
+ // Map and rename keys from current client info for UI display and logging
772
+ const newData ={
773
+ RegisteredCompanyName: getPosCientInfo?.profileDetails?.registeredCompanyName,
774
+ IndustryType: getPosCientInfo?.profileDetails?.industry,
775
+ FirmType: getPosCientInfo?.profileDetails?.clientType,
776
+ RegisteredAddress: getPosCientInfo?.profileDetails?.registeredAddress,
777
+ HeadQuarters: getPosCientInfo?.profileDetails?.headQuarters,
778
+ CompanyWebsite: getPosCientInfo?.profileDetails?.website,
779
+ ProcessingStatus: getPosCientInfo?.status,
780
+ AverageTransactionValue: getPosCientInfo?.averageTransactionValue,
781
+
782
+ };
783
+
784
+ // Prepare activity log object with all relevant details for OpenSearch logging
707
785
  const logObj = {
708
- clientId: req.params?.id,
709
- userName: user?.userName,
710
- email: user?.email,
711
- date: new Date(),
712
- logType: 'brandDetails',
713
- logSubType: 'brandInfo',
714
- changes: updateKeys,
715
- eventType: 'update',
716
- showTo: [ 'client', 'tango' ],
786
+ clientId: req.params?.id, // ID of the client whose data was updated
787
+ userName: req?.user?.userName, // Name of the user performing the update
788
+ email: req?.user?.email, // Email of the user performing the update
789
+ date: new Date(), // Timestamp of the update event
790
+ logType: 'brandDetails', // Type of log (e.g., related to brand details)
791
+ logSubType: 'brandInfo', // Subtype for more specific categorization
792
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
793
+ eventType: 'update', // Type of event (update operation)
794
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
795
+ previous: getPreCientInfo, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
796
+ current: getPosCientInfo, // Current (new) client information after update based on DB keys (for internal reference and comparison).
797
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
798
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
717
799
  };
800
+ logger.info( { logObj: logObj, updateKeys: updateKeys, length: updateKeys?.length } );
718
801
 
802
+ // Insert activity log into OpenSearch if there are fields that were updated
719
803
  if ( updateKeys.length ) {
720
- await insertOpenSearchData( openSearch.activityLog, logObj );
804
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
721
805
  }
722
806
 
723
807
 
724
- const updateAck = await brandInfoUpdate( {
725
- clientId: req.params?.id, registeredCompanyName: req.body?.registeredCompanyName, industry: req.body?.industry,
726
- clientType: req.body?.clientType, registeredAddress: req.body?.registeredAddress, headQuarters: req.body?.headQuarters,
727
- website: req.body?.website, status: req.body?.status, logo: req.files?.logo ? `brandLogo.${req.files.logo.name.split( '.' )[1]}` : undefined, averageTransactionValue: req.body?.averageTransactionValue,
728
- } );
729
-
730
808
  if ( req.body?.status === 'active' ) {
731
809
  await updateManyStore( { clientId: req.params?.id }, { status: 'active' } );
732
810
  await updateManyUser( { clientId: req.params?.id }, { isActive: true } );
@@ -743,7 +821,6 @@ export async function updateBrandInfo( req, res ) {
743
821
 
744
822
  if ( req.body?.status === 'deactive' ) {
745
823
  await updateManyStore( { clientId: req.params?.id }, { status: 'deactive' } );
746
- // await updateManyCamera( { clientId: req.params?.id }, { isUp: false, isActivated: false } );
747
824
  await updateManyUser( { clientId: req.params?.id }, { isActive: false } );
748
825
  }
749
826
 
@@ -769,7 +846,7 @@ export async function updateBrandInfo( req, res ) {
769
846
  await postApi( `${url.oldapidomain}/oldBrandUpdate/${data?._id}`, { clientStatus: data.clientStatus } );
770
847
 
771
848
  if ( updateAck ) {
772
- res.sendSuccess( { result: 'Updated Successfully' } );
849
+ return res.sendSuccess( { result: 'Updated Successfully' } );
773
850
  }
774
851
  } catch ( error ) {
775
852
  logger.error( { error: error, message: req.params, function: 'updateBrandInfo' } );
@@ -878,7 +955,25 @@ export async function updateSignatoryDetails( req, res ) {
878
955
  export async function updateTicketConfiguration( req, res ) {
879
956
  try {
880
957
  const openSearch = JSON.parse( process.env.OPENSEARCH );
881
- let findClient = await findOneClient({clientId:req.params?.id})
958
+ const fields ={
959
+ 'ticketConfigs.downTimeType': 1,
960
+ 'ticketConfigs.installationReAssign': 1,
961
+ 'ticketConfigs.infraDownTime': 1,
962
+ 'ticketConfigs.MinFilesCount': 1,
963
+ 'ticketConfigs.rcaTicketAssign': 1,
964
+ 'ticketConfigs.refreshAlert': 1,
965
+ 'ticketConfigs.statusCheckAlert': 1,
966
+ 'ticketConfigs.accuracyPercentage': 1,
967
+ 'ticketConfigs.reTrain': 1,
968
+ };
969
+
970
+ // Get updated client ticket configuration before the update operation (fetch selected fields only)
971
+ let findClient = await findOneClient(
972
+ { clientId: req.params?.id }, // Filter by clientId
973
+ fields,
974
+ );
975
+
976
+ // update the requested values in Mongo DB
882
977
  const updateAck = await ticketConfigurationUpdate( {
883
978
  clientId: req.params?.id, MinFilesCount: req.body?.MinFilesCount, accuracyPercentage: req.body?.accuracyPercentage, downTimeType: req.body?.downTimeType,
884
979
  infraDownTime: req.body?.infraDownTime, installationReAssign: req.body?.installationReAssign, isRcaTicketAssign: req.body?.isRcaTicketAssign,
@@ -886,40 +981,75 @@ export async function updateTicketConfiguration( req, res ) {
886
981
  refreshAlert: req.body?.refreshAlert, sendToAdmin: req.body?.sendToAdmin, sendToUser: req.body?.sendToUser, statusCheckAlert: req.body?.statusCheckAlert,
887
982
  } );
888
983
 
889
- let updateKeys = [];
984
+ let updateKeys = []; // List of updated field names in readable format
985
+
986
+ // Check if request body has any fields to update
890
987
  if ( Object.keys( req.body ).length > 0 ) {
891
988
  Object.keys( req.body ).forEach( ( element ) => {
892
- updateKeys.push( camelCaseToWords( element ) );
989
+ updateKeys.push( camelCaseToWords( element ) ); // Convert camelCase field name to readable format and add to updateKeys
893
990
  } );
894
991
  }
895
992
 
896
- const user = await getUserNameEmailById( req.userId );
897
- let updatedClient = await findOneClient({clientId:req.params?.id})
898
993
 
994
+ // Get updated client ticket configuration after the update operation (fetch selected fields only)
995
+ let updatedClient = await findOneClient( { clientId: req.params?.id }, fields );
996
+
997
+ // Map and rename keys from previous client info for UI display and logging
998
+ const oldData = {
999
+ InfraDowntimeType: findClient?.ticketConfigs?.downTimeType,
1000
+ AutoReAssignInstallation: findClient?.ticketConfigs?.installationReAssign,
1001
+ InfraDowntime: findClient?.ticketConfigs?.infraDownTime,
1002
+ InfraFiles: findClient?.ticketConfigs?.MinFilesCount,
1003
+ RcaTicket: findClient?.ticketConfigs?.rcaTicketAssign,
1004
+ RefreshTicketAlert: findClient?.ticketConfigs?.refreshAlert,
1005
+ StatusCheckAlert: findClient?.ticketConfigs?.statusCheckAlert,
1006
+ DataMismatchAccuracyPercentage: findClient?.ticketConfigs?.accuracyPercentage,
1007
+ DataMismatchReTraining: findClient?.ticketConfigs?.reTrain,
1008
+ };
1009
+
1010
+ // Map and rename keys from current client info for UI display and logging
1011
+ const newData ={
1012
+ InfraDowntimeType: updatedClient?.ticketConfigs?.downTimeType,
1013
+ AutoReAssignInstallation: updatedClient?.ticketConfigs?.installationReAssign,
1014
+ InfraDowntime: updatedClient?.ticketConfigs?.infraDownTime,
1015
+ InfraFiles: updatedClient?.ticketConfigs?.MinFilesCount,
1016
+ RcaTicket: updatedClient?.ticketConfigs?.rcaTicketAssign,
1017
+ RefreshTicketAlert: updatedClient?.ticketConfigs?.refreshAlert,
1018
+ StatusCheckAlert: updatedClient?.ticketConfigs?.statusCheckAlert,
1019
+ DataMismatchAccuracyPercentage: updatedClient?.ticketConfigs?.accuracyPercentage,
1020
+ DataMismatchReTraining: updatedClient?.ticketConfigs?.reTrain,
1021
+
1022
+ };
1023
+
1024
+ // Prepare activity log object with all relevant details for OpenSearch logging
899
1025
  const logObj = {
900
- clientId: req.params?.id,
901
- userName: user.userName,
902
- email: user.email,
903
- date: new Date(),
904
- logType: 'configuration',
905
- logSubType: 'ticketConfig',
906
- changes: updateKeys,
907
- eventType: 'update',
908
- showTo: [ 'tango' ],
909
- current:updatedClient.ticketConfigs,
910
- previous:findClient.ticketConfigs
1026
+ clientId: req.params?.id, // ID of the client whose data was updated
1027
+ userName: req?.user?.userName, // Name of the user performing the update
1028
+ email: req?.user?.email, // Email of the user performing the update
1029
+ date: new Date(), // Timestamp of the update event
1030
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1031
+ logSubType: 'ticketConfig', // Subtype for more specific categorization
1032
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1033
+ eventType: 'update', // Type of event (update operation)
1034
+ showTo: [ 'tango' ], // Visibility of the log (who can see this log)
1035
+ current: updatedClient.ticketConfigs, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1036
+ previous: findClient.ticketConfigs, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1037
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1038
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
911
1039
  };
912
1040
 
1041
+ // Insert activity log into OpenSearch if there are fields that were updated
913
1042
  if ( updateKeys.length ) {
914
- await insertOpenSearchData( openSearch.activityLog, logObj );
1043
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
915
1044
  }
916
1045
 
917
1046
  if ( updateAck ) {
918
1047
  res.sendSuccess( { result: 'Updated Successfully' } );
919
1048
  }
920
1049
  } catch ( error ) {
1050
+ const err = error.message || 'Internal Server Error';
921
1051
  logger.error( { error: error, message: req.params, function: 'updateTicketConfiguration' } );
922
- return res.sendError( 'Internal Server Error', 500 );
1052
+ return res.sendError( err, 500 );
923
1053
  }
924
1054
  }
925
1055
 
@@ -927,6 +1057,9 @@ export async function updateFeatureConfiguration( req, res ) {
927
1057
  try {
928
1058
  const openSearch = JSON.parse( process.env.OPENSEARCH );
929
1059
  const url = JSON.parse( process.env.URL );
1060
+
1061
+ // Get updated client Feature configuration before the update operation (fetch selected fields only)
1062
+ const previousData = await findOneClient( { clientId: req.params?.id }, { featureConfigs: 1 } );
930
1063
  const inputData = req.body;
931
1064
  if ( inputData?.bouncedLimitValue ) {
932
1065
  inputData.missedOpportunityFromValue = inputData?.bouncedLimitValue;
@@ -937,11 +1070,15 @@ export async function updateFeatureConfiguration( req, res ) {
937
1070
  }
938
1071
  const updateAck = await featureConfigurationUpdate( { clientId: req.params?.id }, inputData );
939
1072
 
940
- let updateKeys = [];
1073
+ // Get updated client Feature configuration after the update operation (fetch selected fields only)
1074
+ const postData = await findOneClient( { clientId: req.params?.id }, { featureConfigs: 1 } );
1075
+ let updateKeys = []; // List of updated field names in readable format
941
1076
 
1077
+ // Check if request body has any fields to update
942
1078
  if ( Object.keys( inputData ).length > 0 ) {
943
1079
  Object.keys( inputData ).forEach( ( element ) => {
944
- updateKeys.push( camelCaseToWords( element ) );
1080
+ element === 'billableCalculation'? 'potentialCalculation': element; // Replace 'billableCalculation' with 'potentialCalculation' if present
1081
+ updateKeys.push( camelCaseToWords( element ) ); // Convert camelCase field name to readable format and add to updateKeys
945
1082
  } );
946
1083
  }
947
1084
 
@@ -990,21 +1127,105 @@ export async function updateFeatureConfiguration( req, res ) {
990
1127
  }
991
1128
 
992
1129
  await postApi( `${url.oldapidomain}/oldBrandUpdate/${data?._id}`, { brandConfigs: data.brandConfigs } );
1130
+ const keysArray = [
1131
+ 'isExcludedArea', 'isPasserByData', 'isNormalized', 'isbillingDisabled',
1132
+ 'isCameraDisabled', 'isFootfallDirectory', 'isNOB', 'isNewTraffic',
1133
+ 'isTrax', 'isNewZone', 'isNewReports', 'isNewDashboard', 'streamBy',
1134
+ ];
1135
+ // Map and rename keys from previous client info for UI display and logging
1136
+ const oldData = {
1137
+ StoreOpenTime: previousData?.featureConfigs?.open,
1138
+ StoreCloseTime: previousData?.featureConfigs?.close,
1139
+ ConversionCalculations: previousData?.featureConfigs?.conversionCalculation,
1140
+ MissedOpportunityCalculation: previousData?.featureConfigs?.missedOpportunityCalculation,
1141
+ PotentialCalculations: previousData?.featureConfigs?.billableCalculation,
1142
+ ConversionCondition: previousData?.featureConfigs?.conversion.condition,
1143
+ ConversionValue: previousData?.featureConfigs?.conversion.value,
1144
+ MissedOpportunityFromCondition: previousData?.featureConfigs?.missedOpportunityFrom?.condition,
1145
+ MissedOpportunityToCondition: previousData?.featureConfigs?.missedOpportunityTo?.condition,
1146
+ MissedOpportunityFromValue: previousData?.featureConfigs?.missedOpportunityFrom?.value,
1147
+ MissedOpportunityToValue: previousData?.featureConfigs?.missedOpportunityTo?.value,
1148
+ BouncedLimitCondition: previousData?.featureConfigs?.bouncedLimit?.condition,
1149
+ BouncedLimitValue: previousData?.featureConfigs?.bouncedLimit?.value,
1150
+ InfraAlertValue: previousData?.featureConfigs?.infraAlert?.value,
1151
+ InfraAlertCondition: previousData?.featureConfigs?.infraAlert?.condition,
1152
+ ConversionCalculations: previousData?.featureConfigs?.conversionCalculation,
1153
+ ExcludedArea: previousData?.featureConfigs?.isExcludedArea ==true ? 'Enable': 'Disable',
1154
+ PasserBydata: previousData?.featureConfigs?.isPasserByData ==true ? 'Enable': 'Disable',
1155
+ NormalizedDataDuringDowntime: previousData?.featureConfigs?.isNormalized ==true ? 'Enable': 'Disable',
1156
+ Billing: previousData?.featureConfigs?.isbillingDisabled ==true ? 'Enable': 'Disable',
1157
+ CameraBlurring: previousData?.featureConfigs?.isCameraDisabled ==true ? 'Enable': 'Disable',
1158
+ FootfallDirectory: previousData?.featureConfigs?.isFootfallDirectory ==true ? 'Enable': 'Disable',
1159
+ NOBStatus: previousData?.featureConfigs?.isNOB ==true ? 'Enable': 'Disable',
1160
+ EnableAnalyze: previousData?.featureConfigs?.isNewDashboard ==true ? 'Enable': 'Disable',
1161
+ Traffic: previousData?.featureConfigs?.isNewTraffic==true ? 'Enable': 'Disable',
1162
+ Zone: previousData?.featureConfigs?.isNewZone ==true ? 'Enable': 'Disable',
1163
+ Zonev2: previousData?.featureConfigs?.isNewZoneV2 ==true ? 'Enable': 'Disable',
1164
+ Reports: previousData?.featureConfigs?.isNewReports ==true ? 'Enable': 'Disable',
1165
+ Trax: previousData?.featureConfigs?.isTrax ==true ? 'Enable': 'Disable',
1166
+ StreamType: previousData?.featureConfigs?.streamBy =='Edge' ? 'Edge App': 'RTSP',
1167
+ FootfallDirectoryOnlyAudit: previousData?.featureConfigs?.isFootfallDirectoryAudit ==true ? 'Enable': 'Disable',
1168
+ FootfallDirectoryOnlyFew: previousData?.featureConfigs?.isFootfallDirectoryLimit ==true ? 'Enable': 'Disable',
1169
+
1170
+ };
993
1171
 
1172
+ // Map and rename keys from current client info for UI display and logging
1173
+ const newData ={
1174
+ StoreOpenTime: postData?.featureConfigs?.open,
1175
+ StoreCloseTime: postData?.featureConfigs?.close,
1176
+ ConversionCalculations: postData?.featureConfigs?.conversionCalculation,
1177
+ MissedOpportunityCalculation: postData?.featureConfigs?.missedOpportunityCalculation,
1178
+ PotentialCalculations: postData?.featureConfigs?.billableCalculation,
1179
+ ConversionCondition: postData?.featureConfigs?.conversion.condition,
1180
+ ConversionValue: postData?.featureConfigs?.conversion.value,
1181
+ MissedOpportunityFromCondition: postData?.featureConfigs?.missedOpportunityFrom?.condition,
1182
+ MissedOpportunityToCondition: postData?.featureConfigs?.missedOpportunityTo?.condition,
1183
+ MissedOpportunityFromValue: postData?.featureConfigs?.missedOpportunityFrom?.value,
1184
+ MissedOpportunityToValue: postData?.featureConfigs?.missedOpportunityTo?.value,
1185
+ BouncedLimitCondition: postData?.featureConfigs?.bouncedLimit?.condition,
1186
+ BouncedLimitValue: postData?.featureConfigs?.bouncedLimit?.value,
1187
+ InfraAlertValue: postData?.featureConfigs?.infraAlert?.value,
1188
+ InfraAlertCondition: postData?.featureConfigs?.infraAlert?.condition,
1189
+ ConversionCalculations: postData?.featureConfigs?.conversionCalculation,
1190
+ ExcludedArea: postData?.featureConfigs?.isExcludedArea ==true ? 'Enable': 'Disable',
1191
+ PasserBydata: postData?.featureConfigs?.isPasserByData ==true ? 'Enable': 'Disable',
1192
+ NormalizedDataDuringDowntime: postData?.featureConfigs?.isNormalized ==true ? 'Enable': 'Disable',
1193
+ Billing: postData?.featureConfigs?.isbillingDisabled ==true ? 'Enable': 'Disable',
1194
+ CameraBlurring: postData?.featureConfigs?.isCameraDisabled ==true ? 'Enable': 'Disable',
1195
+ FootfallDirectory: postData?.featureConfigs?.isFootfallDirectory ==true ? 'Enable': 'Disable',
1196
+ NOBStatus: postData?.featureConfigs?.isNOB ==true ? 'Enable': 'Disable',
1197
+ EnableAnalyze: postData?.featureConfigs?.isNewDashboard ==true ? 'Enable': 'Disable',
1198
+ Traffic: postData?.featureConfigs?.isNewTraffic==true ? 'Enable': 'Disable',
1199
+ Zone: postData?.featureConfigs?.isNewZone ==true ? 'Enable': 'Disable',
1200
+ Zonev2: postData?.featureConfigs?.isNewZoneV2 ==true ? 'Enable': 'Disable',
1201
+ Reports: postData?.featureConfigs?.isNewReports ==true ? 'Enable': 'Disable',
1202
+ Trax: postData?.featureConfigs?.isTrax ==true ? 'Enable': 'Disable',
1203
+ StreamType: postData?.featureConfigs?.streamBy =='Edge' ? 'Edge App': 'RTSP',
1204
+ FootfallDirectoryOnlyAudit: postData?.featureConfigs?.isFootfallDirectoryAudit ==true ? 'Enable': 'Disable',
1205
+ FootfallDirectoryOnlyFew: postData?.featureConfigs?.isFootfallDirectoryLimit ==true ? 'Enable': 'Disable',
994
1206
 
1207
+ };
1208
+
1209
+ // Prepare activity log object with all relevant details for OpenSearch logging
995
1210
  const logObj = {
996
- clientId: req.params?.id,
997
- userName: user?.userName,
998
- email: user?.email,
999
- date: new Date(),
1000
- logType: 'configuration',
1001
- logSubType: 'featureConfig',
1002
- changes: updateKeys,
1003
- eventType: 'update',
1004
- showTo: [ 'client', 'tango' ],
1211
+ clientId: req.params?.id, // ID of the client whose data was updated
1212
+ userName: user?.userName, // Name of the user performing the update
1213
+ email: user?.email, // Email of the user performing the update
1214
+ date: new Date(), // Timestamp of the update event
1215
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1216
+ logSubType: Object.keys( inputData ).some( ( key ) => keysArray.includes( key ) ) ? 'dashboardConfig' : 'featureConfig', // Subtype for more specific categorization
1217
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1218
+ eventType: 'update', // Type of event (update operation)
1219
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
1220
+ previous: previousData.featureConfigs, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1221
+ current: postData.featureConfigs, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1222
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1223
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
1005
1224
  };
1225
+
1226
+ // Insert activity log into OpenSearch if there are fields that were updated
1006
1227
  if ( updateKeys.length ) {
1007
- await insertOpenSearchData( openSearch.activityLog, logObj );
1228
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
1008
1229
  }
1009
1230
 
1010
1231
  if ( updateAck ) {
@@ -1020,6 +1241,18 @@ export async function updateFeatureConfiguration( req, res ) {
1020
1241
  export async function domainDetailsConfiguration( req, res ) {
1021
1242
  try {
1022
1243
  const openSearch = JSON.parse( process.env.OPENSEARCH );
1244
+
1245
+ // Get updated client domain configuration before the update operation (fetch selected fields only)
1246
+ const getPreData = await findOneClient(
1247
+ { clientId: req.params?.id }, // filter by clientId
1248
+ {
1249
+ domainName: '$domainConfig.ssoLogin.domainName',
1250
+ isEnable: '$domainConfig.ssoLogin.isEnable',
1251
+ ipWhitelist: '$domainConfig.ipWhitelisting.enableWhitelisting',
1252
+ WhitelistedIps: '$domainConfig.ipWhitelisting.allowedIps',
1253
+ TwoFactorAuthentication: '$domainConfig.enableOtp',
1254
+ },
1255
+ );
1023
1256
  const updateAck = await domainDetailsConfigurationUpdate( {
1024
1257
  clientId: req.params?.id, domainName: req.body?.domainName, isEnable: req.body?.isEnable,
1025
1258
  enableWhitelisting: req.body?.enableWhitelisting, allowedIps: req.body?.allowedIps, enableOtp: req.body?.enableOtp,
@@ -1027,30 +1260,66 @@ export async function domainDetailsConfiguration( req, res ) {
1027
1260
 
1028
1261
  let updateKeys = [];
1029
1262
 
1030
-
1263
+ // Check if request body has any fields to update
1031
1264
  if ( Object.keys( req.body ).length > 0 ) {
1032
1265
  Object.keys( req.body ).forEach( ( element ) => {
1033
1266
  updateKeys.push( camelCaseToWords( element ) );
1034
1267
  } );
1035
1268
  }
1036
1269
 
1037
- const user = await getUserNameEmailById( req.userId );
1270
+ // Get updated client domain configuration after the update operation (fetch selected fields only)
1271
+ const getPostData = await findOneClient(
1272
+ { clientId: req.params?.id }, // Filter by clientId
1273
+ {
1274
+ domainName: '$domainConfig.ssoLogin.domainName',
1275
+ isEnable: '$domainConfig.ssoLogin.isEnable',
1276
+ ipWhitelist: '$domainConfig.ipWhitelisting.enableWhitelisting',
1277
+ WhitelistedIps: '$domainConfig.ipWhitelisting.allowedIps',
1278
+ TwoFactorAuthentication: '$domainConfig.enableOtp',
1279
+ },
1280
+ );
1281
+
1038
1282
 
1283
+ // Map and rename keys from previous client info for UI display and logging
1284
+ const oldData = {
1285
+ DomainName: getPreData?.domainConfig?.ssoLogin?.domainName,
1286
+ IsEnable: getPreData?.domainConfig?.ssoLogin?.isEnable ==true ? 'Enable': 'Disable',
1287
+ IPWhitelist: getPreData?.domainConfig?.ipWhitelisting?.enableWhitelisting ==true ? 'Enable': 'Disable',
1288
+ WhitelistedIps: ( getPreData?.domainConfig?.ipWhitelisting?.allowedIps ).join( ', ' ),
1289
+ TwoFactorAuthentication: getPreData?.domainConfig?.enableOtp ==true ? 'Enable': 'Disable',
1290
+
1291
+ };
1039
1292
 
1293
+ // Map and rename keys from current client info for UI display and logging
1294
+ const newData ={
1295
+ DomainName: getPostData?.domainConfig?.ssoLogin?.domainName,
1296
+ IsEnable: getPostData?.domainConfig?.ssoLogin?.isEnable ==true ? 'Enable': 'Disable',
1297
+ IPWhitelist: getPostData?.domainConfig?.ipWhitelisting?.enableWhitelisting ==true ? 'Enable': 'Disable',
1298
+ WhitelistedIps: ( getPostData?.domainConfig?.ipWhitelisting?.allowedIps ).join( ', ' ),
1299
+ TwoFactorAuthentication: getPostData?.domainConfig?.enableOtp ==true ? 'Enable': 'Disable',
1300
+ };
1301
+
1302
+
1303
+ // Prepare activity log object with all relevant details for OpenSearch logging
1040
1304
  const logObj = {
1041
- clientId: req.params?.id,
1042
- userName: user?.userName,
1043
- email: user?.email,
1044
- date: new Date(),
1045
- logType: 'configuration',
1046
- logSubType: 'domainDetails',
1047
- changes: updateKeys,
1048
- eventType: 'update',
1049
- showTo: [ 'client', 'tango' ],
1305
+ clientId: req.params?.id, // ID of the client whose data was updated
1306
+ userName: req?.user?.userName, // Name of the user performing the update
1307
+ email: req?.user?.email, // Email of the user performing the update
1308
+ date: new Date(), // Timestamp of the update event
1309
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1310
+ logSubType: 'domainDetails', // Subtype for more specific categorization
1311
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1312
+ eventType: 'update', // Type of event (update operation)
1313
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
1314
+ previous: getPreData, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1315
+ current: getPostData, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1316
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1317
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
1050
1318
  };
1051
1319
 
1320
+ // Insert activity log into OpenSearch if there are fields that were updated
1052
1321
  if ( updateKeys.length ) {
1053
- await insertOpenSearchData( openSearch.activityLog, logObj );
1322
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
1054
1323
  }
1055
1324
 
1056
1325
  if ( updateAck ) {
@@ -1152,7 +1421,6 @@ export async function updateDocuments( req, res ) {
1152
1421
 
1153
1422
  const user = await getUserNameEmailById( req.userId );
1154
1423
 
1155
-
1156
1424
  const logObj = {
1157
1425
  clientId: req.params?.id,
1158
1426
  userName: user?.userName,
@@ -1214,14 +1482,29 @@ export async function auditConfiguration( req, res ) {
1214
1482
  auditConfigs: 1, _id: 0,
1215
1483
  };
1216
1484
  const previousClient = await findOneClient( query, fields );
1485
+ previousClient.auditConfigs.ratio = previousClient?.auditConfigs?.ratio? ( previousClient.auditConfigs.ratio )*100 : null;
1217
1486
  const record = {
1218
1487
  'auditConfigs.count': inputData.count,
1219
1488
  'auditConfigs.audit': inputData.audit,
1220
1489
  'auditConfigs.ratio': Number( normalizeNumber( inputData.ratio, 0, 100 ) ),
1221
1490
  };
1222
- logger.info( { record: record, query: query } );
1223
- const a = await updateOneClient( query, record );
1224
- logger.info( { a: a } );
1491
+ await updateOneClient( query, record );
1492
+
1493
+
1494
+ // Map and rename keys from previous client info for UI display and logging
1495
+ const oldData = {
1496
+ AuditStatus: inputData?.audit ==true ? 'Enable': 'Disable',
1497
+ MappingPercentage: inputData?.ratio,
1498
+ AuditCount: inputData?.count,
1499
+
1500
+ };
1501
+
1502
+ // Map and rename keys from current client info for UI display and logging
1503
+ const newData ={
1504
+ AuditStatus: previousClient.auditConfigs?.audit ==true ? 'Enable': 'Disable',
1505
+ MappingPercentage: previousClient.auditConfigs?.ratio,
1506
+ AuditCount: previousClient.auditConfigs?.count,
1507
+ };
1225
1508
  const logObj = {
1226
1509
  clientId: req.params?.id,
1227
1510
  userName: req.user?.userName,
@@ -1232,13 +1515,15 @@ export async function auditConfiguration( req, res ) {
1232
1515
  eventType: 'update',
1233
1516
  showTo: [ 'tango' ],
1234
1517
  changes: [ `Audit config for client id ${req.params?.id}` ],
1235
- previous: previousClient,
1518
+ previous: previousClient.auditConfigs,
1236
1519
  current: {
1237
1520
  clientId: req.params?.id,
1238
1521
  count: inputData.count,
1239
1522
  audit: inputData.audit,
1240
- ratio: normalizeNumber( inputData.ratio, 0, 100 ),
1523
+ ratio: inputData.ratio,
1241
1524
  },
1525
+ oldData: oldData,
1526
+ newData: newData,
1242
1527
  };
1243
1528
 
1244
1529
  await insertOpenSearchData( openSearch.activityLog, logObj );
@@ -1281,17 +1566,6 @@ export async function clientList( req, res ) {
1281
1566
  $match: {
1282
1567
  userEmail: { $eq: req?.user?.email },
1283
1568
  userType: 'tango',
1284
- // $expr: {
1285
- // $cond: {
1286
- // if: {
1287
- // $and: [
1288
- // { $eq: [ '$tangoUserType', 'csm' ] },
1289
- // ],
1290
- // },
1291
- // then: { $eq: [ '$isClientApproved', true ] },
1292
- // else: true,
1293
- // },
1294
- // },
1295
1569
 
1296
1570
  },
1297
1571
  }, {
@@ -1607,17 +1881,6 @@ export async function clientListV1( req, res ) {
1607
1881
  $match: {
1608
1882
  userEmail: { $eq: req?.user?.email },
1609
1883
  userType: 'tango',
1610
- // $expr: {
1611
- // $cond: {
1612
- // if: {
1613
- // $and: [
1614
- // { $eq: [ '$tangoUserType', 'csm' ] },
1615
- // ],
1616
- // },
1617
- // then: { $eq: [ '$isClientApproved', true ] },
1618
- // else: true,
1619
- // },
1620
- // },
1621
1884
 
1622
1885
  },
1623
1886
  },
@@ -2013,9 +2276,37 @@ export async function detailedClientCount( req, res ) {
2013
2276
  }
2014
2277
  }
2015
2278
 
2016
- export async function getActivityLogs( req, res ) {
2279
+ export async function getActivityLogs1( req, res ) {
2017
2280
  try {
2281
+ const inputData = req.body;
2018
2282
  const openSearch = JSON.parse( process.env.OPENSEARCH );
2283
+ const clientQuery =[
2284
+ {
2285
+ $match: {
2286
+ $and: [
2287
+ {
2288
+ clientId: { $in: inputData },
2289
+ },
2290
+ ],
2291
+ },
2292
+ },
2293
+ {
2294
+ $group: {
2295
+ _id: null,
2296
+ clientName: { $push: '$clientName' },
2297
+ },
2298
+ },
2299
+ {
2300
+ $project: {
2301
+ _id: 0,
2302
+ clientName: 1,
2303
+ },
2304
+ },
2305
+ ];
2306
+ const getClientName = await aggregateClient( clientQuery );
2307
+ if ( getClientName?.length == 0 || getClientName[0]?.clientName?.length ===0 ) {
2308
+ return res.sendError( 'No Data Found', 204 );
2309
+ }
2019
2310
  const query = {
2020
2311
  '_source': [
2021
2312
  'userId', 'userName', 'email', 'date', 'logType', 'logSubType',
@@ -2023,13 +2314,27 @@ export async function getActivityLogs( req, res ) {
2023
2314
  ],
2024
2315
  'query': {
2025
2316
  'bool': {
2026
- 'must': [
2317
+ // 'must': [
2318
+ // {
2319
+ // 'terms': {
2320
+ // 'clientId.keyword': [ req.body.clientId ],
2321
+ // },
2322
+ // },
2323
+ // ],
2324
+
2325
+ 'should': [
2326
+ {
2327
+ 'terms': {
2328
+ 'clientId.keyword': [ inputData.clientId ],
2329
+ },
2330
+ },
2027
2331
  {
2028
2332
  'terms': {
2029
- 'clientId.keyword': [ req.body.clientId ],
2333
+ 'clientName.keyword': [ getClientName[0].clientName ], // Add clientName condition
2030
2334
  },
2031
2335
  },
2032
2336
  ],
2337
+ 'minimum_should_match': 1, // Ensures at least one condition must match
2033
2338
  },
2034
2339
  },
2035
2340
  'from': ( req.body.offset - 1 ) * req.body.limit,
@@ -2076,30 +2381,22 @@ export async function getActivityLogs( req, res ) {
2076
2381
 
2077
2382
  const hits = logs?.body?.hits?.hits;
2078
2383
  const totalDocuments = logs?.body?.hits?.total?.value;
2384
+
2079
2385
  let temp = [];
2080
2386
  if ( totalDocuments ) {
2081
2387
  hits.map( ( hit, i ) => {
2082
- temp.push( hit?._source );
2388
+ let respo ={};
2389
+ hit._source.logSubType = hit._source.logSubType === 'documentUpload'? 'documentsUpload': hit._source.logSubType === 'domainDetails'? 'securityFeatures': hit?._source?.logSubType;
2083
2390
  if ( ( ( hit?._source?.eventType ).match( /update/ ) || ( hit?._source?.eventType ).match( /edit/ ) )&& hit?._source?.previous ) {
2084
- const previous = removeIndexedKeysAndFlatten( hit?._source?.previous );
2085
- const current=removeIndexedKeysAndFlatten( hit?._source?.current );
2086
- delete previous?._id;
2087
- delete current?._id;
2088
- delete previous?.updatedAt;
2089
- delete current?.updatedAt;
2090
- delete current?.createdAt;
2091
-
2092
- logger.info( { previous2: hit?._source?.previous, previous: previous, current: hit?._source?.current, current: current } );
2093
- const diff = Object.entries( current )
2094
- .reduce( ( acc, [ key, value ] ) => {
2095
- if ( value !== previous[key] & typeof previous[key] === 'string' ) {
2096
- acc[key] = { previous: previous[key], current: value };
2097
- }
2098
-
2099
- return acc;
2100
- }, {} );
2101
- logger.info( { diff: diff } );
2102
- temp[i].updatedValue = diff;
2391
+ const previous = hit?._source?.previous;
2392
+ const current=hit?._source?.current;
2393
+ const logType =hit?._source?.logType;
2394
+
2395
+ const logSubType =hit?._source?.logSubType;
2396
+ respo = findDifferences1( previous, current, logType, logSubType );
2397
+ hit._source.updatedValue = respo;
2398
+ temp.push( hit?._source );
2399
+ hit._source.changes =logSubType === 'ticketConfig'? Object.keys( respo ).map( ( item ) => item ) : hit._source.changes;
2103
2400
  } else {
2104
2401
  temp.push( hit?._source );
2105
2402
  }
@@ -2114,25 +2411,292 @@ export async function getActivityLogs( req, res ) {
2114
2411
  return res.sendError( 'Internal Server Error', 500 );
2115
2412
  }
2116
2413
  }
2117
- function removeIndexedKeysAndFlatten( obj ) {
2118
- const result = {};
2119
2414
 
2120
- for ( const [ key, value ] of Object.entries( obj ) ) {
2121
- // Skip numeric keys and preserve arrays intact
2122
- if ( /^\d+$/.test( key ) ) continue;
2123
2415
 
2124
- if ( Array.isArray( value ) ) {
2125
- result[key] = value;
2126
- } else if ( typeof value === 'object' && value !== null ) {
2127
- Object.assign( result, removeIndexedKeysAndFlatten( value ) );
2416
+ function findDifferences1( previous, current, logType, logSubType, path = '' ) {
2417
+ const dbKeys = JSON.parse( process.env.DB_KEYS );
2418
+ const ignoredKeys = new Set( [
2419
+ '_id', 'updatedAt', 'createdAt', 'password', 'clientId',
2420
+ 'storeId', 'refreshToken', 'employeeId', 'fcmToken', 'permission', 'updateFeatureConfig',
2421
+ ] );
2422
+ const documents = dbKeys.DOCUMENTS;
2423
+ // Get correct key mapping based on logType
2424
+ const keyMapping = logType == 'stores' ?documents.stores: logType == 'users' ?documents.users :logSubType=='reportConfig'?documents.reports: logType=='brandInfo'?documents.client:documents.client;
2425
+
2426
+
2427
+ let differences = {};
2428
+
2429
+ for ( const key in current ) {
2430
+ if ( !Object.prototype.hasOwnProperty.call( current, key ) || ignoredKeys.has( key ) ) continue;
2431
+
2432
+ const prevValue = previous[key];
2433
+ const currValue = current[key];
2434
+
2435
+ if ( typeof prevValue === 'object' && typeof currValue === 'object' && prevValue !== null && currValue !== null ) {
2436
+ if ( logSubType!== 'userUpdated' ) {
2437
+ if ( key == 'spocDetails' ) {
2438
+ if ( _.isEqual( prevValue, currValue ) ) {
2439
+ continue;
2440
+ } else {
2441
+ let result = compareArrayObjects( prevValue, currValue );
2442
+ differences = { ...differences, ...result };
2443
+ }
2444
+ } else if ( Array.isArray( prevValue ) && prevValue.every( ( item ) => typeof item === 'string' ) ||Array.isArray( currValue ) && currValue.every( ( item ) => typeof item === 'string' ) ) {
2445
+ JSON.stringify( prevValue ) !== JSON.stringify( currValue )?
2446
+ differences[`${path}${key}`] = { previous: prevValue.length> 0 ? prevValue.join( ',' ): null, current: currValue.length > 0? currValue.join( ',' ): null } : null;
2447
+
2448
+ // differences[`${path}${key}`] = { previous: removed.length> 0 ? prevValue.join( ',' ): prevValue.join( ', ' ) :, current: added.length > 0? addedUsers.join( ', ' ) : currValue.join( ',' ) } : null;
2449
+ } else {
2450
+ const nestedDiffs = findDifferences1( prevValue, currValue, logType, logSubType, `${path}${key}.` );
2451
+ Object.assign( differences, nestedDiffs );
2452
+ }
2453
+ } else {
2454
+ const nestedDiffs = findDifferences1( prevValue, currValue, logType, logSubType, `${path}${key}.` );
2455
+ Object.assign( differences, nestedDiffs );
2456
+ }
2457
+ } else if ( prevValue !== currValue ) {
2458
+ if (
2459
+ ( prevValue === '' && currValue === '' ) ||
2460
+ ( Array.isArray( prevValue ) && Array.isArray( currValue ) && prevValue.length === 0 && currValue.length === 0 )
2461
+ ) {
2462
+ continue;
2463
+ }
2464
+ differences[`${path}${key}`] = { previous: prevValue, current: currValue };
2465
+ }
2466
+ }
2467
+
2468
+ // **Transform & Filter Differences**
2469
+ let updatedDifferences = {};
2470
+ Object.keys( differences ).forEach( ( key ) => {
2471
+ let newKey = key.replace( /spocDetails\.\d+\./, 'spocDetails.' ).replace( /WhitelistedIps\.\d+/, 'Whitelisted Ips' );
2472
+ let diff = differences[key];
2473
+ if ( newKey.toLowerCase().includes( 'isactive' ) ) {
2474
+ diff = {
2475
+ previous: diff.previous ? 'Active' : 'Deactive',
2476
+ current: diff.current ? 'Active' : 'Deactive',
2477
+ };
2478
+ }
2479
+
2480
+ const userFriendlyKey = getUserFriendlyKey( newKey, keyMapping );
2481
+ if ( userFriendlyKey ) {
2482
+ newKey = userFriendlyKey;
2483
+ }
2484
+ const binaryKeys = [ 'Two Factor Authentication', 'Mat Enabled', 'Audit Status', 'Infra Email Alert', 'Ip Whitelist', 'Excluded Area', 'Passer-by data', 'Normalized data during downtime', 'Billing', 'Camera Blurring', 'Footfall Directory', 'Footfall Directory Audit', 'Footfall Directory Limit', 'NOB Status', 'Traffic', 'Trax', 'Zone V1', 'Zone V2', 'Reports', 'Analyze' ];
2485
+ if ( binaryKeys.includes( newKey ) ) {
2486
+ diff.previous = ( diff.previous == true || diff.previous == 'Enabled' ) ? 'Enabled' : 'Disabled';
2487
+ diff.current = ( diff.current == true || diff.current == 'Enabled' ) ? 'Enabled' : 'Disabled';
2488
+ }
2489
+ if ( newKey === 'Server Type' ) {
2490
+ diff.previous = ( diff.previous == true || diff.previous == 'Server' ) ? 'Server' : 'Serverless';
2491
+ diff.current = ( diff.current == true || diff.current == 'Server' ) ? 'Server' : 'Serverless';
2492
+ }
2493
+
2494
+ if ( newKey === 'Stream Type' ) {
2495
+ diff.previous = ( diff.previous == 'Edge' || diff.previous == 'Edge App' ) ? 'Edge App' : 'RTSP';
2496
+ diff.current = ( diff.current == 'Edge' || diff.current == 'Edge App' ) ? 'Edge App' : 'RTSP';
2497
+ }
2498
+
2499
+ // **Transform Roles Permission Keys**
2500
+ const rolesPermissionMatch = newKey.match( /rolespermission\.(\d+)\.modules\.(\d+)\.(isAdd|isEdit)/ );
2501
+ if ( rolesPermissionMatch ) {
2502
+ const [ , roleIndex, moduleIndex, action ] = rolesPermissionMatch;
2503
+ const role = previous.rolespermission?.[roleIndex];
2504
+ const module = role?.modules?.[moduleIndex];
2505
+
2506
+ if ( module ) {
2507
+ newKey = `User's ${module.name} Module ${action === 'isAdd' ? 'Add' : 'Edit'}`;
2508
+ diff.previous = diff.previous ? 'Enabled' : 'Disabled';
2509
+ diff.current = diff.current ? 'Enabled' : 'Disabled';
2510
+ }
2511
+ }
2512
+
2513
+ if ( !newKey || ( diff.previous === '' && !diff.current ) ) return;
2514
+
2515
+ if (
2516
+ ( diff.previous === '' || diff.previous === null || ( Array.isArray( diff.previous ) && diff.previous.length === 0 ) ) &&
2517
+ diff.current === undefined
2518
+ ) {
2519
+ return;
2520
+ }
2521
+ if (
2522
+ ( diff.current === '' || diff.current === null || ( Array.isArray( diff.current ) && diff.current.length === 0 ) ) &&
2523
+ diff.previous === undefined
2524
+ ) {
2525
+ return;
2526
+ }
2527
+
2528
+ updatedDifferences[newKey] = diff;
2529
+ } );
2530
+
2531
+ return updatedDifferences;
2532
+ }
2533
+
2534
+
2535
+ function getUserFriendlyKey( keyPath, mapping ) {
2536
+ const keys = keyPath.split( '.' ); // Split the key path (e.g., storeProfile.pincode → ['storeProfile', 'pincode'])
2537
+ let value = mapping;
2538
+
2539
+ for ( const key of keys ) {
2540
+ if ( value && typeof value === 'object' && key in value ) {
2541
+ value = value[key]; // Traverse down the object
2542
+ } else {
2543
+ return null; // Return original if not found
2544
+ }
2545
+ }
2546
+
2547
+ return typeof value === 'string' ? value : keyPath; // Return mapped value or original key
2548
+ }
2549
+
2550
+ export async function getActivityLogs( req, res ) {
2551
+ try {
2552
+ const inputData = req.body;
2553
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
2554
+ const clientQuery =[
2555
+ {
2556
+ $match: {
2557
+ $and: [
2558
+ {
2559
+ clientId: { $eq: inputData.clientId },
2560
+ },
2561
+ ],
2562
+ },
2563
+ },
2564
+ {
2565
+ $group: {
2566
+ _id: null,
2567
+ clientName: { $push: '$clientName' },
2568
+ },
2569
+ },
2570
+ {
2571
+ $project: {
2572
+ _id: 0,
2573
+ clientName: 1,
2574
+ },
2575
+ },
2576
+ ];
2577
+ const getClientName = await aggregateClient( clientQuery );
2578
+ if ( getClientName?.length == 0 || getClientName[0]?.clientName?.length ===0 ) {
2579
+ return res.sendError( 'No Data Found', 204 );
2580
+ }
2581
+ const query = {
2582
+ '_source': [
2583
+ 'userId', 'userName', 'email', 'date', 'logType', 'logSubType',
2584
+ 'changes', 'eventType', 'previous', 'current', 'oldData', 'newData',
2585
+ ],
2586
+ 'query': {
2587
+ 'bool': {
2588
+ 'must': [
2589
+ {
2590
+ 'terms': {
2591
+ 'clientId.keyword': [ inputData.clientId ],
2592
+ },
2593
+ },
2594
+
2595
+ ],
2596
+ 'should': [
2597
+ {
2598
+ 'terms': {
2599
+ 'clientName.keyword': getClientName[0].clientName, // Add clientName condition
2600
+ },
2601
+ },
2602
+ ],
2603
+ },
2604
+ },
2605
+ 'from': ( req.body.offset - 1 ) * req.body.limit,
2606
+ 'size': req.body.limit,
2607
+ 'sort': [
2608
+ {
2609
+ 'date': {
2610
+ 'order': 'desc',
2611
+ },
2612
+ },
2613
+ ],
2614
+ };
2615
+
2616
+ if ( req.body?.logTypeFilters?.length ) {
2617
+ query.query.bool.must.push(
2618
+ {
2619
+ 'terms': {
2620
+ 'logType.keyword': req.body.logTypeFilters,
2621
+ },
2622
+ },
2623
+ );
2624
+ }
2625
+
2626
+ if ( req.body?.startDate || req.body?.endDate ) {
2627
+ query.query.bool.must.push( {
2628
+ 'range': {
2629
+ 'date': {
2630
+ 'gte': req.body?.startDate ? req.body.startDate : '',
2631
+ 'lte': req.body?.endDate ? req.body.endDate : '',
2632
+ },
2633
+ },
2634
+ } );
2635
+ }
2636
+
2637
+ query.query.bool.must.push(
2638
+ {
2639
+ 'terms': {
2640
+ 'showTo.keyword': [ req.user.userType ],
2641
+ },
2642
+ },
2643
+ );
2644
+
2645
+ const logs = await getOpenSearchData( openSearch.activityLog, query );
2646
+
2647
+ const hits = logs?.body?.hits?.hits;
2648
+ const totalDocuments = logs?.body?.hits?.total?.value;
2649
+
2650
+ let temp = [];
2651
+ if ( totalDocuments ) {
2652
+ hits.map( ( hit, i ) => {
2653
+ let respo ={};
2654
+ hit._source.logSubType = hit._source.logSubType === 'documentUpload'? 'documentsUpload': hit._source.logSubType === 'domainDetails'? 'securityFeatures': hit?._source?.logSubType;
2655
+ if ( ( ( hit?._source?.eventType ).match( /update/ ) || ( hit?._source?.eventType ).match( /edit/ ) )&& hit?._source?.previous ) {
2656
+ const previous = hit?._source?.oldData;
2657
+ const current=hit?._source?.newData;
2658
+ respo = findDifferences( previous, current );
2659
+ hit._source.updatedValue = respo;
2660
+ temp.push( hit?._source );
2661
+ // hit._source.changes =logSubType === 'ticketConfig'? Object.keys( respo ).map( ( item ) => item ) : hit._source.changes;
2662
+ } else {
2663
+ temp.push( hit?._source );
2664
+ }
2665
+ },
2666
+ );
2667
+ res.sendSuccess( { data: temp, count: totalDocuments } );
2128
2668
  } else {
2129
- result[key] = value;
2669
+ res.sendError( 'No data found', 204 );
2130
2670
  }
2671
+ } catch ( error ) {
2672
+ logger.error( { error: error, message: req.body, function: 'getActivityLogs' } );
2673
+ return res.sendError( 'Internal Server Error', 500 );
2131
2674
  }
2675
+ }
2676
+
2677
+ function findDifferences( oldData, newData ) {
2678
+ const allKeys = new Set( [ ...Object.keys( oldData ), ...Object.keys( newData ) ] );
2679
+ const diff = {};
2680
+
2681
+ allKeys.forEach( ( key ) => {
2682
+ const previous = key in oldData ? oldData[key] : null;
2683
+ const current = key in newData ? newData[key] : null;
2684
+
2685
+ // Only include fields that are added or updated (where previous !== current)
2686
+ if ( previous !== current ) {
2687
+ diff[splitCamelCase( key )] = {
2688
+ previous,
2689
+ current,
2690
+ };
2691
+ }
2692
+ } );
2132
2693
 
2133
- return result;
2694
+ return diff;
2134
2695
  }
2135
2696
 
2697
+ function splitCamelCase( text ) {
2698
+ return text.replace( /([a-z])([A-Z])/g, '$1 $2' );
2699
+ }
2136
2700
 
2137
2701
  async function postApi( url, data ) {
2138
2702
  const requestOptions = {
@@ -2201,3 +2765,34 @@ export async function clientCsmAssignAction( req, res ) {
2201
2765
  return res.sendError( 'Internal Server Error', 500 );
2202
2766
  }
2203
2767
  }
2768
+
2769
+ function compareArrayObjects( prevArray, currArray ) {
2770
+ let changes = {};
2771
+ // logger.info( { prevArray: prevArray, currArray: currArray } );
2772
+ // Find the longest array length to avoid index mismatch
2773
+ let maxLength = Math.max( prevArray.length, currArray.length );
2774
+
2775
+ for ( let i = 0; i < maxLength; i++ ) {
2776
+ let prevObj = prevArray[i] || {}; // Default to empty object if missing
2777
+ let currObj = currArray[i] || {};
2778
+ let diff = {};
2779
+
2780
+ Object.keys( { ...prevObj, ...currObj } ).forEach( ( key ) => {
2781
+ if ( prevObj[key] !== currObj[key] && key !== '_id' ) {
2782
+ const key1 = key == 'contact'? 'Contact Number': key == 'email'? 'Email ID':key == 'name'?'Name': key == 'designation'?'Designation':key;
2783
+ const name = i ==0? 'Primary Spoc': `Alternate Spoc ${i+1}`;
2784
+ diff[`${name} ${key1}`] = {
2785
+ previous: prevObj[key] || 'null',
2786
+ current: currObj[key] || 'null',
2787
+ };
2788
+ }
2789
+ } );
2790
+
2791
+ if ( Object.keys( diff ).length > 0 ) {
2792
+ // changes.push( { index: i, changes: diff } );
2793
+ changes = { ...changes, ...diff };
2794
+ }
2795
+ }
2796
+
2797
+ return changes;
2798
+ }