From 7e3b9bc2807d773b80457a6796daa3b5909eb78a Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Mon, 13 Oct 2025 11:25:45 +0530
Subject: [PATCH 1/5] @coderabbitai

---
 src/helpers/userInvite.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/helpers/userInvite.js b/src/helpers/userInvite.js
index 81e7526b2..a12bb2b5e 100644
--- a/src/helpers/userInvite.js
+++ b/src/helpers/userInvite.js
@@ -916,6 +916,8 @@ module.exports = class UserInviteHelper {
 										username: userUpdate[0].dataValues?.username,
 										status: userUpdate[0].dataValues?.status,
 										deleted: userUpdate[0].dataValues?.deleted_at ? true : false,
+										tenant_code: user.tenant_code,
+										organization_code: user.organization_code,
 										created_at: userUpdate[0].dataValues?.created_at || null,
 										updated_at: userUpdate[0].dataValues?.updated_at || new Date(),
 										oldValues,

From 7dd11b33a40b204ca9a8dfcff90c26c72ffd9b52 Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Wed, 15 Oct 2025 19:53:31 +0530
Subject: [PATCH 2/5] Event issue fixed

---
 src/helpers/userInvite.js | 68 +++++++++++++++++++++------------------
 1 file changed, 36 insertions(+), 32 deletions(-)

diff --git a/src/helpers/userInvite.js b/src/helpers/userInvite.js
index a12bb2b5e..162282356 100644
--- a/src/helpers/userInvite.js
+++ b/src/helpers/userInvite.js
@@ -841,29 +841,28 @@ module.exports = class UserInviteHelper {
 
 							let oldValues = {},
 								newValues = {},
-								userFetch = {}
+								userFetch =
+									isOrgUpdate ||
+									isRoleUpdated ||
+									modifiedKeys.length > 0 ||
+									additionalCsvHeaders.length > 0
+										? await userQueries.findAllUserWithOrganization(
+												{ id: existingUser.id },
+												{},
+												user.tenant_code
+										  )
+										: {}
+
+							userFetch = userFetch?.[0] || {}
+
 							if (isOrgUpdate || isRoleUpdated) {
 								oldValues.organizations = existingUser.organizations
-								userFetch = await userQueries.findAllUserWithOrganization(
-									{ id: existingUser.id },
-									{},
-									user.tenant_code
-								)
-
-								newValues.organizations = userFetch[0].organizations
+								newValues.organizations = userFetch.organizations
 							}
+							let userMeta = {}
 
 							if (modifiedKeys.length > 0 || additionalCsvHeaders.length > 0) {
-								if (Object.keys(userFetch).length == 0) {
-									userFetch = await userQueries.findAllUserWithOrganization(
-										{ id: existingUser.id },
-										{},
-										user.tenant_code
-									)
-								}
-
-								userFetch = userCredentials.find((user) => user.id == existingUser.id)
-								let userMeta = { ...userFetch?.meta }
+								userMeta = { ...userFetch?.meta }
 								userFetch = await utils.processDbResponse(userFetch, prunedEntities)
 								const comparisonKeys = [...modifiedKeys, ...additionalCsvHeaders]
 								comparisonKeys.forEach((modifiedKey) => {
@@ -888,24 +887,30 @@ module.exports = class UserInviteHelper {
 									}
 								})
 
-								oldValues = userFetch
-								oldValues.email = oldValues.email
-									? emailEncryption.decrypt(oldValues.email)
-									: oldValues.email
 								/*
 								user meta with entity and _id from external micro-service is passed with entity information and value of the _ids
 								to prarse it to a standard format with data for emitting the event
 								*/
 								userMeta = utils.parseMetaData(userMeta, prunedEntities, externalEntityNameIdMap)
-								oldValues = {
-									...oldValues,
-									...userMeta,
-								}
-								oldValues.phone = oldValues.phone
-									? emailEncryption.decrypt(oldValues.phone)
-									: oldValues.phone
+							}
+							oldValues =
+								isOrgUpdate ||
+								isRoleUpdated ||
+								modifiedKeys.length > 0 ||
+								additionalCsvHeaders.length > 0
+									? { ...userFetch, ...oldValues }
+									: {}
+							oldValues = {
+								...oldValues,
+								...userMeta,
 							}
 
+							oldValues.email = oldValues?.email
+								? emailEncryption.decrypt(oldValues.email)
+								: oldValues?.email
+							oldValues.phone = oldValues?.phone
+								? emailEncryption.decrypt(oldValues.phone)
+								: oldValues?.phone
 							if (Object.keys(oldValues).length > 0 || Object.keys(newValues).length > 0) {
 								const eventBody = eventBodyDTO({
 									entity: 'user',
@@ -916,8 +921,6 @@ module.exports = class UserInviteHelper {
 										username: userUpdate[0].dataValues?.username,
 										status: userUpdate[0].dataValues?.status,
 										deleted: userUpdate[0].dataValues?.deleted_at ? true : false,
-										tenant_code: user.tenant_code,
-										organization_code: user.organization_code,
 										created_at: userUpdate[0].dataValues?.created_at || null,
 										updated_at: userUpdate[0].dataValues?.updated_at || new Date(),
 										oldValues,
@@ -1136,8 +1139,9 @@ module.exports = class UserInviteHelper {
 							created_by: user.id,
 							name: parsedData?.name,
 							username: parsedData?.username,
-							email: raw_email,
+							email: raw_email ? raw_email : null,
 							phone: parsedData?.phone ? parsedData?.phone : null,
+							phone_code: parsedData?.phone_code ? parsedData?.phone_code : null,
 							organizations,
 							tenant_code: user?.tenant_code,
 							...metaData,

From b8558a19a1252e7f70f68c830fc19075200018ca Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Thu, 16 Oct 2025 11:24:20 +0530
Subject: [PATCH 3/5] code rabbit comments

---
 src/helpers/userInvite.js | 73 ++++++++++++++-------------------------
 1 file changed, 25 insertions(+), 48 deletions(-)

diff --git a/src/helpers/userInvite.js b/src/helpers/userInvite.js
index 162282356..58c04c032 100644
--- a/src/helpers/userInvite.js
+++ b/src/helpers/userInvite.js
@@ -20,11 +20,11 @@ const ProjectRootDir = path.join(__dirname, '../')
 const inviteeFileDir = ProjectRootDir + common.tempFolderForBulkUpload
 const entityTypeQueries = require('@database/queries/entityType')
 const UserCredentialQueries = require('@database/queries/userCredential')
-const { Op } = require('sequelize')
+const { Op, and } = require('sequelize')
 const emailEncryption = require('@utils/emailEncryption')
 const userOrganizationQueries = require('@database/queries/userOrganization')
 const userOrganizationRoleQueries = require('@database/queries/userOrganizationRole')
-const { eventBodyDTO, keysFilter } = require('@dtos/userDTO')
+const { eventBodyDTO, keysFilter, transformUser } = require('@dtos/userDTO')
 const { broadcastEvent } = require('@helpers/eventBroadcasterMain')
 const { generateUniqueUsername, generateUniqueCodeString } = require('@utils/usernameGenerator.js')
 const userRolesQueries = require('@database/queries/userOrganizationRole')
@@ -822,24 +822,22 @@ module.exports = class UserInviteHelper {
 							userUpdateData.meta ||
 							userUpdateData.password_update
 						) {
-							// const userCred = await UserCredentialQueries.findOne({
-							// 	email: encryptedEmail,
-							// })
-
 							const [count, userUpdate] = await userQueries.updateUser(
 								{ id: existingUser.id },
 								userUpdateData
 							)
 							updatedUserIds.push(existingUser.id)
-
+							// find the keys which are modified
 							let modifiedKeys = Object.keys(userUpdate[0].dataValues).filter((key) => {
 								const current = userUpdate[0].dataValues[key]
 								const previous = userUpdate[0]._previousDataValues[key]
 								return current !== previous && !_.isEqual(current, previous)
 							})
+							// remove critical and obvious datas from the keys to be sent in event
 							modifiedKeys = keysFilter(modifiedKeys)
 
-							let oldValues = {},
+							// format old values for event and remove transform it to match the standards
+							let oldValues = transformUser(userUpdate[0]?._previousDataValues) || {},
 								newValues = {},
 								userFetch =
 									isOrgUpdate ||
@@ -851,19 +849,16 @@ module.exports = class UserInviteHelper {
 												{},
 												user.tenant_code
 										  )
-										: {}
-
+										: {} // fetch the user with organization and roles if any of the critical fields are updated
 							userFetch = userFetch?.[0] || {}
 
+							// if org or role is updated assign the old and new values
 							if (isOrgUpdate || isRoleUpdated) {
 								oldValues.organizations = existingUser.organizations
-								newValues.organizations = userFetch.organizations
+								newValues.organizations = userFetch?.organizations
 							}
-							let userMeta = {}
-
+							// if any keys are modified in user table or additional csv headers are present prepare the new values
 							if (modifiedKeys.length > 0 || additionalCsvHeaders.length > 0) {
-								userMeta = { ...userFetch?.meta }
-								userFetch = await utils.processDbResponse(userFetch, prunedEntities)
 								const comparisonKeys = [...modifiedKeys, ...additionalCsvHeaders]
 								comparisonKeys.forEach((modifiedKey) => {
 									if (modifiedKey == 'meta') {
@@ -891,26 +886,25 @@ module.exports = class UserInviteHelper {
 								user meta with entity and _id from external micro-service is passed with entity information and value of the _ids
 								to prarse it to a standard format with data for emitting the event
 								*/
-								userMeta = utils.parseMetaData(userMeta, prunedEntities, externalEntityNameIdMap)
 							}
-							oldValues =
-								isOrgUpdate ||
-								isRoleUpdated ||
-								modifiedKeys.length > 0 ||
-								additionalCsvHeaders.length > 0
-									? { ...userFetch, ...oldValues }
-									: {}
+							// parse meta data to fetch the entity name and value instead of _ids
+							const userOldMeta = oldValues?.meta
+								? utils.parseMetaData(oldValues.meta, prunedEntities, externalEntityNameIdMap)
+								: {}
+							// delete the meta raw field
+							delete oldValues.meta
+							// merge the parsed meta data with old values
 							oldValues = {
 								...oldValues,
-								...userMeta,
+								...userOldMeta,
+							}
+							// decrypt email and phone for event
+							if (oldValues?.email) {
+								oldValues.email = emailEncryption.decrypt(oldValues.email)
+							}
+							if (oldValues?.phone) {
+								oldValues.phone = emailEncryption.decrypt(oldValues.phone)
 							}
-
-							oldValues.email = oldValues?.email
-								? emailEncryption.decrypt(oldValues.email)
-								: oldValues?.email
-							oldValues.phone = oldValues?.phone
-								? emailEncryption.decrypt(oldValues.phone)
-								: oldValues?.phone
 							if (Object.keys(oldValues).length > 0 || Object.keys(newValues).length > 0) {
 								const eventBody = eventBodyDTO({
 									entity: 'user',
@@ -931,23 +925,6 @@ module.exports = class UserInviteHelper {
 								broadcastEvent('userEvents', { requestBody: eventBody, isInternal: true })
 							}
 
-							// Update UserCredential with organization_id and potentially password
-							// const credentialUpdateData = { organization_id: user.organization_id }
-
-							// // Add password to update data if provided
-							// if (invitee.password) {
-							// 	// Assuming you have a password hashing utility
-							// 	// You might need to adjust this based on your password handling
-							// 	credentialUpdateData.password = invitee.password // Consider hashing
-							// }
-
-							// await UserCredentialQueries.updateUser(
-							// 	{
-							// 		email: encryptedEmail,
-							// 	},
-							// 	credentialUpdateData
-							// )
-
 							const currentRoles = existingUser.roles.map((role) => role.title)
 							let newRoles = []
 							if (userUpdateData?.roles) {

From e60da9c51e8997c4c52281908759db9c54072cad Mon Sep 17 00:00:00 2001
From: adithya_dinesh <96181253+adithyadinesh0412@users.noreply.github.com>
Date: Thu, 16 Oct 2025 16:00:26 +0530
Subject: [PATCH 4/5] Remove unused 'and' import from sequelize

---
 src/helpers/userInvite.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/helpers/userInvite.js b/src/helpers/userInvite.js
index 58c04c032..8718e0e3c 100644
--- a/src/helpers/userInvite.js
+++ b/src/helpers/userInvite.js
@@ -20,7 +20,7 @@ const ProjectRootDir = path.join(__dirname, '../')
 const inviteeFileDir = ProjectRootDir + common.tempFolderForBulkUpload
 const entityTypeQueries = require('@database/queries/entityType')
 const UserCredentialQueries = require('@database/queries/userCredential')
-const { Op, and } = require('sequelize')
+const { Op } = require('sequelize')
 const emailEncryption = require('@utils/emailEncryption')
 const userOrganizationQueries = require('@database/queries/userOrganization')
 const userOrganizationRoleQueries = require('@database/queries/userOrganizationRole')

From 683f04fd259c43df7887f02c24c6bf24234a9bfb Mon Sep 17 00:00:00 2001
From: adithya_dinesh <adithya@shikshalokam.org>
Date: Thu, 16 Oct 2025 16:55:11 +0530
Subject: [PATCH 5/5] Org Migration error message fix

---
 src/helpers/userInvite.js | 39 +++++++++++++++++----------------------
 1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/src/helpers/userInvite.js b/src/helpers/userInvite.js
index 81e7526b2..4699ab577 100644
--- a/src/helpers/userInvite.js
+++ b/src/helpers/userInvite.js
@@ -39,7 +39,15 @@ let externalEntityNameIdMap = {}
 let emailAndPhoneMissing = false
 let updatedUserIds = []
 let loginUrl = ''
-
+const outPutFileKeys = ['name', 'email', 'phone_code', 'phone', 'username', 'password', 'roles', 'statusOrUserId'] // final output file headers
+const filterOutput = (input) => {
+	return outPutFileKeys.reduce((acc, key) => {
+		if (input.hasOwnProperty(key)) {
+			acc[key] = input[key]
+		}
+		return acc
+	}, {})
+}
 module.exports = class UserInviteHelper {
 	static async uploadInvites(data) {
 		return new Promise(async (resolve, reject) => {
@@ -559,8 +567,9 @@ module.exports = class UserInviteHelper {
 				if (invitee.phone && !invitee.phone_code) {
 					invalidFields.push('phone_code')
 				}
-				// check if the phone is duplicate
+
 				if (invitee.phone && invitee.phone_code) {
+					// check if the phone is duplicate
 					if (duplicateChecker.includes(`${invitee.phone_code}${invitee.phone}`)) {
 						duplicateValues.push('phone')
 					}
@@ -644,7 +653,7 @@ module.exports = class UserInviteHelper {
 					invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
 					delete invitee.meta
 					invitee.statusOrUserId = errorMessageArray.join('. ')
-					input.push(invitee)
+					input.push(filterOutput(invitee))
 					continue
 				}
 
@@ -673,7 +682,7 @@ module.exports = class UserInviteHelper {
 						: 'User already exist or invited'
 					invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
 					delete invitee.meta
-					input.push(invitee)
+					input.push(filterOutput(invitee))
 					continue
 				}
 
@@ -703,6 +712,7 @@ module.exports = class UserInviteHelper {
 							)
 							if (currentOrgs.length <= 0) {
 								invitee.statusOrUserId = `User not found in tenant : ${user.tenant_code} `
+								input.push(filterOutput(invitee))
 								continue
 							}
 							await userOrganizationQueries.changeUserOrganization({
@@ -926,23 +936,6 @@ module.exports = class UserInviteHelper {
 								broadcastEvent('userEvents', { requestBody: eventBody, isInternal: true })
 							}
 
-							// Update UserCredential with organization_id and potentially password
-							// const credentialUpdateData = { organization_id: user.organization_id }
-
-							// // Add password to update data if provided
-							// if (invitee.password) {
-							// 	// Assuming you have a password hashing utility
-							// 	// You might need to adjust this based on your password handling
-							// 	credentialUpdateData.password = invitee.password // Consider hashing
-							// }
-
-							// await UserCredentialQueries.updateUser(
-							// 	{
-							// 		email: encryptedEmail,
-							// 	},
-							// 	credentialUpdateData
-							// )
-
 							const currentRoles = existingUser.roles.map((role) => role.title)
 							let newRoles = []
 							if (userUpdateData?.roles) {
@@ -993,6 +986,8 @@ module.exports = class UserInviteHelper {
 					} else {
 						//user doesn't have access to update user data
 						invitee.statusOrUserId = 'Unauthorised to bulk upload user from another organisation'
+						invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
+						input.push(filterOutput(invitee))
 						continue
 					}
 				}
@@ -1313,7 +1308,7 @@ module.exports = class UserInviteHelper {
 					invitee.statusOrUserId = `${invitee.statusOrUserId} , Login URL : ${loginUrl}`
 				}
 				delete invitee.meta
-				input.push(invitee)
+				input.push(filterOutput(invitee))
 			}
 			if (updatedUserIds.length > 0) {
 				// flush all user active sessions after update
